Docker

Le chemin le plus court vers une instance en service. Un seul projet Compose contient PostgreSQL, le backend et le frontend, avec des volumes nommés pour tout ce qui doit survivre à un conteneur.

Prérequis

  • Docker Engine avec le greffon Compose (docker compose version doit répondre).
  • Deux noms d'hôte pointant vers la machine, et un reverse proxy portant le certificat.
  • Les quatre secrets des prérequis communs.

Le fichier Compose

Voici la forme utilisée par le deploy/compose/remote.compose.yml du projet, réduite à ce dont une première installation a besoin. Enregistrez-le sous compose.yml.

services:

  front:
    container_name: aetheus-front
    image: aetheus-front:latest
    # Boucle locale uniquement : le reverse proxy est le seul point d'entrée. Publier sur 0.0.0.0
    # exposerait l'application en clair, et les règles iptables de Docker contournent un pare-feu hôte.
    ports:
      - "127.0.0.1:10025:8080"
    environment:
      - API_BASE_URL=https://api.example.com
      - APP_VERSION=1.0.0
    depends_on:
      back:
        condition: service_healthy
    mem_limit: 256m
    restart: unless-stopped

  back:
    container_name: aetheus-back
    image: aetheus-back:latest
    ports:
      - "127.0.0.1:10026:8080"
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      # Le point d'entrée exécute les migrations, l'application ne doit donc pas les rejouer.
      - Database__SkipMigrations=true
      - AETHEUS_RUN_MIGRATIONS=true
      - DataProtection__KeyPath=/app/data/dp-keys
      - ConnectionStrings__Default=Host=aetheus-database;Port=5432;Database=aetheus;Username=aetheus;Password=${DB_PASSWORD}
      - Auth__JwtKey=${JWT_KEY}
      - Auth__AdminPassword=${ADMIN_PASSWORD}
      - Auth__EncryptionKey=${ENCRYPTION_KEY}
      - Auth__EncryptionSalt=${ENCRYPTION_SALT}
      - Aetheus__PublicApiBaseUrl=https://api.example.com
      - Cors__Origins__0=https://app.example.com
    volumes:
      - git-repos:/app/data/git-repos
      - dp-keys:/app/data/dp-keys
      - artifacts:/app/data/artifacts
      - packages:/app/data/packages
    depends_on:
      database:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-sf", "http://127.0.0.1:8080/health/live"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 120s
    mem_limit: 1g
    networks:
      - default
      - backend
    restart: unless-stopped

  database:
    container_name: aetheus-database
    image: postgres:18-alpine
    environment:
      - POSTGRES_DB=aetheus
      - POSTGRES_USER=aetheus
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - PGDATA=/var/lib/postgresql/data
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U aetheus -d aetheus"]
      interval: 5s
      timeout: 3s
      retries: 10
    # Hors du réseau par défaut : la base est joignable par le backend et par rien d'autre.
    networks:
      - backend
    restart: unless-stopped

networks:
  backend:
    driver: bridge

volumes:
  db-data:
  git-repos:
  dp-keys:
  artifacts:
  packages:

Le fichier .env

Compose lit .env dans le même répertoire. Gardez-le hors du gestionnaire de versions et lisible seulement par le compte qui exécute Docker.

DB_PASSWORD=...
JWT_KEY=...
ADMIN_PASSWORD=...
ENCRYPTION_KEY=...
ENCRYPTION_SALT=...
chmod 600 .env

Obtenir les images

Construisez-les depuis un clone du dépôt. Les deux Dockerfile attendent la racine du dépôt comme contexte de build.

docker build \
    --file deploy/docker/Dockerfile.back \
    --build-arg APP_VERSION=1.0.0 \
    --tag aetheus-back:latest .

docker build \
    --file deploy/docker/Dockerfile.front \
    --tag aetheus-front:latest .

L'image du frontend est un petit serveur statique qui réécrit wwwroot/appsettings.json au démarrage à partir de API_BASE_URL. C'est pourquoi l'adresse d'API du frontend est ici une variable d'environnement, alors que la voie binaires doit éditer le fichier à la main.

Premier démarrage

docker compose up -d
docker compose logs -f back

Sur une base vierge, le point d'entrée du backend applique les migrations avant que l'application ne démarre : le premier démarrage est donc plus long que les suivants. C'est ce que prend en compte le start_period de 120 secondes du healthcheck.

Une fois le backend sain :

curl -sf http://127.0.0.1:10026/health/ready

Faites ensuite pointer votre reverse proxy vers 127.0.0.1:10025 pour l'hôte applicatif et 127.0.0.1:10026 pour l'hôte API, puis connectez-vous en admin.

Les volumes, et pourquoi ils comptent

VolumeContient
db-dataPostgreSQL. Tout.
git-reposLes dépôts Git hébergés en interne, définitions de pipelines comprises.
artifactsLes artefacts de build, donc la capacité à redéployer ou revenir en arrière.
dp-keysLes clés de protection de données. Les perdre invalide sessions et charges protégées.
packagesLes paquets servis par le registre interne.

Un docker compose down conserve les volumes nommés. Un docker compose down -v les supprime, sans retour possible.

Mise à jour

  1. Sauvegardez la base et les volumes.

    docker compose exec -T database \
        pg_dump -U aetheus aetheus > aetheus-$(date +%Y%m%dT%H%M%SZ).sql
  2. Construisez ou récupérez les nouvelles images et étiquetez-les.

  3. Recréez les services. Les migrations s'exécutent dans le point d'entrée du backend au démarrage.

    docker compose up -d
  4. Vérifiez la disponibilité avant de renvoyer du trafic.

    curl -sf http://127.0.0.1:10026/health/ready

Mises à jour sans coupure

La configuration ci-dessus redémarre les conteneurs sur place : il y a donc une brève indisponibilité. Le dépôt contient aussi un Compose blue-green, deploy/compose/remote-bluegreen.compose.yml, où deux piles de couleurs partagent une base et où le reverse proxy bascule de l'une à l'autre. C'est ainsi qu'Aetheus se déploie lui-même, et c'est ce que pilote la voie par pipeline.