• English icon
  • German icon
Erklärvideo

So bekommt eine neue Version nur 20 % des Traffics auf Azure Container Apps

· Reihe: Container auf Azure

azureazure-container-appstraffic-splittingcanary-deploymentdevops

Das Video ist auf Englisch.

Ich wollte eine neue Version meiner App mit zwanzig Prozent des Traffics testen. Also habe ich meine Azure Container App auf mehrere Revisionen umgestellt, die neue Version deployt und 200 Requests geschickt. Die neue Version hat alle 200 beantwortet.

Dieser Explainer zeigt, wie Container Apps entscheidet, welche Revision einen Request beantwortet, wie Sie einen echten 80/20-Split bekommen, was dieser Split für einen einzelnen Nutzer bedeutet und was eine alte Revision bei null Prozent noch tat. Alles hier stammt aus einem echten Durchlauf in meinem eigenen Azure-Abonnement. Das Video ist auf Englisch.

Die wichtigsten Punkte

  • Der Single-Revision-Modus kann nicht aufteilen. Er ist der Standard: eine aktive Revision zur Zeit, ein Deploy ist das Release. Gewichte setzen schlägt fehl mit „configured for single revision“.
  • Der Multiple-Revision-Modus hält eine neue Version nicht zurück. Die Standard-Traffic-Regel ist latestRevision: true mit 100 %. Sie zeigt auf die jeweils neueste Revision, also übernimmt jedes Deploy den gesamten Traffic, sobald es bereit ist. Benennen Sie die Revisionen in den Gewichten, bevor Sie deployen, dann startet eine neue Revision bei null.
  • 80/20 gilt pro Request, nicht pro Nutzer. 1.000 Requests teilten sich 806 / 194. Ein Client auf einer Keep-Alive-Verbindung sah beide Versionen. Sticky Sessions gibt es nur im Single-Revision-Modus. Für Tester gibt ein Label einer Revision eine eigene Adresse.
  • Jede aktive Revision betreibt ihre eigenen Replicas. Bei 30 Requests pro Sekunde liefen bei einer Revision fünf Replicas, bei zwei Revisionen mit 50/50 zwei plus drei. Eine Revision bei null Prozent behielt viereinhalb Minuten lang vier Replicas und danach ihr Minimum von einer, bis ich sie deaktiviert habe.

Eine Revision zur Zeit

Jede Änderung am Container (ein neues Image, eine neue Umgebungsvariable, eine neue Scale-Regel: alles im template der App) erzeugt eine neue Revision, einen unveränderlichen Snapshot der App. Jede Revision läuft als Replicas, Kopien Ihres Containers. Änderungen an der configuration der App (Secrets, Ingress, Traffic-Regeln, Labels) erzeugen keine Revision.

Standardmäßig läuft eine Container App im Single-Revision-Modus: eine aktive Revision zur Zeit. Als ich Version zwei deployt habe, hat v2 den gesamten Traffic übernommen, sobald sie bereit war, und kurz danach wurde v1 automatisch deaktiviert. In diesem Modus ist ein Deploy das Release.

Ich habe trotzdem einen Split angefordert, 80 % für v1 und 20 % für v2:

> az containerapp ingress traffic set -n shop -g rg-demo --revision-weight shop--v1=80 shop--v2=20
ERROR: Containerapp 'shop' is configured for single revision. Set revision mode to multiple in order to set ingress traffic.

Eine aktive Revision heißt: Es gibt nichts aufzuteilen.

Die neueste bekommt alles

Der Multiple-Revision-Modus erlaubt mehrere aktive Revisionen gleichzeitig, und Sie entscheiden, wie sie sich den Traffic teilen. Ein Befehl stellt den Modus um:

az containerapp revision set-mode -n shop -g rg-demo --mode multiple

Dann habe ich v3, meinen Canary, genauso deployt wie vorher. v2 war daneben weiter aktiv. 200 Requests später hatte v3 alle 200 beantwortet. Die Traffic-Regeln haben gezeigt, warum:

> az containerapp ingress traffic show -n shop -g rg-demo
[
{
"latestRevision": true,
"weight": 100
}
]

Das ist der Standard. Die Regel nennt keine Revision, sie zeigt auf die jeweils neueste. Der Multiple-Modus hat v2 nur neben der neuen weiterlaufen lassen.

Die Lösung: Revisionen benennen. Erst die Gewichte per Revisionsname festlegen, dann deployen, dann die Gewichte selbst verschieben:

az containerapp ingress traffic set -n shop -g rg-demo \
--revision-weight shop--v2=100
az containerapp update -n shop -g rg-demo \
--set-env-vars APP_VERSION=v3 --revision-suffix v3
az containerapp ingress traffic set -n shop -g rg-demo \
--revision-weight shop--v2=80 shop--v3=20

Später in meinem Durchlauf, während die Gewichte noch v2 und v3 benannten, habe ich v4 deployt: Sie wurde mit einer Replica aktiv und bekam 0 % des Traffics.

Single-Revision-ModusMultiple-Revision-Modus
Aktive Revisioneneineso viele, wie Sie aktiv lassen
Ein neues Deployübernimmt den gesamten Traffic, sobald bereit; die alte wird deaktiviertStandardregel: die neueste bekommt 100 %, sobald bereit; benannte Gewichte: startet bei 0 %
Traffic-Splittingabgelehntper Gewicht (Revisionsname oder Label)
Sticky Sessionsverfügbarnicht verfügbar
Alte Revisionenwerden für Sie deaktiviertlaufen weiter, bis Sie sie deaktivieren

Die Doku nennt einen dritten Modus, Deployment Labels (Preview): Labels wie stage und prod bekommen neue Revisionen zugewiesen, und alte werden abgeschaltet, wenn kein Label mehr auf sie zeigt.

Zwanzig Prozent wovon?

Mit den Gewichten 80 und 20 habe ich 1.000 Requests geschickt, jeden über eine neue Verbindung: 806 für v2, 194 für v3. Nah an 80/20, nicht exakt: Jeder Request wird für sich zufällig nach den Gewichten verteilt.

Dann ein Nutzer: ein Client, eine Keep-Alive-Verbindung, zehn Requests nacheinander. Er sah v2 v2 v2 v3 v2 v3 v2 v2 v2 v2. Der Split gilt pro Request, nicht pro Nutzer. Session-Affinität (Sticky Sessions) wird nur im Single-Revision-Modus unterstützt. Wenn ein Nutzer bei einer Version bleiben muss, reichen die Gewichte allein nicht.

Für Tester gibt es ein Label. Ein Label gibt einer Revision eine eigene Adresse, unabhängig von den Gewichten:

az containerapp revision label add -n shop -g rg-demo \
--label canary --revision shop--v3

Die Adresse des Labels ist der App-Name, drei Bindestriche, das Label, dann die Domain der Container-Apps-Umgebung: https://shop---canary.<env-domain>.westeurope.azurecontainerapps.io/. Sie hat jedes Mal v3 geantwortet, 100 von 100 Aufrufen, während v3 weiter 20 % des normalen Traffics hatte. Ein Label kann auch auf einer Revision bei 0 % liegen, so testen Sie, bevor echte Nutzer sie sehen. Echte Nutzer folgen den Gewichten. Tester nutzen das Label.

Jedes Gate hat seine eigene Crew

Jede aktive Revision hat ihre eigenen Replicas, und jede skaliert nach ihrem eigenen Anteil an den Requests. Meine HTTP-Scale-Regel verlangte etwa eine Replica pro zehn Requests pro Sekunde (--scale-rule-http-concurrency 10).

30 Requests/s für 3 Minutenv2v3Summe
v3 bei 100 %4 (übrig, 0 % Traffic)59
50 / 50 (nach dem Herunterskalieren)235

Fünfzig-fünfzig aufgeteilt brauchte dieselbe Last fünf Replicas, genauso viele wie eine Revision allein. Der Split selbst hat in meinem Durchlauf keine zusätzlichen Replicas gekostet; das Modul warnt, dass er es kann, weil jede Revision ihr eigenes Minimum behält und für sich aufrundet. Was sich summiert, ist jede Revision, die Sie aktiv lassen.

Die alte Revision bei null Prozent

Als ich v2 auf 0 % gesetzt habe, hatte sie noch vier Replicas von den tausend Requests davor. Die behielt sie viereinhalb Minuten, dann zwei, und acht Minuten nachdem sie ihren Traffic verloren hatte, eine. Und die eine bleibt: Das ist die minimale Replica-Anzahl, die ich der App gegeben habe, und jede aktive Revision behält sie. Null Prozent Traffic heißt nicht null Replicas.

Am Ende habe ich die Revisionen deaktiviert, die ich nicht mehr brauchte:

az containerapp revision deactivate -n shop -g rg-demo --revision shop--v4

Eine inaktive Revision betreibt keine Replicas und kostet nichts (Container Apps behält bis zu 100 davon, Sie können eine wieder aktivieren).

Setzen Sie das Gewicht vor dem Deaktivieren auf null. In meinem Durchlauf hat revision deactivate auch bei einer Revision funktioniert, die noch 50 % des Gewichts hatte. Die Requests gingen sofort an die andere Revision (200 von 200, auch zwei Minuten später), aber die Traffic-Liste zeigte die inaktive Revision weiter mit 50 %, bis ich die Gewichte neu gesetzt habe.

Was beim Ausprobieren passiert ist

Getestet am 21. Oktober 2026 in meinem eigenen Azure-Abonnement (westeurope, Azure CLI 2.88.0); danach wurde alles gelöscht. Die App ist meine eigene kleine Python-API, die {"version": "v3", ...} antwortet; eine neue Version ist ein neuer Wert von APP_VERSION plus ein Revision-Suffix. split.py, ebenfalls von mir, hat gezählt, welche Version antwortet, einen Nutzer auf einer Verbindung gespielt, Last mit fester Rate geschickt und die Replicas jeder aktiven Revision abgefragt. Die Umgebung brauchte 17,5 Minuten, jedes Update etwa 20 Sekunden plus 15 Sekunden, bis die neue Revision lief.

Nach v3 im Multiple-Modus, nichts festgelegt:

> az containerapp revision list -n shop -g rg-demo -o table
Active Replicas TrafficWeight Name
True 1 0 shop--v2
True 1 100 shop--v3
> python split.py --app shop --rg rg-demo send 200
200 requests to the app URL in 4 s
v3 200 100.0 %

Der 80/20-Split, 1.000 Requests und ein Nutzer:

> python split.py --app shop --rg rg-demo send 1000
1000 requests to the app URL in 18 s
v2 806 80.6 %
v3 194 19.4 %
> python split.py --app shop --rg rg-demo user 10
one client, one connection, 10 requests:
v2 v2 v2 v3 v2 v3 v2 v2 v2 v2
v2: 8, v3: 2

Das Label:

> curl.exe -s https://shop---canary.<env-domain>.westeurope.azurecontainerapps.io/
{"version": "v3", "replica": "?"}

v4 deployt, während die Gewichte v2 und v3 benannten:

Active Replicas TrafficWeight Name
True 2 50 shop--v2
True 3 50 shop--v3
True 1 0 shop--v4

v2 bei 0 % nach dem Lasttest (mein Tool fragte alle 15 Sekunden ab):

2 s shop--v2 4 repl. (0 %) · shop--v3 5 repl. (100 %)
53 s shop--v2 2 repl. (0 %) · shop--v3 5 repl. (100 %)
277 s shop--v2 1 repl. (0 %) · shop--v3 4 repl. (100 %)
517 s shop--v2 1 repl. (0 %) · shop--v3 1 repl. (100 %)

Das Deaktivieren mit verbliebenem Gewicht:

> az containerapp revision list -n shop -g rg-demo --all -o table
Active Replicas TrafficWeight Name
True 6 50 shop--v3
False 0 50 shop--v4

Und ein Detail: Direkt nach containerapp create mit Revision-Suffix zeigte die Liste eine zusätzliche, automatisch benannte Revision (shop--ttolvpb, aktiv, eine Replica, 0 %) neben shop--v1; beim nächsten Deploy war sie inaktiv.

Weiterlesen

Video-Transkript (Englisch)

I wanted to test a new version of my app on twenty percent of the traffic: a canary. So I switched my Container App to multiple revisions and deployed it. Then I sent two hundred requests. The new version answered all two hundred. And the old one? Still running, with nobody to serve.

In this video, you'll see how Container Apps decides which revision answers each request, and how to get a real twenty percent. And at the end, what that old revision was still doing.

Why can't a normal deploy split traffic? Every change to the container, like a new image or a new environment variable, builds a new revision: a snapshot of your app that never changes. Mine are named shop, two dashes, v1, v2, and so on. Each revision runs as replicas, copies of your container. By default, a Container App runs in single revision mode: one revision is active at a time. I deployed version two of my app that way. As soon as v2 was ready, it took all the traffic, and a little later v1 was deactivated on its own: no replicas, no traffic. In this mode, a deploy is the release. So I asked for a split anyway: eighty percent to v1, twenty percent to v2. What did Azure do? It refused. I ran the traffic command, ingress traffic set, with a weight for each revision: eighty for v1, twenty for v2. Configured for single revision. Set the revision mode to multiple. One active revision means there's nothing to split. A split needs multiple revision mode.

So what changes in multiple revision mode? Several revisions can be active at once, and you decide how they share the traffic. One command switches the mode: revision set-mode, with the mode multiple, for my app, called shop. It answered: multiple. Then I deployed v3, my canary, the same way as before. v2 was still active next to it. How much of the traffic did v3 get? All of it. I sent two hundred requests to the app's address, the way users reach it, and counted which version answered each one. Version three, two hundred times. My canary was the release. To see why, I asked Container Apps for the app's traffic rules, the weights that decide who answers. One rule: the latest revision, one hundred percent. That's the default. The rule doesn't name a revision. It points at whatever is newest, so every new deploy takes all the traffic once it's ready. Multiple mode only kept v2 running next to it. The fix is to name the revisions. First, set the weights with the same traffic command as before, and name the revision: v2 gets one hundred percent. Then deploy the new version as usual, here a new value for the version variable, and the revision name v3. It starts, and gets nothing. Then you move the weights yourself, eighty for v2, twenty for v3. Much later in my run, with the weights still named, I deployed v4 and listed the app's active revisions. v4 was active, with one replica, and zero percent of the traffic. This series runs one Azure service at a time, for real. Subscribe to follow the series.

Twenty percent of what, exactly? With the weights at eighty and twenty, I sent a thousand requests, each one on a new connection, and counted the answers. Eight hundred and six for v2, one hundred and ninety-four for v3. Close to eighty-twenty, not exact. Each request is sent on its own, by chance, following the weights. Now one user, on one connection, sends ten requests in a row. Which versions does that user see? Both. Here's that run: the same tool as a single client that keeps its connection open. v2, v2, v2, v3, v2, v3. Eight times v2, twice v3. The split is per request, not per user. Sticky sessions, which keep a user on one replica, only exist in single revision mode. So if a user has to stay on one version, the weights alone won't do it. For testers, there's a label. A label gives one revision its own address, whatever the weights say. I put the label canary on v3 with one command: revision label add, with the label and the revision. The traffic list now shows the label next to v3, still at twenty percent. Then I called the label's address: the app name, three dashes, the label, then the domain of its environment, the shared space where Container Apps run. Version three. And a hundred more calls through the label: a hundred answers from v3. Real users follow the weights. Testers use the label.

Do two revisions cost more than one? Every active revision has its own replicas, and each one scales on its own share of the requests. My scale rule, the setting that decides when to add replicas, asked for about one replica per ten requests a second. First, v3 alone, with all the traffic. My tool sent thirty requests a second for three minutes and listed the replicas of every active revision. v3 went to five replicas. Then I split the same load fifty-fifty and ran the tool again. Two replicas for v2, three for v3. Five in total, the same as one revision alone. Each revision scaled on its own half. The split itself didn't cost extra replicas here. So what does cost extra?

To sum it up. In single revision mode, a deploy is the release, and there's nothing to split. In multiple mode, the default rule sends everything to the latest revision. Name your revisions in the weights before you deploy. The weights split requests, not users. Give testers a label. And every active revision runs its own replicas.

The old revision from the start. When I moved v2 to zero percent, it still had four replicas from the thousand requests before. After the load test, three and a half minutes later, my tool kept watching every fifteen seconds. Four replicas, then two, then one. Eight minutes in total after v2 lost its traffic. And that one stays. It's the minimum replica count I gave the app, one, and every active revision keeps it. Zero percent of the traffic is not zero replicas. At the end, I deactivated the revisions I didn't need, one command each: revision deactivate. Deactivate succeeded. An inactive revision runs no replicas and costs nothing. So: name the weights before you deploy, and deactivate the old revision when you're done.

In single mode, I watched v2 take over from v1. The video on screen now shows how a new revision takes over, and why it waits until it's ready. 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