0%
Introducción a Kubernetes

Introducción

Por qué orquestar, y lo que Docker Compose no sabe hacer.

15-20 min

Introducción a Kubernetes

Sabes construir una imagen y lanzar un contenedor. En tu equipo, docker compose up arranca una API, una base de datos y una caché en unos segundos, y todo funciona. Entonces llega la pregunta que lo cambia todo: ¿qué ocurre cuando esta aplicación tiene que funcionar de forma continua, en varias máquinas, sin que nadie se levante de madrugada para reiniciarla?

Compose describe un estado deseado, pero nada lo mantiene a lo largo del tiempo. Si el proceso falla, restart: always lo relanza. Si la máquina cae, nada vuelve a lanzar nada. Si el tráfico se triplica, arrancas réplicas a mano, sus direcciones cambian y hay que reconfigurar el reverse proxy. Si despliegas una nueva versión, hay un corte entre la parada de la antigua y el arranque de la nueva. Cada uno de estos problemas tiene una respuesta manual evidente. Lo insostenible es su acumulación.

La respuesta de Kubernetes no es «más comandos». Es una inversión de responsabilidad: tú describes el estado que quieres, de forma permanente, y un conjunto de bucles de control trabaja de manera continua para que la realidad se corresponda con esa descripción. Es el único concepto realmente estructurante del sistema: todo lo demás —Pods, Deployments, Services, volúmenes— no es más que una declinación de esta idea sobre un objeto concreto.

Esta lección presenta ese modelo y después describe la arquitectura de un clúster. No por afición a la nomenclatura: saber qué componente decide qué es lo que te permitirá diagnosticar. Un Pod bloqueado porque el planificador no encuentra ningún nodo disponible y un Pod bloqueado porque el kubelet no consigue descargar la imagen son dos averías que no tienen ni la misma causa ni el mismo lugar donde mirar.

Lo imperativo frente a lo declarativo

Con Docker, das órdenes. Cada comando describe una acción que se ejecuta ahora:

# Imperativo : je décris les actions, une par une, dans l'ordre.
docker build -t mon-api:1.2.0 .
docker run -d --name api-1 -p 8080:8080 mon-api:1.2.0
docker run -d --name api-2 -p 8081:8080 mon-api:1.2.0
docker stop api-1        # pour mettre à jour : coupure assumée
docker rm api-1
docker run -d --name api-1 -p 8080:8080 mon-api:1.3.0

Este script funciona una vez. No dice nada de lo que debe seguir siendo cierto. Nadie sabe, al leerlo, si debe haber dos réplicas o tres, ni qué versión se supone que está en ejecución.

Con Kubernetes, describes un objetivo. El fichero siguiente no dice «arranca tres contenedores», dice «debe haber tres réplicas de esta versión, de forma permanente»:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mon-api
spec:
  replicas: 3                    # état voulu, pas une action ponctuelle
  selector:
    matchLabels:
      app: mon-api
  template:
    metadata:
      labels:
        app: mon-api
    spec:
      containers:
        - name: api
          image: mon-api:1.3.0
          ports:
            - containerPort: 8080

La diferencia práctica es inmediata. Mata uno de los tres contenedores: unos segundos después nace un cuarto, porque la cuenta ya no se corresponde con el objetivo. Apaga un nodo: las réplicas que alojaba se recrean en otro sitio. Nadie ha ordenado nada, un controlador simplemente ha constatado una diferencia.

El bucle de reconciliación

Es el mecanismo central, y es sencillo:

  1. Un controlador lee el estado deseado (lo que has declarado).
  2. Observa el estado real del clúster.
  3. Si ambos difieren, actúa para reducir la diferencia.
  4. Vuelve a empezar. Indefinidamente.

Hay un controlador por tipo de responsabilidad, y son deliberadamente miopes: el que gestiona los ReplicaSets no sabe nada de la red, el que gestiona los Services no sabe nada de los volúmenes. Cada uno vigila su propia diferencia. La orquestación global emerge de estos bucles independientes; no la dirige un único director de orquesta.

Esta arquitectura tiene una consecuencia que conviene aceptar pronto: Kubernetes es asíncrono. Cuando kubectl apply te responde, no ha desplegado nada, solo ha registrado tu intención. El trabajo real se hace después, y puede fallar después. Por eso nunca se verifica un despliegue por lo que devuelve apply, sino siempre por el estado observado:

# apply enregistre l'intention : le retour ne prouve rien sur l'exécution.
kubectl apply -f deployment.yaml

# La vérification se fait sur l'état réel, ensuite.
kubectl get deployment mon-api
kubectl get pods -l app=mon-api

# Et si un Pod ne démarre pas, la raison est dans les événements de fin de description.
kubectl describe pod mon-api-7d4b8c9f5-xk2mp

La arquitectura de un clúster

Un clúster se divide en dos roles: el plano de control (control plane), que decide, y los nodos de trabajo (worker nodes), que ejecutan.

El plano de control

ComponenteFunciónQué implica si falla
kube-apiserverÚnica puerta de entrada. Valida y persiste toda petición.Ya no es posible ninguna modificación; las cargas ya lanzadas siguen funcionando.
etcdBase de datos clave-valor que almacena el estado deseado y el estado observado.Perder etcd sin copia de seguridad = perder el clúster lógico.
kube-schedulerElige en qué nodo colocar cada nuevo Pod.Los nuevos Pods se quedan en Pending.
kube-controller-managerEjecuta los bucles de reconciliación.Se acaba la autorreparación: los Pods muertos no se reemplazan.

El punto destacable: kubectl solo habla con el API server. No abre ninguna conexión hacia un nodo ni controla ningún contenedor. Toda la seguridad y toda la trazabilidad pasan, por tanto, por un único punto.

Los nodos de trabajo

En cada nodo se ejecutan tres elementos:

  • kubelet: el agente que recibe la lista de Pods que deben ejecutarse en su nodo, pide al runtime que los arranque, ejecuta las sondas de salud y reporta el estado.
  • runtime de contenedores: containerd o CRI-O en la mayoría de los casos. Es quien descarga las imágenes y lanza los procesos. Tus imágenes Docker funcionan sin modificación: respetan el formato OCI, que estos runtimes saben leer.
  • kube-proxy: programa las reglas de red del nodo para que la dirección estable de un Service llegue a los Pods correctos.
# Voir les nœuds et leur état vu du plan de contrôle
kubectl get nodes

# Détail d'un nœud : capacité, ressources déjà réservées, conditions
kubectl describe node minikube

# Les composants du plan de contrôle sont eux-mêmes des Pods, dans kube-system
kubectl get pods -n kube-system

Este último comando merece una pausa: el plano de control está administrado por el propio Kubernetes. No es un capricho, es lo que hace que sus componentes sean observables y reparables con las mismas herramientas que tus aplicaciones.

El Pod, y por qué no es un contenedor

Kubernetes no despliega contenedores. Despliega Pods. Un Pod es un grupo de uno o varios contenedores que comparten el mismo espacio de red y pueden compartir volúmenes. Los contenedores de un mismo Pod se comunican por localhost y siempre se colocan en el mismo nodo.

apiVersion: v1
kind: Pod
metadata:
  name: demo-pod
  labels:
    app: demo
spec:
  containers:
    - name: web
      image: nginx:1.27-alpine
      ports:
        - containerPort: 80

¿Por qué esta capa intermedia? Porque algunas necesidades son inseparables del proceso principal: un recolector de logs que lee el mismo directorio, un proxy que termina el TLS antes de la aplicación. Tratarlos como contenedores independientes obligaría a garantizar su colocalización por medios indirectos. El Pod hace explícita esa garantía.

El segundo punto, más importante en la práctica: un Pod es desechable. No tiene un nombre estable, su dirección IP cambia en cada recreación y nada sobrevive a su desaparición. Por eso casi nunca se crea un Pod directamente: el manifiesto anterior es un ejemplo pedagógico, no un modelo de producción. Se crea un Deployment, que crea y reemplaza los Pods por ti. La lección 3 detalla esta cadena.

Lo que ganas, lo que pagas

Kubernetes aporta, sin que tengas que escribirlo:

  • la autorreparación: los Pods y nodos defectuosos se reemplazan automáticamente;
  • el descubrimiento de servicios: un nombre DNS estable delante de un conjunto de Pods cambiantes;
  • las actualizaciones progresivas con vuelta atrás, sin cortes;
  • el escalado manual o automático según la carga medida;
  • el reparto bajo restricciones: el planificador coloca los Pods en función de la CPU y la memoria realmente disponibles.

La autorreparación se comprueba con una sola manipulación, y es la demostración más elocuente del modelo declarativo:

# On supprime une réplique à la main…
kubectl delete pod mon-api-7d4b8c9f5-xk2mp

# …et le compte est déjà rétabli, avec un Pod au suffixe différent.
kubectl get pods -l app=mon-api

El coste es real y hay que nombrarlo. Kubernetes añade una capa de abstracción con su propia complejidad: más conceptos que conocer, más configuración YAML, un clúster que mantener o pagar y un conjunto de herramientas de diagnóstico que aprender. Para un sitio web de presentación en una sola máquina, docker compose sigue siendo la opción correcta, y pretender lo contrario sería deshonesto. Kubernetes resulta rentable cuando tienes varios servicios, un requisito de disponibilidad o varios equipos que despliegan sobre la misma infraestructura.

El vocabulario común a todos los manifiestos

Todo objeto de Kubernetes, sea cual sea su tipo, se escribe con los mismos cuatro campos de primer nivel. Reconocerlos hace legible cualquier manifiesto:

apiVersion: apps/v1      # groupe et version de l'API qui définit ce type
kind: Deployment         # le type d'objet
metadata:                # identité : nom, namespace, labels, annotations
  name: mon-api
  labels:
    app: mon-api
spec:                    # l'état voulu ; son contenu dépend du kind
  replicas: 3

Una sola regla sobre apiVersion que conviene recordar en este curso, y que evita el error más frecuente del principiante:

  • v1 para Pod, Service, ConfigMap, Secret, PersistentVolume, PersistentVolumeClaim;
  • apps/v1 para Deployment, ReplicaSet, StatefulSet, DaemonSet.

Un apiVersion: v1 en un Deployment es rechazado por el API server con un mensaje poco explícito. La lista exacta para un tipo dado se lee directamente desde el clúster:

# Quel apiVersion pour ce type ? La réponse vient du cluster, pas de la mémoire.
kubectl api-resources | grep -i deployment

# Documentation d'un champ, sans quitter le terminal
kubectl explain deployment.spec.strategy

kubectl explain es la herramienta más infrautilizada del ecosistema. Describe los campos de la versión de la API realmente instalada en tu clúster, algo que ningún artículo de blog puede garantizar.

Lo que cubre el resto del curso

  1. Instalación y configuración — un clúster local con minikube o kind, kubectl, contextos y namespaces.
  2. Pods, Services y Deployments — los recursos del día a día, los selectores de labels, las actualizaciones progresivas y las sondas.
  3. ConfigMaps y Secrets — sacar la configuración de las imágenes, y lo que «Secret» no significa.
  4. Almacenamiento persistente — volúmenes, claims, StorageClass y StatefulSet.
  5. Despliegue de aplicaciones complejas — multiservicio, Ingress, recursos, autoescalado, vistazo a Helm.

Todos estos objetos ya son conocidos por tu futuro clúster. Este comando los lista con su nombre abreviado, el que escribirás a diario:

kubectl api-resources | grep -E 'deployments|services|configmaps|secrets|persistentvolumeclaims'
# deployments   deploy   apps/v1   true   Deployment
# services      svc      v1        true   Service
# configmaps    cm       v1        true   ConfigMap

Conclusiones

  • Kubernetes es declarativo: describes un estado deseado y unos bucles de reconciliación lo mantienen. apply registra una intención, no garantiza un resultado: la verificación se hace siempre sobre el estado observado.
  • El plano de control decide (API server, etcd, scheduler, controller-manager), los nodos ejecutan (kubelet, runtime, kube-proxy). Saber quién hace qué es la base del diagnóstico.
  • La unidad desplegada es el Pod, no el contenedor: un grupo colocalizado que comparte red y volúmenes y, sobre todo, desechable. Se crea mediante un controlador, nunca a mano en producción.
  • Todos los manifiestos comparten apiVersion, kind, metadata, spec. Recuerda v1 para Pod/Service/ConfigMap/Secret/PVC y apps/v1 para Deployment/StatefulSet.
  • La orquestación tiene un coste de complejidad asumido. Se justifica por el número de servicios, el requisito de disponibilidad y el número de equipos, no por la moda.

Referencia: la documentación oficial está en español para buena parte de los conceptos. Mantén abierto kubernetes.io/es/docs/concepts/ durante este curso.

Comentarios

Los comentarios funcionan con GitHub Discussions

Inicia sesión con GitHub para participar en la discusión

¡Enlace copiado!