Saltar al contenido principal

Mantenimiento de Kubernetes: backup, actualización y restauración

Bienvenido a la especialización CKA del curso. Empezamos con una de las tareas más habituales (y más preguntadas en el examen) de un administrador: hacer backup del cluster, actualizarlo de versión y, si algo sale mal, restaurarlo.

Kubernetes publica tres versiones menores al año y cada una recibe soporte durante aproximadamente 14 meses, así que actualizar no es opcional: es rutina. La regla de oro es actualizar de versión menor en versión menor (de 1.32 a 1.33, nunca saltando a 1.34 directamente).

Backup de la base de datos etcd

Todo el estado del cluster vive en etcd, así que un backup de etcd es un backup del cluster. Antes de cualquier actualización, por estable que sea, crea una copia.

  1. Lo primero es localizar el directorio de datos de etcd:
sudo grep data-dir /etc/kubernetes/manifests/etcd.yaml

Los siguientes comandos se ejecutan dentro del contenedor de etcd, cuyo pod se llama etcd-<nombre_nodo>. Puedes localizarlo listando los pods del sistema con kubectl -n kube-system get pods.

  1. Comprobamos el estado de la base de datos de etcd:
kubectl -n kube-system exec -it etcd-<nombre_nodo> -- sh -c \
"ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt \
ETCDCTL_CERT=/etc/kubernetes/pki/etcd/server.crt \
ETCDCTL_KEY=/etc/kubernetes/pki/etcd/server.key \
etcdctl endpoint health"

En versiones antiguas verás ETCDCTL_API=3 antepuesto a estos comandos. Desde etcd 3.4 la API v3 es la predeterminada, así que ya no es necesario (aunque tampoco molesta).

  1. Comprobamos los miembros del cluster de etcd:
kubectl -n kube-system exec -it etcd-<nombre_nodo> -- sh -c \
"ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt \
ETCDCTL_CERT=/etc/kubernetes/pki/etcd/server.crt \
ETCDCTL_KEY=/etc/kubernetes/pki/etcd/server.key \
etcdctl --endpoints=https://127.0.0.1:2379 member list -w table"
  1. Por último, hacemos la copia de seguridad con el comando snapshot save:
kubectl -n kube-system exec -it etcd-<nombre_nodo> -- sh -c \
"ETCDCTL_CACERT=/etc/kubernetes/pki/etcd/ca.crt \
ETCDCTL_CERT=/etc/kubernetes/pki/etcd/server.crt \
ETCDCTL_KEY=/etc/kubernetes/pki/etcd/server.key \
etcdctl --endpoints=https://127.0.0.1:2379 snapshot save /var/lib/etcd/snapshot.db"

Si hacemos un ls en el directorio del paso 1 (normalmente /var/lib/etcd), podremos ver el snapshot que acabamos de extraer:

sudo ls -l /var/lib/etcd

Guarda ese fichero fuera del nodo, junto con una copia de /etc/kubernetes/pki (los certificados del cluster). Un backup que vive en el mismo disco que el original no es un backup.

Restaurar etcd desde un snapshot

La otra mitad de la moneda, y pregunta recurrente del CKA. La restauración se hace desde el propio nodo (no dentro del contenedor), con la herramienta etcdutl (o etcdctl snapshot restore en versiones antiguas):

  1. Restauramos el snapshot en un directorio nuevo:
sudo etcdutl snapshot restore /var/lib/etcd/snapshot.db --data-dir /var/lib/etcd-restaurado
  1. Editamos el manifiesto estático de etcd para que apunte al nuevo directorio. En /etc/kubernetes/manifests/etcd.yaml, cambiamos la ruta del volumen etcd-data:
volumes:
- hostPath:
path: /var/lib/etcd-restaurado # Antes: /var/lib/etcd
type: DirectoryOrCreate
name: etcd-data
  1. Al guardar el fichero, el kubelet detectará el cambio y recreará el pod de etcd automáticamente (es un static pod, concepto que veremos en troubleshooting). Puede tardar un par de minutos. Verificamos:
kubectl -n kube-system get pods
kubectl get nodes

El cluster habrá vuelto al estado exacto del momento del snapshot.

Para backups completos de aplicaciones y volúmenes (no solo del estado del cluster), la herramienta de referencia es Velero, que merece mención aunque no entre en el examen.

Vaciar un nodo: cordon y drain

Antes de actualizar o mantener un nodo, hay que sacar de él las cargas de trabajo de forma controlada:

kubectl cordon <nombre_nodo> # Marca el nodo como no programable (no mueve nada)
kubectl drain <nombre_nodo> --ignore-daemonsets # Cordon + desaloja todos los pods
kubectl uncordon <nombre_nodo> # Vuelve a habilitar el nodo

Detalles que importan (y que el examen pregunta):

  • --ignore-daemonsets es casi siempre necesario: los pods de DaemonSets no se pueden desalojar (volverían a crearse en el mismo nodo).
  • Si hay pods "sueltos" (sin deployment ni replicaset que los gestione), drain se negará a borrarlos salvo que añadas --force.
  • Los pods con emptyDir perderán esos datos; drain avisará y pedirá --delete-emptydir-data.

Actualizar el cluster con kubeadm

Vamos a actualizar el cluster a una nueva versión. El proceso se hace nodo a nodo, empezando por los del control plane. Usaremos como ejemplo la actualización a la 1.33.

  1. Los paquetes de cada versión menor viven en un repositorio distinto de pkgs.k8s.io, así que lo primero es apuntar el repositorio a la nueva versión:
sudo sed -i 's/v1.32/v1.33/' /etc/apt/sources.list.d/kubernetes.list
sudo apt update
  1. Consultamos las versiones de parche disponibles con madison:
sudo apt-cache madison kubeadm
  1. Desbloqueamos el paquete kubeadm (lo bloqueamos en la instalación para evitar actualizaciones accidentales), lo actualizamos y volvemos a bloquearlo:
sudo apt-mark unhold kubeadm
sudo apt install -y kubeadm=1.33.1-1.1
sudo apt-mark hold kubeadm
  1. Comprobamos la versión que acabamos de instalar:
sudo kubeadm version
  1. Vaciamos el nodo antes de actualizarlo (si estuviéramos actualizando un worker, el drain se lanza desde un nodo con kubectl configurado, normalmente el maestro):
kubectl drain <nombre_nodo> --ignore-daemonsets
  1. El comando kubeadm nos permite previsualizar los cambios que va a generar la actualización con el subcomando plan:
sudo kubeadm upgrade plan
  1. Realizamos la actualización con apply (solo en el primer nodo del control plane):
sudo kubeadm upgrade apply v1.33.1

En el resto de nodos del control plane y en los workers se usa, en su lugar:

sudo kubeadm upgrade node
  1. Actualizamos el resto de paquetes a la misma versión:
sudo apt-mark unhold kubelet kubectl
sudo apt install -y kubelet=1.33.1-1.1 kubectl=1.33.1-1.1
sudo apt-mark hold kubelet kubectl
  1. Aunque hayamos actualizado correctamente, si ejecutamos kubectl get nodes nos seguirá mostrando la versión anterior. La actualización se hará efectiva cuando reiniciemos el kubelet:
sudo systemctl daemon-reload
sudo systemctl restart kubelet
  1. Por último, volvemos a habilitar la programación de pods en el nodo:
kubectl uncordon <nombre_nodo>

Verificamos el estado y la versión de los nodos:

kubectl get nodes

Repetimos el proceso en cada nodo del cluster, de uno en uno, comprobando que todo está Ready antes de pasar al siguiente. Así el servicio nunca se interrumpe.

Resumen

  • Backup: etcdctl snapshot save (con sus certificados) + copia de /etc/kubernetes/pki. Antes de cada actualización, siempre.
  • Restauración: etcdutl snapshot restore a un directorio nuevo + editar el manifiesto estático de etcd.
  • Mantenimiento de nodos: cordon, drain --ignore-daemonsets, uncordon.
  • Actualización: repositorio nuevo → kubeadm → upgrade planapply (primer control plane) o node (resto) → kubelet y kubectl → nodo a nodo.

Volver al índice