kubeadm, backup y restore de etcd, y upgrade de cluster
Instala un cluster kubeadm de tres nodos, haz backup y restore de etcd, e inspecciona certificados antes de ejecutar el upgrade completo del control plane y los workers.
Progreso 0 de 16 secciones
1. Resumen técnico
Este módulo cubre el ciclo de vida completo de un cluster Kubernetes gestionado con kubeadm: desde la preparación del sistema operativo hasta el upgrade entre versiones menores. kubeadm es la herramienta oficial para el bootstrapping de clusters Kubernetes: automatiza la generación de certificados TLS, el despliegue de los static pods del control plane que viste en M02, el RBAC inicial, y la unión de nodos worker. En el examen aparecen tareas de backup y restore de etcd, upgrade de control plane y workers, e inspección de certificados.
2. Prerrequisitos
De M02 (Arquitectura) usarás el concepto de static pods, el rol de cada componente del control plane, y la regla de que solo kube-apiserver habla directamente con etcd. Este módulo pone en práctica esa teoría con manifiestos y comandos reales.
3. Entorno de práctica
Cluster kubeadm de 3 nodos, Ubuntu 24.04.4 LTS: cka-cp1 (control-plane, 172.16.46.130), cka-worker1 (worker, 172.16.46.128), cka-worker2 (worker, 172.16.46.129). Versión de instalación: v1.35.5, con un ciclo de upgrade a v1.36 practicado en este módulo. CNI Calico.
4. Objetivos de aprendizaje medibles
Al terminar este módulo serás capaz de:
- Preparar un sistema operativo Ubuntu 24.04 para alojar un nodo de Kubernetes, explicando el propósito de cada ajuste.
- Inicializar un control plane con kubeadm y explicar qué ocurre internamente en cada fase.
- Unir workers al cluster y explicar el mecanismo de confianza detrás del token de bootstrap.
- Realizar un backup y un restore de etcd en menos de 5 minutos.
- Ejecutar el upgrade completo de un cluster, control plane primero y workers después, sin saltar versiones menores.
- Inspeccionar certificados, kubeconfig y static pods reales para diagnosticar problemas del control plane.
5. Preparación del sistema operativo
Estos ajustes se ejecutan en los 3 nodos antes de instalar ningún componente de Kubernetes.
5.1 Módulos de kernel
overlay permite a containerd usar overlayfs como storage driver para las capas de las imágenes de contenedor. br_netfilter permite que iptables inspeccione tráfico que pasa por bridges de red, necesario para NetworkPolicy y kube-proxy.
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay
sudo modprobe br_netfilterVerificación:
lsmod | grep overlay
lsmod | grep br_netfilter5.2 Parámetros sysctl
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --systembridge-nf-call-iptables y su equivalente para IPv6 hacen que el tráfico de los bridges de red pase por iptables. ip_forward permite al nodo reenviar paquetes entre interfaces, necesario para la comunicación pod a pod.
Validación:
sysctl net.bridge.bridge-nf-call-iptables
sysctl net.bridge.bridge-nf-call-ip6tables
sysctl net.ipv4.ip_forwardnet.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 16. Instalación de containerd y kubetools
6.1 Scripts de instalación
Dos scripts automatizan esta parte, ejecutados en los 3 nodos en este orden.
setup-container.sh instala containerd desde GitHub releases, configura SystemdCgroup = true, obligatorio para que kubelet funcione correctamente, instala runc desde opencontainers, aplica un fix de apparmor específico de Ubuntu 24.04, instala crictl para inspección directa de contenedores, y crea /tmp/container.txt como flag de control para el siguiente script.
setup-kubetools-previousversion.sh verifica que el script anterior se ejecutó, detecta automáticamente la versión N-1 respecto a la última disponible, instala kubelet, kubeadm y kubectl en esa versión, marca los paquetes con apt-mark hold para evitar actualizaciones accidentales, deshabilita swap, requerido por kubelet, y configura crictl para usar el socket de containerd.
chmod +x setup-container.sh
chmod +x setup-kubetools-previousversion.sh
sudo ./setup-container.sh
sudo ./setup-kubetools-previousversion.shVerificación:
sudo systemctl status containerd
kubeadm version
kubelet --version
kubectl version --client
crictl version
sudo swapon --show
apt-mark showholdactive (running)
kubeadm version: &version.Info{GitVersion:"v1.35.5"...}
Kubernetes v1.35.5
Client Version: v1.35.5
crictl version v1.35.0
(sin salida, swap desactivado)
kubelet
kubeadm
kubectl6.2 Por qué apt-mark hold es obligatorio
Los paquetes marcados con hold no se actualizan con apt upgrade. Sin esto, una actualización automática del sistema podría saltar directamente a una versión de Kubernetes varias releases por delante de la del cluster, algo que kubeadm no soporta: los upgrades deben hacerse de una versión menor a la siguiente, sin saltos.
7. Inicialización del cluster con kubeadm
7.1 Qué ocurre internamente en kubeadm init
kubeadm init despliega el control plane en un orden específico. Primero ejecuta preflight checks, verificando swap desactivado, puertos libres, containerd corriendo y permisos de root. Luego genera la CA del cluster y todos los certificados TLS derivados de ella en /etc/kubernetes/pki/. A continuación genera los ficheros kubeconfig para el administrador, kubelet, scheduler y controller-manager. Después despliega los static pods del control plane, los mismos que estudiaste en M02, escribiendo sus manifiestos en /etc/kubernetes/manifests/. Espera a que la API responda, configura el RBAC inicial, genera el token de bootstrap para los workers, e instala kube-proxy y CoreDNS como addons, ya como pods normales gestionados vía la API que en ese punto ya está operativa.
Ejecutar únicamente en cka-cp1:
sudo kubeadm initGuarda toda la salida del comando: incluye el kubeadm join que necesitarás en la sección 7.3.
Configurar kubectl:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/configVerificación:
kubectl get nodes
kubectl get pods -n kube-systemNAME STATUS ROLES VERSION
cka-cp1 NotReady control-plane v1.35.5
NAME READY STATUS
etcd-cka-cp1 1/1 Running
kube-apiserver-cka-cp1 1/1 Running
kube-controller-manager-cka-cp1 1/1 Running
kube-scheduler-cka-cp1 1/1 Running
coredns-... 0/1 PendingNotReady y CoreDNS en Pending son esperados en este punto: aún no hay CNI instalado.
7.2 Instalar Calico
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yamlwatch kubectl get pods -n kube-systemEspera hasta que calico-node y calico-kube-controllers estén Running, y CoreDNS pase a Running. Ctrl+C para salir del watch.
kubectl get nodesNAME STATUS ROLES VERSION
cka-cp1 Ready control-plane v1.35.57.3 Unir los workers
Ejecutar en cka-worker1 y cka-worker2 el comando kubeadm join que apareció en la salida de la sección 7.1. Si se perdió, regenerarlo desde cka-cp1:
kubeadm token create --print-join-commandVerificación final desde cka-cp1:
kubectl get nodes -o wide
kubectl get pods -ANAME STATUS ROLES VERSION
cka-cp1 Ready control-plane v1.35.5
cka-worker1 Ready <none> v1.35.5
cka-worker2 Ready <none> v1.35.58. Inspección de certificados y static pods reales
8.1 Confirmar que un pod del control plane es un static pod, no un pod normal
Conexión con M02: viste que la prueba definitiva de un mirror pod es la anotación kubernetes.io/config.mirror, no ownerReferences.
kubectl get pod kube-apiserver-cka-cp1 -n kube-system -o yaml | grep config.mirrorkubernetes.io/config.mirror: 7a3f9c8e1b2d4a6f5e0c9d8b7a6f5e0cEl valor es un hash del manifiesto fuente; su presencia confirma que este pod es un mirror pod de un static pod real gestionado localmente por kubelet, no un objeto creado a través del apiserver.
sudo cat /etc/kubernetes/manifests/kube-apiserver.yamlRevisa los flags de arranque del apiserver en la salida: --advertise-address, --etcd-servers, --service-cluster-ip-range, --cluster-cidr. Son útiles para el troubleshooting de esta sección.
8.2 Certificados
kubeadm gestiona todos los certificados del cluster en /etc/kubernetes/pki/, incluyendo la CA raíz, el certificado del apiserver, el certificado que usa el apiserver para hablar con kubelet, y los certificados de etcd en su propio subdirectorio.
sudo kubeadm certs check-expirationLos certificados de kubeadm expiran al año de su emisión. El examen puede incluir una tarea de renovación:
sudo kubeadm certs renew all9. Backup y restore de etcd
9.1 Por qué importa
etcd almacena todo el estado del cluster, tal como viste en M02. Si se corrompe o pierde datos sin backup, el cluster pierde su memoria completa. El backup y restore de etcd aparece con frecuencia en el examen y hay que ejecutarlo en menos de 5 minutos.
9.2 Instalar etcdctl
sudo apt install etcd-client
etcdctl versionetcdctl version: 3.4.30
API version: 3.49.3 Localizar los certificados de etcd
etcdctl necesita autenticarse contra etcd usando TLS. Los certificados están en /etc/kubernetes/pki/etcd/.
ls /etc/kubernetes/pki/etcd/ca.crt
server.crt
server.key9.4 Backup
sudo ETCDCTL_API=3 etcdctl snapshot save /opt/etcd-backup.db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.keyVerificar que el backup es válido:
sudo ETCDCTL_API=3 etcdctl snapshot status /opt/etcd-backup.db --write-out=table
ls -lh /opt/etcd-backup.db+----------+----------+------------+------------+
| HASH | REVISION | TOTAL KEYS | TOTAL SIZE |
+----------+----------+------------+------------+
| a1b2c3d4 | 4821 | 687 | 2.1 MB |
+----------+----------+------------+------------+
-rw------- 1 root root 2.1M /opt/etcd-backup.db9.5 Restore
etcd no puede restaurarse mientras está corriendo. Como corre como static pod, hay que moverlo temporalmente fuera de /etc/kubernetes/manifests/ para que kubelet lo detenga.
Paso 1, crear los datos restaurados en una ruta nueva, sin sobreescribir etcd en caliente:
sudo ETCDCTL_API=3 etcdctl snapshot restore /opt/etcd-backup.db \
--data-dir=/var/lib/etcd-restoredPaso 2, detener etcd moviendo su manifiesto:
sudo mv /etc/kubernetes/manifests/etcd.yaml /tmp/etcd.yamlwatch crictl psEspera hasta que etcd desaparezca de la lista. Ctrl+C para salir.
Paso 3, reemplazar el directorio de datos:
sudo mv /var/lib/etcd /var/lib/etcd-old
sudo mv /var/lib/etcd-restored /var/lib/etcdPaso 4, restaurar el manifiesto:
sudo mv /tmp/etcd.yaml /etc/kubernetes/manifests/etcd.yamlwatch crictl psEspera hasta que etcd reaparezca como Running, puede tardar 1 a 2 minutos. Ctrl+C para salir.
Paso 5, verificar la recuperación completa del cluster:
kubectl get nodes
kubectl get pods -ASi kubectl no responde de inmediato, espera unos 60 segundos: etcd necesita tiempo para inicializarse completamente antes de que el apiserver pueda conectarse.
10. Upgrade del cluster
10.1 Reglas del proceso
El upgrade de un cluster kubeadm sigue un orden estricto: primero el control plane, luego los workers, uno a la vez, y nunca saltando versiones menores. Al ejecutar kubeadm upgrade apply, kubeadm verifica compatibilidad, descarga las nuevas imágenes del control plane, actualiza sus static pods uno por uno, actualiza certificados y kubeconfig, y actualiza los addons kube-proxy y CoreDNS. El upgrade de kubelet y kubectl es un paso aparte porque son paquetes apt independientes, no gestionados por kubeadm.
Objetivo de este módulo: haz un upgrade del cluster de v1.35.5 a v1.36.
10.2 Haz un upgrade del control plane
En cka-cp1, actualizar el repositorio apt a la nueva versión:
sudo sed -i 's|stable:/v1.35|stable:/v1.36|g' /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
apt-cache madison kubeadm | grep 1.36 | head -5Actualizar kubeadm:
sudo apt-mark unhold kubeadm
sudo apt-get install -y kubeadm=1.36.*
sudo apt-mark hold kubeadm
kubeadm versionRevisar el plan antes de ejecutarlo:
sudo kubeadm upgrade planHaz un upgrade del control plane:
sudo kubeadm upgrade apply v1.36.0Drenar el nodo antes de actualizar kubelet:
kubectl drain cka-cp1 --ignore-daemonsets --delete-emptydir-dataHaz un upgrade de kubelet y kubectl:
sudo apt-mark unhold kubelet kubectl
sudo apt-get install -y kubelet=1.36.* kubectl=1.36.*
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload
sudo systemctl restart kubeletDevolver el nodo a servicio:
kubectl uncordon cka-cp1
kubectl get nodesNAME STATUS ROLES VERSION
cka-cp1 Ready control-plane v1.36.0
cka-worker1 Ready <none> v1.35.5
cka-worker2 Ready <none> v1.35.510.3 Haz un upgrade de cada worker
Repetir uno a la vez, nunca en paralelo. En cka-worker1, actualizar el repositorio y kubeadm igual que en 10.2, luego:
sudo kubeadm upgrade nodekubeadm upgrade node, a diferencia de kubeadm upgrade apply, solo actualiza la configuración local de kubelet en un worker; no toca componentes del control plane.
Desde cka-cp1, drenar el worker:
kubectl drain cka-worker1 --ignore-daemonsets --delete-emptydir-dataDe vuelta en cka-worker1, haz un upgrade de kubelet y kubectl igual que en 10.2, y reinicia kubelet.
Desde cka-cp1, devolver a servicio:
kubectl uncordon cka-worker1Repetir el mismo proceso completo para cka-worker2.
Verificación final:
kubectl get nodes
kubectl get pods -ANAME STATUS ROLES VERSION
cka-cp1 Ready control-plane v1.36.0
cka-worker1 Ready <none> v1.36.0
cka-worker2 Ready <none> v1.36.011. Diagrama ASCII - orden del upgrade
ANTES: cluster completo en v1.35.5
PASO 1 - control plane (cka-cp1):
kubeadm upgrade apply v1.36.0
drain -> upgrade kubelet/kubectl -> uncordon
cka-cp1 ahora en v1.36.0
workers siguen en v1.35.5 (temporalmente mixto,
es normal y soportado durante el upgrade)
PASO 2 - worker 1 (cka-worker1):
kubeadm upgrade node
drain (desde cka-cp1) -> upgrade kubelet/kubectl
-> uncordon (desde cka-cp1)
cka-worker1 ahora en v1.36.0
PASO 3 - worker 2 (cka-worker2):
mismo proceso que el paso 2
DESPUES: cluster completo en v1.36.0
Regla: nunca actualizar dos workers en paralelo,
y nunca actualizar un worker antes que el control plane.12. Laboratorio guiado
Objetivo: recuperar un cluster tras simular una pérdida de datos en etcd, el escenario de examen más crítico de este módulo.
Paso 1, crear un recurso de prueba antes del backup:
kubectl create namespace antes-del-backupPaso 2, backup:
sudo ETCDCTL_API=3 etcdctl snapshot save /opt/backup-lab.db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.keyPaso 3, simular pérdida de datos creando un recurso DESPUÉS del backup:
kubectl create namespace despues-del-backup
kubectl get namespacesPaso 4, restaurar desde el backup, siguiendo los 5 pasos de la sección 9.5, usando /opt/backup-lab.db y un directorio /var/lib/etcd-restored-lab.
Paso 5, verificar el resultado esperado:
kubectl get namespacesantes-del-backup debe existir. despues-del-backup NO debe existir: el restore retrocedió el estado del cluster exactamente al momento del snapshot.
13. Checkpoint
Pregunta 1
¿Cuál es el orden correcto para hacer un upgrade de un cluster con 1 control plane y 2 workers?
Mostrar respuesta
Respuesta: primero el control plane completo, control plane apply, drain, upgrade de kubelet y kubectl, uncordon; luego cada worker, uno a la vez, nunca en paralelo, con kubeadm upgrade node en lugar de upgrade apply.
Pregunta 2
¿Por qué no se puede hacer el restore de etcd mientras etcd está corriendo?
Mostrar respuesta
Respuesta: etcd bloquearía el directorio de datos y los nuevos datos restaurados entrarían en conflicto con el estado en memoria del proceso activo. Hay que detener etcd moviendo su manifiesto fuera de /etc/kubernetes/manifests/ para que kubelet lo detenga.
Pregunta 3
¿Cómo confirmas de forma definitiva que un pod del namespace kube-system es un mirror pod de un static pod?
Mostrar respuesta
Respuesta: revisando la anotación kubernetes.io/config.mirror en su metadata; su presencia confirma el origen static pod.
Pregunta 4
¿Qué diferencia hay entre kubeadm upgrade apply y kubeadm upgrade node?
Mostrar respuesta
Respuesta: kubeadm upgrade apply se ejecuta en el control plane y actualiza todos sus componentes más los addons. kubeadm upgrade node se ejecuta en los workers y solo actualiza la configuración local de kubelet, sin tocar componentes del control plane.
Pregunta 5
¿Dónde guarda kubeadm los certificados TLS del cluster, y cómo verificas su expiración?
Mostrar respuesta
Respuesta: en /etc/kubernetes/pki/, y se verifica con sudo kubeadm certs check-expiration.
14. Laboratorio cronometrado
Objetivo: 20 minutos
Objetivo: completar en menos de 20 minutos.
Tarea:
- Haz un backup de etcd y guárdalo en /opt/backup/etcd-cronometrado.db, verificando que el snapshot es válido.
- Guarda en /tmp/certs-expiracion.txt la salida completa de la verificación de expiración de certificados.
- Guarda en /tmp/mirror-pod.txt el valor de la anotación config.mirror del pod de kube-scheduler.
Criterios de éxito:
ls -lh /opt/backup/etcd-cronometrado.db
cat /tmp/certs-expiracion.txt
cat /tmp/mirror-pod.txtMostrar solución de referencia
sudo mkdir -p /opt/backup
sudo ETCDCTL_API=3 etcdctl snapshot save /opt/backup/etcd-cronometrado.db \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
sudo ETCDCTL_API=3 etcdctl snapshot status /opt/backup/etcd-cronometrado.db --write-out=table
sudo kubeadm certs check-expiration > /tmp/certs-expiracion.txt
kubectl get pod kube-scheduler-cka-cp1 -n kube-system \
-o jsonpath='{.metadata.annotations.kubernetes\.io/config\.mirror}' > /tmp/mirror-pod.txt15. Troubleshooting - casos reales
Caso 1: kubeadm upgrade apply falla con error de etcd
Síntoma:
El comando de upgrade falla con un error del tipo “error execution phase upgrade/etcd: couldn’t upgrade control plane: timed out waiting for the condition”.
Diagnóstico paso a paso:
sudo ETCDCTL_API=3 etcdctl endpoint health \
--endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.keyConfirmar que etcd está healthy antes de reintentar. Si no lo está, revisar sus logs directamente con crictl.
crictl ps | grep etcdSolución:
Resolver el problema de etcd, revisando el Caso 3 de la sección 9, antes de reintentar el upgrade.
Caso 2: kubectl drain falla por pods con storage local
Síntoma:
El comando kubectl drain se detiene con un error del tipo “cannot delete Pods with local storage” o pods pendientes de drenar.
Diagnóstico paso a paso:
La causa habitual es la presencia de pods con volúmenes emptyDir, o pods standalone sin ningún controller detrás.
kubectl drain cka-worker1 --ignore-daemonsets --delete-emptydir-data --force--ignore-daemonsets ignora pods de DaemonSets, que no pueden moverse por diseño. --delete-emptydir-data acepta perder los datos de volúmenes emptyDir. --force elimina pods sin ningún controller detrás.
Solución:
Usar los tres flags juntos cuando la tarea del examen lo permita explícitamente; en producción real, evaluar antes si perder esos datos es aceptable.
Caso 3: etcd no arranca tras el restore
Síntoma:
Tras seguir los pasos de restore de la sección 9.5, kubectl get nodes no responde y crictl ps no muestra etcd como Running.
Diagnóstico paso a paso:
crictl ps -a | grep etcdsudo journalctl -u kubelet --no-pager | tail -50Causas comunes: permisos incorrectos en el directorio de datos, el manifiesto de etcd apunta a un --data-dir distinto al que se restauró, o el snapshot está corrupto.
sudo chown -R root:root /var/lib/etcd
sudo chmod 700 /var/lib/etcdsudo cat /etc/kubernetes/manifests/etcd.yaml | grep data-dirSolución:
Corregir permisos con los comandos anteriores si ese era el problema, o corregir el --data-dir del manifiesto para que coincida con la ruta real donde se restauraron los datos.
16. Entorno requerido
Entorno principal: cluster kubeadm de 3 nodos. Este módulo instala y opera ese cluster desde cero; es el módulo fundacional para todos los que siguen.
Alternativa K3s (plan B): K3s no usa kubeadm en absoluto, así que ninguno de los pasos de instalación de este módulo aplica directamente. K3s reemplaza etcd por SQLite por defecto, y su comando de backup es k3s etcd-snapshot save cuando corre con el backend etcd habilitado explícitamente, o herramientas propias de SQLite en su configuración por defecto. El upgrade de K3s se gestiona con su propio instalador, no con kubeadm upgrade. Si tu cluster kubeadm queda inutilizable, K3s sirve como entorno alternativo ligero para seguir practicando módulos posteriores que no dependen de kubeadm específicamente.
Siguiente módulo: M04 - Control Plane de Alta Disponibilidad (HA)