Docker¶
Queste poche righe vogliono essere una sintesi pragmatica dei comandi da usare per operare correttamente nello sviluppo di applicazioni django/quasar di cui facciamo il deploy con Docker.
Non ha alcuna ambizione di essere esaustiva e probabilmente è poco utile fuori da Thux in quanto fa riferimento ad alcune utility interni.
Le premesse sono intese a rendere plausibili e facili da memorizzare i comandi, una presentazione più rigorosa del setup sarà curata in un webinar.
Deploy¶
L’ambiente in cui usiamo docker è molto semplice: usiamo docker-compose
per
descrivere la relazione fra i docker e per avviarli. Questo significa che, al
momento, non stiamo usando né swarm né kubernetes per l’orchestrazione.
Al momento abbiamo le seguenti macchine docker:
- docker1.thux.dev
per clienti senza macchina propria
- docker-c.thux.dev
per tutti i servizi: hepapod, runner, jitsy-meet, npm, pypi… Heptapod ha anche il servizio
registry.thux.dev
, ovvero uno storage di immagini- acomea-dk
una macchina per acomea (per ora il nuovo cms)
- onplay.it
una macchina per adrenalinik
Ogni macchina ha traefik, un web proxy che gira pure in docker e che gestisce i certificati https.
Setup¶
Usiamo un container per l’applicazione di backend ed un db postgres per il frontend ed un db ospitato da un sistema operativo tradizionale (non dockerizzato, ma pur sempre virtualizzato).
Nel caso di frontend separato il docker che ospita il frontend ha al suo interno
un nginx a cui arriva tutto il traffico e fa eventualmente da proxy verso
django. Il container django serve i file statici con uwsgi. I file di
configurazione uwsgi.py
e nginx.conf
stanno nella radice dei rispettivi
progetti assieme al file che contiene la ricetta per creare il docker
Dockerfile
.
Un estratto dei file del backend Django:
django/
├── apps
├── Dockerfile
├── setup.py
├── uwsgi
└── web
Un estratto dei file del frontend Vuejs/Quasar:
quasar/
├── Dockerfile
├── nginx.conf
├── quasar.conf.js
└── src
immagini¶
Le immagini che produciamo (direttamente al commit di mercurial tramite il meccanismo delle pipeline di gitlab) hanno dentro il codice python/js/css oltre all’interprete Python (uwsgi) ma non hanno dati e configurazioni «delicate» (password).
docker-compose.yml¶
Ogni applicazione è composta da una o più immagini che devono essere accessibili fra di loro. Ad esempio un cms è tipicamente solo una applicazione Django quindi un solo container, jeetsi-meet è composto da 4 servizi. Con zoooom potremmo decidere di avere 4 docker differenti per backend, vettori, supplier, customer (risulta più facile gestire gli aggiornamenti) o produrre una sola immagine per i 3 frontend.
In ogni caso la scelta viene scritta in un file docker-compose.yml
che
mostra in modo molto chiaro il layout deciso. In questo file vengono presentati
i servizi
(django, quasar, jeetsy, heptapod, registry….) che verranno
eseguiti ed eventualmente il numero di container che devono eseguire quel
servizio (caratteristica che avrà un senso solo per traffico molto elevato).
Questo file sta in un repository suo, indipendente dagli altri ma strettamente legato a questi:
opencapital-2020/
├── docker-compose.yml
├── local.py
├── logs
│ ├── django
│ │ ├── opencapital-api_exceptions.log
│ │ ├── opencapital-exceptions.log
│ │ └── opencapital.log
│ ├── nginx
│ └── uwsgi
│ ├── access.log
│ └── errors.log
├── media
└── README.rst
Alla fine, nel server di produzione il codice sta dentro il container in modo molto meno visibile ed accessibile di prima. Soprattutto non facilmente modificabile.
volumes¶
Oltre all’immagine nel momento in cui avviamo il docker dobbiamo anche fornire:
configurazione (es.:
local.py
)dati (es.: media)
spazio per i log
Queste cose vengono collegate con un bind
in modo tale che siano visibili e
modificabili sia dal docker che da fuori:
services:
django:
image: registry.thux.dev/customers/open-capital/office2020.opencapital.it
volumes:
- ./media:/code/media
- ./logs/django:/var/log/django
- ./local.py:/code/web/settings/local.py
networks:
- django
Nell’estratto qui sopra si notano 3 volumi dove il :
divide a sinistra la
cartella nel server ospitante a destra il nome che avrà nel container.
Quindi concretamente: se vogliamo cambiare i valori del file local.py
dovremo cambiarli nel file che sta nella cartella dove c’è
docker-compose.yml
e non nella solita posizione.
CI/CD¶
La produzione dell’immagine che eseguiamo in produzione viene fatta direttamente
nel server heptapod da un «runner» (che è una container a parte) che segue le
istruzioni scritte nel file .gitlab-ci.yml
. Questa è chiamata una «pipeline»
ed è ben visibile in heptapod.
Tendenzialmente non ci sarà necessità di creare file diversi e questo file sarà presente già nel cookiecutter.
Se il codice contiene errori è probabile che la pipeline fallisca ed è probabilmente poco opportuno fare merge di un branch che sia fallito. Di sicuro è impossibile fare il deploy di un branch che sia fallito.
Nota
tag delle immagini
Abbiamo scelto di mettere un tag alle immagini corrispondente al branch di
cui fa parte. Nel caso di un topic il nome sarà ad esempio:
topic-default-ci
.
Operatività¶
Dal momento che c’è l’immagine pronta, il passaggio in produzione è semplicemente (suppongo di avere già fatto io la prima configurazione con il db):
docker-compose up -d
Quando si deve aggiornare l’immagine che però ha lo stesso nome (in quanto sullo stesso branch) si può lanciare:
docker-compose pull
Ho modificato il comando dj
in modo che si renda conto che siamo in un
contesto docker e faccia le cose correttamente, ovvero esegua il comando
all’interno del docker anche se lo lanciate dal server ospitante. Ad esempio
all’inizio:
dj migrate
dj createsuperuser
Se modificate il file di setting o volete spegnere il solo django:
dj reload
dj stop
dj start
Ogni altro comando funziona come vi aspettate…
Se per qualche motivo volete spegnere il docker:
docker-compose down
topics¶
Se occasionalmente vi interessa installare un topic o un branch in modo semplice, sostituendo via variabili d’ambiente il nome dell’immagine django/quasar.
Nel file docker-compose.yml trovate una direttiva simile a questa:
django:
image: registry.thux.dev/customers/acomea/office2020.acomea.it:$TAG_DJ
Dove TAG_DJ
è la variabile che va sostituita per avere il nome completo.
Editate il file
.ENV
in modo che ci sia ad esempio:TAG_DJ=topic-default-my-topic-name
o ad esempio:
TAG_DJ=branch-default
A questo punto potete procedere con i comandi sopra
docker-compose pull && docker-compose up -d
django multipli¶
Progetti come adk
hanno varie istanze di django: office2020
, gfp2020
, www2020
.
In questi casi tutte vengono fatte partire dal medesimo docker-compose ed è
possibile arrivare ad interagire direttamente con quelle istanze uando l’opt
-t
(target) di dj
.
Per conoscere l’esatto nome di target da usare dovere analizzare il fiel
docker-compose.yml
e guardare il noe del servizio (django, gfp, www2020 nel
nostro caso) e quindi:
dj -t gfp shell
dj -t www2020 shell
Andrà direttamente ad aprire la shell corretta.
dj sh¶
dj
offre anche la possibilità di arrivare direttamente alla shell del
docker senza usare la sintassi più lunga (docker-compose exec django
)
dj sh
dj -t gfp sh
Requirements¶
L’installazione avviene in automatico all’interno della pipeline, quindi è
vitale che il file requirements.txt
sia correttamente compilato con tutte le
dipendenze con versione.
È altresì importante che siano messe le più recenti compatibilmente con il sistema usato. In questo modo, soprattutto per le installazioni con file binari, si riesce ad evitare la compilazione mettendo le wheel.
Per ottenere un requirements.txt
recente potete ad esempio eliminare ogni
vincolo non necessario (es.: “Django=>2.2,<3” lasciatelo…) e reinstallare
tutto. Ovviamente qeesto va bene in sviluppo, occorre essere molto più prudenti
in produzione, particolarmente se non ci sono test.
Attenzioni¶
Rispetto a quanto succedeva prima, tenete presente che:
deve esistere
requirements.txt
deve esistere
manage.py
settings/__init__.py
deve permettere creazione link da variabile ENV
deve permettere creazioen dinaminca di SECRET_KEY
dj compilemessages
non è necessario in quanto è già fatto all’interno del dockerdj collectstatic
non è necessarioIl database sta di fatto su una macchina diversa, non viene raggiunto come
localhost
se avete necessità di provare una immagine di un topic lanciate il comando così:
TAG=topic-default-my-topic docker-compose up -d
Per dockerizzare un nostro progetto¶
È suff:
aggiungere
Dockerfile
(quasi sempre lo stesso usato ad esempio in opencapital o cookiecutter)uwsgi.ini
uguale per tutti i progettiaggiungere
settings/__init__.py
(opencapital o cookiecutter).gitlab-ci.yml
(opencapital o cookiecutter)Zoooom è più complesso in quanto aveva bisogno di librerie aggiuntive, non escludo di semplificarlo.