Aller au contenu

Pipeline GitLab CI/CD — déploiement Docker sur VPS via SSH

Objectif : déployer une application conteneurisée sur un VPS OVH via GitLab CI/CD, avec staging automatique et production manuelle. Quand l'utiliser : à chaque nouveau projet à déployer sur un VPS depuis GitLab. Template associé : gitlab-ci-template.yml (à copier en .gitlab-ci.yml et adapter).

Architecture

Push develop ──► lint ─► test ─► build (image :staging) ─► scan ─► deploy staging   (AUTO)
Push main    ──► lint ─► test ─► build (image :prod)    ─► scan ─► deploy prod       (MANUEL)
  • Les images sont construites par le runner, poussées sur le GitLab Container Registry, taggées par branche + SHA (jamais de mélange staging/prod).
  • Le déploiement se fait par SSH vers le VPS : on transfère seulement les fichiers de conf (docker-compose, nginx) et un script de déploiement, puis on l'exécute côté serveur. Le serveur tire les images depuis le registry — pas de Git sur le VPS.

Prérequis

  • VPS durci (voir runbooks/vps/durcissement-vps-ovh.md), port SSH custom, avec :
  • un utilisateur de déploiement dédié (ex. deploy),
  • Docker + docker compose installés,
  • un dossier cible (ex. /opt/<projet>-staging/ et /opt/<projet>/),
  • un script de déploiement (deploy-staging.sh / deploy-prod.sh) qui fait le docker login, docker compose pull, up -d, migrations…
  • Un Deploy Token GitLab (ou les creds du registry) pour que le VPS puisse docker pull.
  • Sous-domaines Cloudflare staging.<domaine> et <domaine> pointant sur le VPS.

Variables CI/CD à configurer

GitLab → Settings → CI/CD → Variables :

Variable Rôle Notes
VPS_HOST IP / hostname du VPS
VPS_PORT Port SSH custom ex. 49155
VPS_USER Utilisateur de déploiement ex. deploy
VPS_SSH_KEY Clé privée SSH en base64 masked — voir piège n°1
POSTGRES_PASSWORD / _STAGING Secrets DB prod et staging différents
SECRET_KEY / _STAGING JWT / secrets app prod et staging différents
(autres) selon l'app ex. GOOGLE_CLIENT_ID, URLs publiques

Étapes de mise en place

  1. Copie le template gitlab-ci-template.yml → .gitlab-ci.yml à la racine du projet.
  2. Remplace les <PLACEHOLDER> (domaines, chemins /opt/<projet>, noms d'images) et adapte les jobs lint/test/build à ta stack.
  3. Génère la clé de déploiement et autorise-la côté VPS :
    ssh-keygen -t ed25519 -f ~/.ssh/<projet>_deploy -C "gitlab-ci"
    ssh-copy-id -i ~/.ssh/<projet>_deploy.pub -p <PORT> deploy@<VPS_HOST>
    base64 -w 0 ~/.ssh/<projet>_deploy > cle_base64.txt   # → colle dans VPS_SSH_KEY
    
  4. Crée les variables CI/CD (tableau ci-dessus).
  5. Prépare le VPS : dossiers /opt/<projet>[-staging]/, script deploy-*.sh en place, Docker prêt.
  6. Push sur develop → la pipeline doit dérouler jusqu'au deploy staging automatiquement.
  7. Merge sur main → build/scan auto, puis lance le deploy prod manuellement (bouton play).

Pièges connus (chèrement appris — à ne pas refaire)

  1. Clé SSH en base64, une seule ligne. Coller une clé privée multi-lignes dans une variable GitLab depuis Windows introduit des CRLF → erreur error in libcrypto. On stocke base64 -w 0 de la clé et on la décode dans le job.
  2. Script fichier plutôt que heredoc inline. Un ssh ... <<SSH non quoté fait expanser $i, $?, $(...) par le shell local du runner avant l'envoi. Transférer un .sh et l'exécuter via ssh ... bash script.sh = interprétation une seule fois, côté VPS.
  3. Pas de pipelines merge_request_event. Ils créent un état transitoire imprévisible (MR encore "ouverte" juste après merge → pipeline push bloqué). On déclenche uniquement sur push develop/main via workflow.rules.
  4. Pas de Git sur le serveur. Seuls conf + script de déploiement sont transférés ; les images viennent du registry. Plus léger, plus sûr.
  5. Tags d'image par branche. :$CI_COMMIT_REF_SLUG-$SHA évite qu'un build staging n'écrase une image prod.
  6. rsync --exclude nginx/certs en prod pour ne pas écraser les certificats présents sur le serveur.
  7. Secrets staging ≠ prod. Toujours des valeurs distinctes (_STAGING).

Vérification

  • [ ] Push develop → pipeline verte jusqu'à deploy:staging, site staging à jour
  • [ ] deploy:production reste manuel (n'a pas démarré tout seul)
  • [ ] Aucune clé/secret en clair dans le .gitlab-ci.yml (tout en variables CI/CD)
  • [ ] Le VPS pull bien les images (Deploy Token valide)

Dépannage

  • error in libcrypto → VPS_SSH_KEY mal encodée : refaire base64 -w 0.
  • Host key verification failed → ssh-keyscan -p $VPS_PORT $VPS_HOST échoue (port/IP) ou VPS injoignable.
  • Variables vides côté serveur / erreurs $ bizarres → tu es repassé sur un heredoc inline (piège n°2).
  • deploy:staging skippé → vérifie needs (build/scan) et la règle develop.

Ressources

  • Runbook durcissement VPS : ../vps/durcissement-vps-ovh.md
  • GitLab CI/CD variables : https://docs.gitlab.com/ee/ci/variables/
  • GitLab Container Registry : https://docs.gitlab.com/ee/user/packages/container_registry/
  • Trivy (scan images) : https://trivy.dev/

Dernière mise à jour : 2026-07-02