Kubernetes · Capítulo 1
Fundamentos de Kubernetes
Contenedores, orquestación, la arquitectura de un clúster y cómo Kubernetes habla con los runtimes a través de CRI y OCI.
Antes de hablar de Pods, Deployments o Services hay que entender dos ideas: qué es un contenedor y por qué necesitamos un orquestador para manejar muchos de ellos. Esta nota recorre esos fundamentos y termina en una pieza que suele confundir: cómo Kubernetes se comunica con Docker, containerd y otros runtimes.
Es la primera nota de mi preparación para la certificación KCNA (Kubernetes and Cloud Native Associate) de la CNCF.
El problema que resuelven los contenedores
Una aplicación moderna rara vez es un solo programa. Puede tener un servidor web, una base de datos, un sistema de mensajería y un motor de automatización, y cada uno necesita sus propias librerías y versiones. Cuando además debe funcionar en el portátil del desarrollador, en pruebas y en producción, aparece lo que se conoce como la "matriz del infierno": una tabla enorme de combinaciones entre componentes, librerías y sistemas operativos donde cualquier actualización puede romper algo.
Los contenedores resuelven esto empaquetando cada componente con todas sus dependencias en un entorno aislado. Lo que funciona en la máquina del desarrollador funciona igual en producción, porque viaja exactamente el mismo paquete.
Contenedores frente a máquinas virtuales
Un sistema operativo tiene dos capas: el kernel, que habla con el hardware, y el software encima (librerías, herramientas, gestor de paquetes) que distingue a Ubuntu de Fedora o Red Hat.
- Una máquina virtual virtualiza el hardware: cada VM lleva su propio sistema operativo completo, kernel incluido. Aísla mucho, pero pesa gigabytes y tarda minutos en arrancar.
- Un contenedor no virtualiza el sistema operativo: comparte el kernel del host y solo empaqueta la aplicación y sus librerías. Pesa megabytes y arranca en segundos.
Como todas las distribuciones Linux usan el mismo kernel, en un host Ubuntu puedo ejecutar un contenedor basado en Fedora o Red Hat: el contenedor trae su propio software de usuario y usa el kernel del host.
Ojo con Windows: un contenedor de Windows necesita un kernel de Windows. No puede correr directamente sobre un host Linux, porque no comparten kernel.
Docker no inventó el aislamiento: se apoya en funciones del kernel de Linux (namespaces y cgroups) que ya usaban tecnologías como LXC. Su aporte fue hacer que empaquetar, distribuir y ejecutar contenedores fuera sencillo.
Imágenes y contenedores
Dos conceptos que conviene no mezclar:
- Una imagen es la plantilla: un paquete de solo lectura con la aplicación, sus dependencias y las instrucciones para arrancarla.
- Un contenedor es una instancia en ejecución de esa imagen. De una misma imagen puedes levantar muchos contenedores.
Las imágenes se guardan en un registro. El más conocido es Docker Hub, donde hay imágenes oficiales de bases de datos, servidores web y lenguajes listas para usar.
Primeros comandos con Docker
En Ubuntu o Debian, Docker se instala desde los repositorios y basta un comando para probarlo:
# Instalar Docker (Ubuntu / Debian)
sudo apt-get update
sudo apt-get install -y docker.io
# Comprobar que funciona
sudo docker run hello-world
# Ejecutar un comando dentro de un contenedor Alpine y borrarlo al terminar
sudo docker run --rm alpine echo "hola Tenci.io"
# Levantar servicios a partir de imágenes oficiales
sudo docker run -d --name mi-base mongo
sudo docker run -d --name mi-web -p 8080:80 nginx
docker run descarga la imagen si no está en el equipo y crea un contenedor a partir de ella.
Orquestación de contenedores
Ejecutar un contenedor es fácil. Ejecutar cientos, repartidos en varios servidores, no lo es: ¿qué pasa si un servidor se cae?, ¿cómo escalo cuando sube el tráfico?, ¿cómo se encuentran los contenedores entre sí?
La orquestación es la automatización de todo eso sobre un grupo de máquinas (nodos):
- Alta disponibilidad: si un contenedor o un nodo falla, se vuelve a crear en otro lado.
- Escalado: añadir o quitar copias según la demanda.
- Balanceo de carga entre las copias de una aplicación.
- Configuración declarativa: describes en archivos YAML cómo quieres que esté el sistema y el orquestador se encarga de llegar a ese estado y mantenerlo.
Existen varias opciones (Docker Swarm fue una de las más simples), pero Kubernetes se convirtió en el estándar de facto. Su instalación es más exigente, a cambio de muchas más capacidades y del ecosistema más grande del mundo cloud native.
Arquitectura de Kubernetes
Un clúster de Kubernetes es un conjunto de nodos con dos papeles distintos:
- El plano de control (antes llamado master): toma las decisiones del clúster. En producción se replica en varios nodos para no tener un punto único de fallo.
- Los nodos de trabajo (worker nodes, antiguamente "minions"): ejecutan los contenedores de las aplicaciones.
Componentes principales
| Componente | Dónde vive | Qué hace |
|---|---|---|
| kube-apiserver | Plano de control | La puerta de entrada al clúster. Usuarios, herramientas y el resto de componentes hablan siempre con él. |
| etcd | Plano de control | Base de datos clave-valor distribuida donde se guarda todo el estado del clúster. Mantiene la consistencia entre varios nodos del plano de control. |
| kube-scheduler | Plano de control | Decide en qué nodo se ejecuta cada nuevo contenedor (Pod). Asigna, pero no ejecuta. |
| kube-controller-manager | Plano de control | Los controladores vigilan el estado real y actúan cuando no coincide con el deseado: si un nodo o un contenedor se cae, reaccionan. Son el "cerebro" de la orquestación. |
| kubelet | Cada nodo | Agente que recibe instrucciones del plano de control y se asegura de que los contenedores de su nodo estén corriendo como se espera. |
| Container runtime | Cada nodo | El software que realmente ejecuta los contenedores: containerd, CRI-O, etc. |
Un componente más que verás en cada nodo es kube-proxy, que mantiene las reglas de red para que los servicios sean accesibles.
kubectl: la línea de comandos de Kubernetes
kubectl es la herramienta para gestionar el clúster: crear recursos, consultarlos y ver su
estado. Todo lo que hace pasa por el kube-apiserver.
# Información del clúster y de sus nodos
kubectl cluster-info
kubectl get nodes
# Crear un Pod a partir de una imagen
kubectl run hola-nginx --image=nginx
# Ver los Pods en ejecución
kubectl get pods
Cómo habla Kubernetes con los runtimes: CRI y OCI
Al principio Kubernetes solo sabía trabajar con Docker, con código específico para él. Cuando aparecieron otros runtimes, Kubernetes definió una interfaz común:
- CRI (Container Runtime Interface): el contrato, basado en gRPC, que usa el kubelet para hablar con cualquier runtime. Si un runtime implementa CRI, Kubernetes puede usarlo: containerd, CRI-O y otros.
- OCI (Open Container Initiative): el estándar sobre los contenedores en sí. Tiene dos partes:
- image-spec: cómo debe construirse una imagen.
- runtime-spec: cómo debe comportarse un runtime al ejecutarla.
El fin de dockershim
Docker es anterior a CRI y nunca lo implementó. Para no dejar de soportarlo, Kubernetes mantuvo una capa de traducción llamada dockershim. Era una solución temporal y se eliminó por completo en Kubernetes 1.24.
¿Significa que las imágenes de Docker ya no sirven? No. Las imágenes que construye Docker cumplen el estándar OCI, así que cualquier runtime compatible (como containerd) las ejecuta sin cambios. Lo que desapareció fue usar el motor de Docker como runtime del clúster.
Docker frente a containerd
Docker no es solo un runtime: es un conjunto de herramientas (CLI, API, construcción de imágenes, volúmenes, autenticación, seguridad). Por debajo, Docker usa containerd para ejecutar los contenedores.
containerd es un proyecto independiente y graduado en la CNCF. Se puede instalar solo, sin Docker, como runtime de Kubernetes: es lo habitual hoy cuando no hace falta el resto de herramientas de Docker.
Herramientas de línea de comandos para runtimes
Sin Docker en el nodo, hay tres herramientas que conviene distinguir:
| Herramienta | Viene de | Para qué sirve | ¿Uso diario? |
|---|---|---|---|
| ctr | containerd | Depurar containerd. Poco amigable y con pocas funciones. | No |
| nerdctl | Proyecto de containerd | CLI con la misma sintaxis que Docker para containerd. | Sí, como sustituto de Docker |
| crictl | Kubernetes | Inspeccionar y depurar cualquier runtime compatible con CRI. | Para diagnóstico en nodos |
ctr
Viene incluido con containerd. Sirve para pruebas y depuración, no para administrar entornos:
ctr images pull docker.io/library/redis:alpine
ctr run docker.io/library/redis:alpine redis
nerdctl
Ofrece una experiencia casi idéntica a Docker sobre containerd, soporta Docker Compose y añade funciones que Docker no tiene, como la descarga perezosa de imágenes (lazy pulling), imágenes cifradas, distribución P2P y firma y verificación de imágenes:
nerdctl run --name redis redis:alpine
nerdctl run --name web -p 8080:80 -d nginx:latest
crictl
Lo mantiene la comunidad de Kubernetes y funciona con cualquier runtime CRI. Se instala por separado y su sintaxis recuerda a Docker. Está pensado para inspeccionar y depurar, no para crear contenedores: si creas algo con crictl fuera del control del kubelet, el kubelet puede borrarlo.
crictl pull busybox
crictl images
crictl pods
crictl ps -a
crictl logs <id-del-contenedor>
crictl exec -it <id-del-contenedor> ls
Una diferencia con Docker: crictl entiende de Pods (crictl pods), porque habla el mismo
idioma que el kubelet.
Si en el nodo hay varios runtimes, se indica con cuál trabajar:
crictl --runtime-endpoint unix:///run/containerd/containerd.sock ps
# o de forma permanente para la sesión
export CONTAINER_RUNTIME_ENDPOINT=unix:///run/containerd/containerd.sock
Ideas clave
- Un contenedor comparte el kernel del host; una VM lleva su propio sistema operativo.
- Una imagen es la plantilla; un contenedor, una instancia en ejecución.
- La orquestación automatiza disponibilidad, escalado, balanceo y configuración declarativa. Kubernetes es el estándar.
- Un clúster tiene plano de control (kube-apiserver, etcd, kube-scheduler, kube-controller-manager) y nodos de trabajo (kubelet, runtime, kube-proxy).
- El kubelet habla con los runtimes mediante CRI; las imágenes siguen el estándar OCI.
- dockershim se eliminó en Kubernetes 1.24; las imágenes de Docker siguen funcionando.
- ctr para depurar containerd, nerdctl como reemplazo de Docker, crictl para depurar cualquier runtime CRI.
En la próxima nota: los recursos básicos de Kubernetes, empezando por los Pods.