Un projet Compose par environnement sur la même machine : ports du proxy et origine publique en variables dans docker-compose.prod.yml, réglage mémoire des deux bases TimescaleDB et du webserver Airflow. Le workflow deploy.yml déploie dev en recette et main en production depuis un runner installé sur la VM, jamais sur pull_request. scripts/provision-host.sh prépare les deux dossiers, secrets et certificats compris, sans rien démarrer. L'image frontend quitte dhi.io/nginx, registre authentifié dont personne n'a l'accès, pour nginx:1.28-alpine : elle n'avait jamais été construite. ADR 0009, vues infra et CI/CD, README et .env.example mis à jour. Refs #21, #22
4.8 KiB
0009 - Deux environnements sur la VM ENI, un projet Compose chacun, déployés par un runner auto-hébergé
- Statut : accepté
- Date : 2026-09-21
Contexte
La grille note EC03 à EC06 sur ce qui est déployé et fonctionnel au J10. Au 21/09, rien ne
l'est : la CI s'arrête au merge (issue #21), la topologie Compose avec reverse proxy
(ADR 0007) n'a jamais quitté le poste, et le
module Terraform k3s n'a jamais été appliqué. L'école met à disposition une seule VM,
eadl-2025-nantes-g3, sur une adresse privée que les runners hébergés par GitHub ne joignent
pas, sans DNS public.
Il faut deux environnements, recette et production, parce que la stratégie de branches en a
déjà deux, dev et main, et qu'un déploiement direct en production à chaque merge sur dev
n'est pas défendable.
La branche feat/deploy tentait de déployer par provisioners Terraform : nginx système et copie
du build Angular. La revue postée sur #21 relève huit points bloquants, dont des racines rec
et prod qui ne passent pas terraform validate.
Décision
Un projet Docker Compose par environnement, sur la même machine. Deux clones du dépôt,
/srv/enervision/rec sur dev et /srv/enervision/prod sur main, chacun avec son .env et
son COMPOSE_PROJECT_NAME. Le nom de projet préfixe volumes, réseau et conteneurs : les deux
stacks ne partagent rien.
Les ports du proxy et l'origine publique deviennent des variables de
docker-compose.prod.yml. La production garde 80 et 443. La recette publie 8443 et ramène sa
redirection HTTP sur la boucle locale, faute de quoi elle renverrait vers la production. Base,
Mailpit et Airflow restent sur 127.0.0.1, décalés d'un port.
Deux noms d'hôte, enervision.local et rec.enervision.local, sur la même IP. Le cookie de
rafraîchissement est posé par hôte, pas par port : un seul nom ferait se déconnecter la
production à chaque connexion en recette.
Un runner GitHub Actions auto-hébergé sur la VM exécute deploy.yml : un push sur dev
déploie la recette, un push sur main déploie la production après approbation dans
l'environnement GitHub prod. Le job aligne le clone sur la branche puis lance make stack-up.
Les images sont construites sur la machine.
Les secrets vivent dans le .env de chaque dossier, générés sur la machine par
scripts/provision-host.sh, jamais dans git ni dans GitHub. Le runner n'a besoin d'aucun
secret.
Alternatives écartées
- k3s avec un namespace par environnement : le cluster serait vide, sans manifeste, sans
registre, sans stockage persistant. C'est la cible de
10-infra.md, pas celle de la semaine. - Provisioners Terraform de
feat/deploy: voir la revue sur #21. Terraform reste l'outil de provisionnement de la machine, pas de livraison applicative. - Deux machines, VM Proxmox et VM Azure ENI : une deuxième infrastructure à justifier devant le jury et à provisionner, pour un bénéfice nul sur la grille.
- Un seul proxy frontal routant par nom d'hôte vers les deux stacks : des URL sans port, mais le proxy devrait joindre deux réseaux Compose où les services portent les mêmes noms. La complexité dépasse le gain.
- Images publiées sur GHCR et déployées par digest : la bonne pratique, remise à plus tard. Un registre à authentifier sur la machine, alors que le runner y construit déjà.
Conséquences
- Deux TimescaleDB sur une machine de 8 Go : sans réglage, chacune se réserverait 25 % de la
RAM au premier démarrage. L'overlay fixe
TS_TUNE_MEMORYà 2 Go etTS_TUNE_NUM_CPUSà 2 par base, et 2 workers gunicorn par webserver Airflow. La montée à 32 Go prévue par les consignes est à demander. - Un runner auto-hébergé sur un dépôt public exécute le code qu'on lui envoie.
deploy.ymlne se déclenche jamais surpull_request, le runner tourne sous un utilisateur dédié, et le dépôt doit exiger une approbation pour les workflows des PR externes. - Les deux environnements construisent leurs images séparément à partir du même commit : ce qui tourne en production a été construit deux fois, pas promu. Le passage à GHCR lèvera cette limite.
- Le
make stack-updu runner reconstruit l'image Airflow, qui copieml/etapps/backend/, à chaque push : plusieurs minutes par déploiement, acceptable pour la cadence du projet. environments/prodde Terraform reste vide. Le provisionnement de la machine est porté parscripts/provision-host.sh, que Terraform pourra appeler parremote-execle jour où une racine visant la VM existera.- L'image frontend quitte
dhi.io/nginx, registre authentifié dont personne n'a l'accès, pournginx:1.28-alpine, la même image que le proxy. Elle n'avait jamais été construite.