Kubernetes logo

Después del curso de seguridad he vuelto un poco al mundo devops y sistemas y he comenzado uno de kubernetes, mas lo que estoy intentando aprender a través de la documentación oficial, que a priori resulta bastante buena.

Mi idea es montar un cluster en casa con unas cuentas cositas y de paso ir documentándolas en el blog, que siempre me viene muy bien para volver a recordar cómo se hace algo.

Así que voy a empezar por algo sencillo, para ir poniendo en práctica lo que he ido aprendiendo en el curso. Como siempre cualquier corrección o consejo es bienvenido, ya sea a través de comentario o redes sociales.

Lo primero que quiero hacer es crear un namespace para el tutorial, en él se encontrarán todos los elementos de kubernetes que voy a ir creando, así a la hora de eliminarlo todo nos bastará con eliminar el namespace.

Creación de un namespace

kubectl create namespace tutorial

Con este comando creamos un nuevo namespace donde podremos trabajar tranquilamente sin liarlo con otras cosas que tengamos en el cluster, como el cluster en si mismo.

Definimos el nuevo namespace como el actual

Para no tener que ir diciendo en todo momento en que namespace queremos realizar las acciones voy a definir el namespace tutorial como el actual, así todas las acciones que ejecute se harán dentro de él.

kubectl config set-context $(kubectl config current-context) --namespace=tutorial

Lo que hace es definir el contexto actual con el mismo valor que tenemos ahora mismo, esto es el cluster en el que vamos a realizar las acciones y el namespace que acabamos de crear.

¿Qué es un Pod?

Por definirlo con mis propias palabras, con lo que he aprendido (y si me equivoco corregidme): Es una unidad mínima escalable dentro de kubernetes en la cual pueden existir uno o mas contenedores.

Imaginemos que tenemos un Wordpress que queremos meter en un cluster, pues podríamos crear dos pods, uno con la base de datos, como es el caso y otra con el código como tal. Así a la hora de tener que escalar podemos escalar solo una de ellas. A parte quizás queramos meter algún contenedor de monitorización a la bbdd y al codigo, así que podríamos meterlo dentro de cada pod, para que escalasen juntos.

Creando una configuración MUY BÁSICA para MariaDB

A la hora de crear un pod o cualquier otro elemento de kubernetes lo podemos hacer a través de generadores (comandos) o de ficheros. Podemos ver los dos casos con el ejemplo siguiente:

kubectl run tutorial-db --image=mariadb:10.3.8 --restart=Never --port=3306 --dry-run -o yaml > tutorial_db.yaml

El comando anterior es un generador que crea un fichero. Los parámetros --dry-run y -o yaml hacen, primero que el generador no se ejecute contra el cluster, devuelva la salida y que lo haga en formato yaml.

Por otro lado estamos redirigiendo la salida a un fichero con > tutorial_db.yaml .

Si lo ejecutásemos sin los parámetros anteriores se lanzaría contra el cluster, creando el pod.

Destripando el generador

kubectl run tutorial-db --image=mariadb:10.3.8 --restart=Never --port=3306 --dry-run -o yaml > tutorial_db.yaml

Por partes:

  • run : Es la orden del generador
  • tutorial-db : Es el nombre que recibirá el pod.
  • --image=mariadb:10.3.8 : Es la imagen que queremos que ejecute el pod.
  • --restart=Never : Esto es lo que realmente hace que sea un pod. Como podemos ver en ningún lado aparece la palabra pod. Kubectl sabe que queremos un pod porque con este parámetro le decimos que si muere el pod no levante otro. Con otro valor crearía un Deploy.
  • --port=3306 : El puerto que queremos que exponga el pod.

El resto de parámetros ya los he explicado mas arriba.

El fichero de salida

Este es el fichero de salida que nos ha generado el comando:

apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: tutorial-db
name: tutorial-db
spec:
containers:
- image: mariadb:10.3.8
name: tutorial-db
ports:
- containerPort: 3306
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Never
status: {}

En él podemos ver que es de tipo Pod. El metadata podemos modificarlo a nuestro antojo y meter mas etiquetas, aquí podemos ver el nombre que le hemos dado al pod.

En specs vemos los contenedores que tiene dentro, en este caso uno, y las imágenes que corren y los puertos.

Podemos ejecutar el pod con el siguiente comando

kubectl create -f tutorial_db.yaml

Podemos ver los pods con:

kubectl get pods

Si habéis seguido hasta aquí tendréis un bonito pod roto. ¿Por qué?

docker image history

Depurando el error del pod

Cuando tenemos un pod lo primero que podemos hacer es describirlo, esto nos devolverá información sobre la ejecución, reinicios y mas cosas. Para ver esta información debemos ejecutar el siguiente comando:

kubectl describe pod tutorial-db

Esta es la salida que me devuelve ahora mismo. No me devuelve el motivo del error.

Name:               tutorial-db
Namespace:          tutorial
Priority:           0
PriorityClassName:  <none>
Node:               debian/192.168.1.61
Start Time:         Fri, 28 Jun 2019 23:10:47 +0200
Labels:             run=tutorial-db
Annotations:        cni.projectcalico.org/podIP: 192.168.245.202/32
Status:             Failed
IP:                 192.168.245.202
Containers:
tutorial-db:
 Container ID:   docker://d69c0a3f834fca644f44825a99e3de4f6f1f8ff4fa0dc13943aaaa7089f3797e
 Image:          mariadb:10.3.8
 Image ID:       docker-pullable://mariadb@sha256:edef80de393cf4a79504168c663f8b0c6b15060333e5a7d7aee3dc0a4de6e927
 Port:           3306/TCP
 Host Port:      0/TCP
 State:          Terminated
   Reason:       Error
   Exit Code:    1
   Started:      Fri, 28 Jun 2019 23:10:51 +0200
   Finished:     Fri, 28 Jun 2019 23:10:52 +0200
 Ready:          False
 Restart Count:  0
 Environment:    <none>
 Mounts:
   /var/run/secrets/kubernetes.io/serviceaccount from default-token-5t7vz (ro)
Conditions:
Type              Status
Initialized       True
Ready             False
ContainersReady   False
PodScheduled      True
Volumes:
default-token-5t7vz:
 Type:        Secret (a volume populated by a Secret)
 SecretName:  default-token-5t7vz
 Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
              node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type    Reason     Age        From               Message
----    ------     ----       ----               -------
Normal  Scheduled  <invalid>  default-scheduler  Successfully assigned tutorial/tutorial-db to debian
Normal  Pulled     <invalid>  kubelet, debian    Container image "mariadb:10.3.8" already present on machine
Normal  Created    <invalid>  kubelet, debian    Created container tutorial-db
Normal  Started    <invalid>  kubelet, debian    Started container tutorial-db

Como esto en principio no nos dice el motivo del error, o yo no se cómo verlo, lo mejor será sacar el log del pod de la siguiente forma:

kubectl logs tutorial-db

Lo cual nos devuelve lo siguiente:

error: database is uninitialized and password option is not specified
You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD

Pues ya sabemos cual es el problema, no le estamos pasando las variables de entorno necesarias para la creación del contenedor, estas son las mismas que necesitaría el contenedor de docker para ejecutarse en nuestro local.

Seteando las variables de entorno de aquella manera

SPOILER Esta no es la manera de hacerlo, pero como quiero ir paso a paso vamos a empezar por lo mas básico.

Lo que vamos a hacer es crear las variables de entorno directamente en el fichero de creación del pod. Para ello lo editaremos y lo dejaremos como el que tenéis a continuación:

  apiVersion: v1
  kind: Pod
  metadata:
    creationTimestamp: null
    labels:
      run: tutorial-db
    name: tutorial-db
  spec:
    containers:
    - image: mariadb:10.3.8
      name: tutorial-db
      env:
        - name: MYSQL_ROOT_PASSWORD
          value: root_password
        - name: MYSQL_DATABASE
          value: tutorial
        - name:  MYSQL_USER
          value: usuario
        - name: MYSQL_PASSWORD
          value: password
      ports:
      - containerPort: 3306
      resources: {}
    dnsPolicy: ClusterFirst
    restartPolicy: Never
  status: {}

Como podéis ver lo que hacemos es setear env en el fichero con los nombres de las variables y sus valores. La forma correcta de hacerlo es con secrets pero ya llegaré a ello.

Eliminar el pod con errores

Para eliminar el pod es tan sencillo como ejecutar:

kubectl delete pod tutorial-db

Ahora ya podemos lanzar el nuevo fichero con las variables de entorno ya modificadas.

Conclusión

Por ahora tenemos un pod con una base de datos dentro de un namespace dentro de un cluster, pero no es accesible ni se comunica con nada. Esto es lo que he hecho en mi primer día trasteando con kubernetes, a parte de romper varias veces el cluster. El error es real y he querido reproducir todos los pasos que voy siguiendo, errores incluidos. La idea es ir continuando todo hasta montar algo mas o menos útil.

Para cualquier corrección puedes dejarme un comentario abajo, un mensaje en GnuSocial o en Twitter.