Instalación k8s en Ubuntu Server 20.04 LTS

por | 18 octubre, 2021

En este post, vamos a ver cómo realizar la instalación de un clúster Kubernetes ( también conocido cómo k8s ), en el sistema operativo Ubuntu Server 20.04 LTS.

Características de nuestra infraestructura

Nuestra infraestructura, va a contar los siguientes elementos:

Rol de ServerHostnameRecursosIP Red InternaIP Adaptador Puente
Masterk8smaster.k8s.local4GB Ram, 2vcpus10.10.18.1/24192.168.1.3/24
Worker1k8snode1.k8s.local4GB Ram, 2vcpus10.10.18.2/24192.168.1.4/24
Worker2k8snode2.k8s.local4GB Ram, 2vcpus10.10.18.3/24192.168.1.5/24
Nodos & Recursos

Todos los nodos de este tutorial están virtualizados. En mi caso, he utilizado la herramienta VirtualBox, disponible tanto para Windows, Mac y Linux.

Todos los nodos, dispondrán de dos tarjetas de red, una en modo «Adaptador puente (Bridge)» y otra en modo «Red Interna». La primera, la utilizaremos para conectarnos a los nodos por SSH desde Putty o cualquier otra herramienta, la segunda, será la red interna por la cual se comunicarán los nodos del clúster de k8s.

Documentación oficial de Kubernetes

Antes de comenzar, es recomendable conocer la infraestructura de Kubernetes, por lo tanto, en estos casos, lo mejor es leer la documentación del fabricante:

https://kubernetes.io/es/docs/home/

Requerimientos mínimos

Hay dos tipos de servidores utilizados en el despliegue de clusters Kubernetes:

  • Maestro: Un maestro de Kubernetes es donde se ejecutan las llamadas a la API de control para los pods, controladores de réplica, servicios, nodos y otros componentes de un clúster de Kubernetes.
  • Nodo: Un Nodo es un sistema que proporciona los entornos de ejecución para los contenedores. Un conjunto de pods de contenedores puede abarcar varios nodos.


Los requisitos mínimos para una configuración viable son:

  • Memoria: 2 GiB o más de RAM por máquina
  • CPUs: Al menos 2 CPUs en la máquina del control plane
  • Se requiere conectividad a Internet para el pull de los contenedores (también se puede utilizar un registro privado)
  • Conectividad de red completa entre las máquinas del cluster – Esta es privada o pública

Configuración de la red de los nodos

En este paso, se da por entendido que todos sabéis instalar Ubuntu Server sin problema, con una instalación básica y el servidor OpenSSH habilitado es más que suficiente.

Por lo tanto, aclarado este punto, dejo por aquí la configuración de los ficheros de red y el /etc/hosts que debeis configurar en cada máquina, adaptando lo necesario a vuestras necesidades / gustos personales.

Nodo Master

Fichero de red
[email protected]:~$ cat /etc/netplan/00-installer-config.yaml
# This is the network config written by 'subiquity'
network:
  ethernets:
    enp0s3:
      addresses:
      - 192.168.1.3/24
      gateway4: 192.168.1.1
      nameservers:
        addresses:
        - 8.8.8.8
        - 8.8.4.4
    enp0s8:
      addresses:
      - 10.10.18.1/24
      nameservers:
        addresses: []
        search: []
  version: 2
Fichero /etc/hosts
[email protected]:~$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 k8smaster
10.10.18.1      k8smaster.k8s.local     k8smaster
10.10.18.2      k8snode1.k8s.local      k8snode1
10.10.18.3      k8snode2.k8s.local      k8snode2
 
# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
Red y hosts Master

Nodo Worker1

Fichero de red
[email protected]:~$ cat /etc/netplan/00-installer-config.yaml
# This is the network config written by 'subiquity'
network:
  ethernets:
    enp0s3:
      addresses:
      - 192.168.1.4/24
      gateway4: 192.168.1.1
      nameservers:
        addresses:
        - 8.8.8.8
        - 8.8.4.4
    enp0s8:
      addresses:
      - 10.10.18.2/24
      nameservers:
        addresses: []
        search: []
  version: 2
Fichero /etc/hosts
[email protected]:~$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 k8snode1
10.10.18.1      k8smaster.k8s.local     k8smaster
10.10.18.2      k8snode1.k8s.local      k8snode1
10.10.18.3      k8snode2.k8s.local      k8snode2
 
# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
Red y hosts Worker1

Nodo Worker2

Fichero de red
[email protected]~$ cat /etc/netplan/00-installer-config.yaml
# This is the network config written by 'subiquity'
network:
  ethernets:
    enp0s3:
      addresses:
      - 192.168.1.5/24
      gateway4: 192.168.1.1
      nameservers:
        addresses:
        - 8.8.8.8
        - 8.8.4.4
    enp0s8:
      addresses:
      - 10.10.18.3/24
      nameservers:
        addresses: []
        search: []
  version: 2
Fichero /etc/hosts
[email protected]:~$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 k8snode2
10.10.18.1      k8smaster.k8s.local     k8smaster
10.10.18.2      k8snode1.k8s.local      k8snode1
10.10.18.3      k8snode2.k8s.local      k8snode2
 
# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
Red y hosts Worker2

Paso 1. Actualización de paquetería y reinicio de nodos

Antes de empezar ya definitivamente con el proceso de instalación de Kubernetes en sí, es recomendable realizar una actualización de la paquetería. Estos pasos los realizaremos en todos los nodos del clúster.

sudo apt update
sudo apt -y upgrade && sudo systemctl reboot

Paso 2. Instalar kubelet, kubeadm y kubectl

Cuando se reinicien los nodos, ejecutar de nuevo en todos los siguientes comandos:

sudo apt update

sudo apt -y install curl apt-transport-https

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -

echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list

Con lo anterior, añadimos los repos necesarios para instalar los paquetes de kubernetes. Ahora procedemos con la instalación:

sudo apt update

sudo apt -y install vim git curl wget kubelet kubeadm kubectl

sudo apt-mark hold kubelet kubeadm kubectl

Para confirmar la versión que se ha instalado, ejecutar:

kubectl version --client && kubeadm version


Client Version: version.Info{Major:»1″, Minor:»22″, GitVersion:»v1.22.2″, GitCommit:»8b5a19147530eaac9476b0ab82980b4088bbc1b2″, GitTreeState:»clean», BuildDate:»2021-09-15T21:38:50Z», GoVersion:»go1.16.8″, Compiler:»gc», Platform:»linux/amd64″}

kubeadm version: &version.Info{Major:»1″, Minor:»22″, GitVersion:»v1.22.2″, GitCommit:»8b5a19147530eaac9476b0ab82980b4088bbc1b2″, GitTreeState:»clean», BuildDate:»2021-09-15T21:37:34Z», GoVersion:»go1.16.8″, Compiler:»gc», Platform:»linux/amd64″}


Paso 3. Deshabilitar la swap.

En el fichero /etc/fstab, de todos los nodos, comentar la siguiente línea:

Ejecutar también:

sudo swapoff -a

Ahora, habilitamos los módulos del kernel necesarios y configuramos sysctl:

# Habilitar modulos del kernel

sudo modprobe overlay

sudo modprobe br_netfilter

# Añadir la configuracion de kubernetes a sysctl

sudo tee /etc/sysctl.d/kubernetes.conf<<EOF

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

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

net.ipv4.ip_forward = 1

EOF

# Recargar sysctl

sudo sysctl --system

Paso 4. Instalar runtime de contenedores.

En nuestro caso, vamos a instalar solo Docker, aunque kubernetes soporta los siguientes. Repetir esta instalación en todos los nodos:

  • Docker
  • CRI-O
  • Containerd

NOTA: solo es posible elegir un runtime a la vez.

Instalación del runtime Docker

# Configuración de nuevos repositorios e instalación

sudo apt update

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

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

sudo apt update

sudo apt install -y containerd.io docker-ce docker-ce-cli

# Creación de los directorios requeridos

sudo mkdir -p /etc/systemd/system/docker.service.d

# Creación del fichero json para el demonio de Docker

sudo tee /etc/docker/daemon.json <<EOF

{

  "exec-opts": ["native.cgroupdriver=systemd"],

  "log-driver": "json-file",

  "log-opts": {

    "max-size": "100m"

  },

  "storage-driver": "overlay2"

}

EOF

# Iniciar y habilitar servicios

sudo systemctl daemon-reload

sudo systemctl restart docker

sudo systemctl enable docker

Paso 5. Inicialización del nodo Master.

Iniciar sesión en el nodo Master y asegurarse de que el modulo «br_netfilter» está cargado (loaded):

[email protected]:~$ lsmod | grep br_netfilter

br_netfilter           28672  0

bridge                176128  1 br_netfilter

Habilitar el servicio de kubelet:

sudo systemctl enable kubelet

Ahora debemos inicializar la máquina que ejecutará los componentes del control plane, que incluye etcd (la base de datos del clúster) y el servidor API.

Hacemos un pull de las imágenes de los contenedores:

[email protected]:~$ sudo kubeadm config images pull

[config/images] Pulled k8s.gcr.io/kube-apiserver:v1.22.2

[config/images] Pulled k8s.gcr.io/kube-controller-manager:v1.22.2

[config/images] Pulled k8s.gcr.io/kube-scheduler:v1.22.2

[config/images] Pulled k8s.gcr.io/kube-proxy:v1.22.2

[config/images] Pulled k8s.gcr.io/pause:3.5

[config/images] Pulled k8s.gcr.io/etcd:3.5.0-0

[config/images] Pulled k8s.gcr.io/coredns/coredns:v1.8.4

NOTA: Estas son las opciones básicas de kubeadm init que se utilizan para arrancar el cluster.

–control-plane-endpoint :  establece el punto final compartido para todos los nodos del plano de control. Puede ser DNS/IP
–pod-network-cidr : Se utiliza para establecer el CIDR del network add-on  de los pods
–cri-socket : Se utiliza si se tiene más de un runtime de contenedores, para establecer la ruta del socket en tiempo de ejecución
–apiserver-advertise-address : Establece la dirección IP para el servidor API de este nodo de control en particular
kubeadm opciones basicas

Creación del clúster:

[email protected]:~$ sudo kubeadm init --pod-network-cidr=10.20.0.0/16 --upload-certs --control-plane-endpoint=k8smaster.k8s.local

10.20.0.0/16 –> Podemos poner cualquier subred interna que no esté en uso

INFO: el socket del runtime de Docker se encuentra en /var/run/docker.sock

Ahora, es necesario configurar kubectl, utilizando los comandos de la salida del comando anterior:

mkdir -p $HOME/.kube

sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

sudo chown $(id -u):$(id -g) $HOME/.kube/config

Comprobar el estado del clúster:

[email protected]:~$ kubectl cluster-info

Kubernetes control plane is running at https://k8smaster.k8s.local:6443

CoreDNS is running at https://k8smaster.k8s.local:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

Paso 6. Instalar el plugin de red en el nodo Master.

kubectl create -f https://docs.projectcalico.org/manifests/tigera-operator.yaml 

kubectl create -f https://docs.projectcalico.org/manifests/custom-resources.yaml

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

Confirmar que todos los pods están corriendo:

[email protected]:~$ watch kubectl get pods --all-namespaces

NAMESPACE              NAME                                        READY   STATUS    RESTARTS      AGE

kube-system            coredns-78fcd69978-2btfq                    1/1     Running   0             163m

kube-system            coredns-78fcd69978-p8v5h                    1/1     Running   0             163m

kube-system            etcd-k8smaster                              1/1     Running   0             164m

kube-system            kube-apiserver-k8smaster                    1/1     Running   0             164m

kube-system            kube-controller-manager-k8smaster           1/1     Running   5             164m

kube-system            kube-flannel-ds-amd64-7td96                 1/1     Running   4 (42m ago)   43m

kube-system            kube-flannel-ds-amd64-v6pd7                 1/1     Running   4 (42m ago)   43m

kube-system            kube-flannel-ds-kz6jl                       1/1     Running   0             42m

kube-system            kube-flannel-ds-pt7rh                       1/1     Running   0             42m

kube-system            kube-proxy-hpn8w                            1/1     Running   0             163m

kube-system            kube-proxy-lmmng                            1/1     Running   0             46m

kube-system            kube-scheduler-k8smaster                    1/1     Running   5             164m

kubernetes-dashboard   dashboard-metrics-scraper-c45b7869d-lr6w4   1/1     Running   0             76m

kubernetes-dashboard   kubernetes-dashboard-576cb95f94-66nww       1/1     Running   0             76m

tigera-operator        tigera-operator-59f4845b57-vnfbj            1/1     Running   0             144m

Confirmar que el nodo Master está en estado «Ready»:

[email protected]:~$ kubectl get nodes -o wide

NAME        STATUS   ROLES                  AGE    VERSION   INTERNAL-IP   EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION     CONTAINER-RUNTIME

k8smaster   Ready    control-plane,master   165m   v1.22.2   10.10.18.1    <none>        Ubuntu 20.04.3 LTS   5.4.0-88-generic   docker://20.10.9

Paso 7. Añadir nodos Workers al clúster.

Con el comando que nos ha salido por pantalla, al iniciar el clúster, en los nodos workers, ejecutar:

[email protected]:~$ sudo kubeadm join k8smaster.k8s.local:6443 --token iu5vzp.egr842fe327y3hl3 --discovery-token-ca-cert-hash sha256:d107e3cb6333c7c64feaae90cbd0e35624b5c1e8e84191692420ee7574872713 --v=5

[email protected]:~$ sudo kubeadm join k8smaster.k8s.local:6443 --token iu5vzp.egr842fe327y3hl3 --discovery-token-ca-cert-hash sha256:d107e3cb6333c7c64feaae90cbd0e35624b5c1e8e84191692420ee7574872713 --v=5

Ahora, desde el nodo Master, comprobar el estado de los nodos:

[email protected]:~$ kubectl get nodes

NAME        STATUS   ROLES                  AGE    VERSION

k8smaster   Ready    control-plane,master   177m   v1.22.2

k8snode1    Ready    <none>                 60m    v1.22.2

k8snode2    Ready    <none>                 34s    v1.22.2

Paso 8. Desplegar aplicación en el clúster para pruebas.

Desplegamos:

[email protected]:~$ kubectl apply -f https://k8s.io/examples/pods/commands.yaml

pod/command-demo created

Comprobamos:

[email protected]:~$ kubectl get pods

NAME           READY   STATUS      RESTARTS   AGE

command-demo   0/1     Completed   0          25s

Paso 9. Instalar el panel de control de Kubernetes (opcional).

El panel de control de Kubernetes puede utilizarse para desplegar aplicaciones en un clúster de Kubernetes, solucionar problemas de su aplicaciónes y gestionar los recursos del clúster.

Desplegar el panel de control de Kubernetes

Para desplegar el panel de control, ejecutar, en el nodo Master:

[email protected]:~$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended.yaml

Configurar el servicio para usar NodePort

Esto utilizará los valores por defecto para el despliegue. Si quieres hacer algunas modificaciones en el archivo, tendrás que descargarlo en tu máquina local. Para ello, lo descargamos y lo editamos:

[email protected]:~$ wget https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended.yaml -O kubernetes-dashboard-deployment.yml

[email protected]:~$ vim kubernetes-dashboard-deployment.yml

Modificaremos el servicio del panel de control de Kubernetes para que sea del tipo «NodePort»:

kind: Service

apiVersion: v1

metadata:

  labels:

    k8s-app: kubernetes-dashboard

  name: kubernetes-dashboard

  namespace: kubernetes-dashboard

spec:

  ports:

    - port: 443

      targetPort: 8443

      nodePort: 32000

  type: NodePort

  selector:

    k8s-app: kubernetes-dashboard

Guardar los cambios y salir del fichero.

INFO: NodePort expone el Servicio en la IP de cada Nodo en un puerto estático (el NodePort). Un ClusterIP Service, al que el Servicio NodePort se dirige, se crea automáticamente.

Aplicamos los cambios cuando hayamos modificado lo anterior:

[email protected]:~$ kubectl apply -f kubernetes-dashboard-deployment.yml

Para comprobar que el nuevo despliegue se ha lanzado correctamente, ejecutar:

[email protected]:~$ kubectl get deployments -n kubernetes-dashboard

NAME                        READY   UP-TO-DATE   AVAILABLE   AGE

dashboard-metrics-scraper   1/1     1            1           102m

kubernetes-dashboard        1/1     1            1           102m

Ya que hemos cambiado el tipo de servicio a NodePort, confirmamos si el servicio fue realmente creado, ejecutando:

[email protected]:~$ kubectl get service -n kubernetes-dashboard

NAME                        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)         AGE

dashboard-metrics-scraper   ClusterIP   10.110.254.40    <none>        8000/TCP        103m

kubernetes-dashboard        NodePort    10.101.147.103   <none>        443:32000/TCP   103m

Acceder al panel de control vía web

Cómo hemos desplegado el servicio en el puerto 32000/TCP, para acceder a él, basta con acceder a la IP de la tarjeta de red de tipo «Adaptador puente» de la máquina virtual del nodo Master, en mi caso, la 192.168.1.3:

https://192.168.1.3:32000

Kubernetes Dashboard 01

Para poder entrar, vamos a crear un usuario administrador de kubernetes específico para esto.

En el nodo maestro, crear un fichero de nombre «admin-sa.yml», con el siguiente contenido:

[email protected]:~$ cat admin-sa.yml

apiVersion: v1

kind: ServiceAccount

metadata:

  name: devops

  namespace: kube-system

Aplicar el manifiesto YAML para crear la nueva cuenta:

[email protected]:~$ kubectl apply -f admin-sa.yml

El siguiente paso, es otorgarle de privilegios a este nuevo usuario, para ello, crear un fichero de nombre «admin-rbac.yml», con el siguiente contenido:

[email protected]:~$ cat admin-rbac.yml

apiVersion: rbac.authorization.k8s.io/v1

kind: ClusterRoleBinding

metadata:

  name: devops

roleRef:

  apiGroup: rbac.authorization.k8s.io

  kind: ClusterRole

  name: cluster-admin

subjects:

  - kind: ServiceAccount

    name: devops

    namespace: kube-system

Aplicar de nuevo el manifiesto YAML que hemos creado:

[email protected]:~$ kubectl apply -f admin-rbac.yml

Por último, ahora necesitamos saber el Token del usuario para poder acceder al panel de control de Kubernetes. Para ello, ejecutamos lo siguiente:

[email protected]:~$ echo SA_NAME="devops" >> $HOME/.profile

[email protected]:~$ echo export $SA_NAME >> $HOME/.profile

[email protected]:~$ SA_NAME="devops"

[email protected]:~$ export $SA_NAME

Para obtener el token:

[email protected]:~$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep ${SA_NAME} | awk '{print $1}')

[email protected]:~$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep ${SA_NAME} | awk '{print $1}')

Name:         devops-token-5cwf5

Namespace:    kube-system

Labels:       <none>

Annotations:  kubernetes.io/service-account.name: devops

              kubernetes.io/service-account.uid: 304525f1-d4b2-4e42-8a66-4a501b4ff325

Type:  kubernetes.io/service-account-token

Data

====

namespace:  11 bytes

token:      eyJhbGciOiJSUzI1NiIsImtpZCI6Ik54N2RJTjBLSE5vR3drQkZqQ1V6QnNaN3BrUTcyQTR0Zy1LNGFYLXI3ckkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkZXZvcHMtdG9rZW4tNWN3ZjUiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGV2b3BzIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiMzA0NTI1ZjEtZDRiMi00ZTQyLThhNjYtNGE1MDFiNGZmMzI1Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRldm9wcyJ9.bRLZwfem6JESvkBXi_IZr_gfIzGb4MieQysBnimPndWr293GubyeNGSKaqtFM0P7R4xMdMBebqvRnVpCEQ-Xl9CI2iG76vthIK1g1WQeFFHmMBUr91DzDu6Jr8yqmCEBCsvQQuVdqJKk1pKryaVMB08JzIO91-CeYMNvPIkUa5_SXddw5vEnEcuLC6_ySulJcHRCMgqlyHgjTp8CKPXmKdIWc5KwoZKU9eSTNW19zd9GKOD0yEHE-TXN6HCvtLXNTpplx8ZDe3QZMJHksyn3x-_w_eOYhujxfx4m-BrzjPgr4OHnF04LicNwVHJUs6HP_2gtfEZ_41BO7UFi7JOgDw

ca.crt:     1099 bytes

Con este token, acceder de nuevo al panel de control de Kubernetes y elegir esta opción:

Kubernetes Dashboard 02

El resultado tras iniciar sesión, será similar a esto:

Kubernetes Dashboard 03
Kubernetes Dashboard 04

Pues hasta aquí el post de hoy, espero que os sea de utilidad y cualquier duda / sugerencia no olvidéis escribirlas en los comentarios (os leo).

Un saludo Sysadmins! 🙂

Enlaces de interés:

Documentación oficial de k8s: https://kubernetes.io/es/docs/home/

Documentación oficial de Docker: https://docs.docker.com/

Instalación de clúster k8s en Ubuntu Server: https://computingforgeeks.com/deploy-kubernetes-cluster-on-ubuntu-with-kubeadm/

Instalación de dashboard k8s: https://computingforgeeks.com/how-to-install-kubernetes-dashboard-with-nodeport/

Creación de usuario admin del dashboard de k8s: https://computingforgeeks.com/create-admin-user-to-access-kubernetes-dashboard/

About: Miguel Carretas Perulero

Miguel Carretas Perulero ha escrito 83 artículos en este blog.

Deja un comentario