• English icon
  • German icon
Erklärvideo

Azure meldet meine Container App als Healthy. Warum ist sie kaputt?

· Reihe: Container auf Azure

azureazure-container-appshealth-probescontainersdevops

Das Video ist auf Englisch.

Ich habe einer Container App eine einzige Einstellung entfernt. Azure Container Apps hat eine neue Revision gebaut, sie als Healthy markiert und ihr den gesamten Traffic gegeben. Die App antwortete model: not-configured. Kein fehlgeschlagenes Deployment, kein Fehler, alles grün, und die App war kaputt.

Dieses Erklärvideo zeigt, was „Healthy“ wirklich prüft, warum ein falscher Ingress-Port jede Prüfung überspringt und welche Readiness-Probe ein kaputtes Release von den Nutzern fernhält. Alles unten stammt aus einem echten Durchlauf in meinem eigenen Azure-Abonnement.

Kernpunkte

  • Healthy heißt: die Probes bestehen. Ohne eigene Probes legt Container Apps Standard-Probes per TCP auf den Ingress-Zielport an. Sie fragen nur, ob etwas eine Verbindung annimmt.
  • Ingress gehört nicht zu einer Revision. Zielport, Traffic-Regeln und Secrets sind Konfiguration auf App-Ebene: Eine Änderung erzeugt keine Revision und gilt sofort für alle Revisionen, ohne Prüfung dazwischen.
  • Readiness entscheidet über Traffic, Liveness startet neu. Was ein Replikat für seine Arbeit braucht, gehört in die Readiness; die Liveness prüft nur den Prozess selbst, nie die Datenbank.
  • Von außen nach innen suchen. Revisionsliste, dann Konsolen- und Systemlogs, dann Log Analytics für die Historie; eine fehlerhafte Revision deaktivieren statt löschen.

Was Healthy prüft

Wer eine Umgebungsvariable entfernt, ändert das Template der Revision, also baut Container Apps eine neue Revision. Eine Revision ist ein eingefrorener Stand Ihrer Container-Einstellungen: Image, Umgebungsvariablen, Skalierungsregeln. Sie läuft als ein oder mehrere Replikate, die laufenden Kopien.

Im Single-Revision-Modus (Standard) behält die alte Revision den gesamten Traffic, bis die neue bereit ist: bereitgestellt, hochskaliert und durch ihre Startup- und Readiness-Probes. Das ist die Schranke. Ich hatte keine Probes definiert, also bekam die neue Revision ohne MODEL_NAME trotzdem 100 % des Traffics.

Der Grund: Mit aktiviertem Ingress legt Container Apps für jeden nicht definierten Typ Standard-Probes an. Es sind TCP-Prüfungen auf den Ingress-Zielport:

ProbeStandardprüfung
StartupTCP auf den Zielport, jede Sekunde, bis zu 240 Fehlschläge
ReadinessTCP auf den Zielport, alle 5 Sekunden
LivenessTCP auf den Zielport

Die App liest die Einstellung mit einem Fallback, also startete sie, der Port ging auf, und die Schranke ließ sie durch:

MODEL_NAME = os.getenv("MODEL_NAME", "not-configured")

Eine solche Änderung finden Sie, indem Sie die letzte funktionierende Revision mit der neuen vergleichen:

az containerapp revision show -n ai-api -g rg-demo \
--revision ai-api--v2 \
--query "properties.template.containers[0].env" -o table

Für v1 kam MODEL_NAME gpt-5.4-mini zurück, für v2 nichts.

Der falsche Port

Die App lauscht auf Port 8000. Ich habe den Ingress-Zielport auf 3000 gesetzt:

az containerapp ingress update -n ai-api -g rg-demo \
--target-port 3000

Ein falscher Port klingt nach einem fehlgeschlagenen Deployment, aber es gibt keine neue Revision und keine Prüfung. Ingress ist Konfiguration auf App-Ebene, wie Traffic-Regeln und Secrets: Sie liegt vor allen Revisionen und ändert alle auf einmal. Der nächste Request bekam ein 503 mit Connection refused.

Das Konsolenlog zeigt den Widerspruch in einer Zeile: gunicorn meldet Listening at: http://0.0.0.0:8000, Ingress schickt an 3000.

Ein Neustart hilft nicht. Die Standard-Startup-Probe des neuen Replikats klopft jetzt an Port 3000 und scheitert jede Sekunde (Probe of StartUp failed with status code: 1 im Systemlog). Die Revisionsliste zeigte weiter Healthy, weil das alte Replikat weiterlief, und jeder Request schlug weiter fehl.

Readiness und Liveness

Damit Healthy etwas bedeutet, stellen Sie der Schranke bessere Fragen:

ProbeFrageWenn sie scheitert
ReadinessKann dieses Replikat gerade Requests annehmen?das Replikat bekommt keinen Traffic, läuft aber weiter
LivenessLebt der Prozess noch?Container Apps startet das Replikat neu
StartupIst die App gestartet?läuft nur beim Start; danach übernehmen Readiness und Liveness

Die Readiness bekommt einen eigenen Endpunkt. Dieser liefert 503, solange MODEL_NAME fehlt:

@app.route("/health/ready")
def ready():
if MODEL_NAME == "not-configured":
return {"status": "not ready"}, 503
return {"status": "ready"}

Und die Probes im Container-Template (HTTP-Probes werten Status 200 bis 399 als Erfolg):

probes:
- type: Readiness
httpGet: { path: /health/ready, port: 8000 }
periodSeconds: 5
- type: Liveness
httpGet: { path: /health, port: 8000 }
periodSeconds: 10

Die Falle: Prüft Ihre Liveness-Probe auch die Datenbank und ist die Datenbank eine Minute weg, startet jedes Replikat neu, immer wieder. Aus einem kurzen Aussetzer wird ein Ausfall, mit Kaltstarts obendrauf. Gehört die Datenbankprüfung in die Readiness, bekommen die Replikate während des Ausfalls keinen Traffic, nichts startet neu, und sie kommen von selbst zurück, sobald die Datenbank wieder da ist.

Wenn es trotzdem bricht

  1. Die Revisionsliste: Welche Revision ist aktiv, ist sie gesund, wohin geht der Traffic?
  2. Die Logs: Konsolenlogs zeigen, was Ihre App ausgibt; Systemlogs, was die Plattform gesehen hat (Probes, Image-Pulls, Neustarts). az containerapp logs show streamt beide (--type system; Systemlogs gibt es nur als JSON).
  3. Historie in Log Analytics: Der Workspace der Umgebung bewahrt sie auf, einige Minuten verzögert.
  4. Vergleichen Sie die fehlerhafte Revision mit der letzten funktionierenden.
  5. Deaktivieren statt löschen: Das stoppt die Replikate, inaktive Revisionen kosten nichts, und die Spuren bleiben erhalten.

Ein Neustart ist keine Lösung: Er startet dasselbe Image mit derselben Konfiguration.

az containerapp revision list -n ai-api -g rg-demo -o table
az containerapp logs show -n ai-api -g rg-demo --type system
az containerapp revision deactivate -n ai-api -g rg-demo \
--revision ai-api--v5

Was beim echten Durchlauf passiert ist

Getestet am 19. Oktober 2026 in meinem eigenen Azure-Abonnement (westeurope, Azure CLI 2.88.0); danach wurde alles gelöscht. Die App ist die Flask-API aus der Übung von Microsoft Learn, ergänzt um den /health/ready-Endpunkt oben. Mein erster Versuch in germanywestcentral scheiterte schon an der Umgebung: AKSCapacityHeavyUsage, „Creating a new cluster is unavailable at this time in region germanywestcentral“. In westeurope brauchte die Umgebung 18,6 Minuten, der Cloud-Build 69 Sekunden, die App 43 Sekunden.

Die kaputte Revision, live:

> az containerapp revision list -n ai-api -g rg-demo --all -o table
CreatedTime Active Replicas TrafficWeight HealthState ProvisioningState Name
------------------------- -------- ---------- --------------- ------------- ------------------- ---------------
2026-10-19T15:32:45+00:00 False 0 0 Healthy Provisioned ai-api--f5uwknl
2026-10-19T15:33:07+00:00 True 1 0 Healthy Provisioned ai-api--v1
2026-10-19T15:34:06+00:00 True 1 100 Healthy Provisioned ai-api--v2
> (irm https://$FQDN/).model
name
----
not-configured

Jedes az containerapp update dauerte 18 bis 22 Sekunden und kehrte zurück, bevor die neue Revision bereit war: Nach der letzten bereiten Revision gefragt, nannte es noch die alte.

Der falsche Port:

> curl.exe -sS -i -m 30 https://$FQDN/health
HTTP/1.1 503 Service Unavailable
upstream connect error or disconnect/reset before headers. retried and the latest
reset reason: remote connection failure, transport failure reason: delayed connect
error: Connection refused

Derselbe Fehler mit Readiness-Probe (Revision v5, vier Minuten später): v5 wurde nie bereit, die App antwortete weiter mit gpt-5.4-mini aus v4, und das Systemlog zeigte die Schranke bei der Arbeit:

Probe of Readiness failed with status code: 503
Probe of Readiness failed with status code: 503
Probe of Readiness failed with status code: 503

Log Analytics, zweieinhalb Minuten nach dem letzten Aufruf abgefragt:

RevisionName_s TableName Calls Failed
---------------- ------------- ------- --------
ai-api--v4 PrimaryResult 70 0
ai-api--v5 PrimaryResult 53 53

(Die erste Zeile der Revisionsliste, ai-api--f5uwknl, ist eine Platzhalter-Revision, die die CLI anlegt, während sie die verwaltete Identität für den Image-Pull einrichtet.)

Noch ein Detail: Im Single-Revision-Modus zeigte revision list für v5 ein Traffic-Gewicht von 100, obwohl sie nicht bereit war. Die Requests gingen trotzdem an v4, die letzte bereite Revision, und der erste dauerte 24 Sekunden, weil v4 inzwischen auf null skaliert hatte.

Nicht im Video

Tags oder Digests für Produktions-Images, Start, Stopp und Skalierung auf null, CPU- und Speichergrößen (im Consumption-Profil von 0,25 vCPU / 0,5 GiB bis 2 vCPU / 4 GiB pro Replikat), strukturierte Log-Felder und der Multiple-Revision-Modus mit Traffic-Splitting. Sie stehen im Microsoft-Learn-Modul unten.

Weiterführende Links

Video-Transkript (Englisch)

I removed one setting from a container app. Azure built a new revision, marked it Healthy, and gave it all the traffic. And the app answered: model, not configured. No failed deployment. No error. Everything green, and the app was broken.

In this video, you'll see what Healthy really means in Container Apps, and why it missed both mistakes I made on purpose. And at the end, the same broken release again, with one probe added.

What does Healthy actually check? Most people read Healthy as: the app works. So let's test that. Removing an environment variable changes the revision's template, so Container Apps builds a new revision: v2. A revision is a frozen snapshot of your container settings: the image, the environment variables, the scale rules. It runs as one or more replicas, the running copies. Before v2 gets any traffic, it has to pass a gate: its startup and readiness probes. I never defined any. So, with MODEL_NAME gone, does v2 get the traffic? All of it. Healthy, one hundred percent. v1 kept running, with zero traffic. Here's why. Without probes of your own, Container Apps adds default ones: TCP checks on the target port. They ask exactly one question: does something accept a connection on port 8000? And the code reads MODEL_NAME with a fallback: not configured. So the app started, the port opened, and the gate let it through. To find a change like that, compare the last good revision with the new one. v1 has MODEL_NAME. The list for v2 is empty. So Healthy means the probes pass. And the default probe only checks that the door opens, not what happens behind it.

What if the port itself is wrong? The app listens on port 8000. I changed the ingress target port to 3000. A wrong port sounds like a failed deployment. So, does Container Apps build a new revision and test it first? No new revision. No probe, no gate. The very next request got a 503: connection refused. Ingress isn't part of a revision. The target port, traffic rules and secrets are app-level configuration. That configuration sits in front of every revision, and a change goes live for all of them at once. There is no gate in between. The console log shows the mismatch in one line: the app is listening at port 8000, and ingress sends to 3000. And a restart doesn't help. The new replica's default startup probe now knocks on port 3000, and fails, every second. The revision list still said Healthy, because the old replica kept running. And every request still failed. This series runs one Azure service at a time, for real. Subscribe to follow the series.

So how do you make Healthy mean something? You give the gate better questions. Two probes matter here. Readiness asks: can this replica take requests right now? If it fails, the replica gets no traffic, but it keeps running. Liveness asks: is the process still alive? If it fails, Container Apps restarts the replica. Readiness gets its own endpoint. Ours returns a 503 while MODEL_NAME is missing, and 200 when the replica can do its job. In the container's YAML, the readiness probe calls that path every five seconds. Liveness calls the plain health path. Now a trap. Say your liveness probe also checks the database, and the database is down for a minute. What happens to your replicas? They all restart, again and again. A one-minute blip becomes an outage, with cold starts on top. So readiness checks what this replica needs to do its job. Liveness only checks the process itself. Put the database check into readiness instead. During an outage, the replicas stop taking traffic, nothing restarts, and they come back by themselves when the database does. And the startup probe only runs while the app starts. Once it passes, readiness and liveness take over.

When it breaks anyway, where do you look first? Not inside the container. Start with the revision list: which revision is active, is it healthy, and where does the traffic go. Then the logs. Console logs are what your app prints. System logs are what the platform saw: probes, image pulls, restarts. The command az containerapp logs show streams both, live. For history, query Log Analytics, the workspace where the environment keeps its logs. When I queried it two and a half minutes later, every probe call was there. And deactivate a bad revision instead of deleting it. That stops its replicas, it costs nothing, and it keeps the evidence. One more: a restart is not a fix. It starts the same image with the same configuration, as the wrong port showed.

To sum it up. Healthy means the probes pass, and the default probes only check the port. Ingress changes skip the gate, so compare the target port with the logs. Readiness decides traffic, liveness restarts, so keep dependencies out of liveness. And start from the revision list and the logs, not from a shell.

And the broken release from the start, now with that readiness probe. I removed MODEL_NAME again. Revision v5 started, and asked its readiness endpoint every five seconds. 503, every time. It never became ready. Requests stayed on v4, and kept answering: GPT 5.4 mini. Log Analytics counted it later: fifty-three readiness checks on v5, fifty-three failed. The same mistake, and no user saw it.

The video on screen now shows how revisions, secrets and registry pulls work when you deploy to Container Apps. If this was useful, subscribe for more practical .NET and Azure.

Brauchen Sie das in Ihrer eigenen Azure-Umgebung?

Ich entwickle, migriere und betreibe .NET-Plattformen auf Azure, mit Azure DevOps und Terraform. Wenn Ihr Team das sauber aufsetzen möchte, sehen Sie sich mein Angebot an.

Zu den Leistungen