Ejecutando KIND dentro de un clúster de Kubernetes para integración continua.

Kevin Blanco 🥑
Kubernetes Costa Rica
4 min readMay 25, 2020

Este pequeño post explica sobre cómo configurar KIND en un cluster de K8s, sobrepasando algunos retos que se dan al hacerlo, esto se los comparto en base a mi sufrimiento y así, tal vez ustedes no tengan que “sufrir” como yo.

KIND es una gran herramienta para hacer pruebas de integración en Kubernetes. KIND puede desplegar en un clúster de Kubernetes utilizando contenedores Docker (como nodos) en menos de un minuto, mejorando drásticamente la experiencia del desarrollador al realizar pruebas de integración en Kubernetes.

Habilitar el volumen de pruebas necesarias para crear software de nivel empresarial requiere docenas de clústeres que son baratos, compatibles, fáciles de implementar, desplegar, matar, reciclar y monitorear cientos de veces al día.

Configurando un Docker Daemon en un Pod

KIND actualmente depende de Docker (aunque tienen planes para soportar otros tiempos de ejecución de contenedores como podman pronto). Por lo tanto, el primer paso es crear una imagen de contenedor que le permita ejecutar Docker daemon dentro de un Pod, de modo que comandos como ‘docker run’ funcionen dentro del Pod (también conocido como Docker-in-Docker o DIND).

Docker-in-Docker es un problema bien conocido y se ha resuelto durante bastante tiempo. A pesar de eso, todavía se conocen bastantes problemas al intentar configurar Docker-in-Docker correctamente en un clúster de Kubernetes. Si usted tuviese problemas para usar DinD (Docker-in-Docker) le recomiendo investigar sobre los problemas en relación al MTU.

Ejecutando KIND en un Pod

Una vez que configuramos con éxito Docker-in-Docker (DinD), el siguiente paso es iniciar el clúster KIND en ese contenedor. ¡Probé este comando en mi computadora y funcionó perfectamente!

$ docker run -ti --rm --privileged kevinblanco/dind-buster:v0.1.8 /bin/bash
Waiting for dockerd...
[root@257b543a91a5 /]# curl -Lso ./kind https://kind.sigs.k8s.io/dl/v0.7.0/kind-$(uname)-amd64[root@257b543a91a5 /]# chmod +x ./kind[root@257b543a91a5 /]# mv ./kind /usr/bin/[root@257b543a91a5 /]# kind create clusterCreating cluster "kind" ...✓ Ensuring node image (kindest/node:v1.17.0) 🖼✓ Preparing nodes 📦✓ Writing configuration 📜✓ Starting control-plane 🕹️✓ Installing CNI 🔌✓ Installing StorageClass 💾Set kubectl context to "kind-kind"You can now use your cluster with:kubectl cluster-info --context kind-kindHave a nice day! 👋[root@257b543a91a5 /]# kubectl get nodesNAME STATUS ROLES AGE VERSIONkind-control-plane Ready master 11m v1.17.0

Sin embargo, sí intento ejecutar esto en el CI (en el clúster de Kubernetes de Google Cloud), las cosas comienzan a fallar.

$ kubectl apply -f dind.yaml$ kubectl exec -ti dind /bin/bashroot@dind:/# curl -Lso ./kind https://kind.sigs.k8s.io/dl/v0.7.0/kind-$(uname)-amd64root@dind:/# chmod +x ./kindroot@dind:/# mv ./kind /usr/bin/root@dind:/# kind create clusterCreating cluster "kind" ...✓ Ensuring node image (kindest/node:v1.17.0) 🖼✓ Preparing nodes 📦✓ Writing configuration 📜✗ Starting control-plane 🕹️ERROR: failed to create cluster: failed to init node with kubeadm: command "docker exec --privileged kind-control-plane kubeadm init --ignore-preflight-errors=all --config=/kind/kubeadm.conf --skip-token-print --v=6" failed with error: exit status 137

La razón por la que esto sucede es porque el kubeletque se ejecuta dentro del contenedor del nodo KIND (anidado) esta matando aleatoriamente los procesos en el contenedor de nivel superior, esto por la configuración de los cgroup pero no voy a entrar en detalle con la explicación de bajo nivel, porque se extiende mucho esta publicación. La solución a este problema es instruir al kubelet dentro del contenedor del nodo KIND para que use una raíz cgroup diferente (por ejemplo, / kubelet) para sus pods configurando el flag de kubectl --cgroup root (https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/)

Después de eso, el despliegue de un clúster KIND va a funcionar correctamente, y podemos usar este YAML como ejemplo: https://gist.github.com/kevinblanco/fecf4d460af6a824fb27a81dd794c581

Una vez que el pod esté listo (tomará un minuto), se puede exec dentro del pod para verificar.

$ kubectl exec -ti kind-cluster /bin/bash
root@kind-cluster:/# kubectl get nodes
NAME STATUS ROLES AGE VERSION
kind-control-plane Ready master 72s v1.17.0

También puede jugar con el contenedor directamente usando Docker CLI:

$ docker run -ti --rm --privileged kevinblanco/kind-cluster-buster:v0.1.0 /bin/bashWaiting for dockerd...Setting up KIND clusterCreating cluster "kind" ...✓ Ensuring node image (kevinblanco/kind-node:v1.17.0) 🖼✓ Preparing nodes 📦✓ Writing configuration 📜✓ Starting control-plane 🕹️✓ Installing CNI 🔌✓ Installing StorageClass 💾✓ Waiting ≤ 15m0s for control-plane = Ready ⏳• Ready after 31s 💚Set kubectl context to "kind-kind"You can now use your cluster with:kubectl cluster-info --context kind-kindHave a nice day! 👋root@d95fa1302557:/# kubectl get nodesNAME                 STATUS   ROLES    AGE   VERSIONkind-control-plane   Ready    master   71s   v1.17.0root@d95fa1302557:/#

Resumen

Como podemos ver, se pueden dar algunos obstáculos durante este proceso, pero al resolverlas y saber al respecto podemos configurar KIND fácilmente.

El mayor aprendizaje de este problema es el siguiente:

Un contenedor Docker no proporciona un aislamiento completo del host. Hay ciertos recursos del kernel como cgroups (y muchos más) que se comparten dentro del kernel, lo que causa conflictos potenciales si muchos contenedores los manipulan simultáneamente.

Una vez que identificamos y superamos esos obstáculos, nuestra solución funcionó bastante bien. Pero esto es solo un comienzo, una vez corriendo KIND en el cluster debemos configurar las operaciones de control de cluster k8s, pero eso se los cuento en un siguiente post.

--

--

Kevin Blanco 🥑
Kubernetes Costa Rica

Senior DevRel Advocate 🥑 at Appsmith, Certified Google Expert Advocate, Private Pilot