--- title: Déploiement continu description: Publier une image puis déployer une version par clé SSH. --- Le workflow de livraison s'exécute après la CI, idéalement sur un tag de version. Il publie d'abord l'image, puis déclenche une opération étroite et contrôlée sur le serveur. ## Préparer une commande de déploiement côté serveur Le serveur peut exposer un script `/usr/local/sbin/deploy-app`, appartenant à `root`, qui n'accepte qu'un tag SemVer : ```bash #!/usr/bin/env bash set -Eeuo pipefail tag="${1:-}" if [[ ! "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "Tag invalide" >&2 exit 2 fi cd /srv/mon-app printf 'IMAGE_TAG=%s\n' "$tag" > .env.release docker compose --env-file .env.release -f docker-compose.prod.yml pull docker compose --env-file .env.release -f docker-compose.prod.yml \ up -d --no-build --remove-orphans docker compose --env-file .env.release -f docker-compose.prod.yml ps ``` Configure ensuite, avec `visudo`, une règle limitée à ce script pour le compte technique. Le workflow n'a ainsi pas besoin d'un accès Docker général ni d'un mot de passe SSH. ## Publier dans GHCR ```yaml name: Release on: push: tags: ["v*.*.*"] permissions: contents: read jobs: publish: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v6 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository }}-backend tags: type=ref,event=tag - uses: docker/build-push-action@v6 with: context: ./backend push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} ``` GitHub recommande d'épingler les actions à un SHA de commit pour une chaîne d'approvisionnement plus stricte. Les tags majeurs ci-dessus gardent l'exemple lisible ; remplace-les par les SHA validés par ton équipe en production. ## Déployer avec une clé Ajoute un second job au même workflow : ```yaml deploy: runs-on: ubuntu-latest needs: publish permissions: contents: read environment: name: production url: https://app.example.com concurrency: group: production cancel-in-progress: false steps: - name: Préparer SSH env: SSH_PRIVATE_KEY: ${{ secrets.PRODUCTION_SSH_PRIVATE_KEY }} SSH_KNOWN_HOSTS: ${{ secrets.PRODUCTION_SSH_KNOWN_HOSTS }} run: | install -m 700 -d ~/.ssh printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 printf '%s\n' "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts - name: Déployer la version env: DEPLOY_HOST: ${{ vars.PRODUCTION_HOST }} DEPLOY_USER: ${{ vars.PRODUCTION_USER }} IMAGE_TAG: ${{ github.ref_name }} run: | [[ "$IMAGE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] ssh "$DEPLOY_USER@$DEPLOY_HOST" \ "sudo /usr/local/sbin/deploy-app $IMAGE_TAG" ``` ## Secrets et variables attendus | Nom | Type | Contenu | | --- | --- | --- | | `PRODUCTION_SSH_PRIVATE_KEY` | secret d'environnement | clé privée dédiée au déploiement | | `PRODUCTION_SSH_KNOWN_HOSTS` | secret d'environnement | empreinte SSH vérifiée du serveur | | `PRODUCTION_HOST` | variable d'environnement | nom DNS ou adresse du serveur | | `PRODUCTION_USER` | variable d'environnement | compte technique autorisé | La clé doit être dédiée, révocable et limitée à la production. Stocke-la dans l'environnement GitHub `production`, qui peut aussi exiger une approbation et limiter les tags autorisés. Utilise une clé dédiée et vérifie l'empreinte de l'hôte. Ne remplace pas `known_hosts` par un `ssh-keyscan` aveugle au moment du déploiement. Pour aller plus loin : [publier des images Docker](https://docs.github.com/en/actions/tutorials/publish-packages/publish-docker-images) et [protéger les environnements](https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments).