Ejecutando Kubernetes en el ODROID-N2: Crea un Sistema de Organización de Contenedores de Eficiencia Energética

El despliegue de aplicaciones en contenedores es la nueva moda. Con ello, surge la necesidad de automatizar las implementaciones, ampliarlas para satisfacer el aumento de las cargas de trabajo y administrar su ciclo de vida. Kubernetes (K8s) es una de esas utilidades que cubre estas necesidades.

Los contenedores se pueden implementar en dispositivos de hardware de muy diversas características y de una amplia gama de precios. Un ordenador de placa reducida (SBC) totalmente autónoma es uno de esos dispositivos. El ODROID-N2 es un SBC altamente capacitado, y lo convierte en un dispositivo K8 muy rentable. Está disponible con 4 GB de RAM, una tarjeta eMMC como almacenamiento de alta velocidad y una gran variedad de soporte E/S. Junto con la fuente de alimentación, cuesta menos de 100 euros (~ 115$). Con 4 + 2 núcleos de CPU ARM64, el ODROID-N2 es una plataforma interesante para poner en marcha un pequeño clúster K8 con un gasto energético relativamente bajo. Incluso se puede utilizar para experimentar con una implementación K8 de bajo coste, antes de implementar soluciones más costosas.

Este artículo explica cómo configurar K8 en el ordenador de placa reducida ODROID-N2. Dado que hay varias opciones para los sistemas operativos, así como los métodos de distribución y configuración de K8, este artículo parte de las siguientes premisas:

  • Utiliza Arch Linux ARM64 como sistema operativo base (éste es bastante sencillo y está muy actualizado)
  • Vanilla K8s se usará, compilará y empaquetará como paquetes Arch ARM64 en el ODROID-N2
  • Se usará Kubeadm plano para configurar el clúster K8
  • CRI-O como tiempo de ejecución del contenedor (en lugar de Docker)
  • Nodo maestro único y 4 nodos de trabajo

Desafortunadamente, no hay soporte del kernel Linux estándar para ODROID-N2. Sin embargo, Hardkernel ha prometido trabajar en ello. Las siguientes características no funcionan actualmente como cabría esperar:

  • zram para memoria comprimida como dispositivo de intercambio
  • Deshabilita la asignación de memoria de la GPU para usar los 2GB/4GB completos del ODROID-N2

Las experiencias anteriores con Arch Linux ARM 64bit y K8s en Raspberry Pi y ODROID (ODROID-C2 para ser precisos) las puedes encontrar aquí:

Kubernetes auf Raspberry Pi (https://bit.ly/30PDiyu) Kubernetes auf ODROID mit zram (https://bit.ly/2OgXjMX) Kubernetes auf Arch Linux ARM (https://bit.ly/2LEsqzT) Kubernetes mit CRI-O auf Arch Linux ARM (https://bit.ly/2JR5nj4) Kubernetes mit CRI-O Worker auf Arch Linux ARM64 (https://bit.ly/2Y6pl21) Kubernetes auf ODROID Arch Linux ARM Mainline Kernel (https://bit.ly/2JT9A6b) Kubernetes Dashboard auf ARM 64 (https://bit.ly/2LIVPZR)

Instalando Arch Linux en ODROID-N2

Arch Linux es bastante fácil de configurar. Las instrucciones generales de instalación las puedes encontrar aquí: https://bit.ly/2JSlahB. Para facilitar la configuración de múltiples nodos, se puede recurrir a la programación para semi-automatizar el tema del almacenamiento (eMMC o tarjeta SD) y extraer el sistema base. Puesto que debemos aplicar varias personalizaciones, como la copia de claves SSH, la configuración de los derechos sudo y la configuración del nombre de host, realmente vale la pena recurrir a la automatización.

Tras la instalación, también se instalan los siguientes paquetes

  • sudo, htop
  • socat, ethtool, ebtables (para redes K8s CNI)
  • cpupower (reduce el consumo de energía permitiendo regular la CPU durante los períodos de inactividad)
  • nfs-utils (si el almacenamiento NFS se va a usar con K8)

Para utilizar los 6 núcleos de la CPU al comprimir los paquetes de Arch Linux, se pueden configurar los siguientes parámetros en /etc/makepkg.conf:

COMPRESSXZ=(xz -T0 -c -z -)
Esto configurará la compresión multiproceso para la creación de paquetes de Arch Linux

Compilando paquetes K8s Arch Linux ARM 64

Al principio, los paquetes actualizados para K8 y los servicios de soporte serán compilados como paquetes de Arch Linux. Se recomienda crear un directorio para cada paquete que se compile y colocar el archivo PKGBUILD en cada uno.

Puedes encontrar los archivos PKGBUILD usados aquí:

  • runc
  • CNI-Plugins
  • CRI-O
  • CRI Tools
  • Kubernetes, Install File

Por lo general, la creación de un paquete se lleva cabo introduciendo el siguiente comando en cada directorio:

$ makepkg -s
De momento, se pueden compilar todos los paquetes, excepto el paquete Kubernetes Arch.

Para los K8, se deben realizar algunos pasos específicos, ya que una compilación de Kubernetes requiere muchos recursos: en un ODROID-N2 de 4GB es posible llevar a cabo una compilación sin memoria de intercambio adicional, pero como mínimo se necesitan unos 3.5 GB. Si vas a utilizar un modelo de 2 GB, puedes añadir un archivo de intercambio swap:

$ sudo fallocate -l 1000M /swapfile
$ sudo mkswap /swapfile
$ sudo swapon /swapfile
Además del archivo de intercambio, debemos realizar dos configuraciones. El kernel debe permitir exceder la memoria disponible en lugar de estar expectante a la hora de asignar memoria:
$ sudo sysctl -w vm.overcommit_memory=1
y se debe evitar que la cadena de compilación realice compilaciones paralelas con el número de núcleos disponibles, lo cual aumentaría el consumo de memoria:
export GOFLAGS="-p=1"
Aunque cada compilación en sí no se ejecutará en paralelo, cada parte del paquete de Kubernetes puede aprovechar todos los núcleos durante su compilación individual, evitando así reducciones importantes del rendimiento. Dado que Arch usa un sistema de archivos tmpfs para /tmp, primero debemos desmontarlo, de lo contrario, la memoria se asignará a compilaciones temporales y posiblemente conduzca a un estado de "sin memoria":
$ sudo umount /tmp
Una vez finalizadas estas configuraciones, se puede compilar K8:
$ makepkg -s
Cuando se complete la compilación, los siguientes paquetes deberían estar presentes:
cni-plugins-0.7.5-1-aarch64.pkg.tar.xz
cri-o-1.14.0-1-aarch64.pkg.tar.xz
crictl-bin-1.14.0-1-aarch64.pkg.tar.xz
runc-1.0.0rc8-1-aarch64.pkg.tar.xz
kubernetes-1.14.1-1-aarch64.pkg.tar.xz
Estos paquetes ahora se pueden distribuir a todos los nodos ODROID-N2 que participan en el clúster. Por supuesto, también se pueden usar otras máquinas, siempre que todas ellas sean plataformas de hardware ARM64.

Configuración general del nodo ODROID-N2 K8s

Antes de instalar los paquetes, debemos configurar ciertas cosas para que el funcionamiento de la red de contenedores sea el correcto

Las siguientes características del kernel deben estar presentes, de lo contrario, la red K8s no funcionará y podría conducir a errores realmente difíciles de diagnosticar como los siguientes:

iptables: No chain/target/match by that name
Unexpected command output Device 'eth0' does not exist:

  • CGROUP_PIDS
  • NETFILTER_XTABLES, XT_SET

Si al kernel le falta algún servicio o características, como se muestra en el siguiente resultado, la solución más rápida es compilar un nuevo paquete de kernel que incluya las características necesarias.

$ zgrep XT_SET /proc/config.gz
# CONFIG_NETFILTER_XT_SET is not set
$ zgrep CONFIG_NETFILTER_XTABLES /proc/config.gz
CONFIG_NETFILTER_XTABLES=m

Verificación de las características del kernel para K8s CNI

La compilación es bastante fácil, ya que el paquete de kernel Arch Linux se puede compilar utilizando las herramientas habituales. Para acelerar el proceso de compilación, se recomienda editar /etc/makepkg.conf y habilitar la compilación multiproceso usando MAKEFLAGS = "- j6", que contempla los 6 núcleos disponibles en el ODROID-N2.

$ git clone https://github.com/everflux/PKGBUILDs.git
$ cd PKGBUILDs/core/linux-odroid-n2
$ git checkout patch-1
$ makepkg -s
La instalación del paquete del kenel se realiza con pacman. Luego configuraremos la red.
$ sudo sh -c 'echo "net.ipv4.ip_forward=1" >> /etc/sysctl.d/30-ipforward.conf'
$ sudo sysctl -w net.ipv4.ip_forward=1
$ sudo sh -c 'echo "br_netfilter" > /etc/modules-load.d/br_netfilter.conf'
$ sudo sh -c 'echo "xt_set" > /etc/modules-load.d/xt_set.conf'
$ sudo modprobe br_netfilter xt_set
En cada nodo, deben instalarse los paquetes de herramientas de contenedor y Arch8 K8 compilado previamente. Si se compila un paquete de kernel personalizado, también se instalará igualmente.
$ sudo pacman -U *pkg.tar.xz
loading packages...
resolving dependencies...
looking for conflicting packages...
Packages (5) cni-plugins-0.7.5-1  cri-o-1.14.0-1  crictl-bin-1.14.0-1  kubernetes-1.14.1-1  runc-1.0.0rc8-1
Total Installed Size:  1065.89 MiB
:: Proceed with installation? [Y/n]
...

Instalación de todos los paquetes

Después de la instalación, el tiempo de ejecución del contenedor CRI-O requiere configuración. CRI-O respeta la configuración de todo el sistema de registros de contenedores fiables que hay dentro de /etc/containers/policy.json. Para poder extraer imágenes de docker.io (y otros registros) se puede instalar la política por defecto: policy.json

Aquí tienes una configuración mínima para CRI-O: crio.conf. Debe ubicarse en /etc/crio/crio.conf. Para evitar que CRI-O desactive la red de contenedores debido a la ausencia de una configuración de red CNI por defecto, montamos una configuración CNI con un simple circuito cerrado.

$ sudo sh -c 'cat >/etc/cni/net.d/99-loopback.conf <<-EOF
{
  "cniVersion": "0.2.0",
  "type": "loopback"
}
EOF'
Posteriormente, el servicio CRI-O se puede habilitar e iniciar.
$ sudo systemctl daemon-reload
$ sudo systemctl enable crio
$ sudo mkdir -p /etc/cni/net.d
$ sudo systemctl start crio
$ sudo systemctl enable kubelet.service

Configuración maestra Kubernetes ODROID-N2

En el nodo maestro, la configuración del clúster se realizará utilizando kubeadm. Puesto que incluso el último ODROID-N2 con 4 GB de RAM está bastante limitado con memoria, me viene a la mente el servicio adicional zram-swap o usar un archivo de intercambio. Para ejecutar K8 con el intercambio habilitado, se debe facilitar la configuración "--ignore-preflight-errors Swap" a kubeadm:

$ sudo kubeadm init --ignore-preflight-errors Swap --cri-socket=/var/run/crio/crio.sock
Luego puedes conectar cuantos nodos de trabajo quieras ejecutando lo siguiente en cada uno de ellos como root
kubeadm join 10.23.200.120:6443 --token c11wrg… --discovery-token-ca-cert-hash sha256:3f5dc1..

Configuración maestra de Kubernetes

Una vez finalizada la configuración de kubeadm y aparezca el token de unión, se pueden configurar los nodos de trabajo. Pero primero debemos hacer una copia de la configuración del clúster en el directorio de inicio del usuario, para que luego se pueda recuperar para poder configurar kubectl.

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

Configuración del nodo trabajador de Kubernetes

Puesto que la configuración es la misma para los nodos de trabajo y maestros, queda muy poco por hacer: el comando kubeadm se usará para unirse al clúster, después de ello la configuración de trabajo de K8s en el nodo habrá finalizado.

Si los nodos de trabajo tienen habilitado el intercambio, también se debe facilitar el parámetro "--ignore-preflight-errors Swap":

$ sudo kubeadm join 10.23.202.120:6443 --ignore-preflight-errors Swap --token c11wrg.... --discovery-token-ca-cert-hash sha256:3f5dc1...

Acceso y red del Cluster

Para acceder al clúster K8s, el archivo de configuración generado para kubectl se obtiene del maestro.

$ mkdir ~/.kube/config
$ scp master:~/admin.conf ~/.kube/config
Luego, se debe poder acceder al clúster desde kubectl.
$ kubectl get nodes
NAME       STATUS   ROLES  AGE   VERSION
n2-master0   NotReady   master   11m   v1.14.1
n2-worker0   NotReady      5s  v1.14.1
n2-worker1   NotReady      10s   v1.14.1
n2-worker2   NotReady      9s  v1.14.1
n2-worker3   NotReady      8s  v1.14.1

Acceder al clúster K8s recién configurado

Todos los nodos están en estado NotReady ya que no se ha configurado ninguna red para el clúster. Esto se puede solucionar rápidamente usando weave como proveedor de CNI:

$ kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
Una vez consolidada la red Weave, los nodos cambian al estado Ready.
$ kubectl get nodes
NAME       STATUS   ROLES  AGE   VERSION
n2-master0   Ready  master   77m   v1.14.1
n2-worker0   Ready     65m   v1.14.1
n2-worker1   Ready     65m   v1.14.1
n2-worker2   Ready     65m   v1.14.1
n2-worker3   Ready     65m   v1.14.1
Para hacernos con una interfaz basada en web para el clúster, instalamos el panel de K8s. Aunque es proporcionado como una imagen ARM64, la implementación por defecto usa amd64 como plataforma, por lo que es necesario llevar a cabo una pequeña sustitución con sed:
$ curl -sSL https://bit.ly/2G4e9Hu | sed 's/-amd64:/-arm64:/' | kubectl apply -f -

Referencias

https://kubernetes.io/ https://www.trion.de/news/2019/05/06/kubernetes-odroid-n2.html

Be the first to comment

Leave a Reply