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. Il s'adresse aux développeurs et opérateurs qui ont déjà une CI en place et veulent la brancher proprement sur du GitOps. 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
- Déclencher une synchro instantanée avec un webhook Git
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@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- name: Docker meta id: meta uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 with: images: ghcr.io/${{ github.repository }} tags: | type=sha,prefix=sha-
- name: Login to GHCR uses: docker/login-action@06fb636fac595d6fb4b28a5dfcb21a6f5091859c # v4.5.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.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@sha256:aa3df78ecf320f5fafdce71c659f1629e96e9de0968305fe1de670e0ca9176ce 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@sha256:48b0309ca019d89d40f670aa1bc06e426dc0931948452e8491e3d65087abc07d 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 la 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 »ArgoCD Image Updater est un contrôleur qui se déploie dans le namespace
argocd, à côté d'ArgoCD. Une fois installé, il interroge périodiquement vos
registres et écrit lui-même le nouveau tag dans le config-repo. L'installation
se résume à appliquer le manifeste officiel :
kubectl apply -n argocd \ -f https://raw.githubusercontent.com/argoproj-labs/argocd-image-updater/stable/manifests/install.yamlAvant de configurer vos Applications, vérifiez que le pod du contrôleur tourne
bien : un kubectl get pods -n argocd doit faire apparaître le pod
argocd-image-updater à l'état Running.
Configuration 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.
Synchro instantanée avec un webhook Git
Section intitulée « Synchro instantanée avec un webhook Git »Par défaut, ArgoCD scrute Git toutes les 3 minutes : un git push peut donc
mettre jusqu'à trois minutes à se déployer. Pour une synchro instantanée, on
configure un webhook côté dépôt qui prévient ArgoCD à chaque push.
Le serveur ArgoCD expose un endpoint dédié, /api/webhook, qui accepte les
événements GitHub, GitLab, Bitbucket et Gitea :
# L'endpoint répond (400 sur un corps vide, ce qui prouve qu'il est actif)curl -sk -o /dev/null -w "%{http_code}\n" -X POST \ https://argocd.example.com/api/webhook -d '{}'# -> 400Côté GitHub, on ajoute un webhook dans Settings > Webhooks du dépôt :
- Payload URL :
https://argocd.example.com/api/webhook - Content type :
application/json - Secret : une valeur partagée, à renseigner aussi côté ArgoCD (clé
webhook.github.secretdu Secretargocd-secret) pour authentifier l'appel. - Events :
Just the push event.
Un git push déclenche alors la réconciliation en quelques secondes au lieu
d'attendre le prochain cycle de polling. Le polling reste actif en filet de
sécurité si un webhook est manqué.
À 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.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Sécuriser ArgoCD : RBAC, tokens CI et AppProjects pour contraindre les déploiements lancés depuis vos pipelines.