angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Ejecutando múltiples sitios web (Flask) con una sola configuración Docker

Compartiendo el código y (opcionalmente) las plantillas evitamos hacer copias y reducimos el tiempo de mantenimiento.

30 septiembre 2020
post main image
https://unsplash.com/@windows

Desarrollé un sitio web Flask en Docker pero después de algún tiempo cuando mi código se volvió más estable quise usar la misma configuración para otros sitios web. Para un sitio web hice una copia porque tenía que funcionar ayer. Pero lo que realmente quería era compartir todo el código, y algunas de las plantillas. Por supuesto, cada sitio web tiene su propio directorio estático, directorio de registro, plantillas, etc. La razón principal para compartir es que el código todavía está bajo un fuerte desarrollo y no quiero mantener múltiples copias del mismo código.

Extendiendo mi script de construcción e inicio de Docker

Escribí un script interactivo que utilizo para construir los Docker images y para iniciar el contenedor Docker con la versión adecuada. Con estos puedo ejecutar el desarrollo, las pruebas y staginglocal, y staging y la producción, los contenedores incluso en paralelo. Sólo abro otra ventana de la terminal y ejecuto mi script de inicio Docker . Tuve que usar este script para soportar múltiples sitios web. En el directorio principal hay dos subdirectorios:

  • docker-templates
  • docker-versions

Docker-templates contiene las plantillas Docker-compose cuando se crea una nueva versión para un sitio web. Una versión siempre significa un nuevo conjunto de archivos docker-compose y un archivo con variables de entorno. Además, para staging y producción, también contendrá un (tarado) Docker image y opcionalmente un directorio estático y un volcado de base de datos.

El script de construcción Docker toma los archivos del directorio docker-templates apropiado, genera un nuevo número de versión, parchea el nuevo número de versión en los archivos, crea el nuevo directorio de versión en docker-versions, y pone los archivos allí. Esta configuración me permite hacer cambios en Docker-compose y en los archivos de entorno en docker-templates. Las nuevas versiones incluyen los últimos cambios y las versiones más antiguas permanecen sin cambios. Para el desarrollo y las pruebas, todos los archivos de la aplicación están fuera del contenedor, lo que significa que la generación de una nueva imagen sólo es necesaria cuando se añaden o eliminan paquetes (requirements.txt). La nueva estructura de directorios:

.
|--  docker-templates
|   `-- sites
|       |--  peterspython
|       |   |--  docker-compose_development.yml
|       |   |--  docker-compose_production.yml
|       |   |--  docker-compose_shared.yml
|       |   |--  docker-compose_staginglocal.yml
|       |   |--  docker-compose_staging.yml
|       |   |--  docker-compose_testing.yml
|       |   |-- env_development
|       |   |-- env_production
|       |   |-- env_staging
|       |   |-- env_staginglocal
|       |   `-- env_testing
|       `-- anothersite
|           |--  docker-compose_development.yml
|           |--  docker-compose_production.yml
|           |--  docker-compose_shared.yml
|           |--  docker-compose_staginglocal.yml
|           |--  docker-compose_staging.yml
|           |--  docker-compose_testing.yml
|           |-- env_development
|           |-- env_production
|           |-- env_staging
|           |-- env_staginglocal
|           `-- env_testing
|--  docker-versions
|   `-- sites
|       |--  peterspython
|       |   |-- 1.821_development
|       |   |   |-- deployment.env
|       |   |   |--  docker-compose_deployment.yml
|       |   |   `--  docker-compose_shared.yml
|       |   |   ...
|       |   |-- 1.829_production
|       |   |   |-- deployment.env
|       |   |   |--  docker-compose_deployment.yml
|       |   |   |--  docker-compose_shared.yml
|       |   |   `--  peterspython_web_production_image_1.829.tar
|       `-- anothersite
|           |-- 1.778_development
|           |   |-- deployment.env
|           |   |--  docker-compose_deployment.yml
|           |   `--  docker-compose_shared.yml
|           `-- 1.779_development
|               |-- deployment.env
|               |--  docker-compose_deployment.yml
|               `--  docker-compose_shared.yml
|-- project

El script de inicio de Docker me permite seleccionar un sitio web (peterspython, otro sitio) y luego me presenta un menú de todas las versiones. Cuando selecciono una versión, se presenta un menú con una lista de acciones como iniciar un contenedor, ejecutar en un contenedor, etc. Antes de iniciar una acción, el script de inicio Docker copia los archivos desde el directorio de la versión docker al directorio principal, de manera que siempre tengamos los archivos en el mismo lugar como si estuviéramos ejecutando un solo sitio.

Tengan en cuenta que esta configuración no entra en conflicto con el funcionamiento de Docker . Después de iniciar un contenedor Docker con Docker-compose, podemos eliminar los archivos de docker-compose y las variables de entorno. Al reiniciar Docker en un momento determinado, simplemente reinicia los contenedores que estaban funcionando antes sin necesidad de estos archivos.

Extendiendo la estructura del directorio del proyecto

Quería compartir todo el código entre los sitios, pero quería que tuvieran sus propias plantillas, directorio estático, archivos de registro, etc. La nueva estructura de directorios del proyecto:

.
|-- app
|   |--  blueprints
|   |-- factory.py
|   |-- services.py
|   `-- templates
|-- sites
|   |--  peterspython
|   |   |--  docker-volumes
|   |   |-- static
|   |   |-- stylesheets
|   |   `-- templates
|   `-- anothersite
|       |--  docker-volumes
|       |-- static
|       |-- stylesheets
|       `-- templates
|
|-- app_run.py
|--  Dockerfile
|

El código de la aplicación es compartido. Docker-volumes es un montaje de volumen para archivos de registro, etc. Una diferencia con la configuración de sitio único es la ubicación del archivo de configuración config.py de Flask . Estoy cargando este archivo en create_app() usando el método from_object(). Para asegurarme de que el archivo puede ser encontrado, añado el directorio del archivo, especificado por las variables de entorno Docker , a sys.path:

def  create_app(deploy_config):
    ...
    # to find config.py
    sys.path.insert(0, project_docker_volume_config_dir)

	# load config
    app.config.from_object(all_configs[deploy_config])

Otra diferencia con la configuración de un solo sitio son las plantillas. Quiero que funcione de la siguiente manera. Cuando el directorio del sitio no contiene una plantilla, entonces vuelve al directorio de plantillas de la aplicación. Es muy fácil decirle a Jinja donde buscar plantillas. Jinja soporta muchos cargadores, yo fui por el ChoiceLoader:

from jinja2 import (
    ChoiceLoader,
)

def  create_app(deploy_config):
    ...
    # set template paths
    template_loader = ChoiceLoader([
        # site templates
        app.jinja_loader,
        # default templates
        FileSystemLoader('/home/flask/project/app/templates')
    ])
    app.jinja_loader = template_loader
    ...

Conclusión

Aquí presenté una forma de compartir el código y (opcionalmente) plantillas entre múltiples sitios sin hacer una copia de todo el árbol de directorios para un nuevo sitio. Aunque hay muchas cosas que pueden ser mejoradas, estoy usando esto y estoy feliz de haberme tomado el tiempo para implementarlo.

Enlaces / créditos

API
https://jinja.palletsprojects.com/en/2.11.x/api/

Deje un comentario

Comente de forma anónima o inicie sesión para comentar.

Comentarios

Deje una respuesta.

Responda de forma anónima o inicie sesión para responder.