1. Objectif
Ce guide explique comment intégrer un nouveau projet (backend Java, frontend, base de
données PostgreSQL/MySQL/MongoDB, cache Redis, storage) pour que son déploiement, backup
et restore soient entièrement gérés par le système Ansible de sever-admin — exactement
comme le sont déjà ClaraJob, Marketisia/PredictX, auth-server et Traefik.
Principe : on ne réinvente rien. On copie le pattern d’un projet existant équivalent :
| Tu veux ajouter… | Copie le pattern existant de… |
|---|---|
Un stack applicatif complet (API + DB + front) |
|
Un backend Java Spring Boot seul (image GitLab) |
|
Un frontend Vue.js/React buildé sur le serveur |
|
Un site statique non-npm buildé sur le serveur (AsciiDoc/Gradle…) |
|
Une base PostgreSQL (avec backup/restore) |
|
Une base MongoDB (avec backup/restore) |
|
Une base MySQL (avec backup/restore) |
le rôle |
Un cache Redis |
|
Un service d’infrastructure partagé (UI, broker…) |
un fichier |
2. Prérequis
-
Le projet a un repo GitLab dans le groupe
app81724 -
Le repo contient un docker-compose.yml fonctionnel (à la racine, ou dans
docker/comme marketisia) -
Tous ses services sont sur le réseau
proxy_server_network(external) -
Si images privées : la CI du projet pushe vers le GitLab Container Registry et les images sont taguées (
${IMAGE_TAG}dans le compose) -
La clé GitLab du serveur (
/home/admin/.ssh/id_ed25519) a accès au repo (deploy key) -
Tu as le mot de passe vault (
~/.vault_sever_admin)
3. Vue d’Ensemble des 6 Étapes
1. Préparer le docker-compose du projet (conventions du parc)
2. Déclarer les variables dans ansible/inventory/group_vars/all/vars.yml
3. Ajouter les secrets dans vault.yml (ansible-vault edit)
4. Ajouter la cible dans playbooks/deploy.yml (assert + task)
5. (si base de données) Ajouter aux playbooks backup.yml, restore.yml, setup-cron.yml
6. Tester : ENV=local → syntax-check → déploiement réel
4. Étape 1 — Préparer le docker-compose du Projet
Le compose vit dans le repo du projet (pas dans sever-admin). Conventions observées dans tout le parc, à respecter :
# monapp/docker-compose.yml
networks:
proxy_server_network:
external: true # ① réseau partagé, JAMAIS créé par le compose
volumes:
monapp-db-data: # ② volume nommé pour toute donnée persistante
services:
monapp-ddl: # ③ nom de service = nom de container = identifiant unique
image: postgres:16-alpine
container_name: monapp-ddl # dans tout le parc (préfixe par le nom du projet)
restart: unless-stopped # ④ redémarrage auto
environment:
POSTGRES_DB: ${MONAPP_DB_NAME} # ⑤ secrets via variables d'env (.env local
POSTGRES_USER: ${MONAPP_DB_USER} # sur le serveur, vault côté Ansible)
POSTGRES_PASSWORD: ${MONAPP_DB_PASS}
volumes:
- monapp-db-data:/var/lib/postgresql/data
networks:
- proxy_server_network
mem_limit: 256m # ⑥ limite RAM OBLIGATOIRE (Lightsail = RAM limitée)
memswap_limit: 256m
healthcheck: # ⑦ healthcheck sur les services critiques
test: ["CMD-SHELL", "pg_isready -U ${MONAPP_DB_USER} -d ${MONAPP_DB_NAME}"]
interval: 10s
timeout: 5s
retries: 5
monapp-api:
image: registry.gitlab.com/app81724/monapp/monapp-api:${IMAGE_TAG:-latest} # ⑧ tag paramétré
container_name: monapp-api
restart: unless-stopped
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://monapp-ddl:5432/${MONAPP_DB_NAME} # ⑨ hostname
SPRING_DATASOURCE_USERNAME: ${MONAPP_DB_USER} # = nom du
SPRING_DATASOURCE_PASSWORD: ${MONAPP_DB_PASS} # container
depends_on:
monapp-ddl:
condition: service_healthy
networks:
- proxy_server_network
mem_limit: 512m
memswap_limit: 512m
labels: # ⑩ exposition publique via Traefik (pas de port publié)
- "traefik.enable=true"
- "traefik.http.routers.monapp.rule=Host(`monapp.mondomaine.com`)"
- "traefik.http.routers.monapp.entrypoints=websecure"
Les 10 conventions numérotées ci-dessus sont celles appliquées par clarajob_sa,
marketisia et les compose de sever-admin. Détail : Infrastructure Docker.
|
Le |
5. Étape 2 — Déclarer les Variables dans vars.yml
Fichier : ansible/inventory/group_vars/all/vars.yml. Suivre exactement le style
existant (voir les blocs clarajob/marketisia déjà présents) :
# MonApp — description courte (repo principal, docker-compose.yml à la racine)
monapp_project_dir: "/home/admin/app/monapp"
monapp_git_url: "git@gitlab.com:app81724/monapp.git"
# Services applicatifs
monapp_api_service: "monapp-api"
# Base PostgreSQL
monapp_db_service: "monapp-ddl"
monapp_db_name: "{{ vault_monapp_db_name }}"
monapp_db_user: "{{ vault_monapp_db_user }}"
monapp_db_pass: "{{ vault_monapp_db_pass }}"
Règles de nommage observées dans le fichier réel :
-
Préfixe unique par projet :
monapp_* -
*_project_dirsous/home/admin/app/ -
*_service= lecontainer_nameexact du compose -
Toute donnée sensible référence une variable
vault_*— jamais de valeur en clair
Si le compose n’est pas à la racine (cas marketisia) : tu passeras
deploy_compose_file: "docker/docker-compose.yml" à l’étape 4.
6. Étape 3 — Ajouter les Secrets dans vault.yml
cd sever-admin/ansible
ansible-vault edit inventory/group_vars/all/vault.yml
# (le mot de passe est lu automatiquement depuis ~/.vault_sever_admin via ansible.cfg)
Ajouter :
vault_monapp_db_name: "monappdb"
vault_monapp_db_user: "monapp_user"
vault_monapp_db_pass: "MotDePasseFort"
Vérifier : ansible-vault view inventory/group_vars/all/vault.yml | grep monapp
7. Étape 4 — Ajouter la Cible dans deploy.yml
Fichier : ansible/playbooks/deploy.yml. Deux modifications :
7.1. 4a. Étendre la liste du assert (pre_tasks)
- name: "Valider que target_app est fourni et connu"
assert:
that:
- target_app is defined
- target_app in ['clarajob-sa', ..., 'marketisia-front-gui',
'monapp-sa', 'monapp-api', 'monapp-ddl'] # ← AJOUT
fail_msg: >-
... monapp-sa, monapp-api, monapp-ddl # ← AJOUT au message
7.2. 4b. Ajouter les tasks de déploiement
Cas service individuel (API, DB, cache — le cas le plus courant) — rôle deploy :
# ─── MonApp API (Spring Boot — image GitLab, service seul) ──────────
- name: "Deploy MonApp API"
include_role:
name: deploy
vars:
app_name: "monapp-api"
app_service: "{{ monapp_api_service }}"
git_url: "{{ monapp_git_url }}"
project_dir: "{{ monapp_project_dir }}"
compose_dir: "{{ monapp_project_dir }}"
# deploy_compose_file: "docker/docker-compose.yml" # si compose pas à la racine
when: target_app == "monapp-api"
# ─── MonApp DDL (PostgreSQL) ────────────────────────────────────────
- name: "Deploy MonApp DDL"
include_role:
name: deploy
vars:
app_name: "monapp-ddl"
app_service: "{{ monapp_db_service }}"
git_url: "{{ monapp_git_url }}"
project_dir: "{{ monapp_project_dir }}"
compose_dir: "{{ monapp_project_dir }}"
when: target_app == "monapp-ddl"
Le rôle deploy fait automatiquement : git clone/pull → stop → docker login → pull (si TAG)
→ up -d → pause 10s → vérification container → rollback si échec.
Cas frontend buildé sur le serveur (Vue.js/React sans image CI) — rôle deploy_frontend :
- name: "Deploy MonApp GUI (Vue.js + Nginx)"
include_role:
name: deploy_frontend
vars:
app_name: "monapp-gui"
app_service: "{{ monapp_gui_service }}"
git_url: "{{ monapp_git_url }}"
project_dir: "{{ monapp_gui_project_dir }}" # sous-dossier contenant package.json
compose_dir: "{{ monapp_project_dir }}" # dossier du docker-compose.yml
node_image: "node:lts-alpine"
when: target_app == "monapp-gui"
Sans TAG : npm ci && npm run build dans un container Node jetable puis restart Nginx.
Avec TAG : pull de l’image pré-buildée.
Cas site statique buildé avec un autre outil que npm (AsciiDoc/Gradle, Hugo, mkdocs…) —
même rôle deploy_frontend, en surchargeant les deux variables optionnelles de build
(build_image et build_command, défauts = pattern npm), par exemple
build_image: "gradle:8.12.1-jdk17" + build_command: "gradle asciidoctor --no-daemon".
Cas site statique pré-généré et versionné (aucun build serveur) — quand le dossier
généré est committé dans le repo, le rôle deploy simple suffit. Exemple réel du parc,
la cible documentation (build/generatedSite versionné, généré en local par
gradle asciidoctor) :
- name: "Deploy Documentation (AsciiDoc + Nginx)"
include_role:
name: deploy
vars:
app_name: "documentation"
app_service: "{{ documentation_service }}"
git_url: "{{ documentation_git_url }}"
project_dir: "{{ documentation_project_dir }}"
compose_dir: "{{ documentation_project_dir }}"
when: target_app == "documentation"
Le container nginx du compose monte le dossier généré (./build/generatedSite) — aucune
image Docker custom, donc jamais de TAG pour cette cible. Le deploy se réduit à
git pull + restart : rapide et sans dépendance à Maven Central côté serveur.
Cas stack complet (git pull + pull de toutes les images + up -d global) : copier le
bloc inline clarajob-sa de deploy.yml (block/rescue avec rollback) en adaptant repo,
compose et liste d’images à puller.
7.3. 4c. (Recommandé pour les DBs) Backup pre-deploy automatique
Copier le pattern des pre_tasks existants — vérification que le container tourne, puis
backup marqué pre-deploy :
# Dans pre_tasks, après les blocs existants :
- name: "Vérifier si le container monapp-ddl existe"
shell: "docker inspect --format '{{ '{{' }}.State.Running{{ '}}' }}' {{ monapp_db_service }}"
register: monapp_ddl_running
ignore_errors: true
changed_when: false
when: target_app == "monapp-ddl" or target_app == "monapp-sa"
- name: "Backup pre-deploy MonApp DDL (PostgreSQL)"
include_role:
name: backup
vars:
db_service: "{{ monapp_db_service }}"
db_name: "{{ monapp_db_name }}"
db_user: "{{ monapp_db_user }}"
db_pass: "{{ monapp_db_pass }}"
db_type: "postgresql"
dump_description: "pre-deploy"
when: (target_app == "monapp-ddl" or target_app == "monapp-sa")
and monapp_ddl_running.stdout | default('') == "true"
8. Étape 5 — Backup & Restore (si base de données)
8.1. 5a. backup.yml
- name: "Backup MonApp DDL (PostgreSQL)"
include_role:
name: backup
vars:
db_service: "{{ monapp_db_service }}"
db_name: "{{ monapp_db_name }}"
db_user: "{{ monapp_db_user }}"
db_pass: "{{ monapp_db_pass }}"
db_type: "postgresql" # postgresql | mongodb | mysql
dump_description: "auto-backup"
when: target_app == "monapp-ddl" or target_app == "all"
Pour MongoDB, ajouter dump_ext: "archive" (cf. bloc clarajob-mongo).
Pour MySQL, db_type: "mysql" — le rôle utilise alors mysqldump (déjà implémenté).
Les dumps iront automatiquement dans
db-config/dump/monapp-ddl/monapp-ddl-YYYY-MM-DD_HHMMSS.sql.
8.2. 5b. restore.yml
- name: "Restore MonApp DDL (PostgreSQL)"
include_role:
name: restore
vars:
db_service: "{{ monapp_db_service }}"
db_name: "{{ monapp_db_name }}"
db_user: "{{ monapp_db_user }}"
db_pass: "{{ monapp_db_pass }}"
db_type: "postgresql"
dump_file: "{{ dump_file }}"
when: target_app == "monapp-ddl"
8.3. 5c. setup-cron.yml (backup quotidien automatique)
Choisir une heure libre (3h = marketisia, 4h = clarajob-ddl, 5h = clarajob-mongo) :
- name: "Cron backup MonApp DDL — tous les jours à 6h"
cron:
name: "backup-monapp-ddl-daily"
minute: "0"
hour: "6"
job: >
cd {{ server_admin_dir }}/ansible &&
ansible-playbook {{ server_admin_dir }}/ansible/playbooks/backup.yml
-e target_app=monapp-ddl
--vault-password-file /opt/.vault_password
>> /var/log/ansible-backup.log 2>&1
state: present
user: root
Puis réappliquer : make setup-cron.
9. Étape 6 — Tester
cd sever-admin/ansible
# 1. Syntaxe
ansible-playbook --syntax-check playbooks/deploy.yml
ansible-playbook --syntax-check playbooks/backup.yml
ansible-playbook --syntax-check playbooks/restore.yml
# 2. Test en local (ta machine, si Docker dispo)
cd ..
make deploy APP=monapp-ddl ENV=local
make backup APP=monapp-ddl ENV=local
make restore APP=monapp-ddl ENV=local DUMP=<fichier-produit-au-2>
# 3. Production
make deploy APP=monapp-ddl # la DB d'abord
make deploy APP=monapp-api # puis l'API
make backup APP=monapp-ddl # backup manuel de validation
# 4. Vérifier
ssh -i ~/.ssh/serverAdminSSHKeypair.pem admin@18.158.207.98
docker ps | grep monapp
docker logs monapp-api --tail 50
ls -lh /home/admin/app/sever-admin/db-config/dump/monapp-ddl/
10. Recettes par Type de Projet
10.1. Backend Java Spring Boot
-
Image buildée par la CI du projet, pushée sur
registry.gitlab.com/app81724/…, référencée avec${IMAGE_TAG:-latest}dans le compose -
Ansible : rôle
deploy(étape 4b, cas service individuel) -
Connexion DB par hostname de container :
jdbc:postgresql://monapp-ddl:5432/… -
Kafka si besoin : consommer le broker partagé
marketisia-kafka:29092— ne pas redéployer de Kafka dans le projet -
Déploiement :
make deploy APP=monapp-api TAG=v1.0.0
10.2. Frontend Vue.js / React
Deux options (les deux existent dans le parc) :
-
Build serveur (pattern
clarajob-front-gui) : rôledeploy_frontend, le serveur exécutenpm ci && npm run builddans un container Node, Nginx monte ledist/. Déploiement :make deploy APP=monapp-gui(sans TAG) -
Image CI (pattern
marketisia-front-gui) : la CI construit une image Nginx+dist, rôledeployclassique. Déploiement :make deploy APP=monapp-gui TAG=v1.0.0
10.3. Site Statique non-npm (AsciiDoc, Hugo, mkdocs…)
-
Pattern
documentation: site pré-généré en local (gradle asciidoctor) avec le dossierbuild/versionné dans le repo → rôledeploysimple, aucun build serveur. (Alternative si on ne versionne pas le build : rôledeploy_frontendavecbuild_image/build_commandsurchargés) -
Compose :
nginx:lateststock qui monte le dossier généré en volume — aucune image Docker custom, aucun registre -
Pas de vault, pas de backup/restore/cron : le site est entièrement régénérable depuis Git
-
Déploiement :
make deploy APP=documentation(toujours sans TAG)
10.4. Base PostgreSQL
-
Compose :
postgres:16-alpine, volume nommé, healthcheckpg_isready, mem_limit -
Ansible : étapes 4 (deploy + backup pre-deploy) et 5 (backup/restore/cron) avec
db_type: "postgresql" -
Le dump est fait avec
--clean --if-exists→ le restore recrée les objets
10.5. Base MySQL
-
Identique à PostgreSQL avec
db_type: "mysql"— le rôle backup utilisemysqldump --add-drop-table --routines --triggers, le restoremysql < dump -
C’était le type historique du parc (anciens crons WordPress) : tout est déjà supporté
10.6. Base MongoDB
-
Compose :
mongo, healthcheckdb.adminCommand('ping'), volume nommé -
Ansible :
db_type: "mongodb"etdump_ext: "archive"au backup -
⚠️ Le restore utilise
--drop: les collections existantes sont écrasées
10.7. Cache Redis
-
Décision d’abord : le parc a un
redispartagé (sever-admin, mot de passeREDIS_PASSWORD, 32mb LRU) et unclarajob-redisdédié. Un nouveau projet peut : -
utiliser le redis partagé → aucune intégration Ansible, juste l’URL
redis://default:${REDIS_PASSWORD}@redis:6379 -
OU avoir son redis dédié → copier le service du compose cache de sever-admin dans le compose du projet + cible deploy (étape 4b). Pas de backup Ansible pour Redis (cache reconstructible ; l’AOF vit dans le volume)
10.8. Service d’Infrastructure Partagé (nouveau groupe sever-admin)
Pour un service transverse (nouvelle UI d’admin, nouveau broker…) qui appartient à sever-admin et non à un projet applicatif :
# 1. Créer le fichier compose dédié dans sever-admin/
# docker-compose.montool.yml (networks: proxy_server_network external,
# mem_limit, restart, healthcheck...)
# 2. L'ajouter à l'include du docker-compose.yml racine :
include:
- docker-compose.cache.yml
...
- docker-compose.montool.yml # ← AJOUT
# 3. (Optionnel) cible make deploy : copier le pattern adminer/server-admin-doc
# dans deploy.yml (rôle deploy, git_url: server_admin_git_url,
# project_dir/compose_dir: server_admin_dir)
# 4. Démarrer :
ssh admin@18.158.207.98 "cd /home/admin/app/sever-admin && git pull && docker compose up -d montool"
11. Checklist Finale d’Intégration
Compose (repo du projet) :
□ networks: proxy_server_network (external: true)
□ container_name explicites et uniques dans le parc
□ ${IMAGE_TAG:-latest} sur les images GitLab
□ Secrets via ${VARS} (jamais en dur) + .env sur le serveur
□ mem_limit / memswap_limit sur chaque service
□ restart: unless-stopped + healthchecks
□ Volumes nommés pour les données
□ Labels Traefik si exposition publique
Ansible (sever-admin) :
□ vars.yml : bloc de variables préfixées (project_dir, git_url, *_service, db_*)
□ vault.yml : secrets vault_* ajoutés (ansible-vault edit)
□ deploy.yml : assert étendu + task(s) include_role
□ deploy.yml : backup pre-deploy si DB
□ backup.yml / restore.yml : blocs DB ajoutés
□ setup-cron.yml : cron quotidien à une heure libre + make setup-cron relancé
□ Makefile : commentaires d'usage mis à jour
Serveur :
□ Deploy key GitLab du serveur autorisée sur le nouveau repo
□ .env du projet créé sur le serveur (variables du compose)
□ CI du projet pushe bien les images taguées au registry
Validation :
□ ansible-playbook --syntax-check sur les 3 playbooks
□ Cycle complet ENV=local si possible
□ make deploy → docker ps → docker logs OK
□ make backup → dump non-vide dans db-config/dump/<service>/
□ make restore testé avec le dump produit
□ Documentation mise à jour (ce dossier + README du repo)
12. Erreurs Fréquentes d’Intégration
| Erreur | Correction |
|---|---|
|
Oublié d’ajouter la cible dans le |
Le deploy tourne mais l’image ne change pas |
Le compose n’utilise pas |
|
Variable manquante dans vars.yml, ou faute de frappe dans le nom |
Git échoue sur le serveur |
Deploy key GitLab pas ajoutée au nouveau repo pour |
Le container ne joint pas la DB/Kafka |
Service pas sur |
Backup vide |
Credentials vault ≠ credentials réels du container (le .env du serveur fait foi) |
Cron ne tourne pas pour la nouvelle DB |
|
13. Prochaines Étapes
-
Comprendre les rôles utilisés → Architecture
-
Conventions compose détaillées → Infrastructure Docker
-
Commandes exactes → Référence Commandes