El módulo más denso de la Fase 1. Cómo está construido un cluster, cómo se arma y se une un nodo, cómo se actualiza sin romperlo, y cómo proteger su corazón: etcd. Buena parte de esto ya lo viviste armando tu cluster.
En un cluster kubeadm, los 4 componentes corren como static pods: sus manifiestos viven en una carpeta y el kubelet los levanta solo.
# Los 4 manifiestos del control plane (en cp1)
ls /etc/kubernetes/manifests/
# etcd.yaml kube-apiserver.yaml
# kube-controller-manager.yaml kube-scheduler.yaml
# Verlos corriendo como pods
k get pods -n kube-system
# Inspeccionar el manifiesto del apiserver
sudo cat /etc/kubernetes/manifests/kube-apiserver.yaml
Corre en TODOS los nodos como servicio de systemd, no como pod.
sudo systemctl status kubelet
sudo journalctl -u kubelet -n 50 --no-pager
/etc/kubernetes/manifests/. Si editás o movés un manifiesto de ahí, el kubelet reacciona al instante (recrea o elimina el pod). Es la base de varios ejercicios de troubleshooting.Vos declarás QUÉ querés; el cluster trabaja sin parar para que la realidad coincida. Los componentes hacen ese trabajo.
| Componente | Función |
|---|---|
| etcd | Base de datos. Guarda TODO el estado del cluster |
| kube-apiserver | Única puerta de entrada. Todo pasa por él. El único que habla con etcd |
| kube-scheduler | Decide en qué nodo va cada pod nuevo |
| controller-manager | Vigila que realidad = deseado. Recrea pods caídos, etc. |
| kubelet | Agente en cada nodo. Recibe órdenes y arranca containers |
| kube-proxy | Ruteo de red de Services (Módulo 6) |
El bind: cannot assign requested address era etcd. El API server que no levantaba dependía de etcd. El kubelet reiniciándose en loop. Ya conocés a los actores.
sudo kubeadm init \
--pod-network-cidr=10.244.0.0/16 \
--apiserver-advertise-address=<IP-del-cp>
# Configurar kubectl para tu usuario
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# Instalar CNI (sin esto los nodos quedan NotReady)
k apply -f <calico.yaml>
# El comando que imprime 'kubeadm init' al final:
sudo kubeadm join <IP>:6443 --token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
# Regenerar el comando de join completo (en el cp)
kubeadm token create --print-join-command
# Listar tokens existentes
kubeadm token list
Cuando corrés init, pasa por fases en orden (las viste en el output):
/etc/kubernetes/pki/El worker contacta al API server, valida con el token + el hash del CA (para confiar en el control plane), recibe sus certificados, y arranca su kubelet. Por eso join necesita esos dos valores.
discovery-token-ca-cert-hash es el mecanismo de confianza mutua: el worker verifica que el control plane es legítimo, no un impostor.pre-upgrade con el cluster una versión atrás. Cada vez que termines (o rompas) un upgrade, restaurás y lo repetís. El upgrade se gana con repetición pura hasta que el orden sea automático.sudo apt-mark unhold kubeadm
sudo apt-get update && sudo apt-get install -y kubeadm=1.34.x-*
sudo apt-mark hold kubeadm
sudo kubeadm upgrade plan
# Te muestra a qué versiones podés ir
sudo kubeadm upgrade apply v1.34.x
k drain cp1 --ignore-daemonsets
sudo apt-mark unhold kubelet kubectl
sudo apt-get install -y kubelet=1.34.x-* kubectl=1.34.x-*
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload
sudo systemctl restart kubelet
k uncordon cp1
Casi igual, pero con kubeadm upgrade node en vez de apply, y el drain/uncordon se corren desde el control plane.
sudo apt-mark unhold kubeadm
sudo apt-get update && sudo apt-get install -y kubeadm=1.34.x-*
sudo apt-mark hold kubeadm
# En cp1:
k drain w1 --ignore-daemonsets
sudo kubeadm upgrade node
sudo apt-mark unhold kubelet kubectl
sudo apt-get install -y kubelet=1.34.x-* kubectl=1.34.x-*
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload && sudo systemctl restart kubelet
# En cp1:
k uncordon w1
drain protege las cargas moviéndolas antes de tocar el nodo; el uncordon lo reincorpora al final.--ignore-daemonsets en el drain es obligatorio: los DaemonSets (como kube-proxy y calico-node) corren en todos los nodos y no se pueden mover, así que drain falla sin ese flag. Si hay pods con almacenamiento local, puede que necesites --delete-emptydir-data también.etcdctl habla con etcd por TLS. Estos 4 valores van en casi todos los comandos:
# Endpoint + los 3 certificados
--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 save /opt/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.key
# Verificar el snapshot
sudo ETCDCTL_API=3 etcdctl snapshot status /opt/backup.db -w table
# 1. Restaurar a un data-dir NUEVO
sudo ETCDCTL_API=3 etcdctl snapshot restore /opt/backup.db \
--data-dir=/var/lib/etcd-restore
# 2. Apuntar etcd al nuevo data-dir
sudo vim /etc/kubernetes/manifests/etcd.yaml
# cambiar hostPath de /var/lib/etcd
# a /var/lib/etcd-restore (en volumes Y volumeMounts)
# 3. El kubelet recrea etcd solo al detectar el cambio
# Esperá a que el control plane vuelva
k get pods -n kube-system
Un snapshot de etcd es una foto completa del estado del cluster en ese instante: todos los objetos, todo. Restaurarlo es viajar en el tiempo a ese momento.
| Paso | Qué hace |
|---|---|
| restore | Desempaqueta el .db en un data-dir nuevo |
| editar manifest | Decirle a etcd que use ese data-dir |
| kubelet recrea | Al cambiar el static pod, etcd reinicia con los datos restaurados |
ETCDCTL_API=3 es obligatorio en versiones donde no es el default. Ponelo siempre por las dudas — no molesta y evita errores raros.sudo cat /etc/kubernetes/manifests/etcd.yaml | grep file. Si no te acordás las rutas, ahí están.cluster-ready, hacé un backup, borrá un deployment a propósito, restaurá el backup, y verificá que el deployment volvió. Si rompés etcd, restaurás el snapshot de la VM y reintentás.# Ver componentes del control plane
ls /etc/kubernetes/manifests/
k get pods -n kube-system
# Token de join nuevo
kubeadm token create --print-join-command
# Upgrade (orden): kubeadm → plan → apply →
# drain → kubelet → uncordon
# etcd backup
sudo ETCDCTL_API=3 etcdctl snapshot save /opt/backup.db \
--endpoints=https://127.0.0.1:2379 \
--cacert=...ca.crt --cert=...server.crt --key=...server.key
# etcd restore
sudo ETCDCTL_API=3 etcdctl snapshot restore /opt/backup.db \
--data-dir=/var/lib/etcd-restore
# + editar /etc/kubernetes/manifests/etcd.yaml
/etc/kubernetes/manifests/.