Montando un docker registry “Como Dios Manda ™”

registry¡Buenas fans de los contenedores! En este nuevo episodio vamos a ver cómo montar una de las piezas más olvidadas dentro de una infraestructura docker: el Registry. Cuando montamos una infraestructura de contenedores de cierto nivel las partes más sexys (self-healing, orquestación, ingress load balancing, self-healing… Ouh yeah, sigue hablándome sucio) se llevan toda la fama y las partes más básicas, pero “feas”, las dejamos de lado. Sin embargo, sin un Registry propio no podríamos montar nuestro sexy sistema de clustering.

¿Que es un Registry?

Para los más despistados (como yo no hace tanto) vamos a empezar por lo más básico. Un Registry es un lugar donde almacenar imágenes de contenedores que luego utilizará(n) el/los engines para crear nuestros amados contenedores. Es lo que en el mundo pre-container hacía un repositorio de paquetes (rpm/apt/…) o para nuestros ene/amigos developers puede hacer npm o un repositorio de artefactos. Un Registry es una de las piezas clave a la hora de crear nuestros entornos Docker en cuanto empezamos a crear nuestras propias imágenes (minuto 1). Un Registry propio nos permitirá que los distintos motores docker que tengamos (¿he oído cluster docker?) puedan descargar las imágenes que desarrollemos y  que no necesariamente queramos que sean públicas.

¿Y porqué montar el nuestro?

Desde Docker Inc. se ofrece un servicio de Registry que solemos usar a diario: Docker Hub. Este servicio se ofrece como SaaS y tiene una capa de uso gratuita. En este plan gratuito tenemos opción de un Registry privado y uno público. Si necesitamos más, podemos mirar su lista de precios que tampoco son muy elevados.

Entonces, ¿Por qué montar nuestro propio registro? Los motivos más evidentes son los siguientes:

  • Tener el Registry en nuestra infraestructura nos ahorra ancho de banda y nos da mejor tiempo de acceso/descarga. Esto que puede parecer poco importante a día de hoy, en clusters donde lanzamos actualizaciones o entra en juego el self-healing tiene su importancia.
  • Tener una copia “controlada” de imágenes públicas nos protege ante cambios inesperados, o desaparición, de las mismas (que os vamos a contar a los usuarios de npm).
  • Tener tantos namespaces privados como queramos, con las ACL que queramos y la auth conectada contra nuestro sistema de auth preferido (ldap,…).

Y en el fondo porque a los BOFHers nos gusta controlar los servicios 😀

Para ilustrarlo vamos a ver un flujo de trabajo habitual:

  • Desarrollamos un APP en el último lenguaje de moda, pongamos por ejemplo que es Trump Script 😀
  • Hemos decidido que se va a desplegar sobre Docker.  Más que nada porque como le pidamos a operaciones que nos instale el último commit de TS  en sus sagrados servidores nos van a lanzar una mirada asesina de BOFH-er que nos va a dejar petrificados.
  • Así que a la par que programamos, empezamos a crear nuestro Dockerfile.  Empezamos tal que … FROM fulanodetal/lenguajemolon:latest …
  • ¡WARNING! ¡Camino al abismo detectado! Tenemos un registry, así que vamos a intentar hacerlo bien. No tenemos más que hacer un pull de esa imagen, tagearla con nuestro Registry+namespace y hacer un push.
  • Ahora sí, hacemos nuestro Dockerfile con un FROM registry.midominio.com/miproyecto/lenguajequemola:version … (recordad niños, SIEMPRE usamos un tag concreto, nunca latest)
  • Hacemos el build y lo tageamos a nuestro registry ( docker build -t registry.midominio.com/miproyecto/miapp:VERSION ) y pusheamos
  • Empezamos el ciclo normal de deploy en local/dev/stage/pre/prod
  • La APP acaba en un Cluster Docker Rock solid.
  • Todos somos felices, los cambios en la APP fluyen a PROD de manera estable y continua como el agua en un jardín japonés (clack, clack, clak …..).

Fuente: https://commons.wikimedia.org/wiki/File:Shishi-odishi-2.jpg

¿Y qué es Como Dios Manda ™?

Ahora que estamos convencidos de montar nuestro Registry, ¿qué es lo que necesitamos para considerarlo “Como Dios Manda ™ ” ? Vamos a tener que cumplir 3 mandamientos:

SSL: A día de hoy no debería hacer falta argumentar esto. En este caso en concreto si no usamos SSL hay que tocar muchas cosas,  tunear los engines, bla bla bla. SSL es obligatorio y punto.
Auth/Authz: Queremos controlar quién (autenticación) puede (autorización) subir o bajar nuestras imágenes. Todo esto siempre ligado a espacios de nombres que nos den un control fino. Ya si lo conectamos con nuestro sistema previo… Miel sobre hojuelas.
UI: Somos hackers, pero una UI que nos haga más amigable su gestión es algo que se agradece:D Además el cliente (si le damos acceso) siempre agradece estas cosas.

¿ Dónde lo ponemos ?

HuaweiRH2288HV2Entre los motivos que hemos mencionado para montar nuestro propio registry uno de ellos era el hecho de tenerlo “cerca” de nuestra infraestructura. El punto exacto ya dependerá de nuestras necesidades.  Podemos tenerlo en nuestra cloud privada o en la cloud pública. Podemos tener uno para toda la organización o implementar uno por proyecto de suficiente entidad y que se ejecute dentro de la infraestructura del mismo.  La idea es que sea tan sencillo de montar (o eso vamos a intentar) que la decisión no dependa de este factor. Eso sí, una cosa muy importante a tener en cuenta es la necesidad de almacenamiento: ¡Mucho!

 

¿ Cómo lo  vamos a montar ?

Vamos a usar 3 piezas de software juntas que nos permitan alcanzar nuestro deseado registry como dios manda. Y todas ellas en forma de contenedor claro:

  • Un Registry. Vamos a usar el propio registry de docker inc, en su versión 2. Lo podemos descargar desde docker hub: https://hub.docker.com/_/registry/.
  • Un frontal nginx que se encarge de los certs y la gestión de los dominios.
  • Una UI + auth/authz. Por suerte la gente de SUSE tiene liberado una buena pieza de software que hace todo esto: https://github.com/SUSE/Portus . Por desgracia la instalación está pensada para Suse, o a lo sumo instalación manual. Como nos encantan los contenedores vamos a instalarlo haciendo uso de Docker.
  • Un backend de BBDD, MariadDB en este caso.
  • Un agente que ejecuta tareas periódicas (cron).

Adicionalmente necesitamos un certificado SSL que nos sirva para dos dominios registry.midominio.com y portus.midominio.com. Para ello podemos usar un wildcard o usar certificados de let’s encrypt para pruebas. Pero esto último va más allá de este artículo, que si no no teminaremos nunca.

Vamos a ver un pequeño diagrama de lo que vamos a construir:

post_registry_esquema

Para montarlo vamos a usar contenedores docker evidentemente. Vamos a usar docker-compose (por lo tanto necesitamos tenerlo instalado) para definir nuestro servicio. Sirva este post para ver un ejemplo de diseño de arquitectura software en un entorno de contenedores. En este caso vamos a usar la versión 2 de docker-compose file en lugar de la nueva (versión 3) que está pensada para entornos cluster. Nos parece más interesante no complicar en exceso el post y centrarnos en el Registry.

Nos tiramos al barro!

Vamos a empezar por montar un escenario que nos permita testarlo todo. Vamos a usar docker-machine y virtualbox para ello (dos dependencias solo para dev, en prod podemos montarlo como más tilín nos haga). Empezamos por crear un nuevo host docker:

A partir de ahora todos los comandos que lancemos en esa shell serán enviados al docker host. Hacemos un docker info para comprobar que todo está OK.

Para que nuestra arquitectura funcione necesitamos los siguientes archivos de configuración:

  • Configuración del Registry: /opt/registry/config.yml
  • Cert para que el registry hable con Portus: /opt/registry/portus.crt
  • Configuración de portus general: /opt/portus/config.yml
  • Configuración de portus de BBDD: /opt/portus/database.yml
  • Configuración de NGINX para el Registry: /opt/nginx/registry.irontec.com.conf
  • Configuración de NGINX para el Portus: /opt/nginx/portus.irontec.com.conf
  • Docker compose con la definición de la arquitectura: /opt/rcdm/docker-compose.yml

Vamos a empezar por preparar la estructura de directorios necesaria:

Aparte de los dirs de los archivos de configuración hemos creado un directorio para los datos del Registry (/opt/registry_data) y otro para los de Mysql (/opt/mysql). En producción estos directorios (sobre el todo el registry_data) seguramente querramos que estén en una partición/disco dedicado.

Debemos copiar nuestro certificado válido para los dos subdominios que vamos a usar. En nuestro caso será wildcard, ya que disponemos de uno para nuestro dominio. Es importante que este certificado sea válido, ya que de lo contrario tendremos problemas a la hora de añadir el Registry al Portus.

Ahora ya podemos copiarlo:

Con los siguientes comandos vamos a crear todos los archivos de configuración necesarios. Por comodidad vamos a lanzarlos desde la máquina virtual dónde está el engine Docker. Así que primero hacemos un ssh a la misma y luego un sudo. Acto seguido instalamos docker-compose que necesitaremos más adelante:

Vamos con los archivos de configuración. Lo primero es crear la configuración del registry, la parte importante es la de notifications, donde le indicamos que notifique a Portus los nuevos push:

Necesitamos crear el cert que va usar el registry para conectarse al portus. Vamos a copiar un certe autofirmado que se incluye en la distribución de Portus. Evidentemente esto habría que cambiarlo en producción:

Vamos ahora con la otra pata de nuestra arquitectura, Portus. Incluimos un archivo de configuración con muchas opciones deshabilitadas que en producción deberíamos ajustar (auth ldap, smtp, borrado de imagenes,…).

Necesitamos también configurar la parte BBDD de Portus:

Podemos ya definir el frontal Nginx. Creamos la configuración  nginx para el vhost con Portus, que es bastante sencilla:

Creamos la conf nginx para el vhost con el Registry. Esta tiene algún ajuste más fino por la naturaleza de la comunicación que vamos a tener con el registry:

Ahora que tenemos la configuración individual de los servicios ya podemos crear un fichero compose donde definimos nuestra arquitectura:

Ya estamos listos para poder levantar nuestra arquitectura. ¡Vamos a ello! Como explica la cabecera del docker-compose.yml es necesario primero definir una serie de variables que haremos mediante un export:

Vamos a comprobar si todo ha arrancado bien:

Deberíamos tener 5 contenedores en status UP. Si vemos que alguno no arranca o se reinicia tendremos que hacer uso del comando docker logs rcdm_XXXX_1 para ver cuál es el motivo y solucionarlo.

Ya solo nos falta inicializar la base de datos. Para que Portus pueda empezar a funcionar es necesario crear la estructura de base de datos y alimentarla con unos pocos datos iniciales. Para esto, al ser una aplicación ruby, podemos lanzar un par de comandos rake que harán todo el trabajo. Para lanzarnos vamos a usar la definición de nuestro servicio portus (web), decirle a docker-compose que cree un docker (run) partiendo de esa configuración, ejecute el comando (rake ….) y al terminar elimine el contenedor creado  (–rm).

Esta es una manera habitual de lanzar comandos o tareas en entornos de contenedores, donde fuera del contenedor no disponemos de las herramientas necesarias. Otro ejemplo suele ser el uso del comando cliente de mysql o mysqldump y situaciones similares.

Ahora sí deberíamos tener Portus UP & RUNNING. Nos salimos de la VM de docker engine (o usamos otra terminal) y añadimos a nuestro /etc/hosts la IP del docker host para que resuelva los 2 FQDNs y abrimos la web de portus.

A jugar

Ya podemos empezar a jugar con nuestro registry/portus.  Lo primero es entrar a la web de Portus crear un usuario (este primer usuario será admin, esto se ajusta en la conf de Portus creada hace unos momentos).

Portus - 1

Una vez creado el usuario lo siguiente que nos pide Portus es que demos de alta el Registry. Importante: si hemos usado un cert autofirmado deberemos usar como hostname registry:5000 y deshabilitar el uso de SSL.

Portus -2

Y ahora ya podemos ver los namespace. Por defecto al crear un usuario se le asocia un namespace que vemos que está vacío.

Portus - 3

Una vez tenemos Portus configurado podemos ahora conectar nuestro engine al registry

… Y empezar por subir nuestra primera imagen:

Una vez pusheada podemos ir a Portus y ver de manera gráfica cómo se ha subido la imagen:

Portus - 4

A partir de aquí podemos ir jugando. Probar los namespaces, crear más usuarios, crear grupos, asignar permisos en los namespaces a los grupos, tener namespaces públicos, webhooks, etc.

Disclaimer

Todo este post tiene solo un fin didáctico, por lo que la instalación que hemos hecho no está pensada para producción. Los que no hayáis hecho un CUT&PASTE furioso os habréis dado cuenta de que en el docker-compose se señala una imagen docker de propia cosecha irontec/portusweb:devel . Esta imagen, como claramente indica el tag, está pensada para desarrollo, generada directamente del git de portus y lanzada sin opciones de entorno de producción. También habréis notado que hay un certificado publicado directamente en el post y que es un cert autofirmado generado por los developers de Portus.

Otras opciones

Queda también como examen para el lector el explorar otras opciones de montar un “Registry Como Dios Manda ™”. La más evidente para los usuarios de gitlab es hacer uso de su módulo registry. Para los usuarios de solución cloud cada proveedor ofrece un registry, que aunque no cumpla todas nuestras leyes para ser un “Registry Como Dios Manda ™”, puede ser una buena solución. Amazon tiene el ECR y Google su Container Registry. También hay más UI-s para Registry, con diferentes grados de madurez. Finalmente Docker Inc, dentro de su solución “on premise” Docker Data Center, ofrece Docker Trusted Registry pero entraríamos ya en la necesidad de licenciamiento.

Fin

Y eso es todo por aquí amigos. Para ser mi primer post en el blog me ha salido un buen ladrillo sobre algo no muy sexy 🙂 Para próximas entradas espero poder traeros temas más interesante como clusterring, gestión de secretos y otras cosas bellas de Docker.



¿Te gusta este post? Es solo un ejemplo de cómo podemos ayudar a tu empresa...

1 Comentario

¿Por qué no comentas tú también?


  • Me ha encantado el artículo!! Muy, muy sexy! 🙂

    Jon Ander Hernández Hace 6 meses Responde


  • Como consigo más información? estoy por terminar mi carrera en computación en la http://www.ups.edu.ec/ y quiero hacer una especialización en el tema pero no sé en dónde ni que campos relacionados a mi malla me sirvan. Acá esta mi malla http://www.ups.edu.ec/computacion-cuenca si me ayudan. mil Gracias.

    jame brow Hace 5 meses Responde


Queremos tu opinión :)