Automation

Azure Container Apps Jobs : diagnostiquer une règle KEDA avant de relancer les workers

Un runbook de production pour qualifier un Azure Container Apps Job qui ne consomme plus correctement avec règle KEDA, backlog, identité managée, secrets, logs, idempotence, validation et rollback avant de relancer les workers.

30 juil. 2026 azurecontainer-appsjobskedaautomationqueuemanaged-identityobservabilitykqlrunbookrollbackproduction

Un Azure Container Apps Job qui ne consomme plus une queue ressemble vite à un simple problème de workers. La tentation est de relancer le job, d’augmenter le parallélisme ou de purger les messages bloqués. C’est dangereux si le vrai problème vient de la règle KEDA, d’une identité qui ne lit plus la queue, d’un secret expiré, d’un profil de charge saturé, d’une image cassée ou d’un traitement non idempotent.

Le cas d’usage est un job invoice-worker-prod déclenché par backlog sur une queue. Depuis un déploiement, le backlog monte, quelques exécutions passent en erreur, et l’équipe veut relancer les workers pour rattraper le retard. Avant de rejouer, il faut décider si l’action sûre est une correction de règle KEDA, une restauration de secret, un rollback de l’image, une réduction du parallélisme, un replay borné ou une pause volontaire de la consommation.

Figurer le job comme un contrat de consommation

Commencez par écrire ce que le job doit consommer et ce qu’il ne doit pas casser. Une règle KEDA n’est pas seulement un seuil. Elle relie une source d’événements, une identité, des métadonnées, une limite de parallélisme et un code applicatif qui doit supporter les retries.

yaml container-apps-job-contract.yml
job:
name: invoice-worker-prod
environment: aca-env-prod
resource_group: rg-app-prod
trigger: event_driven
workload_profile: consumption-prod

source:
type: storage_queue
account: stprodops
queue: invoices-to-process
expected_backlog_behavior: decreases_during_business_hours
poison_queue: invoices-to-process-poison

scale_rule:
name: invoices-queue
min_executions: 0
max_executions: 20
polling_interval_seconds: 30
message_threshold: 25

runtime_contract:
idempotency_key: invoice_id
retry_safe_until: before_external_payment_capture
managed_identity: aca-invoice-worker-prod
secrets:
  - queue_connection_or_identity_binding
  - downstream_api_token_reference

blocked_until_proven:
- backlog_shape_known
- keda_rule_current
- identity_can_read_queue
- latest_image_healthy
- replay_scope_bounded
- rollback_path_ready

Ce contrat évite de traiter la queue comme un simple compteur. Si le traitement n’est pas idempotent après une étape externe, un replay large peut créer plus d’incident que le backlog initial.

Lire le backlog avant de relancer

Le premier diagnostic porte sur la forme du backlog. Une montée continue peut indiquer que KEDA ne déclenche plus. Des messages anciens et peu nombreux peuvent indiquer un poison message. Des vagues de retries peuvent venir d’un downstream en erreur. Une queue vide avec des alertes de retard peut simplement signaler une observabilité en décalage.

text backlog-reading.txt
Backlog a qualifier
Nombre de messages visibles
Age du plus ancien message
Nombre de messages dequeue plusieurs fois
Messages transferes en poison queue
Heure du dernier message consomme avec succes
Correlation avec le dernier deploiement

Interpretation possible
Backlog monte sans executions: regarder KEDA, identite, metadata de trigger
Executions nombreuses en echec: regarder image, code, secrets, downstream
Quelques messages repetes: isoler poison messages avant replay global
Backlog intermittent: verifier workload profile, quotas et cold start

La décision de relance dépend de cette lecture. On ne rejoue pas une queue entière quand le symptôme réel est concentré sur trois messages invalides ou sur une API aval indisponible.

Comparer la règle KEDA déclarée et effective

La configuration utile est celle que Container Apps voit maintenant. Vérifiez le job, son type de trigger, les scale rules, les secrets référencés et les limites d’exécution avant de modifier le code ou d’augmenter maxExecutions.

bash 01-read-job-scale-rule.sh
RG="rg-app-prod"
JOB="invoice-worker-prod"

az containerapp job show --resource-group "$RG" --name "$JOB" --query "{trigger:properties.configuration.triggerType, replicaTimeout:properties.configuration.replicaTimeout, replicaRetryLimit:properties.configuration.replicaRetryLimit, parallelism:properties.configuration.parallelism, replicaCompletionCount:properties.configuration.replicaCompletionCount, scaleRules:properties.configuration.eventTriggerConfig.scale.rules, registries:properties.configuration.registries}" --output json

az containerapp job execution list --resource-group "$RG" --name "$JOB" --output table

Cherchez les dérives simples : nom de queue modifié, seuil trop haut, secret renommé, identité retirée, limite d’exécutions trop basse, timeout trop court, ou profil de charge différent de celui attendu.

Séparer déclenchement et exécution

Un job peut ne pas démarrer parce que KEDA ne sait pas lire la source. Il peut aussi démarrer correctement puis échouer dans le conteneur. Ces deux états appellent des corrections différentes.

text trigger-vs-runtime.txt
Probleme de declenchement
Aucune execution alors que le backlog monte
Erreurs d'authentification sur la source d'evenements
Scale rule absente ou metadata incoherente
Secret ou identity binding introuvable

Probleme d'execution
Executions creees puis Failed
Logs applicatifs avec exception metier ou downstream
Pull image ou startup probe en echec
Timeout avant fin de traitement
Retries qui retraitent le meme message

Decision
Corriger KEDA ou l'identite si le job ne demarre pas
Corriger image, code, secret ou downstream si le job demarre puis echoue
Ne pas augmenter le scale-out tant que la classe du probleme n'est pas claire

Cette séparation protège la production contre une fausse bonne idée : augmenter la concurrence alors que chaque worker échoue pour la même raison.

Lire les logs avec une fenêtre bornée

Les logs doivent répondre à trois questions : le scaler a-t-il déclenché, le conteneur a-t-il démarré, et le message a-t-il été traité ou rejeté ? Adaptez les tables au mode Log Analytics réellement activé dans l’environnement.

kusto 02-container-apps-job-evidence.kql
let StartTime = datetime(2026-07-30T06:00:00Z);
let EndTime = datetime(2026-07-30T09:00:00Z);
let JobName = "invoice-worker-prod";
ContainerAppSystemLogs_CL
| where TimeGenerated between (StartTime .. EndTime)
| where ContainerAppName_s == JobName
| project TimeGenerated, Reason_s, Log_s, ReplicaName_s
| union (
  ContainerAppConsoleLogs_CL
  | where TimeGenerated between (StartTime .. EndTime)
  | where ContainerAppName_s == JobName
  | project TimeGenerated, Reason_s="console", Log_s, ReplicaName_s
)
| order by TimeGenerated desc

Si les logs ne montrent rien, ce n’est pas une preuve que le job est sain. Vérifiez d’abord Diagnostic Settings, workspace cible, délai d’ingestion et nom réel du job ou de l’exécution.

Valider identité, secrets et accès à la source

Une règle KEDA peut dépendre d’un secret ou d’une identité managée. Un changement RBAC, une rotation de secret ou une modification Key Vault peut empêcher la lecture du backlog sans que le code applicatif ait changé.

text identity-secret-checklist.txt
Identite et acces
Identite managée attendue attachee au job
Role minimal sur la queue ou le namespace
Aucun deny assignment ou policy recente
Secret reference toujours resolue
Key Vault accessible depuis l'environnement Container Apps si utilise
Expiration ou rotation recente documentee

Preuves utiles
Erreurs 401/403 dans logs systeme ou applicatifs
Dernier succes avant rotation
Comparaison avec un job sain sur la meme source
Scope RBAC exact plutot que role elargi au subscription

Ne remplacez pas une identité managée par une connection string durable pour aller vite. Si un secret temporaire est nécessaire pour restauration, il doit avoir une durée, un propriétaire et un retrait prévu.

Choisir replay, correction ou pause

Après les preuves, la décision doit être explicite. Un replay manuel est acceptable seulement si le traitement est idempotent, si le périmètre est borné et si la cause est corrigée ou contournée proprement.

text rerun-decision-matrix.txt
Corriger la regle KEDA
Backlog present, aucune execution, metadata ou secret de scaler en derive

Rollbacker l'image ou la configuration
Executions recentes echouent apres un deploiement applicatif ou IaC

Reduire le parallellisme
Downstream degrade, workers trop nombreux, retries amplifient l'incident

Isoler poison messages
Petit nombre de messages repetes, erreurs fonctionnelles reproductibles

Relancer manuellement
Cause corrigee, idempotence prouvee, fenetre et volume bornes, validation prete

Pause volontaire
Traitement non idempotent ou downstream encore instable

La bonne action peut être de ne pas consommer pendant quelques minutes. Une pause contrôlée avec backlog surveillé vaut mieux qu’un replay qui écrit deux fois dans un système aval.

Valider et préparer le rollback

La clôture doit prouver que la consommation reprend sans masquer la cause. Gardez une validation applicative, pas seulement une baisse de backlog.

text job-closure-and-rollback.txt
Validation finale
La regle KEDA observe la bonne source
Les executions recentes passent en Succeeded
Le backlog baisse au rythme attendu
Aucun poison message nouveau n'apparait
Les logs montrent l'idempotency key ou la correlation metier
Le downstream confirme les traitements attendus

Rollback
Revenir a l'image ou revision precedente du job
Restaurer la precedente scale rule depuis IaC
Remettre le seuil et le parallellisme initiaux
Revoquer tout secret temporaire
Replacer les messages isoles seulement apres correction
Documenter les messages non rejoues

Si une modification manuelle a été faite dans l’urgence, elle reste provisoire jusqu’à retour dans l’IaC ou suppression explicite.

Conclusion

Un Azure Container Apps Job bloqué n’est pas seulement un worker à relancer. C’est une chaîne de consommation : source d’événements, règle KEDA, identité, secrets, image, limites d’exécution, logs, idempotence et système aval.

La décision utile est donc bornée : corriger le scaler si le job ne démarre pas, rollbacker l’image si les exécutions échouent depuis le déploiement, réduire la concurrence si le downstream souffre, isoler les messages invalides, ou relancer seulement après preuve d’idempotence. Le runbook transforme un backlog urgent en reprise contrôlée.