Déploiement — BConnect Business¶
CI/CD par GitHub Actions, images stockées dans le registry Harbor de BINN,
déploiement par SSH + docker compose pull sur le VPS.
Le serveur n'héberge pas le code source : il ne fait que tirer une image déjà construite. La stack ne contient que l'application — MySQL, PostgreSQL, Redis et Traefik sont mutualisés par le projet infra du VPS et joints par réseau Docker externe.
| Workflow | .github/workflows/deploy.yml |
| Registry | binn.registry.169.58.138.150.nip.io (projet Harbor binn) |
| Image | binn.registry.169.58.138.150.nip.io/binn/bconnect-business |
| Image docs | binn.registry.169.58.138.150.nip.io/binn/bconnect-business-docs |
| Répertoire serveur | /srv/apps/BConnect-Business |
| Stack de prod | docker-compose.prod.yml (copié sur le serveur à chaque déploiement) |
| Réseaux externes | traefik-net, infra-data, infra-messaging, bconnect-net |
| URL publique | https://binn.business.169.58.138.150.nip.io |
| URL docs | https://docs.bconnect-business.169.58.138.150.nip.io |
La stack jumelle BConnect POS suit exactement le même modèle et partage le réseau
bconnect-net avec celle-ci.
1. Ce qui se passe automatiquement¶
Un push sur main ou develop, ou un tag v*, construit l'image applicative et
l'image docs, puis les pousse dans Harbor avec ces tags :
sha-<7 premiers caractères du commit>— le seul tag réellement immuable, c'est celui que le déploiement utilise ;- le nom de la branche (
main,develop) ; latest, uniquement sur la branche par défaut ;- le tag git, s'il s'agit d'un tag.
Le déploiement s'enchaîne automatiquement sur un push vers develop. Depuis
toute autre ref, il se lance à la main.
2. Déployer en production¶
Onglet Actions → workflow Build & Deploy → Run workflow.
- Use workflow from : la branche ou le tag à déployer.
- image_tag : laisser vide. L'image est reconstruite depuis la ref choisie puis
déployée. Renseigner un tag (ex.
sha-a1b2c3d) uniquement pour redéployer une image déjà publiée — typiquement un retour arrière : le build est alors sauté.
Le job de déploiement est rattaché à l'environnement GitHub production. Pour exiger
une approbation humaine avant qu'il ne s'exécute :
Settings → Environments → production → Required reviewers.
3. Configuration GitHub (à faire une fois)¶
Settings → Secrets and variables → Actions
Secrets¶
| Nom | Valeur |
|---|---|
REGISTRY_USERNAME |
compte Harbor (admin, ou de préférence un compte robot) |
REGISTRY_PASSWORD |
son mot de passe / jeton |
DEPLOY_HOST |
169.58.138.150 |
DEPLOY_USER |
abdoulaye |
DEPLOY_SSH_KEY |
clé privée SSH complète, sans passphrase (-----BEGIN … KEY----- inclus) |
DEPLOY_PATH |
/srv/apps/BConnect-Business |
Préférez un compte robot Harbor limité au projet
binnplutôt que le compteadmin: sa compromission ne donne alors accès qu'au push/pull d'images.
Variables (facultatif)¶
| Nom | Défaut | Rôle |
|---|---|---|
BCONNECT_DOCKER_NETWORK |
bconnect-net |
réseau Docker interne partagé avec la stack POS |
Cette application est rendue côté serveur : aucune valeur destinée au navigateur
n'est figée au build, tout vient du .env du serveur. Si une variable devait un
jour l'être (clé publique Stripe injectée dans un bundle JS, par exemple), elle
passerait par une variable GitHub et un build-arg, jamais par un secret.
Environnement¶
Settings → Environments → New environment → production, puis y ajouter les
required reviewers si vous voulez une validation manuelle.
4. Préparation du serveur (à faire une fois)¶
Le prérequis d'accès au serveur est regroupé dans scripts/bootstrap-server.sh, à
lancer une fois sur le serveur avec sudo. Il configure le registry insecure, le
sudo sans mot de passe pour le compte de déploiement et vérifie la présence des
.env.
Les vérifications reproductibles côté CI vivent dans scripts/ci/prepare-server.sh.
La création optionnelle de la base MySQL a son propre script,
scripts/ci/prepare-databases.sh. Les détails ci-dessous restent la référence, et
la marche à suivre si vous préférez tout faire à la main.
sudo bash scripts/bootstrap-server.sh
a. Docker et le registry¶
Si le certificat de Harbor n'est pas émis par une autorité reconnue — ce qui est le
cas par défaut avec un nom nip.io — le docker login échoue avec une erreur
x509: certificate signed by unknown authority. Déclarez le registry :
sudo tee /etc/docker/daemon.json >/dev/null <<'JSON'
{ "insecure-registries": ["binn.registry.169.58.138.150.nip.io"] }
JSON
sudo systemctl restart docker
Le workflow fait la même chose sur le runner GitHub. Le jour où Harbor a un certificat valide, ces deux configurations peuvent disparaître.
b. Clé SSH de déploiement¶
ssh-keygen -t ed25519 -C "github-actions-bconnect-business" -f ~/.ssh/bconnect_business_deploy -N ""
La partie publique va dans le ~/.ssh/authorized_keys de abdoulaye sur le serveur ;
la partie privée dans le secret DEPLOY_SSH_KEY.
c. sudo sans mot de passe¶
Le déploiement installe le fichier compose dans un répertoire appartenant à root et
pilote Docker : ces deux commandes doivent passer sans TTY ni mot de passe.
echo 'abdoulaye ALL=(ALL) NOPASSWD: /usr/bin/docker, /usr/bin/install' \
| sudo tee /etc/sudoers.d/bconnect-deploy
sudo chmod 440 /etc/sudoers.d/bconnect-deploy
sudo visudo -c
d. Répertoire de déploiement¶
sudo install -d -o root -g root -m 755 /srv/apps/BConnect-Business
Le workflow ne le crée pas : il vérifie sa présence avec test -d et s'arrête
avec un message explicite s'il manque. Un mkdir -p renverrait 0 sans rien faire et
masquerait un problème de droits.
e. Base de données MySQL¶
À faire avant le premier déploiement. La stack ne contient pas de conteneur
MySQL : la base et son utilisateur doivent exister sur l'instance mutualisée. L'image
ne les crée pas — son docker/entrypoint.sh attend que la connexion réponde, puis
joue doctrine:schema:update --force pour créer les tables.
CREATE DATABASE bconnect_business CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'bconnect_business'@'%' IDENTIFIED BY '<mot de passe>';
GRANT ALL PRIVILEGES ON bconnect_business.* TO 'bconnect_business'@'%';
FLUSH PRIVILEGES;
Sans cette étape, le conteneur affiche Attente de la base de donnees... puis
s'arrête au bout de deux minutes en affichant l'erreur SQL réelle — c'est le symptôme
à reconnaître.
f. Réseaux Docker¶
traefik-net, infra-data et infra-messaging sont créés et gérés par le projet
infra : ne les recréez pas. infra-messaging donne accès au service SMTP
mailer:1025. bconnect-net porte le trafic interne Business ↔ POS et est créé par
le workflow s'il manque.
docker network ls | grep -E 'traefik-net|infra-data|infra-messaging|bconnect-net'
g. Fichier .env de production¶
Les secrets ne transitent jamais par GitHub : ils vivent dans un .env que vous
créez à la main dans /srv/apps/BConnect-Business, à côté du
docker-compose.prod.yml. Le déploiement s'arrête avec une erreur explicite si ce
fichier manque.
cd /srv/apps/BConnect-Business
sudo tee .env >/dev/null <<'ENV'
# --- MySQL mutualise (reseau infra-data) ---
# DB_HOST vaut `mysql` par defaut : inutile de le redefinir tant que le service
# mutualise porte ce nom sur infra-data.
DB_HOST=mysql
DB_PORT=3306
DB_NAME=bconnect_business
DB_USER=bconnect_business
DB_PASSWORD=<mot de passe>
DB_SERVER_VERSION=8.0
# --- Traefik ---
BUSINESS_HOST=binn.business.169.58.138.150.nip.io
DOCS_HOST=docs.bconnect-business.169.58.138.150.nip.io
TRAEFIK_ENTRYPOINT=websecure
TRAEFIK_CERTRESOLVER=letsencrypt
# --- Symfony ---
APP_SECRET=<openssl rand -hex 32>
CORS_ALLOW_ORIGIN=^https://binn\.(business|pos)\.169\.58\.138\.150\.nip\.io$
MAILER_DSN=smtp://mailer:1025
JWT_PASSPHRASE=<openssl rand -hex 32>
# --- Liaison avec le POS ---
# Appel serveur a serveur : passe par bconnect-net, sans sortir par Traefik.
API_BASE_URL=http://bconnect-pos/api
# Lien clique par l'utilisateur : URL publique du POS.
PDV_BASE_URI=https://binn.pos.169.58.138.150.nip.io/
# Doit valoir exactement le SYSTEM_API_KEY du .env de la stack POS.
SECRET_API_KEY=<meme valeur que SYSTEM_API_KEY cote POS>
# --- Paiements ---
STRIPE_SECRET_KEY=sk_live_...
RECAPTCHA3_KEY=...
RECAPTCHA3_SECRET=...
OM_CLIENT_ID=...
OM_CLIENT_SECRET=...
OM_MERCHANT_KEY=...
OM_API_BASE_URL=...
OM_TOKEN_ENDPOINT=...
OM_PAYMENT_ENDPOINT=...
OM_TRANSACTIONSTATUS_ENDPOINT=...
OM_RETURN_URL=https://binn.business.169.58.138.150.nip.io/paiement/retour
OM_CANCEL_URL=https://binn.business.169.58.138.150.nip.io/paiement/annule
OM_NOTIF_URL=https://binn.business.169.58.138.150.nip.io/paiement/notification
ENV
sudo chmod 600 .env
Toutes les variables marquées obligatoires dans docker-compose.prod.yml font échouer
le docker compose up avec un message nommant la variable manquante, plutôt que de
démarrer avec une valeur change-me.
Mot de passe MySQL : il est injecté dans une URL (
DATABASE_URL). S'il contient@,:,/,#ou%, encodez-le en pourcent (@→%40) ou changez-le pour une valeur alphanumérique.
.envdu dépôt : le.envversionné contient encore des clés de développement (reCAPTCHA, passphrase JWT). Il est exclu de l'image par.dockerignoreet ne part donc pas en production, mais ces valeurs restent dans l'historique git et devraient être révoquées.
5. Traefik¶
Le conteneur ne publie aucun port : Traefik l'atteint par traefik-net. Les labels
sont dans docker-compose.prod.yml ; trois valeurs sont pilotees par le .env :
| Variable | Défaut | |
|---|---|---|
TRAEFIK_ENTRYPOINT |
websecure |
entrypoint Traefik |
TRAEFIK_CERTRESOLVER |
letsencrypt |
résolveur ACME |
DOCS_HOST |
docs.bconnect-business.169.58.138.150.nip.io |
hote du portail docs |
Si votre Traefik ne termine pas le TLS (Cloudflare ou proxy amont devant), mettez
TRAEFIK_ENTRYPOINT=web et retirez les deux labels …tls… du fichier compose : une
variable ne suffit pas à supprimer un label.
Le label traefik.docker.network=traefik-net est indispensable ici — le conteneur est
sur trois réseaux, et sans lui Traefik peut router vers la mauvaise IP.
Le portail docs est servi par le service separe bconnect-business-docs, avec nginx
sur le port interne 80. Son image est construite avec le meme tag que l'image
applicative et injectee au deploiement via DOCS_IMAGE.
6. Vérifier un déploiement¶
scripts/check-deploy.sh regroupe ces contrôles et y ajoute ce qui ne se voit pas
dans docker compose ps : que les deux stacks partagent bien bconnect-net et s'y
résolvent mutuellement, et que SECRET_API_KEY (Business) et SYSTEM_API_KEY (POS)
portent la même valeur.
bash scripts/check-deploy.sh
cd /srv/apps/BConnect-Business
docker compose -f docker-compose.prod.yml ps
docker compose -f docker-compose.prod.yml logs -f bconnect-business
docker inspect --format '{{.Config.Image}}' bconnect-business
La dernière commande affiche le tag exact en service — utile pour confirmer que le déploiement a bien pris.
7. Revenir en arrière¶
Relancez Run workflow en renseignant image_tag avec le sha-… de la version
précédente (visible dans Harbor, ou dans le résumé de l'exécution qui l'avait
déployée). Le build est sauté, seul le déploiement s'exécute.
8. Points d'attention¶
- Le schéma est mis à jour au démarrage par
doctrine:schema:update --force, pas par les migrations — c'est le comportement historique dedocker/docker.sh. Sur une base peuplée, cette commande peut proposer des changements destructeurs : à surveiller lors du premier déploiement, et à remplacer pardoctrine:migrations:migratequand la base de production sera établie. - Clés JWT : elles ne sont pas dans le dépôt. L'entrypoint les génère au premier
démarrage dans le volume
business_jwtavecJWT_PASSPHRASE. Changer cette passphrase après coup rend les clés existantes illisibles : il faut alors supprimer le volume, ce qui révoque tous les jetons en circulation. - Volumes
business_documents/business_profil: justificatifs et photos de profil. Ils survivent aux déploiements et ne sont jamais recréés par le workflow ; leur sauvegarde reste à votre charge. La base MySQL, elle, est sauvegardée avec l'instance mutualisée. docker image prune -fest exécuté après chaque déploiement pour éviter que le disque ne se remplisse d'images obsolètes. Il ne touche qu'aux images sans conteneur associé.