Después de realizar la instalación del nodo master del clúster Kubernetes, el siguiente paso es agregar nodos adicionales al clúster. Es en estos nodos donde se van a planificar los pods que realizan las funciones productivas del clúster (en el nodo master sólo realiza tareas de gestión del clúster).

Cómo agregar un nodo a un cluster Kubernetes

En el nodo que vamos a añadir tenemos instalador HypriotOS (una distribución basada en Debian creada específicamente para ejecutar Docker en la Raspberry Pi).

Hypriot OS tiene instalado Docker de fábrica así que comprobamos la versión instalada:

$ docker version
Client:
 Version:      17.04.0-ce
 API version:  1.28
 Go version:   go1.7.5
 Git commit:   4845c56
 Built:        Mon Apr  3 18:22:23 2017
 OS/Arch:      linux/arm

Server:
 Version:      17.04.0-ce
 API version:  1.28 (minimum version 1.12)
 Go version:   go1.7.5
 Git commit:   4845c56
 Built:        Mon Apr  3 18:22:23 2017
 OS/Arch:      linux/arm
 Experimental: false
$

Tareas previas

La instalación de HypriotOS define como nombre del host black-pearl.

Lo primero que haremos será cambiar el nombre del host. Para ello modificamos el fichero /boot/device-init.yaml especificando el nombre elegido para el nuevo nodo. En mi caso, k2

$ sudo nano /boot/device-init.yaml

# hostname for your HypriotOS device
hostname: k2

# optional wireless network settings
wifi:
  interfaces:
#     wlan0:
#       ssid: "MyNetwork"
#       password: "secret_password"

Para que los cambios tenga efecto, es necesario reiniciar el equipo. Antes, sin embargo, vamos a establecer una IP fija.

Creamos una copia del fichero /etc/network/interfaces.d/eth0 antes de editarlo:

$ sudo cp /etc/network/interfaces.d/eth0 /etc/network/interfaces.d/eth0.original
$ sudo nano /etc/network/interfaces.d/eth0
allow-hotplug eth0
iface eth0 inet static
  address 192.168.1.12
  gateway 192.168.1.1

Una vez realizadas las modificaciones del hostname y de la dirección IP, reiniciamos el host.

sudo reboot

Instalación de Kubernetes (kubeadm, kubectl y kubelet)

Seguimos las instrucciones de la página oficial de Kubernetes: Installing Kubernetes on Linux with kubeadm.

Nos conectamos a la máquina vía SSH y nos convertimos en root mediante sudo su -.

$ apt-get update && apt-get install -y apt-transport-https
...
apt-transport-https is already the newest version.0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

El siguiente paso es obtener la clave GPG:

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

Añadimos el repositorio de Kubernetes y actualizamos la lista de paquetes:

$ cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
$ apt-get update

Verificamos que tenemos Docker instalado (en nuestro caso, docker-engine):

$ apt-get install -y docker-engine
...
docker-engine is already the newest version.0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

Ahora es el momento de lanzar la instalación de los diferentes componentes de Kubernets:

$ apt-get install -y kubelet kubeadm kubectl kubernetes-cni
...
The following extra packages will be installed:  ebtables socatThe following NEW packages will be installed:  ebtables kubeadm kubectl kubelet kubernetes-cni socat0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.Need to get 37.1 MB of archives.After this operation, 266 MB of additional disk space will be used.0% [Working]
...
Setting up kubernetes-cni (0.5.1-00) ...
Setting up socat (1.7.2.4-2) ...
Setting up kubelet (1.6.1-00) ...
Setting up kubectl (1.6.1-00) ...
Setting up kubeadm (1.6.1-00) ...
Processing triggers for systemd (215-17+deb8u6) ...
$

Agregar nodo al clúster

Para añadir el host como un nodo adicional del clúster de Kubernetes, usaremos el comando kubeadm join --token {token} {IP-nodo-master}:puerto

El comando kubeadm genera el token al inicializar el clúster, pero si no lo tenemos apuntado, podemos obtenerlo conectando al nodo master:

ssh pirate@k1.local

Para obtener el token, nos convertimos en el usuario root:

sudo su -

A continuación, obtenemos la lista de tokens generados en el clúster:

$ kubeadm token list
TOKEN                     TTL         EXPIRES   USAGES                 DESCRIPTION
5e6517.b9e07...293ff612   <forever>   <never>   authentication,signing   The default bootstrap token generated by 'kubeadm init'.

Copiamos el token y cerramos la conexión con el nodo master.

En el host que vamos a unir como nodo al clúster, ejecutamos (como root):

kubeadm join --token=5e6517.b9e07...293ff612 192.168.1.11:6443
[kubeadm] WARNING: kubeadm is in beta, please do not use it for production clusters.
[preflight] Running pre-flight checks
[preflight] WARNING: docker version is greater than the most recently validated version. Docker version: 17.04.0-ce. Max validated version: 1.12
[discovery] Trying to connect to API Server "192.168.1.11:6443"
[discovery] Created cluster-info discovery client, requesting info from "https://192.168.1.11:6443"
[discovery] Cluster info signature and contents are valid, will use API Server "https://192.168.1.11:6443"
[discovery] Successfully established connection with API Server "192.168.1.11:6443"
[bootstrap] Detected server version: v1.6.0
[bootstrap] The server supports the Certificates API (certificates.k8s.io/v1beta1)
[csr] Created API client to obtain unique certificate for this node, generating keys and certificate signing request
[csr] Received signed certificate from the API server, generating KubeConfig...
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/kubelet.conf"
Node join complete:
   * Certificate signing request sent to master and response  received.
   * Kubelet informed of new secure connection details.

   Run 'kubectl get nodes' on the master to see this machine join.

Verificación

Para comprobar que el nodo k2 se ha añadido correctamente al clúster, nos conectamos al nodo master y obtenemos la lista de nodos:

$ kubectl get nodes
NAME       STATUS     AGE       VERSION
k1         Ready      4d        v1.6.1
k2.local         NotReady   40s       v1.6.1

El nodo k2 del clúster aparece como NotReady. Esta situación debe ser temporal. Tras unos instantes, al ejecutar de nuevo el comando, el status del nuevo nodo debería haber cambiado y mostrarse como Ready:

$ kubectl get nodes
NAME       STATUS    AGE       VERSION
k1         Ready     4d        v1.6.1
k2.local   Ready     1m        v1.6.1

Cambio de nombre del nodo

Incialmente he añadido el nodo al cúster como k2.local, sin darme cuenta que el sufijo .local lo añade el daemon Avahi al publicar el nombre del host en la red local.

He modificado el nombre del nodo en el fichero /boot/device-init.yaml y he reiniciado el nodo, pero a nivel del clúster, el nodo k2.local sigue formando parte del mismo. Por eso aparece como NotReady al ejecutar get nodes. El nuevo nodo k2 sí que aparece en al ejecutar el comando get nodes:

$ kubectl get nodes
NAME       STATUS     AGE       VERSION
k1         Ready      4d        v1.6.1
k2         Ready      37m       v1.6.1
k2.local   NotReady   1h        v1.6.1

Para evitar confusiones, lo más conveniente es eliminar el nodo del clúster mediante kubectl delete node:

$ kubectl delete node k2.local
node "k2.local" deleted

$ kubectl get nodes
NAME      STATUS    AGE       VERSION
k1        Ready     4d        v1.6.1
k2        Ready     45m       v1.6.1
$