La CI construit et valide. ArgoCD déploie. Ces deux responsabilités ne doivent pas se mélanger. Ce guide vous montre comment câbler votre pipeline CI à ArgoCD en respectant la règle d’or GitOps : la CI ne touche jamais le cluster, elle met à jour Git. Deux méthodes sont couvertes : la mise à jour manuelle du tag d’image dans Git, et l’automatisation complète avec ArgoCD Image Updater.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- La séparation des rôles : CI (build + push image) vs ArgoCD (déploiement depuis Git)
- Mettre à jour le tag d’image dans le config-repo depuis un pipeline CI, sans accès cluster
- Gérer les credentials Git dans la CI (deploy key SSH ou Personal Access Token)
- Exemples complets GitHub Actions et GitLab CI validés (epinglés par SHA, permissions minimales)
- Attendre la synchronisation ArgoCD depuis la CI avant de lancer les tests d’intégration
- ArgoCD Image Updater : automatiser la mise à jour du tag sans script
La règle d’or : la CI ne déploie pas
Section intitulée « La règle d’or : la CI ne déploie pas »En architecture GitOps, le cluster n’est visible que depuis Git. Votre pipeline CI n’a donc aucune raison d’avoir un accès direct au cluster. Voici la séparation des rôles :
| Phase | Responsable | Action |
|---|---|---|
| Build & test | Pipeline CI | Build, tests, lint, scan de sécurité |
| Publication image | Pipeline CI | Push de l’image monapp:a1b2c3d dans le registre |
| Mise à jour de Git | Pipeline CI | Modifie le tag dans les manifestes du config-repo |
| Déploiement | ArgoCD | Détecte le commit, réconcilie le cluster |
La CI n’exécute jamais kubectl apply ni helm upgrade. Elle pousse seulement
un commit dans le config-repo.
Méthode manuelle : script de mise à jour du tag
Section intitulée « Méthode manuelle : script de mise à jour du tag »Cette méthode est simple et portable. Le pipeline CI, après avoir buildé et poussé l’image, clone le config-repo, modifie le tag, et pousse un commit.
Le script de mise à jour
Section intitulée « Le script de mise à jour »#!/bin/bashset -euo pipefail
IMAGE_TAG="${1}" # Exemple : sha-a1b2c3dAPP_PATH="${2}" # Exemple : nginx-demo/deployment.yamlCONFIG_REPO_URL="${3}" # URL SSH du config-repo
# Cloner le config-repogit clone "${CONFIG_REPO_URL}" config-repocd config-repo
# Mettre à jour le tag avec yq (ou sed)yq -i ".spec.template.spec.containers[0].image = \"nginx:${IMAGE_TAG}\"" \ "${APP_PATH}"
# Pousser le commitgit config user.email "ci-bot@example.com"git config user.name "CI Bot"git add "${APP_PATH}"git commit -m "chore(deploy): update nginx to ${IMAGE_TAG}"git push origin mainyq est l’outil recommandé pour manipuler du YAML en ligne de commande. Si
vous ne pouvez pas l’installer, sed fonctionne aussi pour les cas simples :
sed -i "s|image: nginx:.*|image: nginx:${IMAGE_TAG}|" "${APP_PATH}"Credentials Git dans la CI
Section intitulée « Credentials Git dans la CI »Le pipeline CI doit s’authentifier sur le config-repo. Deux méthodes :
| Méthode | Avantage | Inconvénient |
|---|---|---|
| Deploy key SSH | Accès limité à un seul dépôt | Gestion des clés dans CI |
| Personal Access Token | Simple à créer | Droit d’accès plus large |
Stockez ces credentials dans les secrets de votre CI (GitHub Secrets, GitLab CI/CD Variables), jamais dans le code.
Exemples de pipelines
Section intitulée « Exemples de pipelines »name: Build and Deploy
on: push: branches: [main]
permissions: contents: read # Lecture du code source uniquement
jobs: build: runs-on: ubuntu-latest permissions: contents: read packages: write # Pour pousser l'image sur GHCR outputs: image_tag: ${{ steps.meta.outputs.version }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false
- name: Docker meta id: meta uses: docker/metadata-action@902fa8ec7d6ecacf9f59e2d269c9b7f7e1c6a7e4 # v5.7.0 with: images: ghcr.io/${{ github.repository }} tags: | type=sha,prefix=sha-
- name: Login to GHCR uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 # v6.16.0 with: push: true tags: ${{ steps.meta.outputs.tags }}
update-config-repo: needs: build runs-on: ubuntu-latest permissions: contents: read steps: - name: Setup SSH key run: | mkdir -p ~/.ssh echo "${{ secrets.CONFIG_REPO_DEPLOY_KEY }}" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 ssh-keyscan github.com >> ~/.ssh/known_hosts
- name: Install yq run: | YQ_VERSION="v4.44.3" curl -sSLo /usr/local/bin/yq \ "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" chmod +x /usr/local/bin/yq
- name: Update image tag in config-repo env: IMAGE_TAG: ${{ needs.build.outputs.image_tag }} run: | git clone git@github.com:votre-org/config-repo.git cd config-repo
yq -i ".spec.template.spec.containers[0].image = \ \"ghcr.io/votre-org/monapp:${IMAGE_TAG}\"" \ nginx-demo/deployment.yaml
git config user.email "gh-actions@example.com" git config user.name "GitHub Actions" git add nginx-demo/deployment.yaml git diff --staged --quiet || \ git commit -m "chore(deploy): update monapp to ${IMAGE_TAG}" git push origin mainstages: - build - update-config
variables: REGISTRY: "${CI_REGISTRY}" IMAGE: "${CI_REGISTRY_IMAGE}"
build: stage: build image: docker:27 services: - docker:27-dind before_script: - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY" script: - | IMAGE_TAG="sha-${CI_COMMIT_SHORT_SHA}" docker build -t "${IMAGE}:${IMAGE_TAG}" . docker push "${IMAGE}:${IMAGE_TAG}" echo "IMAGE_TAG=${IMAGE_TAG}" >> build.env artifacts: reports: dotenv: build.env
update-config-repo: stage: update-config image: alpine:3.21 dependencies: - build before_script: - apk add --no-cache git openssh-client curl - | YQ_VERSION="v4.44.3" curl -sSLo /usr/local/bin/yq \ "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" chmod +x /usr/local/bin/yq - | mkdir -p ~/.ssh echo "$CONFIG_REPO_DEPLOY_KEY" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 ssh-keyscan gitlab.com >> ~/.ssh/known_hosts script: - git clone git@gitlab.com:votre-org/config-repo.git - cd config-repo - | yq -i ".spec.template.spec.containers[0].image = \ \"${IMAGE}:${IMAGE_TAG}\"" nginx-demo/deployment.yaml - git config user.email "gitlab-ci@example.com" - git config user.name "GitLab CI" - git add nginx-demo/deployment.yaml - git diff --staged --quiet || \ git commit -m "chore(deploy): update monapp to ${IMAGE_TAG}" - git push origin main rules: - if: '$CI_COMMIT_BRANCH == "main"'Attendre la synchronisation depuis la CI
Section intitulée « Attendre la synchronisation depuis la CI »Une fois le commit poussé sur le config-repo, vous pouvez vouloir attendre qu’ArgoCD finisse de déployer avant de passer à la suite (tests d’intégration par exemple). Pour cela, exposez l’API ArgoCD et utilisez le CLI :
# Attendre la synchronisation et un état Healthyargocd app wait nginx-demo \ --health \ --sync \ --timeout 120 \ --server argocd.example.com \ --auth-token "$ARGOCD_TOKEN"Créez un token ArgoCD dédié pour la CI (voir le guide
Sécuriser ArgoCD) avec uniquement les
droits get et sync sur les Applications concernées.
ArgoCD Image Updater : automatiser sans scripts
Section intitulée « ArgoCD Image Updater : automatiser sans scripts »Si vous souhaitez aller plus loin et éliminer le script de mise à jour, l’outil ArgoCD Image Updater surveille votre registre de conteneurs et met à jour Git à votre place dès qu’une nouvelle image est publiée.
Installation
Section intitulée « Installation »kubectl apply -n argocd \ -f https://raw.githubusercontent.com/argoproj-labs/argocd-image-updater/stable/manifests/install.yamlConfiguration sur une Application
Section intitulée « Configuration sur une Application »Ajoutez des annotations à votre objet Application :
apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata: name: nginx-demo namespace: argocd annotations: argocd-image-updater.argoproj.io/image-list: nginx=nginx argocd-image-updater.argoproj.io/nginx.update-strategy: semver argocd-image-updater.argoproj.io/nginx.allow-tags: regexp:^1\.27 argocd-image-updater.argoproj.io/write-back-method: git argocd-image-updater.argoproj.io/git-branch: mainAvec cette configuration, dès qu’une image nginx:1.27.* plus récente est
publiée sur Docker Hub, Image Updater met à jour le fichier de manifestes dans
Git. ArgoCD détecte le commit et déploie.
À retenir
Section intitulée « À retenir »- La CI ne fait jamais
kubectl applynihelm upgrade. Elle pousse un commit dans le config-repo. ArgoCD doit déployer. - Séparez app-repo (code applicatif) et config-repo (manifestes de déploiement) pour des droits d’accès distincts.
- Stockez les credentials Git dans les secrets CI, jamais dans le code.
- Utilisez
yqpour modifier proprement les YAML plutôt quesed. - La commande
argocd app waitpermet d’attendre la fin du déploiement dans un pipeline. - ArgoCD Image Updater automatise la détection de nouvelles images et la mise à jour de Git.
- Épinglez toujours les GitHub Actions par SHA de commit.