Les trois environnements passent sur enervision-g3.duckdns.org, rec. et dev. : noms publics qui visent l'IP privée de la VM, donc résolus sans /etc/hosts sur le réseau de l'école et injoignables ailleurs (ADR 0018). - infra/front : nginx sur le réseau de l'hôte, seul exposé en 80 et 443. Aiguille par SNI vers la stack visée sans déchiffrer le TLS, et lui transmet l'IP du client en PROXY protocol. - Proxy de stack : écouteur 4443 en PROXY protocol, real_ip_header ; sans lui, limit_req et get_client_ip() compteraient tous les postes comme un seul. PROXY_FRONT_PORT le publie sur 127.0.0.1. - make tls-duckdns : Let's Encrypt par défi DNS-01 via l'API DuckDNS (acme.sh 3.1.6), rejouable, rejoué à chaque déploiement et chaque nuit. - provision-host.sh fait foi pour l'adressage et les secrets : un .env existant garde ses secrets, reçoit ceux qui manquent (supervision) et voit hôte et ports réalignés. Planifie le renouvellement des certificats. - deploy.yml : nouvelles URL, sonde prod sur 10443, front-up en prod. - Terraform : variable domaine. CI : validation du frontal.
82 lines
2.6 KiB
YAML
82 lines
2.6 KiB
YAML
# Piège : `APP_ENV` et `APP_DEBUG` sont en dur et non en `${APP_ENV:-prod}` : le `.env` du poste
|
|
# vaut `local` et reprendrait le dessus, ce qui laisserait le cookie sans `__Secure-` et
|
|
# rouvrirait `/docs`. Hors `local`, l'API exige en retour une origine CORS non vide.
|
|
# Piège : les listes de ports se cumulent à la fusion des deux fichiers. `!reset` est le seul
|
|
# moyen de dépublier 8000 et 3000 : sans lui, l'API resterait joignable en clair à côté du proxy.
|
|
# Piège : pas de `:?` sur `PUBLIC_HOST`. Compose interpole tout le fichier, y compris pour
|
|
# `stop` et `logs` : la garde vit dans `make stack-up`, qui la compare au certificat servi.
|
|
# Pourquoi : ports du proxy et origine publique en variables, pour que deux environnements
|
|
# cohabitent sur la même machine, chacun dans son projet Compose (ADR 0009).
|
|
|
|
name: enervision
|
|
|
|
services:
|
|
db:
|
|
ports: !override
|
|
- "127.0.0.1:${POSTGRES_PORT:-5433}:5432"
|
|
environment:
|
|
TS_TUNE_MEMORY: ${TS_TUNE_MEMORY:-2GB}
|
|
TS_TUNE_NUM_CPUS: ${TS_TUNE_NUM_CPUS:-2}
|
|
|
|
mailpit:
|
|
ports: !override
|
|
- "127.0.0.1:${MAILPIT_UI_PORT:-8025}:8025"
|
|
|
|
airflow-apiserver:
|
|
ports: !override
|
|
- "127.0.0.1:${AIRFLOW_PORT:-8080}:8080"
|
|
|
|
backend:
|
|
ports: !reset null
|
|
command:
|
|
- uvicorn
|
|
- app.main:create_app
|
|
- --factory
|
|
- --host
|
|
- 0.0.0.0
|
|
- --port
|
|
- "8000"
|
|
- --proxy-headers
|
|
- --forwarded-allow-ips=*
|
|
environment:
|
|
APP_ENV: prod
|
|
APP_DEBUG: "false"
|
|
APP_TRUST_PROXY_HEADERS: "true"
|
|
APP_CORS_ORIGINS: ${PUBLIC_ORIGIN:-https://${PUBLIC_HOST:-enervision.local}}
|
|
APP_FRONTEND_RESET_PASSWORD_URL: ${PUBLIC_ORIGIN:-https://${PUBLIC_HOST:-enervision.local}}/reset-password
|
|
|
|
frontend:
|
|
ports: !reset null
|
|
|
|
proxy:
|
|
image: nginx:1.31-alpine
|
|
depends_on:
|
|
backend:
|
|
condition: service_healthy
|
|
frontend:
|
|
condition: service_started
|
|
ports:
|
|
- "${PROXY_HTTP_PORT:-80}:80"
|
|
- "${PROXY_HTTPS_PORT:-443}:443"
|
|
# Vide : port aléatoire sur la boucle locale, pour que deux stacks sans frontal cohabitent.
|
|
- "${PROXY_FRONT_PORT:-127.0.0.1:}:4443"
|
|
volumes:
|
|
- ./infra/proxy/nginx.conf:/etc/nginx/nginx.conf:ro
|
|
- ./infra/proxy/conf.d:/etc/nginx/conf.d:ro
|
|
- ./infra/proxy/tls:/etc/nginx/tls:ro
|
|
- acme_webroot:/var/www/certbot
|
|
restart: unless-stopped
|
|
|
|
certbot:
|
|
image: certbot/certbot:v5.8.0
|
|
profiles: ["acme"]
|
|
volumes:
|
|
- letsencrypt:/etc/letsencrypt
|
|
- acme_webroot:/var/www/certbot
|
|
- ./infra/proxy/tls:/tls
|
|
- ./infra/proxy/acme-deploy-hook.sh:/deploy-hook.sh:ro
|
|
|
|
volumes:
|
|
acme_webroot:
|
|
letsencrypt:
|