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 generadortutorial-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é?
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.