Instalación clúster de Kubernetes con Kubeadm

En esta entrada se documenta el proceso de creación de un cluster de kubernetes con kubeadm. El clúster está formado (por ahora) con un nodo master y un nodo worker.

La información para la realización de esta se ha sacado, principalmente, de las siguientes webs:

  • https://devopscube.com/setup-kubernetes-cluster-kubeadm/
  • https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/
  • https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/

Que es kubeadm?

Kubeadm es una herramienta proporcionada por el proyecto Kubernetes que facilita la configuración, el despliegue y la administración de un clúster de Kubernetes. Es una de las formas más comunes y recomendadas para crear un clúster de Kubernetes desde cero.

Este automatiza muchos de los pasos que normalmente se requieren para configurar manualmente un clúster.

De qué esta formado un clúster de kubernetes?

De forma resumida y sencilla, un clúster de kubernetes está formado por dos tipos de nodos:

  • Nodo master: Es el responsable de la gestión del clúster. Es desde donde se administra el clúster, se encarga de tomar decisiones sobre la programación, replicación de los Pods y responde a los cambios de estado del clúster. Está formado por varios componentes, API Server. etcd, scheduler…
  • Nodos worker: La función de este tipo de nodos es ejecutar los contendedores. Entre sus componentes principales encontramos el kubectl, kube-proxy y el container runtime(containerd, Cri-o, …)

Requisitos

Para proceder a la instalación del clúster, necesitaremos lo siguiente:

  • One or more machines running a deb/rpm-compatible Linux OS; for example: Ubuntu or CentOS.
  • 2 GiB or more of RAM per machine–any less leaves little room for your apps.
  • At least 2 CPUs on the machine that you use as a control-plane node.
  • Full network connectivity among all machines in the cluster. You can use either a public or a private network.

Esto está sacado de la documentación oficial de kubernetes. En mi caso he creado dos VM con Ubuntu 24.04 y con los requisitos de hardware indicados. Es decir, para el nodo master lo he creado con 2 vCPUs y 2GB de RAM. Para los nodos workers, 1vCPU y 2GB de RAM. En mi caso, para mi laboratorio, los discos de las máquinas serán de 32GB.

La red que usaré para los nodos será la red 192.168.1.0/24 y para la red interna de kubernetes 10.0.0.0/16

Una vez, tengamos las máquinas creadas con lo indicado anteriormente (indicar que esto es un laboratorio personal, por lo que los recursos usados siguen lo mínimo requerido que, para mi caso, es suficiente), es necesario aplicar la siguiente apertura de puertos indicada en la documentación oficial:

https://kubernetes.io/docs/reference/networking/ports-and-protocols/

La indico a continuación:

Nodo master

ProtocolDirectionPort RangePurposeUsed By
TCPInbound6443Kubernetes API serverAll
TCPInbound2379-2380etcd server client APIkube-apiserver, etcd
TCPInbound10250Kubelet APISelf, Control plane
TCPInbound10259kube-schedulerSelf
TCPInbound10257kube-controller-managerSelf

Worker node(s) 

ProtocolDirectionPort RangePurposeUsed By
TCPInbound10250Kubelet APISelf, Control plane
TCPInbound10256kube-proxySelf, Load balancers
TCPInbound30000-32767NodePort Services†All

Configuración del clúster con kubeadm

1. Configuración de IPtables para Tráfico Puente en Kubernetes

Para asegurar que IPTables pueda gestionar correctamente el tráfico entre Pods en Kubernetes, es necesario habilitar la inspección de tráfico puenteado. Esto se debe a que Kubernetes utiliza redes en modo puente para la comunicación entre Pods y nodos. Para ello, primero es necesario asegurarse que el módulo del kernel br_netfilter esté cargado:

sudo tee /etc/modules-load.d/k8s.conf br_netfilter

Tras eso, se ejecutan los siguientes comandos para permitir que IPTables vea y maneje el tráfico puenteado:

net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1

Esto hay que aplicarlo en todos los nodos

2. Deshabilitar swap en todos los nodos

Para que kubeadm funcione correctamente se necesita deshabilitar la swap en todos los nodos:

sudo swapoff -a

Es necesario configurar el fstab para que no genere la swap tras el reinicio de los nodos

3. Instalar containter runtime

Para este laboratorio escogí CRI-O ya que es el que he visto que se emplea en las certificaciones de kubernetes. Otras alternativas podrían ser containerd o docker engine. Es necesario repetir lo siguiente en todos los nodos:

sudo apt-get update -y
sudo apt-get install -y software-properties-common curl apt-transport-https ca-certificates

curl -fsSL https://pkgs.k8s.io/addons:/cri-o:/prerelease:/main/deb/Release.key |
gpg --dearmor -o /etc/apt/keyrings/cri-o-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/cri-o-apt-keyring.gpg] https://pkgs.k8s.io/addons:/cri-o:/prerelease:/main/deb/ /" |
tee /etc/apt/sources.list.d/cri-o.list

sudo apt-get update -y
sudo apt-get install -y cri-o

sudo systemctl daemon-reload
sudo systemctl enable crio --now
sudo systemctl start crio.service

A mayores se instalará el crictl, el cual es una CLI utilizada para interacturar con los contenedores creados

VERSION="v1.28.0" 

wget https://github.com/kubernetes-sigs/cri-tools/releases/download/$VERSION/crictl-$VERSION-linux-amd64.tar.gz 

sudo tar zxvf crictl-$VERSION-linux-amd64.tar.gz -C /usr/local/bin 

rm -f crictl-$VERSION-linux-amd64.tar.gz

4. Instalar Kubeadm y otras utilidades

Esto será necesario realizarlo en todos los nodos. He usado la versión 1.29 ya que es la requerida en las últimas certificaciones:

KUBERNETES_VERSION=1.29

sudo mkdir -p /etc/apt/keyrings

curl -fsSL https://pkgs.k8s.io/core:/stable:/v$KUBERNETES_VERSION/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v$KUBERNETES_VERSION/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list

Tras esto ejecutamos:

apt-get update -y

Procedemos a instalar los paquetes necesarios:

sudo apt-get install -y kubelet=1.29.0-1.1 kubectl=1.29.0-1.1 kubeadm=1.29.0-1.1

Como paso final de la instalación de la paquetería, es necesario indicarle a kubernetes que IP va a usar dicho nodo para comunicarse dentro del clúster. Para ello obtenemos la IP de la interfaz a usar y añadiremos la siguiente linea en el fichero /etc/default/kubelet:

KUBELET_EXTRA_ARGS=--node-ip=<X.X.X.X>

5. Iniciar kubeadm en el nodo master

Para iniciar kubeadm en el nodo máster tendremos que ejecutar lo siguiente:


sudo kubeadm init --apiserver-advertise-address=<X.X.X.X> --apiserver-cert-extra-sans=<X.X.X.X> --pod-network-cidr=<Y.Y.Y.Y/YY> --node-name <hostname> --ignore-preflight-errors Swap

En el comando anterior es necesario modificar lo siguiente:

  • X.X.X.X por la IP del nodo
  • Y.Y.Y.Y por la red interna que queremos usar. En mi caso, 10.0.0.0/16
  • hostname por el hostname del nodo

En la salida de este comando, cuando finalice, se deberá ver algo como lo siguiente:


Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.1.170:6443 --token vt9h3t.ccokyp0qhanji48e \
	--discovery-token-ca-cert-hash sha256:fd7735e8a33478b36ec5ac09b5c5d3daf005de67dc9bff7e527147a8f403efee 

Tras esto, es necesario ejecutar los comandos que se nos indica:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Como comprobación, si se ejecuta lo siguiente:

kubectl get po -n kube-system

Se deberá tener una salida similar a :

root@master:~# kubectl get po -n kube-system
NAME                             READY   STATUS    RESTARTS   AGE
coredns-76f75df574-k5jwb         1/1     Running   0          80s
coredns-76f75df574-s97kg         1/1     Running   0          80s
etcd-master                      1/1     Running   0          93s
kube-apiserver-master            1/1     Running   0          95s
kube-controller-manager-master   1/1     Running   0          93s
kube-proxy-9tczx                 1/1     Running   0          80s
kube-scheduler-master            1/1     Running   0          95s

6. Añadir workers al cluster

Para realizar esto, es necesario ejecutar lo indicado en la salida del comando realizado en el punto 5:

kubeadm join 192.168.1.170:6443 --token vt9h3t.ccokyp0qhanji48e \
	--discovery-token-ca-cert-hash sha256:fd7735e8a33478b36ec5ac09b5c5d3daf005de67dc9bff7e527147a8f403efee 

Si se ha perdido dicha salida, podemos recuperar lo necesario para añadir un worker al cluster ejecutando en el nodo master lo siguiente:

kubeadm token create --print-join-command

La salida del comando deberá indicar que el nodo se unió correctamente y desde el nodo máster podremos ejecutar lo siguiente:

kubectl get nodes

Pudiendo ver lo siguiente:

root@master:~# kubectl get nodes
NAME      STATUS   ROLES           AGE   VERSION
master    Ready    control-plane   93m   v1.29.0
worker1   Ready    <none>          90m   v1.29.0

7. Instalación de plugins adicionales:

Por defecto, kubeadm no configura ni instala ningún plugin de red, por lo que según he visto en las guías mencionadas, se usa Calico para ello. Para instalarlo necesitamos ejecutar:

kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml

Tras ejecutar esto podemos comprobar en el nodo master la existencia de los pods correspondientes a este plugin:

root@master:~# kubectl get po -n kube-system
NAME                                       READY   STATUS              RESTARTS   AGE
calico-kube-controllers-658d97c59c-6rzh4   0/1     ContainerCreating   0          10s
calico-node-49d5l                          0/1     Init:0/3            0          10s
calico-node-pwpqt                          0/1     Init:0/3            0          10s
coredns-76f75df574-k5jwb                   1/1     Running             0          4m12s
coredns-76f75df574-s97kg                   1/1     Running             0          4m12s
etcd-master                                1/1     Running             0          4m25s
kube-apiserver-master                      1/1     Running             0          4m27s
kube-controller-manager-master             1/1     Running             0          4m25s
kube-proxy-9tczx                           1/1     Running             0          4m12s
kube-proxy-qkvh8                           1/1     Running             0          97s
kube-scheduler-master                      1/1     Running             0          4m27s

Por último, se instalará un plugin que permite obtener métricas de los nodos. Se hará de la misma forma que el anterior:

kubectl apply -f https://raw.githubusercontent.com/techiescamp/kubeadm-scripts/main/manifests/metrics-server.yaml

Si se ejecuta el comando anterior, podemos ver el pod correspondiente a dicho plugin

root@master:~# kubectl get po -n kube-system
NAME                                       READY   STATUS              RESTARTS   AGE
...
metrics-server-d4dc9c4f-kmh5m              0/1     ContainerCreating   0          48s

Este plugin nos permite ver lo siguiente:

NAME      CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%   
master    135m         6%     1124Mi          60%       
worker1   330m         33%    767Mi           41%       

Conclusión

Kubeadm simplifica mucho el proceso de instalación de un cluster de kubernetes. Tras los pasos realizados, se podría desplegar un pod ya que ya hemos configurado el clúster de forma completa. Al menos, para un funcionamiento básico del mismo