Raccourcis clavier

Appuyez sur ← ou → pour passer d'un chapitre à l'autre

Appuyez sur S ou / pour rechercher dans la documentation

Appuyez sur ? pour afficher cette aide

Appuyez sur Échap pour masquer cette aide

Déploiement avec Podman

Chatbotaurus utilise Podman, pas Docker, pour ses conteneurs. Ce guide décrit les deux plans de déploiement, l'environnement de développement (podman-compose) et la production (unités Quadlet).

À la fin de ce chapitre, vous aurez créé les réseaux et les volumes, démarré les deux plans en développement, et vous saurez comment la production est déployée, suivie et ramenée à l'image précédente.

Prérequis

  • Podman et podman-compose. Tapez bien podman-compose avec un tiret : podman compose (avec un espace) n'est qu'un adaptateur qui cherche un fournisseur externe.
  • Pour la production : un hôte Linux avec systemd, un compte non privilégié dédié (noté <compte-deploiement> ci-dessous) et les unités Quadlet de containers/.
  • Les fichiers podman-compose.yml et podman-compose.vps2.yml, le dossier containers/ et les scripts cités dans ce chapitre font partie des sources sous licence (voie d'accès : Contribuer). Lancez les commandes podman-compose depuis la racine de ces sources.
  • Aucun fichier .env n'est nécessaire pour un premier essai en développement : les fichiers compose portent des valeurs par défaut de développement. Le stockage objet fait exception : il reste fermé tant que ses variables SEAWEEDFS_S3_* ne sont pas renseignées (voir Problèmes connus, section « Le stockage objet S3 refuse toutes les requêtes »).

Deux plans, deux réseaux

PlanProjet et réseauFichierNom des services
Backend (notre plan)chatbotaurus-vps1podman-compose.ymlforge-*
Clientmgaas-vps2podman-compose.vps2.ymlmcp-*

Règle : un service nommé mcp-* est dans le plan client, tout autre nom dans le plan backend. Tout nouveau serveur MCP est un service du fichier podman-compose.vps2.yml, jamais un podman run isolé.

Les deux réseaux sont créés avant le premier lancement (les fichiers compose les déclarent external) :

podman network create chatbotaurus-vps1
podman network create mgaas-vps2

Les volumes de données sont external eux aussi (bloc volumes: en fin de chaque fichier compose) : compose ne les crée pas. Créez-les avant le premier lancement. Leurs noms portent un préfixe hérité (par exemple <prefixe-du-projet>_forge-pgdata), choisi pour ne jamais recréer un volume vide. Pour le plan backend :

for v in $(awk '/^volumes:/{f=1} f && /^    name:/{print $2}' podman-compose.yml); do
  podman volume exists "$v" || podman volume create "$v"
done

Faites de même avec podman-compose.vps2.yml. La liste des noms se lit avec grep -n "name:" podman-compose.yml.

La boucle ne crée que les volumes absents : vous pouvez la relancer sans risque. Résultat attendu : podman volume ls affiche ces noms et podman network ls les deux réseaux. Si l'un d'eux manque, le premier lancement échoue : voir Problèmes connus, section « Un conteneur ne démarre pas ».

PostgreSQL et Valkey du plan backend sont rattachés aux deux réseaux, avec les alias DNS postgres et valkey sur mgaas-vps2 : les outils du plan client (Odoo, Listmonk, n8n, Grafana, Mattermost, entre autres) les joignent par ces noms.

Développement : démarrer les plans

# Plan backend
podman-compose -p chatbotaurus-vps1 up -d

# Plan client (passerelle MCP et serveurs des offres)
podman-compose -p mgaas-vps2 -f podman-compose.vps2.yml up -d

# Vérifier
podman ps --format "table {{.Names}}\t{{.Status}}"

Résultat attendu : chaque conteneur du plan affiche l'état Up, et ceux qui déclarent une sonde de santé passent à healthy après leur délai de démarrage. Les services des profils container-api et backup ne démarrent pas sans leur profil : c'est normal. Un conteneur à l'état Exited, ou absent hors profil : lisez podman logs <conteneur> et suivez Problèmes connus, section « Un conteneur ne démarre pas ».

En développement, le binaire forge-api tourne sur l'hôte (scripts/dev/run-forge-api.ps1) ; le service forge-api du compose est derrière le profil container-api et ne démarre que sur demande :

podman-compose -p chatbotaurus-vps1 --profile container-api up -d forge-api

Services du plan backend (podman-compose.yml)

ServiceConteneurRôlePort publié (hôte → conteneur)
postgresforge-postgresBase principale (image postgres:16-alpine)5432
valkeyforge-valkeyCache et sessions6380 → 6379 (par défaut)
qdrantforge-qdrantBase vectorielle6333 et 6334
ollamaforge-ollamaLLM locaux (limite mémoire fixée dans le plan compose)11434
authentikforge-authentikSSO / IAM9000 et 9443
forge-apiforge-apiBackend Rust (profil container-api)3000
openbaoforge-openbaoSecrets8200
pgadminforge-pgadminAdministration PostgreSQL5050
victoriametricsforge-victoriametricsMétriques8428
victorialogsforge-victorialogsJournaux9428
faster-whisperforge-faster-whisperTranscription (STT)10300 → 8000
tts-modelsforge-tts-modelsTéléchargement des modèles vocaux—
kokoro-ttsforge-kokoro-ttsSynthèse vocale8880
livekitforge-livekitTemps réel (WebRTC)7880, 7881, 7882/udp
crowdsecforge-crowdsecDétection d'intrusion—
seaweedfsforge-seaweedfsStockage objet S3 (Apache-2.0)8333, lié à la boucle locale
backup-agentforge-backup-agentAgent de sauvegarde (profil backup)—
tusdforge-tusdRéception TUS des archives (profil backup)—

Les sauvegardes sont décrites dans Sauvegarde et restauration.

Plan client (podman-compose.vps2.yml)

Le service mcp-gateway (binaire forge-gateway, port 8811) est le point d'entrée du protocole MCP du plan. Autour de lui : les instances Odoo par offre (mcp-odoo-therapie, mcp-odoo-medical, mcp-odoo-finance et les autres), mcp-element, mcp-cryptpad, mcp-listmonk, mcp-n8n, mcp-grafana, mcp-plausible, mcp-moodle, mcp-calrs, mcp-forgejo, mcp-mattermost, mcp-paperless, mcp-frappe, mcp-searxng, mcp-typesense, mcp-umami, mcp-matomo et leurs bases. La liste exacte se lit avec :

grep container_name podman-compose.vps2.yml

Production : unités Quadlet

En production, chaque conteneur est une unité Quadlet containers/<service>/<service>.container, gérée par systemd sous le compte <compte-deploiement>. Côté plan backend, on y trouve par exemple forge-traefik (ingress), forge-api, forge-ui (fichiers statiques du front servis par nginx), forge-openbao, forge-qdrant, forge-ollama, forge-livekit, forge-authentik-server, forge-authentik-worker, forge-crowdsec et forge-tusd. Sur ce plan, seuls l'ingress et le service temps réel publient des ports sur toutes les interfaces ; les autres unités n'écoutent que sur la boucle locale de l'hôte. Dans l'unité forge-api, le port 3000 n'est publié que sur la boucle locale de l'hôte. Le détail des ports et des règles n'est pas publié. L'ingress du plan client est mcp-caddy. Les unités forge-falco et forge-zeek existent dans containers/, mais deploy-all.sh les a retirées de ses niveaux le 2026-09-19 : elles ne sont pas déployées.

Orchestration depuis un poste d'opérateur

containers/scripts/deploy-all.sh pilote les deux plans par niveaux cumulatifs de 1 (démarrage) à 5 (plateforme).

Prérequis : sur le poste, bash, ssh, tar et curl ; sur chaque hôte cible, le compte de déploiement non privilégié avec sudo, podman, Quadlet, python3, le mode « linger » (voir Problèmes connus, section « Les services Quadlet ne redémarrent pas après un redémarrage de l'hôte ») et le dossier cible. L'étape preflight vérifie ces points. Les hôtes se désignent par --hote-backend et --hote-client (alias SSH ou utilisateur@hôte), le dossier déployé par --app. Les niveaux 4 et 5 sont refusés tant qu'une unité qu'ils exigent n'existe pas dans containers/. Code de sortie : 0 si chaque étape demandée est prouvée, 1 sinon, 2 pour une erreur d'usage.

# Lister ce que le niveau 1 déploie, avec ce que le code prouve
bash containers/scripts/deploy-all.sh --liste 1

# Voir les commandes sans se connecter
bash containers/scripts/deploy-all.sh 1 --plan backend --dry-run

# Étapes, dans cet ordre : preflight, expedier, images, cible, prouver (ou tout)
bash containers/scripts/deploy-all.sh 1 --plan backend --etape prouver

Le script ne contient aucun secret : ils naissent sur la cible (scripts d'initialisation des secrets, exécutés sur l'hôte pendant l'étape cible, de façon idempotente : relancer l'étape est sans danger). Résultat attendu de l'étape prouver : un accès HTTPS, certificat vérifié, à chaque nom public du niveau ; un nom qui ne résout pas imprime l'enregistrement DNS à créer chez votre hébergeur.

Déploiement tiré par l'hôte

Le chemin courant n'est pas une poussée depuis un poste : l'hôte tire. Un minuteur systemd utilisateur lance un script 2 minutes après la fin du passage précédent (OnUnitInactiveSec=120). Le script regarde si la branche principale a avancé :

ScriptRôle
scripts/deploy/vps-autodeploy-front.shConstruit le front (dx build --release) et bascule le dossier servi par forge-ui, puis prouve par HTTPS
scripts/deploy/vps-autodeploy-backend.shConstruit l'image backend sur l'hôte, met à jour l'unité forge-api, redémarre et prouve par HTTPS
scripts/deploy/vps-autodeploy-vps2.shDéploie le plan client : seules les unités dont un fichier source a changé sont redémarrées

Installation et suivi, depuis le poste de l'opérateur :

bash scripts/deploy/installer-autodeploy-hote.sh --vps1   # ou --vps2, --tout
bash scripts/deploy/suivre-autodeploy.sh

Les deux commandes ci-dessus se connectent en SSH : elles lisent l'hôte cible dans la variable HOTE (l'installateur lit aussi HOTE_VPS2 pour le plan client) et la clé SSH dans CLE. Posez ces variables explicitement avant de les lancer. Résultat attendu de suivre-autodeploy.sh : l'état du minuteur (active), le SHA déployé, puis la fin du journal et de la construction du front. Il ne lit pas l'état du backend : comparez le champ sha de /api/v1/healthz au commit attendu (voir Supervision).

Points à connaître :

  • l'image backend est étiquetée par le SHA du commit (localhost/forge-stack:<sha-court>) et construite par podman build -f Containerfile . sur l'hôte ; aucune image n'est supprimée, l'étiquette précédente reste disponible ;
  • si la santé locale n'est pas prouvée après le redémarrage, le script revient à l'image précédente ;
  • un commit qui ne touche que la documentation ne reconstruit pas l'image ;
  • aucune clé ni port n'est exposé par ce mécanisme : l'hôte tire, il ne reçoit rien.

La description complète du processus est dans docs/deploiement/PROCESSUS-PAR-PUSH.md.

Construction des images

Le Containerfile à la racine construit en plusieurs étapes les binaires Rust (forge-api, forge-cli, forge-ops) vers une image finale minimale ; Containerfile.production en est la variante de production.

podman build -t localhost/forge-stack:dev -f Containerfile .

Le front se construit à part :

dx build --release --platform web --package forge-ui

Modèles Ollama

Une fois forge-ollama démarré, téléchargez les modèles dont vous avez besoin :

podman exec forge-ollama ollama pull <nom-du-modèle>
podman exec forge-ollama ollama list

Le modèle utilisé par défaut se règle par la variable OLLAMA_MODEL du backend. Le CLI forge-cli propose aussi une sous-commande model pull. Le service forge-api du compose (profil container-api) fixe lui-même une valeur par défaut de OLLAMA_MODEL (lisible dans podman-compose.yml) ; le binaire lancé sur l'hôte suit, lui, les défauts de Variables d'environnement. Téléchargez le modèle que votre backend demande. Résultat attendu : ollama list affiche le modèle téléchargé.

Contrôle de la chaîne d'approvisionnement

  • Le dépôt tient un SBOM CycloneDX par composant dans docs/sbom/ (python scripts/audit/sbom_cyclonedx.py --check vérifie qu'il correspond au Cargo.lock).
  • Le workflow de CI du dépôt analyse l'image backend avec Trivy (gravité HIGH et CRITICAL). Il est dormant : aucun exécuteur n'y est rattaché, il ne tourne pas aujourd'hui. Les exceptions acceptées sont listées dans un fichier d'exceptions motivées du dépôt. Un script d'analyse des images se lance à la main.

Notes importantes

  • Les unités Quadlet de production sont préférées en mode rootless ; binder les ports 80 et 443 en rootless exige de baisser net.ipv4.ip_unprivileged_port_start (valeur à fixer au plus à 80, avec les droits administrateur ; l'en-tête de l'unité de l'ingress, dans les sources, donne le détail).
  • OpenBao se re-scelle au redémarrage : voir le guide d'exploitation du coffre (hors de ce livre) pour la topologie de descellement.
  • Les fichiers compose portent des valeurs par défaut de développement pour certains mots de passe : surchargez-les par l'environnement (fichier .env), ne les réutilisez jamais en production. En production, les secrets sont créés sur l'hôte par les scripts d'initialisation des secrets et ne figurent pas dans le dépôt.

Voir aussi