Cluster Kubernetes Sobre un ODROID-N2

Introducción

Kubernetes (o k8s para abreviar) es una plataforma extensible de organización de contenedores de código abierto diseñada para administrar cargas de trabajo en contenedores y servicios a escala. Ayuda con la implementación automatizada, el escalado y la administración de cargas de trabajo de aplicaciones centradas en contenedores en un clúster de nodos (básicos, virtuales o en la nube) mediante la organización de la infraestructura del cálculo, la red y almacenamiento a partir de de esas cargas de trabajo de los usuarios.

Los dos tipos principales de nodos en un clúster de Kubernetes son:

Maestro: este nodo actúa a modo de control para todo el clúster. Es responsable de todas las implementaciones de carga de trabajo de las aplicaciones, planificación y decisiones, así como también de detectar y administrar cambios en el estado de las aplicaciones implementadas. Se compone de un almacén de valores clave, un servidor API, un planificador y un administrador de controladores.

Nodo(s) de trabajo: nodo(s) que realmente ejecutan los contenedores de aplicaciones. También se les conoce en ocasiones como Minion (s). El maestro también es un nodo, pero no está destinado a la implementación de aplicaciones. Se compone de un agente llamado kubelet, un proxy de red llamado kube-proxy y un motor de contenedores.

La siguiente Figura ilustra la descripción de la arquitectura de alto nivel de Kubernetes:

Figure 1
Figura 1

Los componentes básicos que forman un clúster de Kubernetes se describen a continuación:

KV Store: un almacén de datos valor-clave altamente confiable, distribuido y consistente que se utiliza para persistir y mantener información del estado sobre los diversos componentes del clúster de Kubernetes. Por defecto, Kubernetes usa etcd como el almacén de valores clave.

Servidor API: actúa como punto de entrada para el plano de control presentando un punto final API para todas las interacciones con y dentro del clúster de Kubernetes. A través del Servidor API se realizan solicitudes para la implementación, administración, gestión y operación de aplicaciones basadas en contenedores. Utiliza el almacén de valores clave para mantener la información de estado sobre todos los componentes del clúster de Kubernetes.

Pod(s): es la unidad de implementación más pequeña en Kubernetes. Uno o más contenedores se ejecutan dentro. Es algo así como un host lógico con red y almacenamiento compartidos. Los pods de aplicaciones están programados para ejecutarse en diferentes nodos de trabajo del clúster de Kubernetes en función de las necesidades de los recursos y las restricciones de la aplicación. Cada pod dentro del clúster tiene su propia dirección IP única. Los contenedores de aplicaciones dentro de un pod se comunican entre sí mediante localhost. Los Pods también son la unidad más pequeña de escalado en Kubernetes. Los Pod (s) son efímeros: pueden ir y venir en cualquier momento.

Scheduler: responsable de programar los pods de la aplicación para que se ejecuten en los nodos de trabajo seleccionados del clúster de Kubernetes partiendo de los requisitos de los recursos de la aplicación, así como las restricciones de afinidad específicas de la aplicación.

Service: proporciona un punto de red estable y lógico para un grupo de pod (s) (basado en una etiqueta relacionada con un pod de aplicación que se ejecuta en los nodos wor2ker del clúster de Kubernetes. Permiten el acceso a una aplicación a través del service-discovery y distribuyen las solicitudes mediante un simple equilibrio de carga. Para acceder a una aplicación, a cada servicio se le asigna una dirección IP interna:puerto en todo el clúster.

Controller Manager: gestiona diferentes tipos de controladores responsables de supervisar y detectar cambios en el estado del clúster de Kubernetes (a través del servidor API) y garantiza que el clúster cambie al estado deseado. Los diferentes tipos de controladores son:

  • Node Controller => responsable de supervisar y detectar el estado y la solidez (arriba o abajo) de los nodos de trabajo en el clúster de Kubernetes.
  • ReplicaSet => anteriormente denominado Controlador de replicación y es responsable de mantener el número deseado de réplicas de pod en el clúster.
  • Endpoints Controller => responsable de detectar y gestionar cambios en los puntos de acceso al servicio de la aplicación (lista de direcciones IP:puerto).

Plugin Network: actúa como puente (red superpuesta) que permite la comunicación entre los pods que se ejecutan en diferentes nodos de trabajo del clúster. Hay diferentes implementaciones de este componente por parte de terceros como calico, franela, weave-net, etc. Todos ellos deben cumplir con una especificación común llamada Container Network Interface o CNI para abreviar.

kubelet: un agente que se ejecuta en cada nodo de trabajo del clúster de Kubernetes. Es responsable de crear e iniciar un pod de aplicación en el nodo de trabajo y asegurar de que todos los contenedores de aplicaciones estén en funcionamiento dentro del pod. Además, también es responsable de informar del estado y solidez del nodo de trabajo, así como de todos los pods que están en ejecución al maestro a través del servidor API.

kube-proxy: un proxy de red que se ejecuta en cada uno de los nodos de trabajo del clúster de Kubernetes y actúa como un punto de entrada para acceder a los diversos puntos del servicio de aplicaciones. Dirige las solicitudes hacía los pods apropiados dentro del clúster.

Container Engine: un tiempo de ejecución de contenedor que se ejecuta en cada uno de los nodos de trabajo para administrar el ciclo de vida de los contenedores, como obtener las imágenes, iniciar y detener contenedores, etc. El motor de contenedores comúnmente utilizado es Docker.

kubectl: herramienta de línea de comandos utilizada para interactuar con el servidor API. Utilizada por los administradores (u operadores) para la implementación y el escalado de aplicaciones, así como para la gestión del clúster de Kubernetes.

Instalación y configuración del sistema

La instalación la realizaremos en un clúster ODROID-N2 de 5 nodos con Armbian Ubuntu Linux.

La siguiente Figura muestra un clúster ODROID-N2 con 5 nodos en funcionamiento:

Figure 2 
Figura 2

Para este tutorial, vamos a suponer que los 5 nodos en el clúster tienen los siguientes nombres de host y direcciones IP:

my-n2-1 192.168.1.51
my-n2-2 192.168.1.52
my-n2-3 192.168.1.53
my-n2-4 192.168.1.54
my-n2-5 192.168.1.55
Abra una ventana de Terminal y abre una pestaña para cada uno de los 5 nodos my-n2-1 a través de my-n2-5. En cada una de las pestañas de Terminal, conectate por ssh al nodo correspondiente.

Cada uno de los nodos my-n2-1 hasta my-n2-5 debe tener un identificador único para que el clúster funcione sin conflictos. El identificador de nodo único se encuentra en el archivo /etc/machine-id, vemos que todos los nodos my-n2-1 hasta my-n2-5 tienen el mismo valor. Esto hay que solucionarlo*. En cada uno de los nodos my-n2-1 a través de my-n2-5, ejecuta los siguientes comandos:

$ sudo rm -f /etc/machine-id
$ sudo dbus-uuidgen --ensure=/etc/machine-id
$ sudo rm /var/lib/dbus/machine-id
$ sudo dbus-uuidgen --ensure
$ sudo reboot now
Una vez más, en cada una de las pestañas de Terminal, conéctate por ssh al nodo correspondiente.

A continuación, necesitamos configurar el repositorio de paquetes para Docker. En cada uno de los nodos my-n2-1 a través de my-n2-5, ejecuta los siguientes comandos:

$ sudo apt-get update
$ sudo apt-get install apt-transport-https ca-certificates curl \
software-properties-common -y
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo \
apt-key add -
$ sudo apt-get update
$ sudo add-apt-repository "deb [arch=arm64] \
-https://download.docker.com/linux/ubuntu xenial stable"
$ sudo apt-get update
Para la versión 1.16 de Kubernetes (la versión en el momento de publicar este artículo), la versión recomendada de Docker es la 18.09.

ATENCIÓN: Para Docker CE 19.xx (y superior) Asegúrate de que la versión de Docker instalada sea * 18.09 *. De lo contrario, te encontrarás con el siguiente error: [ERROR SystemVerification]: unsupported docker version: 19.xx

Necesitamos verificar el último paquete de Docker 18.09 en el repositorio. En cualquiera de los nodos (elegiremos my-n2-1), ejecuta el siguiente comando:

$ apt-cache madison docker-ce
El siguiente sería un resultado típico:
<span style="font-weight: 400;">Output.1</span>

<span style="font-weight: 400;">docker-ce | 5:19.03.5~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:19.03.4~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:19.03.3~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:19.03.2~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:19.03.1~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:19.03.0~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:18.09.9~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:18.09.8~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:18.09.7~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:18.09.6~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:18.09.5~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:18.09.4~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:18.09.3~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:18.09.2~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:18.09.1~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 5:18.09.0~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 18.06.3~ce~3-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 18.06.2~ce~3-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 18.06.1~ce~3-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 18.06.0~ce~3-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 18.03.1~ce-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 18.03.0~ce-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 17.12.1~ce-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 17.12.0~ce-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 17.09.1~ce-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>

<span style="font-weight: 400;">docker-ce | 17.09.0~ce-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable arm64 Packages</span>
En el resultado anterior, vemos que el último paquete para Docker 18.09 es 5: 18.09.9 ~ 3-0 ~ ubuntu-xenial.

A continuación, necesitamos instalar la versión elegida de Docker. En cada uno de los nodos my-n2-1 a través de my-n2-5, ejecuta el siguiente comando:

$ sudo apt-get install docker-ce=5:18.09.9~3-0~ubuntu-xenial -y
El siguiente sería un resultado típico:
Output.2
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
aufs-tools cgroupfs-mount containerd.io docker-ce-cli git git-man liberror-perl pigz
Suggested packages:
git-daemon-run | git-daemon-sysvinit git-doc git-el git-email git-gui gitk gitweb git-cvs git-mediawiki git-svn
The following NEW packages will be installed:
aufs-tools cgroupfs-mount containerd.io docker-ce docker-ce-cli git git-man liberror-perl pigz
0 upgraded, 9 newly installed, 0 to remove and 0 not upgraded.
Need to get 61.3 MB of archives.
After this operation, 325 MB of additional disk space will be used.
Get:1 https://download.docker.com/linux/ubuntu xenial/stable arm64 containerd.io arm64 1.2.10-3 [14.5 MB]
Get:2 http://ports.ubuntu.com/ubuntu-ports bionic/universe arm64 pigz arm64 2.4-1 [47.8 kB]
Get:3 http://ports.ubuntu.com/ubuntu-ports bionic/universe arm64 aufs-tools arm64 1:4.9+20170918-1ubuntu1 [101 kB]
Get:4 http://ports.ubuntu.com/ubuntu-ports bionic/universe arm64 cgroupfs-mount all 1.4 [6320 B]
Get:5 http://ports.ubuntu.com/ubuntu-ports bionic/main arm64 liberror-perl all 0.17025-1 [22.8 kB]
Get:6 http://ports.ubuntu.com/ubuntu-ports bionic-updates/main arm64 git-man all 1:2.17.1-1ubuntu0.4 [803 kB]
Get:7 http://ports.ubuntu.com/ubuntu-ports bionic-updates/main arm64 git arm64 1:2.17.1-1ubuntu0.4 [2941 kB]
Get:8 https://download.docker.com/linux/ubuntu xenial/stable arm64 docker-ce-cli arm64 5:19.03.5~3-0~ubuntu-xenial [29.6 MB]
Get:9 https://download.docker.com/linux/ubuntu xenial/stable arm64 docker-ce arm64 5:18.09.9~3-0~ubuntu-xenial [13.3 MB]
Fetched 61.3 MB in 5s (11.6 MB/s)
Selecting previously unselected package pigz.
(Reading database ... 156190 files and directories currently installed.)
Preparing to unpack .../0-pigz_2.4-1_arm64.deb ...
Unpacking pigz (2.4-1) ...
Selecting previously unselected package aufs-tools.
Preparing to unpack .../1-aufs-tools_1%3a4.9+20170918-1ubuntu1_arm64.deb ...
Unpacking aufs-tools (1:4.9+20170918-1ubuntu1) ...
Selecting previously unselected package cgroupfs-mount.
Preparing to unpack .../2-cgroupfs-mount_1.4_all.deb ...
Unpacking cgroupfs-mount (1.4) ...
Selecting previously unselected package containerd.io.
Preparing to unpack .../3-containerd.io_1.2.10-3_arm64.deb ...
Unpacking containerd.io (1.2.10-3) ...
Selecting previously unselected package docker-ce-cli.
Preparing to unpack .../4-docker-ce-cli_5%3a19.03.5~3-0~ubuntu-xenial_arm64.deb ...
Unpacking docker-ce-cli (5:19.03.5~3-0~ubuntu-xenial) ...
Selecting previously unselected package docker-ce.
Preparing to unpack .../5-docker-ce_5%3a18.09.9~3-0~ubuntu-xenial_arm64.deb ...
Unpacking docker-ce (5:18.09.9~3-0~ubuntu-xenial) ...
Selecting previously unselected package liberror-perl.
Preparing to unpack .../6-liberror-perl_0.17025-1_all.deb ...
Unpacking liberror-perl (0.17025-1) ...
Selecting previously unselected package git-man.
Preparing to unpack .../7-git-man_1%3a2.17.1-1ubuntu0.4_all.deb ...
Unpacking git-man (1:2.17.1-1ubuntu0.4) ...
Selecting previously unselected package git.
Preparing to unpack .../8-git_1%3a2.17.1-1ubuntu0.4_arm64.deb ...
Unpacking git (1:2.17.1-1ubuntu0.4) ...
Setting up aufs-tools (1:4.9+20170918-1ubuntu1) ...
Setting up git-man (1:2.17.1-1ubuntu0.4) ...
Setting up containerd.io (1.2.10-3) ...
Created symlink /etc/systemd/system/multi-user.target.wants/containerd.service → /lib/systemd/system/containerd.service.
Setting up liberror-perl (0.17025-1) ...
Setting up cgroupfs-mount (1.4) ...
Setting up docker-ce-cli (5:19.03.5~3-0~ubuntu-xenial) ...
Setting up pigz (2.4-1) ...
Setting up git (1:2.17.1-1ubuntu0.4) ...
Setting up docker-ce (5:18.09.9~3-0~ubuntu-xenial) ...
update-alternatives: using /usr/bin/dockerd-ce to provide /usr/bin/dockerd (dockerd) in auto mode
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /lib/systemd/system/docker.service.
Created symlink /etc/systemd/system/sockets.target.wants/docker.socket → /lib/systemd/system/docker.socket.
Processing triggers for systemd (237-3ubuntu10.33) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
Processing triggers for libc-bin (2.27-3ubuntu1) ...
A continuación, debemos asegurarnos de que podemos ejecutar los comandos de Docker con el usuario conectado sin la necesidad de sudo. En cada uno de los nodos my-n2-1 a través de my-n2-5, ejecuta los siguientes comandos:
$ sudo usermod -aG docker $USER
$ sudo reboot now
Una vez más, en cada una de las pestañas de Terminal, conéctate por ssh al nodo correspondiente.

Para verificar la instalación de Docker, en cada uno de los nodos my-n2-1 a través de my-n2-5, ejecuta el siguiente comando:

$ docker info
El siguiente sería un resultado típico:
Output.3
Client:
Debug Mode: false

Server:
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 18.09.9
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Native Overlay Diff: true
Logging Driver: json-file
Cgroup Driver: cgroupfs
Plugins:
Volume: local
Network: bridge host macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc
Default Runtime: runc
Init Binary: docker-init
containerd version: b34a5c8af56e510852c35414db4c1f4fa6172339
runc version: 3e425f80a8c931f88e6d94a8c831b9d5aa481657
init version: fec3683
Security Options:
seccomp
Profile: default
Kernel Version: 4.9.196-meson64
Operating System: Ubuntu 18.04.3 LTS
OSType: linux
Architecture: aarch64
CPUs: 6
Total Memory: 3.623GiB
Name: my-n2-1
ID: QF32:QDZN:IQDM:34HX:NK3C:O3AP:Y6JZ:74DV:XXXL:KCBL:7K5D:36B4
Docker Root Dir: /var/lib/docker
Debug Mode: false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Product License: Community Engine
Luego, necesitamos configurar el repositorio de paquetes para Kubernetes. En cada uno de los nodos my-n2-1 a través de my-n2-5, ejecuta los siguientes comandos
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
$ echo "deb http://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
$ sudo apt-get update
A continuación, necesitamos instalar Kubernetes. En cada uno de los nodos my-n2-1 a través de my-n2-5, ejecuta el siguiente comando:
$ sudo apt-get install -y kubeadm
El siguiente sería un resultado típico:
Output.4
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
conntrack cri-tools ebtables kubectl kubelet kubernetes-cni socat
The following NEW packages will be installed:
conntrack cri-tools ebtables kubeadm kubectl kubelet kubernetes-cni socat
0 upgraded, 8 newly installed, 0 to remove and 1 not upgraded.
Need to get 48.3 MB of archives.
After this operation, 280 MB of additional disk space will be used.
Get:2 http://ports.ubuntu.com/ubuntu-ports bionic/main arm64 conntrack arm64 1:1.4.4+snapshot20161117-6ubuntu2 [27.3 kB]
Get:7 http://ports.ubuntu.com/ubuntu-ports bionic-updates/main arm64 ebtables arm64 2.0.10.4-3.5ubuntu2.18.04.3 [74.2 kB]
Get:8 http://ports.ubuntu.com/ubuntu-ports bionic/main arm64 socat arm64 1.7.3.2-2ubuntu2 [322 kB]
Get:1 https://packages.cloud.google.com/apt kubernetes-xenial/main arm64 cri-tools arm64 1.13.0-00 [7965 kB]
Get:3 https://packages.cloud.google.com/apt kubernetes-xenial/main arm64 kubernetes-cni arm64 0.7.5-00 [5808 kB]
Get:4 https://packages.cloud.google.com/apt kubernetes-xenial/main arm64 kubelet arm64 1.16.3-00 [18.5 MB]
Get:5 https://packages.cloud.google.com/apt kubernetes-xenial/main arm64 kubectl arm64 1.16.3-00 [8025 kB]
Get:6 https://packages.cloud.google.com/apt kubernetes-xenial/main arm64 kubeadm arm64 1.16.3-00 [7652 kB]
Fetched 48.3 MB in 5s (9383 kB/s)
Selecting previously unselected package conntrack.
(Reading database ... 157399 files and directories currently installed.)
Preparing to unpack .../0-conntrack_1%3a1.4.4+snapshot20161117-6ubuntu2_arm64.deb ...
Unpacking conntrack (1:1.4.4+snapshot20161117-6ubuntu2) ...
Selecting previously unselected package cri-tools.
Preparing to unpack .../1-cri-tools_1.13.0-00_arm64.deb ...
Unpacking cri-tools (1.13.0-00) ...
Selecting previously unselected package ebtables.
Preparing to unpack .../2-ebtables_2.0.10.4-3.5ubuntu2.18.04.3_arm64.deb ...
Unpacking ebtables (2.0.10.4-3.5ubuntu2.18.04.3) ...
Selecting previously unselected package kubernetes-cni.
Preparing to unpack .../3-kubernetes-cni_0.7.5-00_arm64.deb ...
Unpacking kubernetes-cni (0.7.5-00) ...
Selecting previously unselected package socat.
Preparing to unpack .../4-socat_1.7.3.2-2ubuntu2_arm64.deb ...
Unpacking socat (1.7.3.2-2ubuntu2) ...
Selecting previously unselected package kubelet.
Preparing to unpack .../5-kubelet_1.16.3-00_arm64.deb ...
Unpacking kubelet (1.16.3-00) ...
Selecting previously unselected package kubectl.
Preparing to unpack .../6-kubectl_1.16.3-00_arm64.deb ...
Unpacking kubectl (1.16.3-00) ...
Selecting previously unselected package kubeadm.
Preparing to unpack .../7-kubeadm_1.16.3-00_arm64.deb ...
Unpacking kubeadm (1.16.3-00) ...
Setting up conntrack (1:1.4.4+snapshot20161117-6ubuntu2) ...
Setting up kubernetes-cni (0.7.5-00) ...
Setting up cri-tools (1.13.0-00) ...
Setting up socat (1.7.3.2-2ubuntu2) ...
Setting up ebtables (2.0.10.4-3.5ubuntu2.18.04.3) ...
Created symlink /etc/systemd/system/multi-user.target.wants/ebtables.service → /lib/systemd/system/ebtables.service.
update-rc.d: warning: start and stop actions are no longer supported; falling back to defaults
Setting up kubectl (1.16.3-00) ...
Setting up kubelet (1.16.3-00) ...
Created symlink /etc/systemd/system/multi-user.target.wants/kubelet.service → /lib/systemd/system/kubelet.service.
Setting up kubeadm (1.16.3-00) ...
Processing triggers for man-db (2.8.3-2ubuntu0.1) ...
Processing triggers for systemd (237-3ubuntu10.33) ...
Necesitamos reiniciar todos los nodos. En cada uno de los nodos my-n2-1 a través de my-n2-5, ejecuta el siguiente comando:
$ sudo reboot now
Una vez más, en cada una de las pestañas de Terminal, conéctate por ssh al nodo correspondiente.

Para verificar la instalación de Kubernetes, en cada uno de los nodos my-n2-1 a través de my-n2-5, ejecuta el siguiente comando:

$ kubeadm version
El siguiente sería un resultado típico:
Output.5
kubeadm version: &version.Info{Major:"1", Minor:"16", GitVersion:"v1.16.3", GitCommit:"b3cbbae08ec52a7fc73d334838e18d17e8512749", GitTreeState:"clean", BuildDate:"2019-11-13T11:20:25Z", GoVersion:"go1.12.12", Compiler:"gc", Platform:"linux/arm64"}
A continuación, debemos asegurarnos de que los paquetes para Docker y Kubernetes no se actualicen en el futuro mediante el proceso de actualización de software. En cada uno de los nodos my-n2-1 a través de my-n2-5, ejecuta el siguiente comando:
$ sudo apt-mark hold kubelet kubeadm kubectl docker-ce
El siguiente sería un resultado típico:
Output.6
kubelet set on hold.
kubeadm set on hold.
kubectl set on hold.
docker-ce set on hold.
Por defecto, Docker usa cgroupfs como controlador cgroup. Kubernetes prefiere systemd como controlador cgroup. Necesitamos modificar la configuración del demonio Docker especificando las opciones en un archivo JSON llamado /etc/docker/daemon.json. En cada uno de los nodos my-n2-1 a través de my-n2-5, crea el archivo de configuración /etc/docker/daemon.json con el siguiente contenido:
/etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
A continuación, debemos reiniciar el demonio Docker para que la configuración surta efecto. En cada uno de los nodos my-n2-1 a través de my-n2-5, ejecuta los siguientes comandos:
$ sudo mkdir -p /etc/systemd/system/docker.service.d
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
Nota: No usar el controlador systemd cgroup provocará el siguiente error: [preflight] Running pre-flight checks [WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". Please follow the guide at https://kubernetes.io/docs/setup/cri/

Para verificar que el demonio Docker se inició bien, en cada uno de los nodos my-n2-1 a través de my-n2-5, ejecuta el siguiente comando:

$ journalctl -u docker
El siguiente sería un resultado típico:
Output.7
-- Logs begin at Sat 2019-12-14 21:14:19 EST, end at Sat 2019-12-14 21:49:26 EST. --
Dec 14 21:14:26 my-n2-1 systemd[1]: Starting Docker Application Container Engine...
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.806496732-05:00" level=info msg="systemd-resolved is running, so using resolvconf: /run/systemd/res
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.821800611-05:00" level=info msg="parsed scheme: \"unix\"" module=grpc
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.822661404-05:00" level=info msg="scheme \"unix\" not registered, fallback to default scheme" module
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.824226106-05:00" level=info msg="parsed scheme: \"unix\"" module=grpc
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.824838344-05:00" level=info msg="scheme \"unix\" not registered, fallback to default scheme" module
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.828116839-05:00" level=info msg="ccResolverWrapper: sending new addresses to cc: [{unix:///run/cont
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.828945714-05:00" level=info msg="ClientConn switching balancer to \"pick_first\"" module=grpc
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.828101672-05:00" level=info msg="ccResolverWrapper: sending new addresses to cc: [{unix:///run/cont
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.830093104-05:00" level=info msg="ClientConn switching balancer to \"pick_first\"" module=grpc
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.832076285-05:00" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0x400014e610, CONNECT
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.844251802-05:00" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0x40001343a0, CONNECT
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.846949059-05:00" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0x40001343a0, READY"
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.851896887-05:00" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0x400014e610, READY"
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.857097768-05:00" level=info msg="[graphdriver] using prior storage driver: overlay2"
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.886090322-05:00" level=info msg="Graph migration to content-addressability took 0.00 seconds"
Dec 14 21:14:27 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:27.893602818-05:00" level=info msg="Loading containers: start."
Dec 14 21:14:28 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:28.821256841-05:00" level=info msg="Default bridge (docker0) is assigned with an IP address 172.17.0.0
Dec 14 21:14:29 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:29.134364234-05:00" level=info msg="Loading containers: done."
Dec 14 21:14:29 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:29.374311397-05:00" level=info msg="Docker daemon" commit=039a7df graphdriver(s)=overlay2 version=18.0
Dec 14 21:14:29 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:29.376444960-05:00" level=info msg="Daemon has completed initialization"
Dec 14 21:14:29 my-n2-1 systemd[1]: Started Docker Application Container Engine.
Dec 14 21:14:29 my-n2-1 dockerd[3347]: time="2019-12-14T21:14:29.444607195-05:00" level=info msg="API listen on /var/run/docker.sock"
Dec 14 21:49:11 my-n2-1 dockerd[3347]: time="2019-12-14T21:49:11.323542665-05:00" level=info msg="Processing signal 'terminated'"
Dec 14 21:49:11 my-n2-1 dockerd[3347]: time="2019-12-14T21:49:11.328379659-05:00" level=info msg="stopping event stream following graceful shutdown" error="" m
Dec 14 21:49:11 my-n2-1 systemd[1]: Stopping Docker Application Container Engine...
Dec 14 21:49:11 my-n2-1 systemd[1]: Stopped Docker Application Container Engine.
Dec 14 21:49:11 my-n2-1 systemd[1]: Starting Docker Application Container Engine...
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.499488062-05:00" level=info msg="systemd-resolved is running, so using resolvconf: /run/systemd/res
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.502141612-05:00" level=info msg="parsed scheme: \"unix\"" module=grpc
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.502209240-05:00" level=info msg="scheme \"unix\" not registered, fallback to default scheme" module
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.502278577-05:00" level=info msg="parsed scheme: \"unix\"" module=grpc
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.502295786-05:00" level=info msg="scheme \"unix\" not registered, fallback to default scheme" module
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.505887217-05:00" level=info msg="ccResolverWrapper: sending new addresses to cc: [{unix:///run/cont
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.506035600-05:00" level=info msg="ClientConn switching balancer to \"pick_first\"" module=grpc
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.506181190-05:00" level=info msg="ccResolverWrapper: sending new addresses to cc: [{unix:///run/cont
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.506446245-05:00" level=info msg="ClientConn switching balancer to \"pick_first\"" module=grpc
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.506671465-05:00" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0x40007a2230, CONNECT
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.506255319-05:00" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0x40008b0710, CONNECT
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.509814706-05:00" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0x40008b0710, READY"
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.511738887-05:00" level=info msg="pickfirstBalancer: HandleSubConnStateChange: 0x40007a2230, READY"
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.525913142-05:00" level=info msg="Graph migration to content-addressability took 0.00 seconds"
Dec 14 21:49:11 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:11.529808838-05:00" level=info msg="Loading containers: start."
Dec 14 21:49:12 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:12.258591473-05:00" level=info msg="Default bridge (docker0) is assigned with an IP address 172.17.0.0
Dec 14 21:49:12 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:12.540886055-05:00" level=info msg="Loading containers: done."
Dec 14 21:49:12 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:12.614462758-05:00" level=info msg="Docker daemon" commit=039a7df graphdriver(s)=overlay2 version=18.0
Dec 14 21:49:12 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:12.614718313-05:00" level=info msg="Daemon has completed initialization"
Dec 14 21:49:12 my-n2-1 dockerd[9629]: time="2019-12-14T21:49:12.640530153-05:00" level=info msg="API listen on /var/run/docker.sock"
Dec 14 21:49:12 my-n2-1 systemd[1]: Started Docker Application Container Engine.
A continuación, debemos deshabilitar el intercambio basado en disco. Para ello necesitamos realizar dos cosas.

Primer paso, en cada uno de los nodos my-n2-1 a través de my-n2-5, edita el archivo /etc/default/armbian-zram-config y cambia la línea ENABLED=true por ENABLED=false.

Segundo paso, en cada uno de los nodos my-n2-1 a través de my-n2-5, ejecuta los siguientes comandos:

$ sudo systemctl disable armbian-zram-config
$ sudo reboot now
Una vez más, en cada una de las pestañas de Terminal, conéctate por ssh al nodo correspondiente.

Esto completa la instalación y la configuración del sistema de los nodos del clúster. Próxima parada: configuración de Kubernetes

Configuración de Kubernetes

Para empezar, designaremos el nodo my-n2-1 como nodo maestro y configuraremos el plano de control. Para hacer esto, ejecuta el siguiente comando en my-n2-1:

$ sudo kubeadm init
El siguiente sería un resultado típico:
Output.8
[init] Using Kubernetes version: v1.16.3
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [my-n2-1 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.1.51]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [my-n2-1 localhost] and IPs [192.168.1.51 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [my-n2-1 localhost] and IPs [192.168.1.51 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
W1215 11:58:08.359442 4811 manifests.go:214] the default kube-apiserver authorization-mode is "Node,RBAC"; using "Node,RBAC"
[control-plane] Creating static Pod manifest for "kube-scheduler"
W1215 11:58:08.366477 4811 manifests.go:214] the default kube-apiserver authorization-mode is "Node,RBAC"; using "Node,RBAC"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 25.513764 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.17" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node my-n2-1 as control-plane by adding the label "node-role.kubernetes.io/master=''"
[mark-control-plane] Marking the node my-n2-1 as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: zcp5a6.w03lcuhx068wvkqv
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy
¡Tu plano de control Kubernetes se ha iniciado con éxito!

Para empezar a usar tu clúster, debes ejecutar lo siguiente como usuario normal:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Ahora debe implementar una red pod en el clúster. Ejecuta "kubectl apply -f [podnetwork] .yaml" con una de las opciones listadas en: https://kubernetes.io/docs/concepts/cluster-administration/addons/

Luego puedes unir cualquier número de nodos de trabajo ejecutando lo siguiente en cada uno como root:

kubeadm join 192.168.1.51:6443 --token zcp5a6.w03lcuhx068wvkqv \ --discovery-token-ca-cert-hash sha256:d2e38957f46a9eb089671924bca78ac4e02cdcc8db27e89677a014fe587b67c6

Para utilizar la herramienta de comandos kubectl como usuario no root en el nodo maestro (my-n2-1), ejecuta los siguientes comandos en my-n2-1:

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config
Para hacer una lista de todos los nodos en el clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl get nodes
El siguiente sería un resultado típico:
Output.9
NAME STATUS ROLES AGE VERSION
My-n2-1 NotReady master 2m37s v1.16.3
Para verificar que el clúster de Kubernetes se haya iniciado correctamente, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl get pods -n kube-system -o wide
Lo siguiente sería un resultado típico
Output.10
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
coredns-6955765f44-4gk4f 1/1 Running 0 40m 10.32.0.3 my-n2-1  
coredns-6955765f44-wskl4 1/1 Running 0 40m 10.32.0.2 my-n2-1  
etcd-my-n2-1 1/1 Running 0 40m 192.168.1.51 my-n2-1  
kube-apiserver-my-n2-1 1/1 Running 0 40m 192.168.1.51 my-n2-1  
kube-controller-manager-my-n2-1 1/1 Running 0 40m 192.168.1.51 my-n2-1  
kube-proxy-tklp7 1/1 Running 0 40m 192.168.1.51 my-n2-1  
kube-scheduler-my-n2-1 1/1 Running 0 40m 192.168.1.51 my-n2-1
En el resultado anterior, podemos ver que todos los componentes principales (servidor api, administrador de controladores, etc. y planificador) se encutran funcionando.

Ahora, necesitamos instalar una red de plugins superpuesta para la comunicación entre pod. Para nuestro clúster, elegiremos la implementación weave-net. Para instalar la red superpuesta en el nodo maestro (my-n2-1), ejecuta el siguiente comando:

$ kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
El siguiente sería un resultado típico:
Output.11
serviceaccount/weave-net created
clusterrole.rbac.authorization.k8s.io/weave-net created
clusterrolebinding.rbac.authorization.k8s.io/weave-net created
role.rbac.authorization.k8s.io/weave-net created
rolebinding.rbac.authorization.k8s.io/weave-net created
daemonset.apps/weave-net created
Para verificar que la red de superposición Weave se ha iniciado correcatente, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl get pods -n kube-system -l name=weave-net -o wide
El siguiente sería un resultado típico:
Output.12
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
weave-net-2sjh4 2/2 Running 0 10m 192.168.1.51 my-n2-1
Además, para verificar los registros de la red de superposición Weave, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl logs -n kube-system weave-net-ktjnv weave
El siguiente sería un resultado típico:
Output.13
INFO: 2019/12/08 17:07:12.422554 Command line options: map[conn-limit:200 datapath:datapath db-prefix:/weavedb/weave-net docker-api: expect-npc:true host-root:/host http-addr:127.0.0.1:6784 ipalloc-init:consensus=0 ipalloc-range:10.32.0.0/12 metrics-addr:0.0.0.0:6782 name:9a:59:d0:9a:83:f0 nickname:my-n2-1 no-dns:true port:6783]
INFO: 2019/12/08 17:07:12.422876 weave 2.6.0
INFO: 2019/12/08 17:07:12.780249 Bridge type is bridged_fastdp
INFO: 2019/12/08 17:07:12.780350 Communication between peers is unencrypted.
INFO: 2019/12/08 17:07:12.804023 Our name is 9a:59:d0:9a:83:f0(my-n2-1)
INFO: 2019/12/08 17:07:12.804267 Launch detected - using supplied peer list: []
INFO: 2019/12/08 17:07:12.844222 Unable to fetch ConfigMap kube-system/weave-net to infer unique cluster ID
INFO: 2019/12/08 17:07:12.844324 Checking for pre-existing addresses on weave bridge
INFO: 2019/12/08 17:07:12.853900 [allocator 9a:59:d0:9a:83:f0] No valid persisted data
INFO: 2019/12/08 17:07:12.866497 [allocator 9a:59:d0:9a:83:f0] Initialising via deferred consensus
INFO: 2019/12/08 17:07:12.866684 Sniffing traffic on datapath (via ODP)
INFO: 2019/12/08 17:07:12.872570 Listening for HTTP control messages on 127.0.0.1:6784
INFO: 2019/12/08 17:07:12.873074 Listening for metrics requests on 0.0.0.0:6782
INFO: 2019/12/08 17:07:13.540248 [kube-peers] Added myself to peer list &{[{9a:59:d0:9a:83:f0 my-n2-1}]}
DEBU: 2019/12/08 17:07:13.558983 [kube-peers] Nodes that have disappeared: map[]
INFO: 2019/12/08 17:07:13.661165 Assuming quorum size of 1
10.32.0.1
DEBU: 2019/12/08 17:07:13.911144 registering for updates for node delete events
Para este tutorial, designamos que los nodos my-n2-2 a través de my-n2-5 sean los nodos de trabajo de este clúster de Kubernetes. En el resultado 8, podemos determinar el comando de combinación kubeadm para usar en cada nodo de trabajo. Para cada uno de los nodos my-n2-2 a través de my-n2-5 (en su respectiva pestaña Terminal), ejecuta el siguiente comando:
$ sudo kubeadm join 192.168.1.51:6443 --token zcp5a6.w03lcuhx068wvkqv --discovery-token-ca-cert-hash sha256:d2e38957f46a9eb089671924bca78ac4e02cdcc8db27e89677a014fe587b67c6
El siguiente sería un resultado típico:
Output.14
[preflight] Running pre-flight checks
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.17" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...
Este nodo se ha unido al clúster: * Se envió una solicitud de firma de certificado a un servidor y se recibió una respuesta. * El Kubelet ha sido informado de los nuevos detalles de conexión segura.

Ejecuta 'kubectl get node' en el plano de control para ver como este nodo se une al clúster.

Para hacer una relación de todos los nodos activos en este clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1) (después de esperar unos 30 segundos):

$ kubectl get nodes -o wide
El siguiente sería un resultado típico:
Output.15
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
my-n2-1 Ready master 51m v1.17.0 192.168.1.51  Ubuntu 18.04.3 LTS 4.9.196-meson64 docker://18.9.9
my-n2-2 Ready  2m58s v1.17.0 192.168.1.52  Ubuntu 18.04.3 LTS 4.9.196-meson64 docker://18.9.9
my-n2-3 Ready  2m38s v1.17.0 192.168.1.53  Ubuntu 18.04.3 LTS 4.9.196-meson64 docker://18.9.9
my-n2-4 Ready  2m35s v1.17.0 192.168.1.54  Ubuntu 18.04.3 LTS 4.9.196-meson64 docker://18.9.9
my-n2-5 Ready  2m21s v1.17.0 192.168.1.55  Ubuntu 18.04.3 LTS 4.9.196-meson64 docker://18.9.9
¡Eso es! Esto completa toda la configuración necesaria para este clúster de Kubernetes.

Práctica con Kubernetes

Para hacer un lista de todos los pod (s) que se ejecutan en el clúster de Kubernetes (incluidos los pods del sistema), ejecuta el siguiente comando en el nodo maestro (my-n2-1):

$ kubectl get pods --all-namespaces -o wide
El siguiente sería un resultado típico:
Output.16
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
kube-system coredns-6955765f44-4gk4f 1/1 Running 0 52m 10.32.0.3 my-n2-1  
kube-system coredns-6955765f44-wskl4 1/1 Running 0 52m 10.32.0.2 my-n2-1  
kube-system etcd-my-n2-1 1/1 Running 0 52m 192.168.1.51 my-n2-1  
kube-system kube-apiserver-my-n2-1 1/1 Running 0 52m 192.168.1.51 my-n2-1  
kube-system kube-controller-manager-my-n2-1 1/1 Running 0 52m 192.168.1.51 my-n2-1  
kube-system kube-proxy-9zxfj 1/1 Running 0 3m36s 192.168.1.55 my-n2-5  
kube-system kube-proxy-c7mns 1/1 Running 0 3m53s 192.168.1.53 my-n2-3  
kube-system kube-proxy-dv52p 1/1 Running 0 4m13s 192.168.1.52 my-n2-2  
kube-system kube-proxy-mpwkb 1/1 Running 0 3m50s 192.168.1.54 my-n2-4  
kube-system kube-proxy-tklp7 1/1 Running 0 52m 192.168.1.51 my-n2-1  
kube-system kube-scheduler-my-n2-1 1/1 Running 0 52m 192.168.1.51 my-n2-1  
kube-system weave-net-2sjh4 2/2 Running 0 21m 192.168.1.51 my-n2-1  
kube-system weave-net-68lcd 2/2 Running 0 3m50s 192.168.1.54 my-n2-4  
kube-system weave-net-7fh98 2/2 Running 1 4m13s 192.168.1.52 my-n2-2  
kube-system weave-net-krdtz 2/2 Running 1 3m36s 192.168.1.55 my-n2-5  
kube-system weave-net-ljm6k 2/2 Running 0 3m53s 192.168.1.53 my-n2-3
Como es evidente en el resultado 16 anterior, vemos una entrada para API Server, etcd, Controller Manager, Scheduler y Plugin Network (weave-net) que indica que están en funcionamiento.

Para mostrar información detallada sobre cualquier pod (digamos el Controller Manager) en el clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1):

$ kubectl describe pod kube-controller-manager-my-n2-1 -n kube-system
El siguiente sería un resultado típico:
Output.17
Name: kube-controller-manager-my-n2-1
Namespace: kube-system
Priority: 2000000000
Priority Class Name: system-cluster-critical
Node: my-n2-1/192.168.1.51
Start Time: Sun, 15 Dec 2019 11:58:39 -0500
Labels: component=kube-controller-manager
tier=control-plane
Annotations: kubernetes.io/config.hash: 536dc7132dfd0d2ca1d968c9ede1e024
kubernetes.io/config.mirror: 536dc7132dfd0d2ca1d968c9ede1e024
kubernetes.io/config.seen: 2019-12-15T11:58:35.86446527-05:00
kubernetes.io/config.source: file
Status: Running
IP: 192.168.1.51
IPs:
IP: 192.168.1.51
Controlled By: Node/my-n2-1
Containers:
kube-controller-manager:
Container ID: docker://63b0d105457f52849afa38d2e914b53e68b7e21786fc41cda322bb21bc5b86a4
Image: k8s.gcr.io/kube-controller-manager:v1.17.0
Image ID: docker-pullable://k8s.gcr.io/kube-controller-manager@sha256:0438efb5098a2ca634ea8c6b0d804742b733d0d13fd53cf62c73e32c659a3c39
Port: 
Host Port: 
Command:
kube-controller-manager
--authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
--authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
--bind-address=127.0.0.1
--client-ca-file=/etc/kubernetes/pki/ca.crt
--cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
--cluster-signing-key-file=/etc/kubernetes/pki/ca.key
--controllers=*,bootstrapsigner,tokencleaner
--kubeconfig=/etc/kubernetes/controller-manager.conf
--leader-elect=true
--requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
--root-ca-file=/etc/kubernetes/pki/ca.crt
--service-account-private-key-file=/etc/kubernetes/pki/sa.key
--use-service-account-credentials=true
State: Running
Started: Sun, 15 Dec 2019 11:58:22 -0500
Ready: True
Restart Count: 0
Requests:
cpu: 200m
Liveness: http-get https://127.0.0.1:10257/healthz delay=15s timeout=15s period=10s #success=1 #failure=8
Environment: 
Mounts:
/etc/ca-certificates from etc-ca-certificates (ro)
/etc/kubernetes/controller-manager.conf from kubeconfig (ro)
/etc/kubernetes/pki from k8s-certs (ro)
/etc/ssl/certs from ca-certs (ro)
/usr/libexec/kubernetes/kubelet-plugins/volume/exec from flexvolume-dir (rw)
/usr/local/share/ca-certificates from usr-local-share-ca-certificates (ro)
/usr/share/ca-certificates from usr-share-ca-certificates (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
ca-certs:
Type: HostPath (bare host directory volume)
Path: /etc/ssl/certs
HostPathType: DirectoryOrCreate
etc-ca-certificates:
Type: HostPath (bare host directory volume)
Path: /etc/ca-certificates
HostPathType: DirectoryOrCreate
flexvolume-dir:
Type: HostPath (bare host directory volume)
Path: /usr/libexec/kubernetes/kubelet-plugins/volume/exec
HostPathType: DirectoryOrCreate
k8s-certs:
Type: HostPath (bare host directory volume)
Path: /etc/kubernetes/pki
HostPathType: DirectoryOrCreate
kubeconfig:
Type: HostPath (bare host directory volume)
Path: /etc/kubernetes/controller-manager.conf
HostPathType: FileOrCreate
usr-local-share-ca-certificates:
Type: HostPath (bare host directory volume)
Path: /usr/local/share/ca-certificates
HostPathType: DirectoryOrCreate
usr-share-ca-certificates:
Type: HostPath (bare host directory volume)
Path: /usr/share/ca-certificates
HostPathType: DirectoryOrCreate
QoS Class: Burstable
Node-Selectors: < none >
Tolerations: :NoExecute
Events: < none >
Para hacer una relación de todos los pod(s) de aplicación que se ejecutan en el clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl get pods
El siguiente sería un resultado típico:
Output.18
No resources found in default namespace.
Para hacer una lista de todos los servicios que se ejecutan en el clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl get services
El siguiente sería un resultado típico:
Output.19
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1  443/TCP 64m
Crearemos una simple aplicación web Python para mostrar el nombre del host, así como la dirección IP cuando se invoque a través de HTTP. El siguiente contenido pertenece a la aplicación web Python almacenada en el directorio /tmp en el nodo maestro (my-n2-1):
web-echo.py
from flask import Flask
import socket

app = Flask(__name__)

@app.route("/")
def index():
host_name = socket.gethostname()
host_ip = socket.gethostbyname(host_name)
return 'Hello from container -> ' + host_name + ' [' + host_ip + ']'

if __name__ == "__main__":
app.run(host='0.0.0.0', port=8888)
El siguiente es el contenido de Dockerfile para crear una imagen Docker para la simple aplicación web de Python almacenada en el directorio /tmp en el nodo maestro (my-n2-1):
Dockerfile
FROM python:3.7.5-alpine3.9
RUN pip install flask
ADD web-echo.py /web-echo.py
CMD ["python", "/web-echo.py"]
Para crear una imagen de Docker llamada py-web-echo con la etiqueta v1.0, ejecuta los siguientes comandos en el nodo maestro (my-n2-1):
cd /tmp
docker build -t "py-web-echo:v1.0" .
El siguiente sería un resultado típico:
Output.20
Sending build context to Docker daemon 3.072kB
Step 1/4: FROM python:3.7.5-alpine3.9
3.7.5-alpine3.9: Pulling from library/python
0362ad1dd800: Pull complete
9b941924aae3: Pull complete
fd7b3613915d: Pull complete
078d60b9b97e: Pull complete
7059e1dd9bc4: Pull complete
Digest: sha256:064d9ce3e91a59535c528bc3c38888023791d9fc78ba9e5070f5064833f326ff
Status: Downloaded newer image for python:3.7.5-alpine3.9
---> 578ec6233872
Step 2/4: RUN pip install flask
---> Running in d248e23dd161
Collecting flask
Downloading https://files.pythonhosted.org/packages/9b/93/628509b8d5dc749656a9641f4caf13540e2cdec85276964ff8f43bbb1d3b/Flask-1.1.1-py2.py3-none-any.whl (94kB)
Collecting Jinja2>=2.10.1
Downloading https://files.pythonhosted.org/packages/65/e0/eb35e762802015cab1ccee04e8a277b03f1d8e53da3ec3106882ec42558b/Jinja2-2.10.3-py2.py3-none-any.whl (125kB)
Collecting Werkzeug>=0.15
Downloading https://files.pythonhosted.org/packages/ce/42/3aeda98f96e85fd26180534d36570e4d18108d62ae36f87694b476b83d6f/Werkzeug-0.16.0-py2.py3-none-any.whl (327kB)
Collecting itsdangerous>=0.24
Downloading https://files.pythonhosted.org/packages/76/ae/44b03b253d6fade317f32c24d100b3b35c2239807046a4c953c7b89fa49e/itsdangerous-1.1.0-py2.py3-none-any.whl
Collecting click>=5.1
Downloading https://files.pythonhosted.org/packages/fa/37/45185cb5abbc30d7257104c434fe0b07e5a195a6847506c074527aa599ec/Click-7.0-py2.py3-none-any.whl (81kB)
Collecting MarkupSafe>=0.23
Downloading https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz
Building wheels for collected packages: MarkupSafe
Building wheel for MarkupSafe (setup.py): started
Building wheel for MarkupSafe (setup.py): finished with status 'done'
Created wheel for MarkupSafe: filename=MarkupSafe-1.1.1-cp37-none-any.whl size=12629 sha256=8a200864ca113d03b4de2d951ae4a1d0806a3ff84128349770dfe3fb018a6458
Stored in directory: /root/.cache/pip/wheels/f2/aa/04/0edf07a1b8a5f5f1aed7580fffb69ce8972edc16a505916a77
Successfully built MarkupSafe
Installing collected packages: MarkupSafe, Jinja2, Werkzeug, itsdangerous, click, flask
Successfully installed Jinja2-2.10.3 MarkupSafe-1.1.1 Werkzeug-0.16.0 click-7.0 flask-1.1.1 itsdangerous-1.1.0
Removing intermediate container d248e23dd161
---> 4ee40e66a655
Step 3/4: ADD web-echo.py /web-echo.py
---> 31a0341bf9d7
Step 4/4: CMD ["python", "/web-echo.py"]
---> Running in 1ee52ea10ad3
Removing intermediate container 1ee52ea10ad3
---> 7cd037d24ef7
Successfully built 7cd037d24ef7
Successfully tagged py-web-echo:v1.0
Para hacer una relación de todas las imágenes de Docker en el nodo maestro (my-n2-1), ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ docker images
El siguiente sería un resultado típico:
Output.21
REPOSITORY TAG IMAGE ID CREATED SIZE
py-web-echo v1.0 7cd037d24ef7 3 minutes ago 119MB
k8s.gcr.io/kube-proxy v1.17.0 ac19e9cffff5 7 days ago 114MB
k8s.gcr.io/kube-apiserver v1.17.0 aca151bf3e90 7 days ago 166MB
k8s.gcr.io/kube-controller-manager v1.17.0 7045158f92f8 7 days ago 156MB
k8s.gcr.io/kube-scheduler v1.17.0 0d5c120f87f3 7 days ago 93.7MB
python 3.7.5-alpine3.9 578ec6233872 4 weeks ago 109MB
weaveworks/weave-npc 2.6.0 1c672c2f5870 5 weeks ago 36.6MB
weaveworks/weave-kube 2.6.0 81393394d17d 5 weeks ago 111MB
k8s.gcr.io/coredns 1.6.5 f96217e2532b 5 weeks ago 39.3MB
k8s.gcr.io/etcd 3.4.3-0 ab707b0a0ea3 7 weeks ago 363MB
k8s.gcr.io/pause 3.1 6cf7c80fe444 24 months ago 525kB
Ten en cuenta que creamos la imagen de Docker en el nodo maestro (my-n2-1). Dado que los pods se implementarán en los nodos de trabajo, debemos asegurarnos de que las imágenes docker necesarias estén presentes en los nodos de trabajo.

Para cada uno de los nodos de trabajo my-n2-2 hasta my-n2-5 (en su respectiva pestaña Terminal), ejecuta el siguiente comando:

$ docker pull python:3.7.5-alpine3.9
Para cada uno de los nodos de trabajo my-n2-2 a través de my-n2-5, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ docker save py-web-echo:v1.0 | bzip2 | ssh polarsparc@192.168.1.52 'bunzip2 | docker load'
$ docker save py-web-echo:v1.0 | bzip2 | ssh polarsparc@192.168.1.53 'bunzip2 | docker load'
$ docker save py-web-echo:v1.0 | bzip2 | ssh polarsparc@192.168.1.54 'bunzip2 | docker load'
$ docker save py-web-echo:v1.0 | bzip2 | ssh polarsparc@192.168.1.55 'bunzip2 | docker load'
!!! AVISO IMPORTANTE !!!

No tener las imágenes de Docker en los nodos de trabajo hará que los pod se queden atascados en el estado de creación de los contenedores.

En Kubernetes, un pod es lo que encapsula los contenedores Docker. Para implementar nuestra aplicación web Docker py-web-echo:v1.0 en nuestro clúster de Kubernetes, necesitamos un archivo manifiesto de pod en formato YAML.

Los siguientes son los contenidos del archivo manifiesto del pod llamado web-echo-pod.yaml almacenado en el directorio /tmp en el nodo maestro (my-n2-1):

web-echo-pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: web-echo-pod
labels:
app: web-echo
spec:
containers:
- name: web-echo
image: py-web-echo:v1.0
imagePullPolicy: Never
ports:
- containerPort: 8888
A continuación explicaremos los elementos del archivo de manifiesto web-echo-pod.yaml:

  • apiVersion: especifica la versión de la API (v1 en este ejemplo)
  • kind: especifica el tipo de objeto Kubernetes a utilizar (Pod en este ejemplo)
  • metadata: asocia un nombre (web-echo-pod en este ejemplo) con el tipo de objeto Kubernetes. Además, permite marcar algunas etiquetas, que son simples pares clave-valor, con los Kubernetes.
  • object. En este ejemplo, tenemos una etiqueta con la aplicación clave que tiene un valor de web-echo
  • spec: especifica lo que hay en el pod. En este ejemplo, queremos implementar la imagen Docker py-web-echo: v1.0 que se presenta a través del puerto de red 8888.
  • imagePullPolicy: indica a Kubernetes que no tire de la imagen del contenedor.

Para implementar el pod en nuestro clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1):

$ kubectl apply -f /tmp/web-echo-pod.yaml
El siguiente sería un resultado típico:
Output.22
pod/web-echo-pod created
Para hacer una lista de todos los pods de aplicaciones que se ejecutan en el clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl get pods -o wide
El siguiente sería un resultado típico:
Output.23
1
En el resultado 23, vemos que nuestro pod de aplicación se ha implementado en el nodo my-n2-2 de nuestro clúster Kubernetes.

Para mostrar información detallada sobre el pod de aplicación web-echo-pod implementado, ejecuta el siguiente comando en el nodo maestro (my-n2-1):

$ kubectl describe pods web-echo-pod
El siguiente sería un resultado típico:
Output.24
Name: web-echo-pod
Namespace: default
Priority: 0
Node: my-n2-2/192.168.1.52
Start Time: Sun, 15 Dec 2019 14:58:21 -0500
Labels: app=web-echo
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"labels":{"app":"web-echo"},"name":"web-echo-pod","namespace":"default"},"spe...
Status: Running
IP: 10.44.0.1
IPs:
IP: 10.44.0.1
Containers:
web-echo:
Container ID: docker://0af2c99fd074b5ee3c0b9876eb9ad44ca446400c2190b4af6fa1a18543bff723
Image: py-web-echo:v1.0
Image ID: docker://sha256:7cd037d24ef7c842ffe005cfcb548a802fc13661c08c8bb4635c365f77e5a3aa
Port: 8888/TCP
Host Port: 0/TCP
State: Running
Started: Sun, 15 Dec 2019 14:58:23 -0500
Ready: True
Restart Count: 0
Environment: 
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-tvl5x (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
default-token-tvl5x:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-tvl5x
Optional: false
QoS Class: BestEffort
Node-Selectors: 
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 7m39s default-scheduler Successfully assigned default/web-echo-pod to my-n2-2
Normal Pulled 7m38s kubelet, my-n2-2 Container image "py-web-echo:v1.0" already present on machine
Normal Created 7m38s kubelet, my-n2-2 Created container web-echo
Normal Started 7m37s kubelet, my-n2-2 Started container web-echo
En el anterior resultado 23 (así como en el resultado 24), vemos que la dirección IP de la aplicación web implementada es 10.44.0.1.

Para probar la aplicación web desplegada utilizando el comando curl, ejecuta el siguiente comando en cualquiera de los nodos my-n2-1 a través de my-n2-5:

$ curl http://10.44.0.1:8888
El siguiente sería un resultado típico:
Output.25
Hello from container -> web-echo-pod [10.44.0.1]
Para mostrar los registros log de la aplicación web implementada web-echo-pod, ejecuta el siguienta comando en el nodo maestro (my-n2-1):
$ kubectl logs web-echo-pod
El siguiente sería un resultado típico:
Output.26
* Serving Flask app "web-echo" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:8888/ (Press CTRL+C to quit)
10.32.0.1 - - [15/Dec/2019 20:11:33] "GET / HTTP/1.1" 200 -
10.36.0.0 - - [15/Dec/2019 20:11:58] "GET / HTTP/1.1" 200 -
Para eliminar la aplicación web desplegada web-echo-pod, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl delete pod web-echo-pod
El siguiente sería un resultado típico:
Output.27
pod "web-echo-pod" deleted
*NOT* es muy común al implementar un único Pod. Es más normal implementar un objeto Kubernetes de nivel superior llamado ReplicaSet. Un ReplicaSet define cuántas réplicas de un Pod deben implementarse y mantenerse en el clúster de Kubernetes.

Los siguientes son los contenidos del archivo manifiesto de ReplicaSet llamado web-echo-rs.yaml almacenado en el directorio /tmp en el nodo maestro (my-n2-1):

web-echo-rs.yaml
---
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: web-echo-rs
spec:
replicas: 3
selector:
matchLabels:
app: web-echo
template:
metadata:
labels:
app: web-echo
spec:
containers:
- name: web-echo
image: py-web-echo:v1.0
imagePullPolicy: Never
ports:
- containerPort: 8888
Explicaremos ahora algunos de los elementos del archivo manifiesto web-echo-rs.yaml:

apiVersion: especifica la versión de la API (apps/v1 en este ejemplo) replicas: indica las instancias deseadas del Pod que se ejecutarán en el clúster de Kubernetes selector: identifica y selecciona un grupo de objetos de Kubernetes con la misma etiqueta de valor clave (aplicación clave y valor web-echo en este ejemplo)

template: es la especificación interna de un Pod

Para implementar ReplicaSet en nuestro clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1):

$ kubectl apply -f /tmp/web-echo-rs.yaml
El siguiente sería un resultado típico:
Output.28
replicaset.apps/web-echo-rs created
Para hacer una lista de todos los ReplicaSet implementados que se ejecutan en el clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl get replicasets -o wide
El siguiente sería un resultado típico:
Output.29
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
web-echo-rs 3 3 3 7m web-echo py-web-echo:v1.0 app=web-echo
Para mostrar información detallada sobre el ReplicaSet implementado llamado web-echo-rs, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl describe replicasets web-echo-rs
El siguiente sería un resultado típico:
Output.30
Name: web-echo-rs
Namespace: default
Selector: app=web-echo
Labels: 
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"apps/v1","kind":"ReplicaSet","metadata":{"annotations":{},"name":"web-echo-rs","namespace":"default"},"spec":{"replicas":3,...
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=web-echo
Containers:
web-echo:
Image: py-web-echo:v1.0
Port: 8888/TCP
Host Port: 0/TCP
Environment: 
Mounts: 
Volumes: 
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulCreate 14m replicaset-controller Created pod: web-echo-rs-xn94l
Normal SuccessfulCreate 14m replicaset-controller Created pod: web-echo-rs-9x9b9
Normal SuccessfulCreate 14m replicaset-controller Created pod: web-echo-rs-tbd49
Para hacer una lista de todos los pods de aplicaciones que se ejecutan en el clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl get pods -o wide
El siguiente sería un resultado típico:
Output.31
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-echo-rs-9x9b9 1/1 Running 0 63s 10.42.0.1 my-n2-4  
web-echo-rs-tbd49 1/1 Running 0 63s 10.44.0.1 my-n2-2  
web-echo-rs-xn94l 1/1 Running 0 63s 10.36.0.1 my-n2-3
En el resultado 31, vemos que nuestros pods de aplicación se han implementado en los 3 nodos my-n2-2, my-n2-3 y my-n2-4 con direcciones IP únicas de 10.44.0.1, 10.36 .0.1 y 10.42.0.1 respectivamente.

Como se ha mencionado al principio, los pods de aplicación son efímeros. Pueden aparecer y desaparecer en cualquier momento. Esto significa que sus direcciones IP pueden cambiar en cualquier momento. Necesitamos una abstracción de nivel superior que proporcione una dirección IP estable para que otros pod (s) de aplicaciones puedan usarla. Aquí es donde resula muy útil un objeto de Servicio. Proporciona una única dirección IP estable para que otras aplicaciones la usen y distribuye la carga a través de los diferentes pod (s) de aplicaciones del back-end que están delante.

Hay 3 tipos de servicio(s) en Kubernetes:

  • ClusterIP: expone el Servicio en una dirección IP que es interna al clúster de Kubernetes. Esto significa que se puede acceder al Servicio *SOLO* desde dentro del clúster de Kubernetes. Este es el tipico por defecto.
  • NodePort: expone al Servicio en la dirección IP de cada nodo de trabajo en un puerto alto en el rango de 30000 a 32767. Las aplicaciones externas al clúster de Kubernetes pueden acceder al Servicio en la dirección IP del nodo de trabajo y el puerto de nodo asignado
  • LoadBalancer: Expone el Servicio externamente usando un Load Balancer de proveedores en la nube como AWS, Azure o Google Cloud

El siguiente es el contenido del archivo de manifiesto del Servicio basado en ClusterIP llamado web-echo-svc-cip.yaml almacenado bajo el directorio /tmp en el nodo maestro (my-n2-1):

web-echo-svc-cip.yaml
---
apiVersion: v1
kind: Service
metadata:
name: web-echo-svc-cip
spec:
selector:
app: web-echo
ports:
- name: http
protocol: TCP
port: 8888
Para implementar el Servicio en nuestro clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl apply -f /tmp/web-echo-svc-cip.yaml
El siguiente sería un resultado típico:
Output.32
service/web-echo-svc created
Para hacer una lista de todos los Servicios que se ejecuten en el clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl get services -o wide
El siguiente sería un resultado típico:
Output.33
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1  443/TCP 9h 
web-echo-svc ClusterIP 10.96.238.16  8888/TCP 105s app=web-echo
En el resulado 33 anterior, vemos que se puede acceder a la aplicación web-echo desde cualquier lugar del clúster a través de la dirección IP 10.96.238.16 y el puerto 8888.

Para probar el punto final del Servicio implementado utilizando el comando curl, ejecuta el siguiente comando 5 veces en cualquiera de los nodos my-n2-1 a través de my-n2-5:

$ curl http://10.96.238.16:8888
El siguiente sería un resultado típico:
Output.34
Hello from container -> web-echo-rs-xn94l [10.36.0.1]
Hello from container -> web-echo-rs-9x9b9 [10.42.0.1]
Hello from container -> web-echo-rs-tbd49 [10.44.0.1]
Hello from container -> web-echo-rs-9x9b9 [10.42.0.1]
Hello from container -> web-echo-rs-tbd49 [10.44.0.1]
Para mostrar información detallada sobre el punto final del servicio etiquetado web-echo-svc, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl describe service web-echo-svc
El siguiente sería un resultado típico:
Output.35
Name: web-echo-svc
Namespace: default
Labels: 
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"web-echo-svc","namespace":"default"},"spec":{"ports":[{"name":"ht...
Selector: app=web-echo
Type: ClusterIP
IP: 10.96.238.16
Port: http 8888/TCP
TargetPort: 8888/TCP
Endpoints: 10.36.0.1:8888,10.42.0.1:8888,10.44.0.1:8888
Session Affinity: None
Events:
Para eliminar el objeto desplegado web-echo-svc, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl delete service web-echo-svc
El siguiente sería un resultado típico:
Output.36
service "web-echo-svc" deleted
Los siguientes son los contenidos del archivo de manifiesto de servicio basado en NodePort llamado web-echo-svc-nop.yaml almacenado bajo el directorio /tmp en el nodo maestro (my-n2-1):
web-echo-svc-nop.yaml
---
apiVersion: v1
kind: Service
metadata:
name: web-echo-svc
spec:
type: NodePort
selector:
app: web-echo
ports:
- name: http
protocol: TCP
port: 8888
Para implementar el Servicio en nuestro clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl apply -f /tmp/web-echo-svc-nop.yaml
El siguiente sería un resultado típico:
Output.37
service/web-echo-svc created
Para hacer una lista de todos los Servicios que se ejecutan en el clúster de Kubernetes, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl get services -o wide
El siguiente sería un resultado típico
Output.38
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1  443/TCP 9h 
web-echo-svc NodePort 10.96.144.75  8888:32546/TCP 38m app=web-echo
Para mostrar información detallada sobre el punto del servicio etiquetado web-echo-svc, ejecuta el siguiente comando en el nodo maestro (my-n2-1):
$ kubectl describe service web-echo-svc
El siguiente sería un resultado típico:
Output.39
Name: web-echo-svc
Namespace: default
Labels: 
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"web-echo-svc","namespace":"default"},"spec":{"ports":[{"name":"ht...
Selector: app=web-echo
Type: NodePort
IP: 10.96.144.75
Port: http 8888/TCP
TargetPort: 8888/TCP
NodePort: http 32546/TCP
Endpoints: 10.36.0.1:8888,10.42.0.1:8888,10.44.0.1:8888
Session Affinity: None
External Traffic Policy: Cluster
Events:
En el resultado 39 anterior, vemos que el puerto del nodo de Servicio implementado es 32546.

Abre un navegador y acceda a la url http://192.168.1.53:32546. La siguiente imagen es la pantalla típica del navegador:

Figure 3
Figura 3

BINGO: ¡todo funciona como era de esperar!

Y con esto concluimos los ejercicios básicos que hemos realizado en nuestro grupo de Kubernetes.

Referencias

https://kubernetes.io/docs/home/?path=browse https://www.weave.works/docs/net/latest/overview/ https://docs.docker.com/ https://www.polarsparc.com/xhtml/Practical-K8S-N2.html

Be the first to comment

Leave a Reply