Version : 1.0 · Date : 2026-10-23 · Parcours SUPPORT-IA (support N2/N3 de chatbots sur Azure)
En une phrase : le pipeline Azure DevOps construit l'image et la pousse dans un ACR Premium privé, puis écrit le nouveau tag dans le dépôt GitOps ; Argo CD, dans le cluster, voit ce commit et synchronise ; « rien ne change » veut donc toujours dire qu'un des huit maillons de cette chaîne est cassé, et
Syncedne veut pas dire que l'application marche.

Le choix « push ou pull », Flux comparé à Argo CD et l'organisation de l'IaC sont traités côté architecture dans
AKS/05-exploitation-gitops ; la chaîne de confiance des images (signature, admission) dans
AKS/03-identite-securite. Ici, on suit un changement de bout en bout et on apprend à trouver le
maillon qui casse. Les objets Kubernetes et les commandes kubectl sont dans 04-aks-pour-le-support.
Ce que tu dois savoir expliquer en entretien#
| Question type | Section |
|---|---|
| « Décris votre chaîne de déploiement, du commit au pod » | 1 |
| « Comment le pipeline s'authentifie-t-il à ACR ? Pourquoi un agent self-hosted ? » | 2 |
| « Pourquoi ACR Premium ? Que change le Private Endpoint ? » | 3 |
| « Synced, OutOfSync, Healthy, Degraded : que veulent-ils dire ? Self-heal, prune ? » | 5 |
| « J'ai poussé dans Git et rien ne change » / « le déploiement ne crée pas de nouveaux pods » | 6, 7 |
1. La chaîne en huit maillons#
| # | Maillon | Preuve que c'est passé | Où regarder |
|---|---|---|---|
| ① | Merge sur la branche suivie par la CI | Commit sur main du dépôt applicatif |
Azure Repos, historique |
| ② | Run du pipeline sur un agent | Run démarré, job attribué à un agent | Azure Pipelines : run, logs du job, pool d'agents |
| ③ | Build et push de l'image dans ACR | Tag chat-api:4812 présent dans le registre |
Logs de Docker@2, az acr repository show-tags |
| ④ | Écriture du tag dans le dépôt GitOps | Commit « chat-api 4812 » dans le bon fichier, la bonne branche | Historique du dépôt GitOps |
| ⑤ | Argo CD détecte le commit | Révision de l'Application = ce commit | argocd app get, interface Argo CD |
| ⑥ | Sync appliquée | Sync status Synced, opération Succeeded |
argocd app get, argocd app history |
| ⑦ | Rollout Kubernetes | Nouveau ReplicaSet, pods recréés | kubectl rollout status, kubectl get rs |
| ⑧ | Pull de l'image par le kubelet | Pods Running, Ready, image au bon tag |
kubectl describe pod, Events |
Phrase d'entretien : « Le pipeline ne parle jamais au cluster. Le seul contrat entre CI et CD, c'est le tag écrit dans le dépôt GitOps. Quand rien ne change, je remonte la chaîne : l'image existe-t-elle dans ACR ? le tag est-il dans Git ? Argo CD a-t-il vu ce commit ? la sync a-t-elle réussi ? le rollout a-t-il créé des pods ? »
2. Le pipeline Azure DevOps#
2.1 Les service connections#
| Service connection | Utilisée par | Authentification recommandée | Droits côté Azure |
|---|---|---|---|
| Docker Registry → type Azure Container Registry | Docker@2 (login, buildAndPush) |
Workload Identity federation (aucun secret) ; service principal possible | AcrPush sur le registre |
| Azure Resource Manager | AzureCLI@2, AzureWebAppContainer@1, kubelogin -l azurepipelines |
Workload Identity federation, avec app registration ou managed identity | Selon les commandes (ex. az acr build → droits sur le registre) |
Docker@2 n'accepte que des service connections Docker Registry, pas Azure Resource Manager.
Erreurs d'authentification fréquentes d'une service connection en Workload Identity federation :
| Message | Cause | Correction |
|---|---|---|
AADSTS70021 … Assertion Issuer … Subject: sc://<org>/<projet>/<connexion> |
Issuer ou subject du federated credential ne correspond plus (organisation, projet ou connexion renommés) | Comparer avec l'Issuer et le Subject identifier affichés dans la service connection, corriger le credential |
AADSTS700211 / AADSTS700213 |
Issuer / subject inconnus | Idem |
AADSTS700016: Application … wasn't found |
Identité supprimée ou mauvais client ID | Recréer ou corriger l'identité |
AADSTS7000215: Invalid client secret |
Ancienne connexion à secret expirée | Convertir en Workload Identity federation |
AADSTS700223 |
Workload identity federation restreinte dans le tenant | Utiliser une managed identity pour la fédération |
2.2 Un pipeline type : build, push, mise à jour du dépôt GitOps#
# azure-pipelines.yml du dépôt applicatif chat-api
trigger:
branches: { include: [ main ] }
variables:
imageRepository: chat-api
acrServiceConnection: sc-acr-chat-prd # Docker Registry, Workload Identity federation
# Tag unique : numéro de build (traçable vers le run) ; le SHA court est ajouté en second tag
imageTag: $(Build.BuildId)
pool: pool-mdp-chat # Managed DevOps Pools ou agents self-hosted DANS le VNet (ACR privé)
stages:
- stage: Build
displayName: Build et push de l'image
jobs:
- job: docker
displayName: Docker build and push
steps:
- task: Docker@2
displayName: Build et push vers ACR
inputs:
command: buildAndPush
containerRegistry: $(acrServiceConnection)
repository: $(imageRepository)
Dockerfile: src/Dockerfile
tags: |
$(imageTag)
sha-$(Build.SourceVersion)
- stage: UpdateGitOps
displayName: Écriture du tag dans le dépôt GitOps
dependsOn: Build
jobs:
- job: bump
displayName: Mise à jour de values-prd.yaml
steps:
- checkout: gitops # dépôt déclaré dans resources.repositories
persistCredentials: true # le script peut pousser avec le jeton du pipeline
- script: |
set -euo pipefail
sed -i "s/^\(\s*tag:\s*\).*/\1\"$(imageTag)\"/" apps/chat-api/values-prd.yaml
git config user.email "[email protected]"
git config user.name "Azure Pipelines"
git commit -am "chat-api $(imageTag) [skip ci]"
git push origin HEAD:main
displayName: Commit du nouveau tag
resources:
repositories:
- repository: gitops
type: git
name: Chatbot/gitops-chatbot # projet/dépôt Azure Repos
Points à connaître :
Docker@2a pour tag par défaut$(Build.BuildId); avecbuildAndPush, l'inputargumentsest ignoré (séparerbuildetpushpour passer des--build-arg). La tâche ajoute des labels de traçabilité (com.azure.dev.image.build.sourceversion…).- Pour pousser dans un dépôt Azure Repos, l'identité
<projet> Build Service (<organisation>)doit avoir Contribute sur le dépôt GitOps (par défaut elle ne peut que lire), et lecheckoutdoit avoirpersistCredentials: true. Sinon : refus de permission augit push. [skip ci]dans le message de commit évite de déclencher la CI du dépôt GitOps.KubernetesManifest@1ouHelmDeploy@1déploieraient directement dans le cluster (modèle push) : à éviter quand Argo CD pilote le cluster, sinon deux sources de vérité se battent.
2.3 Stratégie de tag#
| Tag | Avantage | Risque en production | Verdict |
|---|---|---|---|
latest (ou tout tag réutilisé) |
Simple | Template de pod inchangé → aucun rollout ; nodes avec des versions différentes ; retour arrière impossible | À proscrire pour déployer |
$(Build.BuildId) |
Unique, relie l'image au run et à ses logs | Peu lisible | Recommandé |
| SHA du commit | Relie au code | Même SHA si on reconstruit pour une nouvelle image de base | Bon en second tag |
Version sémantique 1.4.2 |
Lisible | Doit rester unique (jamais réécrite) | Bon pour les releases |
Digest @sha256:… |
Immuable par construction | Illisible | Pour verrouiller |
Microsoft recommande les tags uniques pour les déploiements (et les tags stables uniquement pour les images de base), ainsi que
le verrouillage des tags déployés (az acr repository update --write-enabled false).
2.4 Où tourne le build quand ACR est privé#
| Option | Fonctionne avec ACR sans accès public ? | À savoir |
|---|---|---|
| Microsoft-hosted agent | Non (il sort par Internet) | Utile seulement si le registre accepte des IP publiques |
| Agent self-hosted (VM, VMSS) dans le VNet | Oui | À maintenir (OS, Docker, mises à jour) |
| Managed DevOps Pools avec injection dans un subnet | Oui | Subnet délégué à Microsoft.DevOpsInfrastructure/pools, même région que le pool ; ne pas utiliser la plage 172.17.0.0/16 |
az acr build (ACR Tasks) |
Seulement avec un dedicated agent pool dans le VNet (préversion, régions limitées) ou des règles réseau explicites | Depuis le 1er juin 2025, les tasks en managed identity système ne contournent plus le pare-feu sans networkRuleBypassAllowedForTasks |
3. ACR Premium privé#

3.1 Pourquoi Premium#
| Besoin | Disponible en | Commentaire |
|---|---|---|
| Private Link (private endpoints) | Premium | La raison principale dans un SI fermé |
| Geo-replication | Premium | Pull dans la région, résilience régionale ; chaque réplica ajoute ses enregistrements DNS |
| Customer-managed keys | Premium | À configurer à la création du registre |
| Retention policy des manifestes non tagués | Premium, préversion | 0 à 365 jours (7 par défaut) ; ne couvre pas les manifestes OCI |
| Dedicated agent pool pour ACR Tasks | Premium, préversion | Seule façon simple de faire az acr build sur un registre fermé |
| Débit et opérations concurrentes | Plus élevés en Premium | Utile quand des dizaines de nodes tirent en même temps |
| Zone redundancy | Tous les tiers désormais, dans les régions qui ont des zones | La propriété zoneRedundancy affichée peut être trompeuse (« Disabled ») |
| Docker Content Trust | Premium, en retrait | Impossible à activer sur un nouveau registre depuis le 31/05/2026, suppression le 31/03/2028 : utiliser Notation |
3.2 Le Private Endpoint et son DNS#
Un Private Endpoint ACR porte deux noms à résoudre en IP privée : le registre (acrchatprd.azurecr.io) et le data endpoint
régional (acrchatprd.<région>.data.azurecr.io), d'où viennent les couches. Zone privée : privatelink.azurecr.io, liée à chaque
VNet client (agents, AKS, intégration de la Web App). Oublier le data endpoint donne une authentification qui réussit puis des
couches qui échouent.
# Lecture seule : état du registre, réseau, contenu
az acr show -n acrchatprd --query "{sku:sku.name, public:publicNetworkAccess, bypass:networkRuleBypassOptions}" -o table
az acr private-endpoint-connection list -r acrchatprd -o table
az acr repository show-tags -n acrchatprd --repository chat-api --orderby time_desc --top 5 -o table
az acr manifest list-metadata -r acrchatprd -n chat-api -o table
az acr check-health -n acrchatprd --ignore-errors --yes # depuis une machine du VNet
nslookup acrchatprd.azurecr.io # doit répondre une IP privée
Piège : une fois l'accès public coupé,
az acr buildet les ACR Tasks échouent (denied: client with IP … is not allowed accessou 403) tant qu'on n'utilise pas un dedicated agent pool ou une règle explicite. Ce n'est pas une panne du registre.
4. Comment le nouveau tag arrive dans le cluster#
| Méthode | Principe | Pour | Contre |
|---|---|---|---|
| Le pipeline écrit le tag dans le dépôt GitOps (section 2.2) | Commit sur values-prd.yaml ou kustomization.yaml |
Traçable, revue possible par pull request, retour arrière par git revert |
Une étape de plus dans le pipeline |
| Argo CD Image Updater | Surveille le registre et met à jour les Applications (paramètres Argo CD ou write-back Git) selon une stratégie (semver, newest-build, digest…) |
Automatique | Projet qui se déclare « en développement actif » ; seulement Helm, Kustomize ou plugin ; secrets de registre dans le cluster |
kubectl set image ou HelmDeploy@1 depuis le pipeline |
Push direct | Rapide | Contourne Git : Argo CD le défait (selfHeal) ou affiche OutOfSync |
Deux formats fréquents dans le dépôt GitOps :
# Helm : apps/chat-api/values-prd.yaml
image:
repository: acrchatprd.azurecr.io/chat-api
tag: "4812"
---
# Kustomize : apps/chat-api/overlays/prd/kustomization.yaml
images:
- name: chat-api
newName: acrchatprd.azurecr.io/chat-api
newTag: "4812"
5. Argo CD pour le support#
5.1 Le vocabulaire#
| Terme | Sens | Ce que le support en tire |
|---|---|---|
| Application (CRD) | Une source Git (repo, révision, chemin) + une destination (cluster, namespace) + une politique de sync | L'objet à inspecter en premier |
| AppProject | Garde-fous : dépôts sources, destinations et types de ressources autorisés ; default est permissif |
Une Application qui ne se synchronise pas peut violer son projet |
| Target state / Live state | Ce que dit Git / ce qui tourne | La différence = le diff |
| Refresh | Comparer Git et le cluster (sans rien appliquer) | --refresh, ou hard refresh qui vide aussi le cache des manifestes |
| Sync | Appliquer la cible au cluster | Peut être automatique ou manuelle |
| Sync status | Synced ou OutOfSync |
« Git est-il appliqué ? » |
| Health status | Healthy, Progressing, Degraded, Suspended, Missing, Unknown |
« Est-ce que ça marche ? » |
5.2 Une Application commentée#
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: chat-api-prd
namespace: argocd # namespace d'installation de l'extension (argocd par défaut)
spec:
project: chatbot # AppProject : sources et destinations autorisées
source:
repoURL: https://dev.azure.com/contoso/Chatbot/_git/gitops-chatbot
targetRevision: main # branche suivie : un commit sur une autre branche ne sera jamais vu
path: apps/chat-api # chemin : un fichier modifié ailleurs ne sera jamais vu
helm:
valueFiles: [ values-prd.yaml ] # le fichier que le pipeline modifie
destination:
server: https://kubernetes.default.svc
namespace: chatbot
syncPolicy:
automated:
prune: true # supprime ce qui a disparu de Git (désactivé par défaut)
selfHeal: true # annule les modifications manuelles (désactivé par défaut)
syncOptions:
- CreateNamespace=true
retry:
limit: 5
backoff: { duration: 10s, factor: 2, maxDuration: 3m }
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers: [ /spec/replicas ] # le HPA gère les replicas : pas de faux OutOfSync
5.3 Lire ensemble sync status et health#

Règles de santé intégrées utiles :
- Deployment, ReplicaSet, StatefulSet, DaemonSet : sain quand la génération observée égale la génération voulue et que le nombre
de replicas à jour égale le nombre voulu (un rollout bloqué reste
ProgressingpuisDegraded). - Service de type
LoadBalancer: sain quandstatus.loadBalancer.ingresscontient une IP ou un nom. - Ingress : même règle. Un controller qui ne publie pas d'adresse laisse l'Ingress en
Progressingpour toujours. - PersistentVolumeClaim : sain quand il est
Bound. - La santé de l'Application est la pire de ses ressources directes (ordre : Healthy, Suspended, Progressing, Missing, Degraded, Unknown).
5.4 La politique de sync, précisément#
| Réglage | Comportement documenté |
|---|---|
| Détection des changements | Interrogation de Git toutes les 3 minutes par défaut (timeout.reconciliation 120 s + jusqu'à 60 s de jitter) ; un webhook (Azure DevOps est pris en charge) rend la détection immédiate |
automated |
Synchronise seulement si l'Application est OutOfSync ; une seule tentative par commit (SHA + paramètres) ; ne retente pas un commit dont la sync a échoué, sauf retry configuré |
prune |
Désactivé par défaut : une ressource retirée de Git reste dans le cluster (OutOfSync) |
selfHeal |
Désactivé par défaut : une modification manuelle du cluster ne déclenche pas de sync ; activé, la sync est relancée après 5 s par défaut |
allowEmpty |
Protection : sans lui, une sync automatique avec prune refuse de tout supprimer quand la source devient vide |
| Retour arrière | argocd app rollback est impossible quand la sync automatique est active : le retour arrière passe par Git |
5.5 Sync options, différences ignorées, ordre#
- Sync options utiles :
CreateNamespace=true(sinon la sync échoue si le namespace n'existe pas),ServerSideApply=true,PruneLast=true,ApplyOutOfSyncOnly=true; annotationargocd.argoproj.io/sync-options: Prune=falsepour protéger une ressource. ignoreDifferences: pour les champs gérés par un autre acteur (replicas du HPA, champs mutés par un webhook). Mal utilisé, il masque un vrai écart : un champ ignoré ne rendra jamais l'ApplicationOutOfSync.- Faux OutOfSync connus : quantités normalisées par Kubernetes (
1000mcontre1), outils qui posent aussi le labelapp.kubernetes.io/instance(KustomizecommonLabels). - Sync waves : annotation
argocd.argoproj.io/sync-wave(entier, 0 par défaut, négatif autorisé), du plus petit au plus grand, avec 2 s entre deux vagues. - Hooks :
PreSync(ex. migration de schéma),Sync,PostSync(tests de fumée),SyncFail(nettoyage) ; un hook en échec fait échouer la sync ;hook-delete-policydécide quand le Job est supprimé. Les hooks ne tournent pas en sync sélective. - App of apps / ApplicationSet : une Application qui crée d'autres Applications ; réservé aux administrateurs (on peut y choisir n'importe quel projet).
5.6 Argo CD sur AKS : l'extension Microsoft#
Argo CD (extension Microsoft.ArgoCD) |
Flux v2 (extension microsoft.flux) |
|
|---|---|---|
| Statut | Disponible en général pour AKS, préversion pour Azure Arc ; l'activation depuis le portail (GitOps > Enable Argo CD) est marquée Preview | Disponible en général |
| Objets | Application, ApplicationSet, interface web, CLI argocd |
GitRepository, Kustomization, HelmRelease ; ressource Azure fluxConfigurations |
| Identité | Workload Identity (lecture ACR, Azure DevOps sans secret) et SSO Microsoft Entra ID | Workload identity pour la source |
| Points d'attention | Mode haute disponibilité par défaut : au moins 4 nodes (redis-ha.enabled=false sinon) ; depuis 1.0.0-preview, chart communautaire avec des clés de configuration différentes |
Multi-tenancy appliquée par défaut |
az k8s-extension show --resource-group rg-chat-prd --cluster-name aks-chat-prd --cluster-type managedClusters \
--name argocd --query provisioningState --output tsv
kubectl get pods -n argocd # application-controller, repo-server, server, redis
kubectl get applications -n argocd # SYNC STATUS et HEALTH STATUS de chaque Application
kubectl get events -n argocd --sort-by=.lastTimestamp
5.7 Boîte à outils Argo CD (lecture d'abord)#
| Besoin | Commande |
|---|---|
| État, révision, ressources, dernière opération | argocd app get chat-api-prd |
| Forcer la relecture de Git / vider le cache | argocd app get chat-api-prd --refresh / --hard-refresh |
| Ce qui diffère entre Git et le cluster | argocd app diff chat-api-prd |
| Historique des syncs (révision Git de chacune) | argocd app history chat-api-prd |
| Manifestes rendus (ce qu'Argo CD appliquerait) | argocd app manifests chat-api-prd |
| Sans CLI Argo CD | kubectl get application chat-api-prd -n argocd -o yaml (bloc status) |
| Erreur de génération des manifestes | Journaux du argocd-repo-server (Helm, Kustomize) |
| Erreur d'application, sync bloquée | Journaux de argocd-application-controller |
| Action (après accord) : synchroniser | argocd app sync chat-api-prd (--dry-run pour prévisualiser) |
Manifest generation error (cached) : l'erreur de rendu est mise en cache ; un hard refresh suffit si elle était passagère, sinon la
cause est dans les journaux du repo-server (values Helm invalides, chemin inexistant, chart introuvable).
6. J'ai poussé dans Git et rien ne change#
flowchart TD
A["Commit poussé, rien ne change"] --> B{"Le commit est-il sur la branche<br/>et le chemin suivis par l'Application ?"}
B -- Non --> B1["targetRevision, path, valueFiles, repoURL<br/>autre dépôt ou autre overlay"]
B -- Oui --> C{"argocd app get : la révision<br/>affichée est-elle ce commit ?"}
C -- Non --> C1["Attendre 3 min ou refresh<br/>webhook, accès au dépôt, repo-server"]
C -- Oui --> D{"Sync status ?"}
D -- OutOfSync --> D1{"Auto-sync activé ?"}
D1 -- Non --> D2["Sync manuelle attendue"]
D1 -- Oui --> D3["Opération en échec : message<br/>hook, admission, quota, CRD"]
D -- Synced --> E{"Le template de pod a-t-il changé ?"}
E -- Non --> E1["Même tag réutilisé ou champ ignoré<br/>ignoreDifferences"]
E -- Oui --> F{"Health ?"}
F -- Progressing --> F1["Rollout en cours ou bloqué<br/>kubectl rollout status"]
F -- Degraded --> F2["ImagePullBackOff, CrashLoopBackOff<br/>diagnostic du chapitre 04"]
F -- Healthy --> F3["Bonne version en place : vérifier le cache<br/>navigateur, l'Ingress, la bonne URL"]
| Symptôme | Cause | Vérifier | Corriger |
|---|---|---|---|
| Argo CD affiche une ancienne révision | Branche (targetRevision) ou chemin (path) différent de celui modifié |
argocd app get : Repo, Target, Path |
Modifier le bon fichier, ou corriger l'Application |
Le pipeline a modifié values-dev.yaml |
L'Application PRD lit values-prd.yaml |
spec.source.helm.valueFiles |
Corriger le pipeline |
| L'Application pointe vers un autre dépôt (fork, ancien dépôt) | repoURL |
argocd app get |
Corriger repoURL (via Git de l'app of apps) |
Révision à jour mais OutOfSync stable |
Auto-sync désactivé, ou sync automatique déjà tentée et échouée sur ce commit | spec.syncPolicy, argocd app history |
Sync manuelle après accord ; corriger la cause ; retry |
| Sync en échec | Manifeste invalide, refus d'admission (Azure Policy, PSA), quota, namespace absent, hook PreSync en échec |
Message de l'opération, Events | Corriger dans Git |
Synced + Healthy, mais ancienne image |
Tag réutilisé (latest) : rien n'a changé dans le template |
Image et digest dans describe pod |
Tag unique ; imagePullPolicy n'est pas la solution |
Synced + Degraded |
Git appliqué, application KO (ImagePullBackOff…) |
Chapitre 04 | Selon la panne |
| Modification manuelle dans le cluster « qui revient » | selfHeal actif : comportement voulu |
syncPolicy.automated.selfHeal |
Passer par Git |
| Modification manuelle « qui reste » | selfHeal inactif : dérive affichée OutOfSync |
argocd app diff |
Resynchroniser ou reporter dans Git |
| Ressource supprimée de Git toujours présente | prune désactivé |
argocd app get (ressource OutOfSync, à supprimer) |
Prune manuel après accord, ou activer prune |
| Un champ modifié dans Git n'apparaît jamais | Champ dans ignoreDifferences |
spec.ignoreDifferences |
Restreindre l'ignore |
Manifest generation error (cached) |
Erreur Helm ou Kustomize | Hard refresh, journaux du repo-server | Corriger les values ou le chemin |
Ingress Progressing sans fin |
Le controller ne remplit pas status.loadBalancer.ingress |
kubectl get ingress (ADDRESS vide) |
Controller, classe, Service du controller |
| Argo CD ne lit plus le dépôt | Identifiants ou Workload Identity expirés, réseau | Journaux du repo-server, kubectl get secret -n argocd -l argocd.argoproj.io/secret-type=repository |
Rétablir l'accès |
7. Le déploiement ne crée pas de nouveaux pods#
| Symptôme | Cause | Vérifier | Corriger |
|---|---|---|---|
| Aucun nouveau ReplicaSet | Template de pod identique (même tag, changement hors du template) | kubectl rollout history, image du Deployment |
Tag unique, changement dans le template |
| Rollout en pause | spec.paused: true |
kubectl get deploy -o yaml |
kubectl rollout resume (après accord) ou corriger Git |
Nouveau ReplicaSet à 0 pod, ReplicaFailure |
ResourceQuota, LimitRange, refus d'admission | kubectl describe rs <nouveau-rs> (Events FailedCreate) |
Quota, policy, valeurs |
Nouveaux pods Pending |
Capacité, taints, affinité | kubectl describe pod |
Capacité ou placement |
| Nouveaux pods jamais Ready, anciens toujours là | Readiness en échec : le rollout attend (maxUnavailable) |
kubectl rollout status, Events |
Corriger l'application ou la probe |
ProgressDeadlineExceeded |
Rollout bloqué au-delà de 600 s | kubectl describe deploy (Conditions) |
Cause ci-dessus, puis nouveau commit |
replicas revient toujours à la même valeur |
HPA, ou Argo CD qui réapplique la valeur de Git | kubectl get hpa, ignoreDifferences |
Laisser le HPA décider, ignorer /spec/replicas |
| Pipeline vert mais pas de commit GitOps | Push refusé (droits Build Service), étape conditionnelle sautée | Logs de l'étape git push |
Droit Contribute, persistCredentials |
8. Lab : casser et réparer une Application Argo CD (60 minutes)#
Sur un cluster de test avec l'extension Argo CD (quickstart Microsoft : redis-ha.enabled=false pour un petit cluster), et un
dépôt Git de test contenant un Deployment nginx.
az k8s-extension create --resource-group rg-lab --cluster-name aks-lab --cluster-type managedClusters \
--name argocd --extension-type Microsoft.ArgoCD \
--config "redis-ha.enabled=false" --config "configs.params.application\.namespaces=argocd"
kubectl get pods -n argocd
- Créer une Application sans
automated; modifier le tag dans Git : observerOutOfSync,argocd app diff, puis synchroniser. - Activer
automatedsansselfHeal; fairekubectl scale deploy nginx --replicas=5: l'Application passeOutOfSyncet reste ainsi. ActiverselfHeal: la valeur de Git revient. - Mettre un tag inexistant dans Git :
Synced+Degraded; lireImagePullBackOffdans les Events. - Modifier un fichier hors du
pathsuivi : rien ne se passe ; le prouver avecargocd app get. - Réutiliser le même tag pour une nouvelle image : aucun rollout ; le prouver avec
kubectl rollout history. - Nettoyage : supprimer le resource group de test.
9. Questions d'entretien#
1. Décris la chaîne, du commit au pod.
Merge sur
main, le pipeline Azure DevOps construit l'image avecDocker@2sur un agent dans le VNet et la pousse dans l'ACR Premium privé avec un tag unique (Build.BuildId), puis commite ce tag dans le dépôt GitOps. Argo CD voit le commit (3 minutes ou webhook), synchronise, le Deployment fait son rollout et le kubelet tire l'image par le Private Endpoint.
2. Pourquoi ACR Premium ?
Pour le Private Link (le registre n'est joignable que depuis le réseau), la geo-replication, les customer-managed keys, les dedicated agent pools pour ACR Tasks, et plus de débit. La zone redundancy, elle, est désormais disponible sur tous les tiers.
3. Le registre est privé : qu'est-ce que ça change pour la CI ?
Les Microsoft-hosted agents ne l'atteignent plus : agents self-hosted ou Managed DevOps Pools injectés dans le VNet, avec la zone
privatelink.azurecr.io(registre et data endpoint).az acr buildne marche plus sans dedicated agent pool ou règle explicite.
4. Synced et Healthy, quelle différence ?
Synced dit que le cluster correspond à Git ; Healthy dit que les ressources fonctionnent. Synced + Degraded est fréquent : Git est bien appliqué mais le pod est en ImagePullBackOff ou CrashLoopBackOff, c'est un diagnostic Kubernetes.
5. Self-heal et prune ?
Les deux sont désactivés par défaut. Prune supprime du cluster ce qui a disparu de Git ; self-heal réapplique Git quand quelqu'un modifie le cluster à la main. Sans self-heal, la dérive est seulement affichée OutOfSync.
6. J'ai poussé dans Git et rien ne change. Ta démarche ?
Je vérifie que le commit est sur la branche, le chemin et le fichier de values suivis par l'Application ; que la révision affichée par Argo CD est ce commit (sinon refresh, accès au dépôt) ; l'état de la sync (auto-sync, échec, message) ; que le template de pod a réellement changé (tag réutilisé ?) ; puis la santé et le rollout.
7. Pourquoi ne jamais déployer latest ?
Le template de pod ne change pas, donc aucun rollout ; les nodes peuvent tirer des versions différentes ; on ne sait pas ce qui tourne et on ne peut pas revenir en arrière. Microsoft recommande des tags uniques et le verrouillage des tags déployés.
8. Comment fais-tu un retour arrière ?
Par
git revertdu commit de tag : Argo CD resynchronise.argocd app rollbackest refusé quand l'auto-sync est actif, et unkubectl rollout undoserait annulé par self-heal. En urgence absolue, désactiver l'auto-sync puis rollback, en le signalant.
9. Pourquoi une Application peut-elle rester OutOfSync juste après une sync réussie ?
Un acteur modifie un champ après Argo CD (HPA sur
replicas, webhook de mutation), ou une normalisation (1000mcontre1), ou un conflit sur le labelapp.kubernetes.io/instance. On le traite avecignoreDifferencesciblé, pas en ignorant tout.
10. Flux ou Argo CD sur AKS ?
Les deux existent en cluster extension. Argo CD est disponible en général sur AKS (préversion sur Arc), avec interface web, ApplicationSet, Workload Identity et SSO Entra ID, et demande 4 nodes en mode HA. Flux est plus léger et piloté par la ressource Azure
fluxConfigurations. Ici le client a choisi Argo CD : je dois savoir le lire et le diagnostiquer.
11. Le pipeline est vert mais Git n'a pas reçu le tag.
Regarder l'étape
git push: droit Contribute de l'identité « Build Service » du projet sur le dépôt GitOps,persistCredentialssur le checkout, branche protégée par une policy (il faut alors une pull request).
10. Fiche à retenir#
- Huit maillons : merge → run → push ACR → commit GitOps → détection Argo CD → sync → rollout → pull.
-
Docker@2: service connection Docker Registry (Workload Identity federation), tag par défaut$(Build.BuildId),argumentsignoré enbuildAndPush. - Pousser dans Git depuis un pipeline : Build Service avec Contribute,
persistCredentials: true,[skip ci]. - Tags uniques pour déployer, verrouiller les tags déployés ; jamais
latest. - ACR Premium : Private Link, geo-replication, CMK, retention (préversion), agent pool (préversion) ; zone redundancy tous tiers ; DCT en retrait (Notation).
- Private Endpoint ACR : registre et data endpoint dans
privatelink.azurecr.io; agents dans le VNet ;az acr buildbloqué sans agent pool. - Argo CD : Application (source + destination + syncPolicy), AppProject, refresh / hard refresh, sync.
- Sync status (Synced / OutOfSync) ≠ health (Healthy, Progressing, Degraded, Suspended, Missing, Unknown) ; santé de l'app = la pire.
- Détection toutes les 3 min (120 s + 60 s), webhook Azure DevOps possible ; auto-sync une fois par commit.
-
pruneetselfHealdésactivés par défaut ; rollback CLI impossible en auto-sync →git revert. - Sync waves (0 par défaut, 2 s entre vagues), hooks PreSync / Sync / PostSync / SyncFail.
- Extension
Microsoft.ArgoCD: GA sur AKS, préversion sur Arc et dans le portail, HA = 4 nodes.
Références#
Relevé du 23/10/2026.
Microsoft Learn :
- Build and publish Docker images to Azure Container Registry et Docker@2
- Troubleshoot an Azure Resource Manager workload identity service connection
- Run Git commands in pipeline scripts
- Configure Managed DevOps Pools networking
- Azure Container Registry SKU features and limits
- Connect privately to an Azure container registry using Azure Private Link
- Run an ACR task on a dedicated agent pool (préversion) et network bypass policy for tasks
- Retention policy for untagged manifests (préversion)
- Docker Content Trust (retrait)
- Zone redundancy in Azure Container Registry
- Recommendations for tagging and versioning container images
- GitOps with Argo CD (concepts), tutoriel, gérer l'extension et dépannage de l'extension
- GitOps (Flux v2) for AKS
Argo CD (argo-cd.readthedocs.io, version stable) :
- Core Concepts, Projects, Tracking strategies
- Automated Sync Policy, Sync Options, Sync phases and waves
- Resource Health, Diff customization
- Cluster bootstrapping (app of apps), Webhook configuration, FAQ
- argocd app rollback, argocd app get
- Argo CD Image Updater
Kubernetes : Deployments