Monta tu propio cluster Kubernetes

Llevo los últimos meses intentando aprender Kubernetes después de que la experiencia con Docker fuese tan satisfactoria en todos los aspectos. No obstante con Kubernetes caía una y otra vez en los problemas de la complejidad inherente a una plataforma tan adaptada para los pasos a producción de grandes aplicaciones. Muchos de los tutoriales (incluyendo los propios de kubernetes) te instaban a instalarte minikube o usar algunos playgrounds disponibles online como Katacoda o Play with kubernetes. Al final lo que era evidente es que necesitaba un cluster k8s para poder aprender un poco más de kubernetes.

Minikube tiene importantes restricciones y los otro playground son de usar y tirar, por lo que, al final, si quería aprender de verdad tenía que construirme mi propio cluster… Y ahora mismo no me apetece pagar por tener algo puramente experimental, así que aproveché y, dado que tengo dos sobremesa en casa, decidí instalar el cluster en mis propios ordenadores y poder disfrutar de toda la potencia de kubernetes. Aquí un resumen muy resumido de lo que hay que hacer en ubuntu 18.04 (que es lo que tenía en los dos):

Primer paso: instalar docker

Eso creo que ya lo hemos tratado aquí en algunas ocasiones, no obstante ha mejorado mucho la forma de instalarlo desde entonces (en todas las máquinas):

sudo apt install docker.io
sudo systemctl enable docker
sudo addgroup docker ${USER}

Paso 2: instalar kubernetes

Igualmente en todos los nodos:

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add
sudo apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main"
sudo apt-get install kubeadm kubelet kubectl
sudo apt-mark hold kubeadm kubelet kubectl

Si todo ha ido bien podemos ver la versión que hemos instalado:

kubeadm version

Paso 3: inicializar cluster

Para inicializar el cluster primero debemos asignar un nombre a cada nodo, además, previamente tendremos que desactivar el swap que no se lleva bien con este sistema, primero con el master y luego con el resto:

sudo swapoff -a
sudo hostnamectl set-hostname master-node

Y luego en el resto:

sudo hostnamectl set-hostname worker01

Con todos los nodos ya con nombre podemos inicializar en el maestro el cluster:

sudo kubeadm init --pod-network-cidr=10.244.0.0/16

Como resultado (y si todo va bien) el comando nos devolverá el comando a ejecutar en cada uno de los nodos, algo así como:

kubeadm join --discovery-token abcdef.1234567890abcdef --discovery-token-ca-cert-hash sha256:1234..cdef 1.2.3.4:6443

Debemos guardar ese comando ya que lo tendremos que ejecutar posteriormente en cualquier nodo que queramos unir al cluster

Para poder administrar con kubectl necesitamos guardar la configuración que acabamos de generar en el usuario que estemos usando… Dentro del master es sencillo:

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

Con esto ya podremos lanzar nuestros comandos kubectl contra nuestro nuevo cluster

Paso 4: Desplegar red en el cluster

Tal como está configurado ahora mismo no hay forma de comunicarse entre los pods y el resto, vamos a instalar flannel como red virtual, para ello ejecutar:

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

Al cabo de un rato podremos ver si los pods están correctamente desplegados con:

kubectl get pods --all-namespaces

Paso 5: añadir nodos a la red

Como ya dijimos en el paso 3, tenemos un comando a ejecutar en cada nodo de la red para unirse al cluster que acabamos de crear. Ejecutamos ese comando en cada uno de los nodos que queremos unir y luego, dentro del master, podemos comprobar si están presentes todos los nodos y el estado en que están:

kubectl get nodes
NAME STATUS ROLES AGE VERSION
master-node Ready master 3d17h v1.18.5
worker01 Ready 3d17h v1.18.5
worker02 Ready 2d22h v1.18.5
worker03 Ready 2d17h v1.18.5

Si todos están en estado Ready hemos triunfado… Listos para desplegar lo que queramos en nuestro cluster casero… A ver si nos da tiempo a explorarlo con cierta extensión.

Variables de entorno y docker

Después de un tiempo definiendo distintos contenedores docker y coordinando despliegues con docker compose me he encontrado con circunstancias que me obligaban a modificar los archivos de definición cada vez que necesitaba hacer un nuevo despliegue o paso a producción y eso, bueno, eso es un poco molesto. Así que sabiendo que la gente que ha desarrollado todo esto era más lista que yo me puse a buscar cómo proponen que lo hagamos.

Y la respuesta es… Mediante varibles de entorno (al menos una de ellas), así que vamos a ver cómo podemos, como ejercicio, poner el número de versión de nuestro despliegue como varible de entorno..

Uno de mis docker-composes tenía este aspecto:

    web-php:
        container_name: web-php
        build: .
        image: nexus.biblioetech.com/biblioeteca/nomorepass:1.0.0
        ports:
            - "80:80"
        environment: 
            DBHOST: "db"
        links:
            - "web-mysql:db"

Como véis el número de versión acompañaba al nombre de la imagen y esto es así para que al construirla ya llevase la etiqueta adecuada para subirla al repositorio privado. El problema era que cada vez que hacíamos una release nueva teníamos que tocar este archivo a mano… Si queremos no tener que tocar el archivo tenemos que poner algo así:

    web-php:
        container_name: web-php
        build: .
        image: nexus.biblioetech.com/biblioeteca/nomorepass:${VERSION}
        ports:
            - "80:80"
        environment: 
            DBHOST: "db"
        links:
            - "web-mysql:db"

De esta forma solo hay que definir la varible de entorno VERSION al valor que acabamos de generar. Ahora bien, es fácil que nos olvidemos de asignar esta variable de entorno si no está en ningún sitio del repositorio, así que lo más sencillo es incluirla en un archivo .env que será el que docker-compose cargue antes de ejectuar el build… Y quedaría así:

VERSION=1.0.0

En este archivo podemos incluir todas las variables que necesitemos y, lo que es más, podremos pasarselas al Dockerfile si lo necesitamos, eso si, el método es un poco más rebuscado (eso lo veremos un poco más adelante).

PC-COMPONENTES? nunca más

Soy un asiduo comprador por internet desde que el e-commerce se inventó, compraba libros en Amazon antes de que llegasen a España y metí mi tarjeta de crédito en optize allá por los años 90 cuando nadie se fiaba de ello. No he dejado de comprar en tiendas virtuales desde entonces (y hasta he tenido las mías propias en las que he vendido desde impresoras hasta leds), por eso se me hace tan complicado de entender como pueden existir todavía empresas que no saben tratar las incidencias que se dan regularmente en el comercio electrónico. Para ello os voy a poner una imagen:

Esta es la ¿ruta? que ha seguido GLS para entregarme un portatil que compré en pccomponentes el día 3 de Mayo. La fecha prevista de entrega era el día 5 (como es habitual en las entregas nacionales), pero como ese día no llegó les di un poco de margen por eso del coronavirus… El día 6 no llegó tampoco, el día 7 entro en la web del transportista y les digo que quiero que me llegue por la mañana (a ver si respiran) y nada, ahí seguimos, el día 8 viernes tampoco y el día 11 tampoco y lo más sangrante es que NO SABEN DONDE ESTÁ.

Llegado a este punto y dado que el portatil lo ha comprado mi empresa y lo necesito para trabajar, intento contactar con pccomponentes para que me busquen una solución (esto ya me había pasado con Amazon y lo habitual es devolverte el dinero o enviarte otra vez la mercancía) y la respuesta de pccomponentes es: «nada que hacer». Todas las respuestas son igualitas a esta:

Les llamo por teléfono y me dicen que no nos pueden devolver el dinero hasta que ellos no reciban devuelta la mercancía.. ¿¿Se puede ser más inútil?? El cliente no recibe la mercancía que tu has cobrado en una semana y le dices que no le vas a devolver el dinero hasta que la reciban ellos… Eso suponiendo que alguien, en algún momento, encuentre esa mercancía y decida devolverla, si no… Esperar a que el transportista decida declarar que ha perdido la mercancía y mientras ellos se quedan con mi dinero.

No entiendo esta actitud. Me he gastado muchos miles de euros comprando cosas en esa tienda en el pasado y al primer problema que tienen dejan indefenso y cabreado al cliente. Cliente que, obviamente, dejará de serlo inmediatamente.

Bye, bye PC-Componentes, no se si recibiré la mercancía o me devolveréis el dinero (o tendré que formular una denuncia por estafa), pero se acabó eso de compraros nada más (y mi opinión sobre vuestro servicio no dejaré de expresarla allá por donde me pregunten). Si la culpa es del transporte (que lo es), los responsables seguís siendo vosotros que sois los que tenéis mi dinero.

Mover el directorio docker en ubuntu

Usar docker es sencillo y rápido, pero, como todo, tienes que tener cuidado con todo lo que ocupa espacio ya que los discos y las particiones son finitas… Y los contendores docker ocupan espacio (bastante), así que aquí dejo el método para mover el directorio de datos de la partición por defecto a otra cualquiera que queramos y para la que tengamos espacio de sobra.

Docker y ubuntu

Lo primero que hay que saber es que el directorio por defecto donde docker guarda sus imágenes, contenedores y demás cosas que ocupan espacio es:

/var/lib/docker

Que, generalmente, está en la partición raiz (o la que tengamos para var) que no suele ser muy grande. Supongamos que queremos moverla a un disco de datos con capacidad suficiente como este:

/mnt/disks/data/docker

Para ello lo que tendremos que hacer es: parar el servicio docker

sudo service docker stop

Crear un archivo llamado daemon.json en el directorio /etc/docker con este contenido:

{ 
   "graph": "/path/to/your/docker" 
}

Copiamos el directorio original al nuevo y lo renombramos por seguridad (ya lo borraremos después)

sudo rsync -aP /var/lib/docker/ /path/to/your/docker
sudo mv /var/lib/docker /var/lib/docker.old

Reiniciamos el servicio docker y todo debería estar como antes del cambio (podéis comprobar con un docker ps si se ha levantado todo lo que teníais funcionando antes).

sudo service docker start

Después de comprobar que todo está ok, ya podemos borrar el original y habremos terminado con la operación del todo:

sudo rm -rf /var/lib/docker.old

La insoportable dependencia de las tiendas de aplicaciones

El avance imparable de la informática en todas las áreas nos ha creado nuevos problemas a los desarrolladores, no ya el hecho de tener que conocer nuevas herramientas cada mes, o nuevos frameworks de desarrollo que tardas más tiempo en aprender que el que te ahorras, sino la pérdida completa del control de lo que tus usuarios pueden instalarse, dónde y cómo. Os pondré un ejemplo.

NoMorePass es una aplicación móvil de la que os llevo hablando bastante y que desarrollamos nosotros; a nivel básico consiste en la app y un conjunto de extensiones para los navegadores que nos permiten intercambiar las contraseñas mediante un código QR. La app ahora mismo tiene su máxima utilidad cuando se usa desde la extensión. Desarrollar una extensión para navegador no es especialmente complicado, como ya os expliqué en esta entrada, probarlas tampoco es difícil con el modo desarrollador de los navegadores, pero una vez que la tenemos lista tenemos que pasar por un infierno inesperado… Ponerla a disposición del público.

Antes se podía distribuir un archivo empaquetado que al abrirse permitía instalar la extensión, pero esa forma de distribuir extensiones se volvió peligrosa (a pesar de que los navegadores preguntaban muchas veces si de verdad querías instalar eso) y Google decidió que para chrome solo se podían instalar extensiones que pasasen por su tienda de aplicaciones el chome web store. Firefox, por su parte, no lo hace obligatorio por el momento, pero la mejor manera de que una extensión se conozca es tenerla en la página de add-ons de firefox. Lo mismo pasa con Opera (aunque esta es otra odiséa que ya os contaré) y, hasta el momento, con Edge.

No tengo nada en contra de las tiendas de aplicaciones mientras estas funcionen de manera ágil y razonable. A efectos prácticos se convierten en cuellos de botella que pueden afectar en gran medida a la forma en la que se produce la distribución de los productos. Como ejemplo valga lo que me ha pasado en las últimas semanas con la extensión de chrome.

El día 19 de noviembre terminé de desarrollar una mejora a la extensión que había en la tienda, pasaba de la versión 2.1.1 a la 3.0.0 y tras probarla en local y funcionando perfectamente intenté subirla al web store… Se quejó que de que pedía demasiados permisos (cosa que no hizo con la versión anterior) y modifiqué la extensión para que funcionase sin necesidad de pedirlos, generé una nueva versión y la subí a toda prisa… Tanta prisa que se me coló un bug que impedía que funcionase el botón derecho (una funcionalidad que se usa mucho cuando la web no está reconocida o queremos rellenar un formulario con una contraseña que tenemos)… Me di cuenta de ello minutos después de haber «publicado» la versión y cuando intenté subir la versión corregida veo que NO ME DEJA... Dice que la extensión está pendiente de revisión y no se puede enviar otra versión. WTF!!!

No había mucho problema, la versión anterior funcionaba perfectamente y las nuevas funcionalidades podían esperar… Así que esperé ¡¡5 DIAS!! a que Google pasara a producción la versión con el bug un sábado por la mañana… Inmediatamente subí la nueva versión (que solo tiene 2 líneas de código distintas con la versión anterior) esperando que se aprobase inmediatamente y que los clientes no tuviesen que sufrir el bug… Pero no, volvieron a poner en revisión la nueva versión y en esas estamos, llevamos ya 4 días con la versión defectuosa de la extensión y con clientes que ya se han quejado… Y no podemos hacer nada, Google nos ha jodido y no nos da ninguna pista de cuando se publicará de verdad la nueva versión.

No hay nada que podamos decir a los clientes, no hay forma de volver a una versión antigua si tenías la auto-actualización de las extensiones (google no guarda las versiones viejas) y no hay forma de acelerar el proceso de publicación. Tampoco hay forma de hablar con Google (he mandado múltiples formularios de contacto con un sonoro silencio por respuesta), tampoco puedo dar un método alternativo para instalar la extensión porque Google obliga a usar su tienda si-o-si, así que no queda más que esperar a que se publique finalmente la nueva versión y disculparnos ante los clientes y asumamos las pérdidas que esto nos pueda ocasionar como algo inevitable… Muy mal google.

ACTUALIZACIÓN 1: 28/11/2019 Google ha leído este post y ha decidido ir un paso más adelante, nos ha RECHAZADO la extensión por pedir más permisos de los que necesitamos (cuando la anterior versión pedía exactamente los mismos)…

Nada, nueva versión pidiendo menos permisos (v3.0.2) y a esperar otros cuatro días (con suerte)…