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

Meerdere (Flask) websites draaien met een enkele Docker setup

Door het delen van de code en (optioneel) sjablonen vermijden we het maken van kopieën en verminderen we de onderhoudstijd.

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

Ik heb één Flask website ontwikkeld op Docker maar na verloop van tijd, toen mijn code stabieler werd, wilde ik dezelfde opzet gebruiken voor andere websites. Voor een website heb ik een kopie gemaakt omdat die gisteren moest werken. Maar wat ik echt wilde was alle code delen, en een aantal templates. Natuurlijk heeft elke website zijn eigen statische directory, logging directory, templates, etc. De belangrijkste reden voor het delen is dat de code nog volop in ontwikkeling is en ik wil niet meerdere kopieën van dezelfde code onderhouden.

Uitbreiding van mijn Docker bouw en start script

Ik heb een interactief script geschreven waarmee ik de Docker images bouw en de Docker container met de juiste versie start. Hiermee kan ik de ontwikkeling, het testen, en staginglocal, en staging en de productie, containers zelfs parallel lopen. Ik open gewoon een ander terminalvenster en voer mijn Docker startscript uit. Ik moest dit script gebruiken om meerdere websites te ondersteunen. In de hoofddirectory zijn er twee subdirectories:

  • docker-templates
  • docker-versions

Docker-templates bevat de Docker-compose sjablonen bij het maken van een nieuwe versie voor een website. Een versie betekent altijd een nieuwe set docker-compose bestanden en een bestand met omgevingsvariabelen. Daarnaast bevat het voor staging en productie ook een (tarred) Docker image en optioneel een statische directory en database dump.

Het Docker bouwscript neemt de bestanden uit de juiste docker-templates directory, genereert een nieuw versienummer, patcht het nieuwe versienummer in de bestanden, creëert de nieuwe versie-directory in docker-versions, en plaatst de bestanden daar. Met deze opzet kan ik wijzigingen aanbrengen in Docker-compose en de omgevingsbestanden in docker-templates. Nieuwe versies bevatten de laatste wijzigingen en oudere versies blijven ongewijzigd. Voor het ontwikkelen en testen zijn alle applicatiebestanden buiten de container, wat betekent dat het genereren van een nieuwe afbeelding alleen nodig is bij het toevoegen of verwijderen van pakketten (requirements.txt). De nieuwe mappenstructuur:

.
|--  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

Met het Docker startscript kan ik een website selecteren (peterspython, anothersite) en me vervolgens een menu van alle versies presenteren. Wanneer ik een versie selecteer, wordt een menu gepresenteerd met een lijst van acties zoals het starten van een container, het uitvoeren in een container, etc. Voordat ik een actie start, kopieert het Docker start script de bestanden van de docker-versie directory naar de hoofddirectory, zodat we altijd de bestanden op dezelfde plaats hebben alsof we een enkele site draaien.

Merk op dat deze setup niet in strijd is met hoe Docker werkt. Nadat we een Docker container met Docker-compose starten, kunnen we de bestanden met docker-compose en omgevingsvariabelen verwijderen. Wanneer u Docker op een bepaald moment herstart, worden de containers die eerder liepen gewoon opnieuw gestart zonder dat deze bestanden nodig zijn.

Uitbreiding van de projectdirectorystructuur

Ik wilde alle code tussen de sites delen, maar wil dat ze hun eigen sjablonen, statische directory, logbestanden, etc. hebben. De nieuwe project directory structuur:

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

De code in de app wordt gedeeld. Docker-volumes is een volume-koppeling voor logbestanden enz. Een verschil met de single site setup is de locatie van het Flask configuratiebestand config.py. Ik laad dit bestand in create_app() met behulp van de from_object() methode. Om er zeker van te zijn dat het bestand gevonden kan worden, voeg ik de directory van het bestand, gespecificeerd door de Docker omgevingsvariabelen, toe aan 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])

Een ander verschil met de single site setup zijn de templates. Ik wil dat het als volgt werkt. Wanneer de site-directory geen sjabloon bevat, valt deze terug naar de app-template-map. Het is heel eenvoudig om Jinja te vertellen waar ze moeten zoeken naar sjablonen. Jinja ondersteunt veel laders, ik ging voor de 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
    ...

Conclusie

Hier presenteerde ik een manier om de code en (optioneel) templates te delen tussen meerdere sites zonder een kopie te maken van de hele directorystructuur voor een nieuwe site. Hoewel er veel dingen zijn die verbeterd kunnen worden, gebruik ik dit en ik ben blij dat ik de tijd heb genomen om dit te implementeren.

Links / credits

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

Laat een reactie achter

Reageer anoniem of log in om commentaar te geven.

Opmerkingen

Laat een antwoord achter

Antwoord anoniem of log in om te antwoorden.