Skip to main content

Instalación con Kubeadm

Vamos a hacer una instalación del cluster usando kubeadm.

La herramienta kubeadm nos ayuda a montar un cluster de varios nodos usando las mejores prácticas de Kubernetes.

Solo para recordar, el cluster Kubernetes consiste en varios componentes, incluyendo el kube-apiserver, etcd, los controladores, etc.

![alt text](/docs/kubernetes/certifications/cka/Installation Configuration Validation/pics/image.png)

Vimos algunos de los requisitos en torno a la seguridad y certificados para permitir la comunicación entre todos los componentes. Instalar todos estos varios componentes individualmente en diferentes nodos y modificar todos los archivos de configuración necesarios para garantizar que todos los componentes apunten unos a otros y establecer certificados para que funcionen es una tarea tediosa.

La herramienta kubeadm nos ayuda, cuidando de todas estas tareas.

Primero, necesitas tener varios sistemas o VMs aprovisionadas. Por lo tanto, vamos a necesitar un par de nodos para formar nuestro cluster Kubernetes. Estas pueden ser máquinas físicas o virtuales. Como es un lab vamos a crear máquinas virtuales usando VirtualBox y Vagrant.

Con Vagrant podemos crear un bootstrap después de que la máquina arranque, colocando los comandos necesarios en un script.

Los comandos para los nodos que serán los master son diferentes de los que serán workers, pero kubeadm nos ayudará también en esta tarea.

Todos los nodos necesitan un container runtime en ejecución. Kubeadm subirá los controllers como pods estáticos dentro de nuestro cluster.

Procedimiento para despliegue del Cluster Kubernetes

  1. Aprovisionamiento de Máquinas (ALL): 1.1. Garantizar que las máquinas cumplan con los requisitos mínimos, incluyendo:

    • Actualización del sistema operativo
    • Deshabilitar el uso de swap.
    • Desactivar el firewall o configurarlo correctamente para permitir el tráfico necesario.
  2. Instalación del Container Runtime (ALL):

    • Instalar containerd en todas las máquinas conforme las instrucciones del fabricante.
  3. Instalación de kubeadm (ALL):

  4. Configuración del Master (MASTER1):

    • En una de las máquinas master vamos a inicializar el cluster usando kubeadm como si fuera single node.
  5. Deploy del CNI para crear la red del cluster (MASTER1):

    • Vamos a desplegar una solución de CNI para conseguir tener la red de nuestro cluster.
  6. Agregar otros masters:

    • En las máquinas designadas como master, ejecutar los comandos específicos de kubeadm para que se unan al cluster con función de master.
  7. Adición de Workers al Cluster:

    • En las máquinas designadas como workers, ejecutar los comandos específicos de kubeadm para que se unan al cluster.

Requisitos

Vamos a necesitar Vagrant instalado y VirtualBox. Es bueno echar un vistazo al enlace https://github.com/kodekloudhub/certified-kubernetes-administrator-course

Crea una carpeta para el proyecto.

mkdir kubeadm-env
cd kubeadm-env

Y vamos a crear un archivo llamado Vagrantfile con este contenido:

NUM_EXTRA_MASTER_NODE = 2
NUM_WORKER_NODE = 2

IP_NW = "192.168.56."
MASTER_IP_START = 11
NODE_IP_START = 20

Vagrant.configure("2") do |config|

config.vm.box = "ubuntu/jammy64"
config.vm.boot_timeout = 900

config.vm.box_check_update = false

# Provision Master Nodes
config.vm.define "kubemaster" do |node|
node.vm.provider "virtualbox" do |vb|
vb.name = "kubemaster01"
vb.memory = 2048
vb.cpus = 2
end
node.vm.hostname = "kubemaster01"
node.vm.network :private_network, ip: IP_NW + "#{MASTER_IP_START}"
node.vm.network "forwarded_port", guest: 22, host: "#{2710}"
end

# Provision Extra Masters
(1..NUM_EXTRA_MASTER_NODE).each do |i|
config.vm.define "kubemaster0#{i+1}" do |node|
node.vm.provider "virtualbox" do |vb|
vb.name = "kubemaster0#{i+1}"
vb.memory = 2048
vb.cpus = 2
end
node.vm.hostname = "kubemaster0#{i+1}"
node.vm.network :private_network, ip: IP_NW + "#{MASTER_IP_START + i + 1}"
node.vm.network "forwarded_port", guest: 22, host: "#{2710 + i + 1}"
end
end

# Provision Worker Nodes
(1..NUM_WORKER_NODE).each do |i|
config.vm.define "kubenode0#{i}" do |node|
node.vm.provider "virtualbox" do |vb|
vb.name = "kubenode0#{i}"
vb.memory = 1024
vb.cpus = 1
end
node.vm.hostname = "kubenode0#{i}"
node.vm.network :private_network, ip: IP_NW + "#{NODE_IP_START + i}"
node.vm.network "forwarded_port", guest: 22, host: "#{2720 + i}"
end
end
end

Este archivo solo subirá 5 VMs, y no ejecutará nada dentro de ellas como el bootstrap. Los pasos 1, 2 y 3 sirven para todos los nodos, entonces vamos a montar un script para hacer un bootstrap general.

#!/bin/bash

echo "##### Actualizar Sistema #####"
sudo apt-get update
sudo apt-get upgrade -y

## Siguiendo la documentación https://kubernetes.io/docs/setup/production-environment/
## https://docs.docker.com/engine/install/ubuntu/container-runtimes/

echo "##### Desactivando el swap #####"
sed -i '/swap/d' /etc/fstab
swapoff -a

echo "##### Desactivando el firewall #####"
systemctl disable --now ufw >/dev/null 2>&1

echo "##### Activando módulos del kernel necesarios para containerd #####"
cat >>/etc/modules-load.d/containerd.conf<<EOF
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter

echo "##### Corrección de Módulos #####"
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

# Parámetros sysctl requeridos por setup, parámetros persisten entre reinicios
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

# Aplicar parámetros sysctl sin reinicio
sudo sysctl --system

# Verificando
sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward

echo "##### Instalar Containerd #####"

sudo apt-get update
sudo apt-get install ca-certificates curl

echo "##### Instalar Containerd: Agregar clave GPG oficial de Docker #####"

sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

echo "##### Instalar Containerd: Agregar el repositorio a las fuentes Apt #####"

echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

echo "##### Instalar Containerd: Instalar Paquetes #####"

sudo apt-get update
sudo apt-get install containerd.io -y

echo "##### Instalar Containerd: Verificando Servicio #####"
systemctl status containerd.service

echo "##### Instalar Containerd: Corregir Cgroups Driver para systemd #####"

sudo mv /etc/containerd/config.toml /etc/containerd/config.toml.default

sudo tee /etc/containerd/config.toml > /dev/null <<EOF
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
EOF

sudo systemctl restart containerd.service
systemctl status containerd.service

#https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
echo "##### Instalar Kubeadm kubelet y kubectl #####"
sudo apt-get install -y apt-transport-https ca-certificates curl gpg

echo "##### Instalar Kubeadm kubelet y kubectl: Agregar clave GPG oficial de Kubernetes"
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

echo "##### Instalar Kubeadm kubelet y kubectl: Agregar el repositorio a las fuentes Apt"
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update

echo "##### Instalar Kubeadm kubelet y kubectl: Instalar Paquetes #####"
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

Hice todos los comentarios necesarios en este script para que entiendas lo que vamos a hacer, ahora vamos a agregar este script en todas las VMs.

Guarda este script en la misma carpeta donde está el Vagrantfile con el nombre de bootstrap.sh

ls -lha
total 20K
drwxrwxr-x 3 david-prata david-prata 4,0K fev 25 02:12 .
drwxrwxr-x 3 david-prata david-prata 4,0K fev 24 21:02 ..
-rw-rw-r-- 1 david-prata david-prata 3,0K fev 25 02:15 bootstrap.sh
-rw-rw-r-- 1 david-prata david-prata 1,7K fev 25 02:17 Vagrantfile

Vamos a agregar las líneas abajo comentadas.

NUM_EXTRA_MASTER_NODE = 2
NUM_WORKER_NODE = 2

IP_NW = "192.168.56."
MASTER_IP_START = 11
NODE_IP_START = 20

Vagrant.configure("2") do |config|

config.vm.box = "ubuntu/jammy64"
config.vm.boot_timeout = 900

config.vm.box_check_update = false

# Provision Master Nodes
config.vm.define "kubemaster" do |node|
# Nombre mostrado en la interfaz gráfica
node.vm.provider "virtualbox" do |vb|
vb.name = "kubemaster01"
vb.memory = 2048
vb.cpus = 2
end
node.vm.hostname = "kubemaster01"
node.vm.network :private_network, ip: IP_NW + "#{MASTER_IP_START}"
node.vm.network "forwarded_port", guest: 22, host: "#{2710}"
node.vm.provision "shell", path: "bootstrap.sh" ## AGREGADO
end

# Provision Extra Masters
(1..NUM_EXTRA_MASTER_NODE).each do |i|
config.vm.define "kubemaster0#{i+1}" do |node|
node.vm.provider "virtualbox" do |vb|
vb.name = "kubemaster0#{i+1}"
vb.memory = 2048
vb.cpus = 2
end
node.vm.hostname = "kubemaster0#{i+1}"
node.vm.network :private_network, ip: IP_NW + "#{MASTER_IP_START + i + 1}"
node.vm.network "forwarded_port", guest: 22, host: "#{2710 + i + 1}"
node.vm.provision "shell", path: "bootstrap.sh" ## AGREGADO
end
end

# Provision Worker Nodes
(1..NUM_WORKER_NODE).each do |i|
config.vm.define "kubenode0#{i}" do |node|
node.vm.provider "virtualbox" do |vb|
vb.name = "kubenode0#{i}"
vb.memory = 1024
vb.cpus = 1
end
node.vm.hostname = "kubenode0#{i}"
node.vm.network :private_network, ip: IP_NW + "#{NODE_IP_START + i}"
node.vm.network "forwarded_port", guest: 22, host: "#{2720 + i}"
node.vm.provision "shell", path: "bootstrap.sh" ## AGREGADO
end
end
end

En cada nodo entonces estaremos preparados para montar el cluster.

En el primer nodo master necesitamos inicializar el cluster.

kubeadm init --apiserver-advertise-address=<IP DEL MASTER> --pod-network-cidr=<RED QUE TENDRÁN LOS PODS>

Nosotros ya sabemos cuál es la IP de los masters. Definimos en el Vagrantfile las IPs de forma automática.

IPSNODE
kubemaster01192.168.56.11
kubemaster02192.168.56.12
kubemaster03192.168.56.13
kubemaster01192.168.56.21
kubemaster01192.168.56.22

Vamos a definir la red que será usada por los pods para 10.244.0.0/16, podría ser otra, claro.

Entonces el comando sería: kubeadm init --apiserver-advertise-address=192.168.56.11 --pod-network-cidr=10.244.0.0/16

Podemos automatizar esto.

IP_ADDR=`ip -4 address show enp0s8 | grep inet | awk '{print $2}' | cut -d/ -f1`
POD_CIDR="10.244.0.0/16"

echo "##### Inicializando el cluster #####"
sudo kubeadm init --apiserver-advertise-address=$IP_ADDR --pod-network-cidr=$POD_CIDR

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

Vamos a hablar un poco sobre este comando. No tenemos aquí un load balancer frente a nuestro cluster. Si lo tuviéramos deberíamos pasar algunos parámetros adicionales en este comando --control-plane-endpoint=<IP_LB:6443> --upload-certs.

El comando init hace pull de algunas imágenes que serán usadas automáticamente. Si quieres hacer el pull de estas imágenes antes de ejecutar el comando init para ser más rápido es posible, pero no es obligatorio. kubeadm config images pull.

Ejecutando el script arriba tenemos el cluster con un único nodo master. En la consola muestra los comandos necesarios que debemos continuar ejecutando, mira la salida.

###
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.56.11:6443 --token 9n1vty.afan7eb99kaxltcj \
--discovery-token-ca-cert-hash sha256:6ebd7fd44972113263b6e85e8bdddd841774a39db9a48bd04d575c034c188247

El kubeconfig generado queda en /etc/kubernetes/admin.conf. Necesitamos de él para que kubectl funcione, luego vamos a colocar los comandos arriba dentro de nuestro script. Otra cosa que necesitamos hacer es instalar un CNI en el cluster. Ya vamos también a aplicarlo en el cluster usando kubectl que acabamos de hacer funcionar. Podríamos usar otras opciones de CNI, pero vamos a elegir Weavenet.

IP_ADDR=`ip -4 address show enp0s8 | grep inet | awk '{print $2}' | cut -d/ -f1`
POD_CIDR="10.244.0.0/16"

echo "##### Init Cluster #####"
sudo kubeadm init --apiserver-advertise-address=$IP_ADDR --pod-network-cidr=$POD_CIDR

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

##https://github.com/weaveworks/weave?tab=readme-ov-file
echo "##### Deploy CNI weavenet #####"
kubectl apply -f https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n') >/dev/null 2>&1

kubeadm token create --certificate-key $(kubeadm init phase upload-certs --upload-certs | tail -1 ) --print-join-command > /join_masters.sh