Automation

Azure Container Registry : diagnostiquer une réplication d'image incomplète avant de relancer un déploiement multirégion

Un runbook de production pour distinguer réplication ACR, routage régional, digest, identité et réseau avant de relancer ou rollbacker un déploiement de conteneurs.

13 sept. 2026 azurecontainer-registryacrgeo-replicationakscontainersdevopsci-cdobservabilitykqlautomationrunbookrollbackproduction

Un pipeline pousse orders-api:2026.09.13.4 dans un Azure Container Registry géorépliqué, puis déploie le même tag sur deux clusters AKS. Le cluster proche du runner démarre. L’autre alterne entre manifest unknown, ImagePullBackOff et des pulls réussis après plusieurs minutes. Relancer le déploiement peut masquer l’incident, mais ne prouve ni que le même artefact est servi dans chaque région ni que la prochaine release sera sûre.

ACR réplique les images de façon asynchrone entre des réplicas actives. Un push terminé sur une région ne garantit donc pas qu’un pull immédiat, routé vers une autre région, observe déjà le tag. Le runbook doit séparer cinq causes : propagation encore en cours, tag réécrit, routage vers une réplica inattendue, refus d’identité ou de réseau, et dégradation régionale. La sortie attendue est une décision : attendre avec une borne, promouvoir un digest vérifié, exclure temporairement une réplica du routage global, corriger le chemin client ou rollbacker la release.

Figer l’artefact et la chronologie

Un tag est un pointeur mutable. Commencez par relever le digest produit par le pipeline, l’heure UTC de fin du push, les régions du runner et des clusters, ainsi qu’un pod sain et un pod en échec. Conservez l’erreur complète du kubelet : unauthorized, manifest unknown, blob unknown, 429 et timeout n’ouvrent pas la même branche.

yaml acr-multiregion-incident.yml
incident: inc-acr-20260913-021
registry: acrplatformprod
repository: orders-api
tag: 2026.09.13.4
expected_digest: sha256:<digest-from-build>
push_completed_utc: 2026-09-13T14:02:18Z

producer:
runtime: azure-devops-runner-weu
region: westeurope

consumers:
- cluster: aks-orders-weu
  region: westeurope
  result: success
- cluster: aks-orders-neu
  region: northeurope
  result: manifest-unknown

preserve:
- pipeline run and push output
- immutable digest from the build
- pod events and node identity
- registry replication status
- DNS answers from each runtime
- ACR login and repository events
- last known good deployment digest

Gelez toute réécriture du tag pendant le diagnostic. Si deux pipelines poussent successivement le même tag vers des réplicas différentes, les régions peuvent temporairement résoudre ce tag vers des digests distincts. Une relance utilisant encore le tag ajoute alors une nouvelle variable.

Vérifier l’état déclaré des réplicas

La géoréplication ACR nécessite le niveau Premium et fonctionne en actif-actif : chaque réplica disponible peut recevoir des lectures et des écritures. Le endpoint global choisit une région selon le profil réseau du client et l’état de santé du service. Ne supposez donc pas que le runner pousse dans la région d’origine du registre.

bash 01-acr-replication-state.sh
ACR="acrplatformprod"
RG="rg-platform-prod"

az acr show --name "$ACR" --resource-group "$RG" --query "{loginServer:loginServer,sku:sku.name,provisioningState:provisioningState,dataEndpointEnabled:dataEndpointEnabled}" --output yaml

az acr replication list --registry "$ACR" --query "[].{region:location,status:provisioningState,globalRouting:regionEndpointEnabled,zoneRedundancy:zoneRedundancy}" --output table

az acr check-health --name "$ACR" --ignore-errors --yes

Un provisioningState sain est nécessaire, mais il ne prouve pas que le digest attendu est déjà visible depuis chaque chemin. Consultez aussi Resource Health et les métriques ACR sur la fenêtre. Une réplica Online peut subir du throttling ; le failover piloté par la santé ne bascule pas simplement parce que des requêtes renvoient 429.

Prouver le digest, pas seulement le tag

Interrogez d’abord le manifeste depuis un contexte contrôlé, puis tirez l’image par digest. Le digest relie le build, le registre et le déploiement sans dépendre d’un tag mutable.

bash 02-verify-acr-digest.sh
ACR="acrplatformprod"
LOGIN_SERVER="$ACR.azurecr.io"
REPOSITORY="orders-api"
TAG="2026.09.13.4"
EXPECTED="sha256:<digest-from-build>"

az acr manifest show-metadata --registry "$ACR" --name "$REPOSITORY:$TAG" --query "{digest:digest,created:createdTime,lastUpdate:lastUpdateTime}" --output yaml

docker pull "$LOGIN_SERVER/$REPOSITORY@$EXPECTED"
docker image inspect "$LOGIN_SERVER/$REPOSITORY@$EXPECTED" --format '{{json .RepoDigests}}'

Exécutez le pull depuis un runner dans chaque région consommatrice ou depuis un pod de diagnostic soumis aux mêmes DNS, règles de sortie et identité que le workload. Un succès depuis un poste d’administration ne valide pas le chemin AKS. Un échec par tag suivi d’un succès par digest indique un problème de visibilité ou de mutation du tag ; un échec identique par digest oriente vers réplication, routage, réseau ou disponibilité.

Identifier la région réellement servie

Le endpoint global acrplatformprod.azurecr.io est routé. Comparez les réponses DNS depuis le runner de build et depuis chaque cluster. Une résolution différente est normale ; une résolution figée par un cache long peut empêcher un client de suivre une exclusion ou un failover.

bash 03-acr-runtime-path.sh
LOGIN_SERVER="acrplatformprod.azurecr.io"

getent ahosts "$LOGIN_SERVER"
dig "$LOGIN_SERVER" CNAME +short
dig "$LOGIN_SERVER" A +short

curl -sS -D- -o /dev/null "https://$LOGIN_SERVER/v2/"

kubectl -n orders describe pod <failing-pod>
kubectl -n orders get events --sort-by=.lastTimestamp --field-selector involvedObject.name=<failing-pod>

Un 401 sur /v2/ sans jeton prouve au moins DNS, TCP, TLS et réponse du registre ; ce n’est pas un échec d’authentification du workload. Si le registre utilise des endpoints de données dédiés, le firewall ou le proxy doit autoriser le endpoint de données de chaque région répliquée. Un Private Endpoint éventuel modifie le chemin DNS et réseau, mais il ne change ni le caractère asynchrone de la réplication ni la nécessité de valider le digest.

Lire les événements ACR avec la région

Les diagnostics ACR peuvent alimenter ContainerRegistryRepositoryEvents et ContainerRegistryLoginEvents. Utilisez-les pour comparer opérations, régions, identités et résultats sur la même fenêtre que le pipeline et les événements AKS.

kusto 04-acr-regional-evidence.kql
let StartTime = datetime(2026-09-13T13:50:00Z);
let EndTime = datetime(2026-09-13T14:30:00Z);
union isfuzzy=true ContainerRegistryRepositoryEvents, ContainerRegistryLoginEvents
| where TimeGenerated between (StartTime .. EndTime)
| where LoginServer startswith "acrplatformprod"
| where isempty(Repository) or Repository == "orders-api"
| project TimeGenerated,
        Region,
        OperationName,
        Repository,
        Tag,
        Digest,
        Identity,
        CallerIpAddress,
        ResultType,
        ResultDescription,
        DurationMs
| order by TimeGenerated asc

Adaptez les colonnes au schéma réellement présent dans le workspace. Cherchez une séquence, pas une ligne isolée : push terminé dans une région, lectures du manifeste dans une autre, erreurs transitoires, puis réussite. Si les événements montrent Unauthorized pour une seule identité, ne traitez pas cela comme un retard de réplication. Si les pulls réussissent mais prennent plus de temps et renvoient 429, mesurez la charge par réplica avant d’ajouter des retries concurrents.

Poser une barrière de promotion multirégion

Le pipeline ne devrait pas considérer docker push comme la fin d’une promotion mondiale. Il doit publier le digest, puis exécuter un canari dans chaque région cible avant d’ouvrir le rollout.

yaml acr-multiregion-promotion-gate.yml
artifact:
repository: orders-api
tag: 2026.09.13.4
digest: sha256:<immutable-digest>

regional_canaries:
- region: westeurope
  pull_by_digest: required
  start_container: required
- region: northeurope
  pull_by_digest: required
  start_container: required

promote_when:
- every region pulls the expected digest
- tag resolves to the same digest where tags remain in use
- no unauthorized, manifest-unknown or blob-unknown event remains
- pull latency and throttling stay inside the deployment budget

stop_when:
- a region observes another digest
- replication or Resource Health is degraded
- the data endpoint is unreachable from a target runtime
- retries increase load without changing the result

rollback:
deployment_digest: sha256:<last-known-good>
keep_failed_digest_for_investigation: true

Le canari doit démarrer le conteneur, pas seulement télécharger le manifeste. Cette étape valide aussi les couches, la décompression et les contraintes runtime. Déployez ensuite par digest dans les manifests Kubernetes ; gardez le tag comme repère humain, pas comme identité de release.

Décider attente, correction, exclusion ou rollback

Si toutes les réplicas sont saines et que le même digest devient visible dans la fenêtre acceptée, attendez avec backoff borné puis reprenez par région. Si le tag pointe vers plusieurs digests, stoppez les producteurs concurrents, choisissez l’artefact autorisé et republiez un tag unique sans lancer le rollout avant les canaris.

Si une seule réplica sert des erreurs et que Resource Health ou les logs la désignent, vous pouvez l’exclure temporairement du routage global avec az acr replication update --global-endpoint-routing false. Cette action ne supprime pas la réplica et n’arrête pas sa synchronisation. Elle change cependant le trafic des clients : vérifiez la capacité des régions restantes, les caches DNS et un plan de réactivation. Les endpoints régionaux, lorsqu’ils sont utilisés, ne bénéficient pas de ce reroutage automatique.

Corrigez l’identité ou le réseau lorsque le digest est présent mais que le runtime reçoit Unauthorized, un refus firewall ou un timeout. N’élargissez pas AcrPull, les plages IP ou l’accès public pour traiter un manifest unknown non qualifié.

Rollbackez le déploiement vers le dernier digest connu si la propagation dépasse le budget de changement, si l’artefact observé diffère du build approuvé ou si aucune région de secours n’a prouvé sa capacité. Un rollback de workload ne consiste pas à supprimer immédiatement le digest fautif : conservez-le avec les logs nécessaires à l’analyse.

Conclusion

Un pull ACR multirégion intermittent n’est pas résolu par une relance chanceuse. Il faut relier le digest du build, la région du push, la réplica servie, l’identité du nœud, les événements ACR et le résultat du canari.

La décision devient alors explicite : attendre une propagation bornée, corriger un tag mutable, réparer le chemin runtime, retirer temporairement une réplica du routage ou revenir au digest précédent. Le déploiement peut reprendre lorsque chaque région tire et démarre exactement l’artefact approuvé, et que le rollback reste immédiatement exécutable.