Docker como solución última y un poco desesperada

Para el problema de turno, que no es otro que arreglar un sistema donde algunas aplicaciones -tras actualizar el sistema- dejan de funcionar como en este caso me ha ocurrido con Guacamole.

No lo he tenido en cuenta antes porque no me había hecho falta. Generalmente empleo Guacamole para acceder a las diferentes máquinas en la red empleando un navegador web. El sistema ha funcionando bastante bien hasta que actualicé Debian a la versión 10. Desde ese momento uno de los componentes -guacamole- dejó de arrancar y, por tanto,, de enlazar el navegador web y el componente de conexión con las máquinas llamado guacd.

El sistema lo había instalado empleando los paquetes Debian habituales así que me dirigí al registro de errores para ver si era algo que podía resolver con sencillez. Y no, más bien al contrario. La versión de Debian era anticuada, no se había actualizado correctamente porque no tenía soporte para FreeRDP2 y si te tocaba el error pues no había un método fácil de ponerlo en marcha. Construirlo desde los fuentes era una opción pero me encontré con tantas dificultades que hoy se me ocurrió recurrir a los contenedor Linux mediante Docker. Si los habían construido correctamente deberían funcionar igualmente; sólo tenía que ceder, instalar docker y aprender un mínimo sobre él.

Así que me he puesto manos a la obra y he visto que existen imágenes para el programa, una para el programa guacamole y otra para guacd . Tras instalar el programa docker como paquete Debian descargar estas imágenes y lanzar los contenedores ha sido trivial. Y mira, funcionan muy bien. O eso creo porque aparte de estar ocupando CPU, memoria y puertos de red no estoy seguro de nada más.

Si, los contenedores son instancias de las imágenes, un símil de clase y objeto en el paradigma POO, y sin tocar las imágenes se pueden crear montones de contenedores cambiando los parámetros para que no colisionen entre ellos. Hasta aquí lo entiendo. Veo además que cada contenedor tiene su propio árbol de directorios y archivos como es lógico, y que desaparecen cada vez que se elimina el contenedor (primero se detiene con stop y luego se para con kill). Y eso no me sirve.

Como se ve en el esquema de arriba los dos componentes tienen que tener acceso mutuo a través de red para funcionar. En el caso de estas dos imágenes está previsto que funcionen por separado así que se pueden conectar vía red a cualquier máquina o emplear un enlace especial dentro de docker. Por enlace especial me refiero a que docker crea un interfaz de red en modo puente, asigna direcciones IP a cada contenedor y tiene mecanismos para indicar la conexión de un contenedor con otro. No es problemático mientras no se pongan exquisitos con dónde conectar y cómo. Yo en cualquier caso prefiero saltarme ese paso e indicar a docker que usen la red existente y no se líe creando un puente entre ella y los demás. Son los únicos procesos del sistema que van a emplear esos puertos y la máquina es un frontal a la red que bastante tiene ya la pobre.

He estado consultando algunas guías y documentación y he conseguido entender que, aunque los contenedores están aislados del sistema operativo, existe la posibilidad de compartir datos con él. Los volúmenes son uno de los mecanismos. En la ejecución de un contenedor se pueden crear asociaciones entre una ruta exterior al mismo y una interior de manera que dicho contenedor tiene el mismo acceso que cualquier otro proceso.

Las variables de entorno son otra de las opciones aunque acordarse de todas ellas, como en el ejemplo de abajo, puede ser complicado:

# docker run  --name consolas-guacamole --network host -e GUACAMOLE_HOME=/etc/guacamole --dns 192.168.100.1 -e MYSQL_HOSTNAME=db.empresa.net  -e MYSQL_DATABASE=guacamole -e MYSQL_USER=guacamole -e MYSQL_PASSWORD=XXXXXXXXXXX -e GUACD_HOSTNAME=localhost  -d -v /etc/guacamole:/etc/guacamole guacamole/guacamole

En el anterior párrafo el volumen montado corresponde a la configuración del programa que prefiero conservar en el sistema y así mantenerlo con más facilidad asegurándome de que sobreviva al reinicio de contenedor.

Docker compose

El caso es que tener los dos contenedores funcionando es sencillo si estás haciendo pruebas, pero como la sintaxis se complica poco a poco es preferible describírselo a una herramienta de terceros y que ella se encargue de gestionarlos como un todo. Ahí es donde entra Docker Compose.

Esta herramienta sirve para gestionar varios contenedores relacionados entre sí, como un todo, con la opción inclusive de integrarlo con systemd o algún mecanismo alternativo de arranque de un sistema Linux.

Una vez que tenemos descargadas las dos imágenes que necesitamos (guacamole/guacd y guacamole/guacamole) e inicializada la base de datos que va a utilizar, podemos ponernos con cada contenedor por separado y luego unirlos con el programa compose.

version: '3'

services:
  consolas-guacd:
            hostname: localhost
            image: guacamole/guacd
            network_mode: host
            dns:
                - 192.168.100.1
                - 192.168.100.3
                - 192.168.100.7  
            restart: always

  consolas-guacamole:
            image: guacamole/guacamole
            restart: always
            network_mode: host
            dns:
                - 192.168.100.1
                - 192.168.100.3
                - 192.168.100.7  
            env_file:
                - /etc/guacamole/guacamole.env
            volumes:
                - "/etc/guacamole:/etc/guacamole" 

Y en el archivo indicado sitúo las variables de entorno que necesita para -entre otros- conectar con la base de datos:

GUACD_HOSTNAME=localhost
GUACAMOLE_HOME=/etc/guacamole
MYSQL_HOSTNAME=db.venexma.net
MYSQL_DATABASE=guacamole
MYSQL_USER=guacamole
MYSQL_PASSWORD=XXXXXXXXX

Lo de la base de datos es muy recomendable por pocas máquinas que tengas. Emplear un archivo de configuración plano está bien pero necesitas acceso ssh, luego editar el susodicho archivo y reiniciar los servicios. Usando una base de datos tienes un panel de control que permite añadir máquinas, usuarios y demás cosas interesantes.

Para comprobar la sintaxis de un archivo de configuración se puede emplear el parámetro config de la siguiente forma:

victor@barbacana:~/git/guacamole-docker$ docker-compose config
ERROR: The Compose file './docker-compose.yml' is invalid because:
Unsupported config option for services.guacamole: 'network'
Unsupported config option for services.guacd: 'network'
services.guacamole.environment contains {"GUACD_HOSTNAME": "guacd"}, which is an invalid type, it should be a string
victor@barbacana:~/git/guacamole-docker$ 

Se puede cambiar el archivo a comprobar con el parámetro -f y si se omite se usa docker-compose.yml en el directorio de ejecución.

Y una vez que los contenedores están en marcha podemos obtener información sobre qué ocurre revisando los registros y pegándote con el resultado porque, obviamente, son programas Java:

$ sudo docker logs consolas-guacamole
...
10:15:18.826 [http-nio-8080-exec-9] DEBUG o.a.i.d.pooled.PooledDataSource - Testing connection 1131696568 ...
10:15:18.827 [http-nio-8080-exec-9] DEBUG o.a.i.d.pooled.PooledDataSource - Connection 1131696568 is GOOD!
10:15:18.827 [http-nio-8080-exec-9] DEBUG o.a.i.d.pooled.PooledDataSource - Returned connection 1131696568 to pool.
$ 

Acceso a través de Apache

Crear un frontal web como servidor virtual es bastante sencillo en Apache. He empleado la configuración habitual que redirige las conexiones en claro a conexiones cifradas y todo lo que queda es la siguiente estrofa:

<Location />     
   Require all granted     
   ProxyPass http://localhost:8080/guacamole/ flushpackets=on 
   ProxyPassReverse http://localhost:8080/guacamole/
   ProxyPassReverseCookiePath /guacamole/ / 
</Location>

Referencias