Gancio tuneado para despliegues automatizados
inicio del viaje
estamos con ganas de poner a andar una instancia de Gancio, una app federada de publicacion de eventos y, por supuesto, libre
gancio cuenta con unas instrucciones de instalacion que nos permiten levantar una instancia utilizando docker y docker-compose. sin embargo, esta forma de iniciar la instancia de gancio depende de un proceso de setup interactivo, de varios pasos.
los procesos interactivos, que requieren intervencion de una usuaria, no suelen ir muy bien con los procesos de despliegue automaticos, y requieren el uso de hacks para lidiar con ellos.
nota: en nuestro caso ejecutaremos gancio utilizando una base de datos SQLite, con lo que solo analizamos este proceso de instalacion y ejecucion de la app. parte de lo que hagamos sera util para una instalacion que utilice Postgres, aunque pensamos que igual y puedan surgir desafios adicionales en ese caso
que hace este setup interactivo?
para ver como podemos evitar ejecutar el proceso de setup interactivo, vamos a investigar que es lo que hacen estos comandos.
lso comandos en cuestion son
touch config.json db.sqlite
mkdir user_locale
docker-compose run --rm gancio gancio setup --docker --db=sqlite
el primer comando, touch
, busca los archivos config.json
y db.sqlite
en
el sistema de archivos, y si no los encuentra, los crea vacios de contenido.
el segundo comando mkdir
, crea un nuevo directorio, vacio de contenido
tambien.
el tercer comando, docker run ...
ejecuta el docker de gancio, e inicia un
proceso de setup dentro del docker.
mirando el sistema de archivos luego de ejecutar el proceso de setup
interactivo, vemos que los archivos config.json
y db.sqlite
,que habiamos
inicialmente creado sin contenido, ahora si tienen cosas dentro.
mirando los logs de la ejecucion del proceso de setup confirmamos que da
contenido al archivo de configuracion (config.json
) e inicia la base de datos
creando las tablas que gancio necesita para funcionar (db.sqlite
).
el directorio que creamos antes no tiene ningun cambio observable.
alternativas a ejecutar el setup interactivo
pensando en alternativas a ejecutar el setup interactivo, nuestro primer
objetivo es el generar el archvio config.json
desde una plantilla (ej:
config.json.sample
).
vemos que si ejecutamos el proceso de setup interactivo, teniendo ya creado un
config.json
, gancio ignora nuestra configuracion, y nos vuelve a
re-preguntar los datos que hemos informado en el archivo. esto no es lo que
esperabamos.
volvemos a intentarlo, esta vez ignorando el paso de configuracion/setup y
ejecutando docker-compose up
directamente. asi evitamos el proceso de setup
interactivo.
probamos diferente configuraciones y observamos las siguientes casuisticas.
- si existe
db.sqlite
, y contiene datos, gancio la utiliza - si existe
db.sqlite
, y esta vacia, al iniciarse gancio genera las tablas necearias en la bbdd - si NO existe
db.sqlite
, gancio no puede iniciarse y falla
con esto sabemos ahora que si queremos iniciar gancio sin ejecutar el setup interactivo necesitamos, como minimo, lo siguiente
- un archivo de configuracion llamado
config.json
con los datos de la instancia - un archivo para la base de datos llamado
db.sqlite
, que puede estar vacio o tener el contenido de una instancia previa del mismo gancio - no es necesario crear ningun directorio, ya que gancio mismo lo crea durante la primera ejecucion, si no existe
rizando el rulo ajeno
bien, ya sabemos que para evitar el proceso de setup interactivo necesitamos
un archivo de configuracion config.json
con un cierto contenido, y un
archivo llamado db.sqlite
que puede estar vacio o contener los datos de una
ejecucion previa.
ahora, estos archivos tienen que poder ser escritos por la usuaria ejecutando
gancio dentro del docker. eso no es un problema si la usuaria dentro del
docker es root
, pero si queremos ejecutar con una usuaria no root
,
entonces esta nueva usuaria debe tener permisos de lectura y escritura sobre,
al menos, db.sqlite
e idealmente tambien sobre config.json
para poder
interactuar con esta configuracion desde la web de gancio.
esto si se convierte en un desafio. por fuera del docker podemos crear estos archivos facilmente con nuestra usuaria, siguiendo las instrucciones de instalacion. ahora, estos archivos no necesariamente seran accesibles por la usuaria ejecutando gancio dentro del docker. que podemos hacer al respecto?
necesitamos cambiar la propiedad de los archivos que creamos localmente a la usuaria que dentro del docker ejecutara gancio. esto o bien lo hacemos dentro del docker, o bien fuera (en el host)
si deseamos hacerlo por fuera del docker, necesitaremos ser root
en el
sistema que ejecuta el demonio de docker. esto no suele ser el caso, asi que
descartaremos este metodo.
nos queda entonces, como unica posibilidad, la de realizar el ajuste a la propiedad de los archivos por dentro de un docker.
reina de mi burbuja
creemos que tenemos 2 factores a nuestro favor:
- que ‘solo’ tenemos que actuar como
root
sobre los archivos que necesita acceder gancio - que dentro de un docker SI podemos ser
root
facilmente
volvemos a vislumbrar 2 caminos para este objetivo
- utilizando 2 dockers diferentes, 1 para gestionar permiosos de archivos (donde seremos root), y otro para ejecutar gancio (donde seremos la usuaria que ejecuta gancio). en esta solucion, apalancamos docker-compose
- utilizando 1 solo docker, donde podamos ser primero
root
y ajustar los permisos de los archivos, y luego ser la usuaria de gancio para ejecutar el servicio. en esta solucion, apalancamos el entrypoint de docker
solucion con docker-compose: dos dockers diferente
si asumimos que continuaremos utilizando el docker-compose.yaml
que nos
sugiere el proyecto de gancio, podemos hacerle una modificaciones para que
antes de comenzar a ejecutar gancio, ejecute un docker donde sea root y cambie
la propiedad de los archivos a la que sea necesaria.
para esto, agregamos al docker-compose.yaml
un nuevo servicio que llamamos
ego
y que se ocupara de apropiarse de los archivos que gancio utilizara. el
docker de ego puede ser cualquier imagen que se ejecute como root (ej. debian,
alpine, ubuntu), en nuestro caso, utilizamos el mismo docker de gancio, ya que
esta configurado para ejecutarse como root
por defecto.
nuestro servicio ego
se ocupa entonces de generar los archivos necesarios
para poder iniciar gancio sin necesidad de ejecutar un proceso de setup
interactivo.
build:
context: .
dockerfile: Dockerfile
restart: "no"
image: gancio:latest
container_name: ego
command: >
sh -c "touch /opt/gancio/config.json &&
touch /opt/gancio/db.sqlite &&
chown -R gancio:nogroup /opt/gancio"
volumes:
- ./data:/opt/gancio
para forzar al servicio de gancio a esperar que ego
termine sus tareas de
apropiarse de las cosas, le agregamos una dependencia, tambien en el
docker-compose.yaml
gancio:
...
depends_on:
- ego
...
esta solucion logra el objetivo que buscamos, con el costo adicional de
complejizar el docker-compose.yaml
solucion con docker: entry-point con cambio de usuaria
pensando en como simplificar la solucion de arriba, se nos ocurre que otra
posibilidad es ejecutar los comandos como root
para cambiar los permisos
dentro del mismo docker donde luego se ejecuta gancio con un usuario
especifico.
en este caso, podriamos recurir a un entrypoint que:
- se ejecute como
root
- cree archivos necearios
- modifique permisos
- desescale permisos a usuario que ejecutara gancio
- dejar docker listo para ejecutar gancio de forma regular
esto lo podemos lograr si, en lugar de iniciar el docker como la usuaria que
ejecuta gancio, lo hacemos como root
y durante la ejecucion del Entrypoint
cambiamos a la usuaria de gancio cuando ya hayamos realizado las tareas
necesarias
encontramos que el siguiente Entrypoint
alcanza este objetivo, ya que luego
de ejecutar las primeras acciones como root
ejecuta el Command
que se le
pase al docker como la usuaria que ejecuta gancio
# verifica que exista un archivo de configuracion
[ -f /opt/gancio/config.json ] || { echo 'falta el archivo de configuracion config.json'; exit 1; }
# si no existe, crea la base de datos sqlite
[ -f /opt/gancio/db.sqlite ] || touch /opt/gancio/db.sqlite
# recupera permisos a nombre del usuario de gancio]
chown -R gancio:nogroup /opt/gancio
# desescala permisos y prepara ejecucion de comandos
su gancio --command="$@"
asi, todas las instrucciones que encontramos en la documentacion de gancio se
ejecutaran de la misma manera en nuestro docker, solo que en lugar de ejecutar
la accion como usuaria root
, lo hara como la usuaria de gancio
ponerle un lazo
con esto logramos nuestro objetivo de tener una forma automatizable de desplegar gancio.
cada nuevo despliegue necesitara su propia carpeta para datos (eg. data
), y
dentro de ella el archivo de configuracion de la instancia (eg.
data/config.json
)
agregamos unos parametros tanto a la cosntruccion del docker, como a la ejecucion de gancio.
al construir el docker podemos controlar
- rama/tag del repositorio de gancio a construir (GANCIO_VERSION)
- uid de la usuaria que ejecutara gancio (GANCIO_UID) estos valores los debemos pasar como parte de la construccion del docker
docker-compose build --build-arg GANCIO_UID=110 --build-arg GANCIO_VERSION=master
al ejecutar gancio podemos controlar
- la ruta donde estan almacenados los datos locales (GANCIO_DATA_PATH)
- el puerto en el que se expondra el servicio (GANCIO_PORT)
estos valores se deben editar en el archivo
.env
(tienes un ejemplo en.env.sample
)
TODO si iniciamos una instancia con
una base de datos vacia, no logramos loguearnos como admin para poder
gestionarla
hemos visto que si bien se crea la bbdd, no se crea ningun usuario, y por esto
no podemos loguearnos
tenemos que pensar como hacer para crear un usuario inicial que luego pueda
modificarse
\o/