• English icon
  • German icon
Erklärvideo

Ich habe die ConfigMap geändert. Warum zeigt meine App noch den alten Wert?

· Reihe: Container auf Azure

azureakskubernetesconfigmapkubectldevops

Das Video ist auf Englisch.

Ich habe eine Einstellung in einer Kubernetes-ConfigMap auf Azure Kubernetes Service (AKS) geändert: die API-Version, von 1.0.0 auf 2.0.0. kubectl meldete „configured“, die ConfigMap zeigte 2.0.0. Meine laufende App antwortete weiter mit 1.0.0. Kein Fehler, kein Neustart.

Dieser Beitrag zeigt, warum ein laufender Pod alte Einstellungen behält, wann er neue übernimmt, und einen Namenstrick, der jede Änderung der Einstellungen zu einem Rollout macht, den Sie rückgängig machen können. Alles stammt aus der Übung des Microsoft-Learn-Moduls, ausgeführt in meinem eigenen Cluster.

Das Wichtigste

  • Eine Umgebungsvariable ist eine Kopie vom Start. Kubernetes kopiert die Werte einer ConfigMap einmal in die Umgebung eines Containers, wenn er startet, und aktualisiert sie in einem laufenden Prozess nie.
  • Eine geänderte ConfigMap startet nichts. Ein Deployment rollt neue Pods nur aus, wenn sich sein eigenes Pod-Template ändert. Das Template enthält den Namen der ConfigMap, nicht ihre Werte; die Rollout-Historie blieb bei einer Revision.
  • Gemountete Dateien ziehen nach, etwa eine Minute später. Das kubelet aktualisiert eine gemountete ConfigMap nach eigenem Takt. In meinem Lauf wechselte die Datei nach 81 Sekunden. Eine per subPath gemountete Datei änderte sich nie, und eine App, die ihre Einstellungen beim Start liest, bemerkte so oder so nichts.
  • Der Neustart ist das Update. kubectl rollout restart schreibt einen Zeitstempel ins Pod-Template; das Deployment startet einen neuen Pod, und der kopiert die aktuellen Einstellungen. Eine neue, unveränderliche ConfigMap pro Version leistet dasselbe, mit Historie und Undo.

Beim Start kopiert

Eine ConfigMap ist ein Kubernetes-Objekt, das Einstellungen als Schlüssel und Werte hält, außerhalb Ihres Container-Images. Die Übung legt drei Einstellungen hinein: einen Namen, die API-Version und einen Log-Pfad. Ein Pod umschließt Ihren Container; ein Deployment hält Ihre Pods am Laufen, und sein Pod-Template ist das Rezept für jeden neuen Pod. In der Übung lädt dieses Rezept die ganze ConfigMap mit envFrom, einem einzigen Verweis über den Namen, sodass jeder Schlüssel zur Umgebungsvariable wird:

envFrom:
- configMapRef:
name: api-config

Ich habe die Änderung angewendet und Kubernetes nach dem Wert in der ConfigMap gefragt:

> kubectl get configmap api-config -o jsonpath="{.data.API_VERSION}"
2.0.0

Dann habe ich die App über ihre öffentliche Adresse nach ihrer Version gefragt, und den Container selbst:

> python cfg.py get /healthz
GET /healthz -> 200
{"status":"alive",… "version":"1.0.0","student":"Ehsan"}
> kubectl exec deploy/aks-config-api -- printenv API_VERSION
1.0.0

Umgebungsvariablen werden einmal gesetzt, wenn der Container startet. Kubernetes schreibt sie in einem laufenden Prozess nie neu. kubectl describe pod zeigt beide Hälften davon: woher die Einstellungen kommen und dass der Container vor der Änderung gestartet ist, ohne Neustart seitdem.

Started: Wed, 28 Oct 2026 13:30:55 +0100
Restart Count: 0
Environment Variables from:
api-config ConfigMap Optional: false

Kubernetes hat den neuen Wert gespeichert. Hat es dafür einen neuen Pod ausgerollt? Nein. Die Rollout-Historie, die Liste aller Versionen des Pod-Templates, hatte weiterhin einen Eintrag:

> kubectl rollout history deployment aks-config-api
REVISION CHANGE-CAUSE
1 <none>

Ein Deployment startet neue Pods nur, wenn sich sein Pod-Template ändert. Das Template enthält den Namen der ConfigMap, nicht ihre Werte; eine geänderte ConfigMap ist also keine Änderung am Deployment.

Als Dateien gemountet

Eine ConfigMap lässt sich auch als Volume mounten: Jeder Schlüssel wird zu einer Datei in einem Ordner. Ich habe das im Deployment ergänzt und die API-Version zusätzlich als einzelne Datei per subPath gemountet:

volumeMounts:
- name: config-volume
mountPath: /etc/config
- name: config-volume
mountPath: /etc/api-version
subPath: API_VERSION

Die Änderung am Template startete einen Rollout: ein neuer Pod, nach 22 Sekunden bereit. Der neue Pod kopierte die ConfigMap so, wie sie bei seinem Start war, und antwortete deshalb mit 2.0.0. In ihm lag in /etc/config eine Datei pro Schlüssel: API_VERSION, LOG_PATH, STUDENT_NAME.

Dann habe ich die ConfigMap auf 3.0.0 geändert. Ein kleines Skript von mir hat sie angewendet und drei Minuten lang jede Sekunde vier Stellen gelesen: die gemountete Datei, die subPath-Datei, die Umgebungsvariable und die Antwort der App.

> python cfg.py change k8s/configmap-v3.yaml --secs 180
1.1s file 2.0.0 | subPath 2.0.0 | env 2.0.0 | app 2.0.0
1.7s applied: configmap/api-config configured
81.1s file 3.0.0 (was 2.0.0)
180.2s file 3.0.0 | subPath 2.0.0 | env 2.0.0 | app 2.0.0
Was der Pod liestNach der Änderung
Umgebungsvariable (envFrom)nie, bis der Container neu startet
Gemounteter Ordner (/etc/config)nach dem nächsten Sync des kubelets: hier 81 s
Einzelne Datei per subPathnie, bis der Container neu startet
Eine App, die ihre Einstellungen beim Start liestnie, bis sie neu liest oder neu startet

Gemountete Dateien folgen der ConfigMap, aber nicht sofort. Das kubelet, der Kubernetes-Agent auf jedem Node, prüft gemountete ConfigMaps bei seinem regelmäßigen Sync (standardmäßig einmal pro Minute) und liest sie aus seinem eigenen Cache; rechnen Sie mit ein bis zwei Minuten, nicht mit einer Sekunde.

Wie ändert sich eine Datei, ohne dass ein Lesevorgang mittendrin bricht? Die Dateien sind Links in einen versteckten Ordner:

> kubectl exec deploy/aks-config-api -- ls -la /etc/config
… ..data -> ..2026_10_28_12_33_19.3747003581
… API_VERSION -> ..data/API_VERSION

Das kubelet schreibt einen kompletten neuen Ordner und schaltet dann den einen ..data-Link um. Deshalb änderte sich auch die subPath-Datei nie: subPath mountet die Datei selbst, so wie sie beim Start war, nicht den Link; die Umschaltung erreicht sie nie.

Und die App? Die API der Übung liest ihre Version einmal beim Start aus der Umgebungsvariable. Die Datei änderte sich unter ihr, und die App schaute nie hin. Eine gemountete Datei hilft nur, wenn Ihr Code die Datei liest, und sie erneut liest.

Den Rollout auslösen

Die Lösung ist ein Befehl, und die Übung nennt ihn als Tipp:

> kubectl rollout restart deployment aks-config-api
deployment.apps/aks-config-api restarted

22 Sekunden später war der neue Pod bereit, die App antwortete mit 3.0.0, und auch die subPath-Datei zeigte 3.0.0. rollout restart fasst die ConfigMap nicht an. Es schreibt eine Annotation ins Pod-Template:

> kubectl get deployment aks-config-api -o jsonpath="{.spec.template.metadata.annotations}"
{"kubectl.kubernetes.io/restartedAt":"2026-10-28T13:35:01+01:00"}

Für das Deployment ist das eine Änderung: Es startet einen neuen Pod, und der kopiert beim Start die aktuellen Einstellungen. Erst die ConfigMap ändern, dann das Deployment neu starten.

Ein Rollout, den Sie rückgängig machen können

Der Namenstrick: Geben Sie den nächsten Einstellungen eine neue ConfigMap mit neuem Namen, markieren Sie sie als unveränderlich, und lassen Sie das Deployment auf diesen Namen zeigen.

apiVersion: v1
kind: ConfigMap
metadata:
name: api-config-v4
data:
API_VERSION: "4.0.0"
immutable: true

Der Name ist Teil des Pod-Templates; das Anwenden des Deployments rollte deshalb von selbst aus, und die App antwortete mit 4.0.0. Jede Änderung der Einstellungen ist jetzt eine Revision: kubectl rollout undo brachte das Deployment zurück zum Template mit api-config, und die App antwortete wieder mit 3.0.0. Und die unveränderliche ConfigMap lässt sich nicht versehentlich ändern:

> kubectl apply -f k8s/versioned/configmap-v4-edit.yaml
The ConfigMap "api-config-v4" is invalid: data: Forbidden: field is immutable when `immutable` is set

Umbenennen müssen Sie nicht von Hand: Kustomize, in kubectl eingebaut (kubectl apply -k), erzeugt die ConfigMap mit einem Hash ihres Inhalts im Namen und passt die Verweise an. Neue Einstellungen, neuer Name.

Was bei meinem Lauf passiert ist

Getestet am 28. Oktober 2026 in meinem eigenen Azure-Abonnement (AKS 1.36 in westeurope, Azure CLI 2.88.0, kubectl 1.36); danach wurde alles gelöscht. Der Cluster hatte einen Node Standard_D2s_v5 (erstellt in 271 s), und die API der Übung antwortete 51 s nach kubectl apply. Jeder Rollout (das Datei-Deployment, der Neustart, die neue ConfigMap, das Undo) dauerte 22–23 s. Die erste Anfrage direkt nach einem Rollout lief zweimal einmal in ein Timeout, bevor die nächste antwortete; das Skript des Laufs hat wiederholt und beides ausgegeben.

Was das Video auslässt

  • Azure App Configuration: Ihr Kubernetes-Provider läuft im Cluster und erzeugt ConfigMaps aus Einstellungen, die in App Configuration liegen; mehrere Cluster teilen sich so einen Ort für Einstellungen.
  • binaryData, Verweise mit optional: true und das Limit von 1 MiB pro ConfigMap.
  • Die Checksum-Annotation von Helm am Pod-Template: dieselbe Idee wie der Namenstrick, umgesetzt von einem Chart.
  • Secrets und persistenter Speicher, die beiden anderen Teile des Moduls (eigene Beiträge).

Weiterführende Links

Video-Transkript (Englisch)

I changed one setting in a Kubernetes ConfigMap on Azure Kubernetes Service: the API version, from version one to version two. kubectl, the Kubernetes command line, said configured. The ConfigMap said version two. My app, still running, answered version one. No error, no restart. Nothing.

In this video, you'll see why a running pod keeps old settings, and when it picks up new ones. At the end: a naming trick that makes every settings change a rollout you can undo.

So where did version two go? A ConfigMap is a Kubernetes object that holds settings as keys and values, outside your container image. The Microsoft Learn module's exercise puts three settings in one: a name, the API version, and a log path. A pod wraps your container. A Deployment keeps your pods running, and its pod template is the recipe for every new pod. In the exercise, that recipe loads the ConfigMap with envFrom, a single line that points at it by name. So every key becomes an environment variable. I applied my change and asked Kubernetes for the value stored in the ConfigMap now. Two point zero point zero. The change was there. Then I asked the app itself, over its public address, which version it runs. One point zero point zero. The old value. So I asked the container directly, with kubectl exec and printenv. The same, inside the container: one point zero point zero. Environment variables are set once, when the container starts. Kubernetes never rewrites them inside a running process. I described the pod, to see where its settings come from, and when it started. Environment variables from api-config, the ConfigMap. Started before my change. Restart count: zero. Kubernetes did keep the new value. Did it roll out a new pod for it? No. I asked the Deployment for its rollout history: the list of every version of its pod template. One revision. Nothing rolled out. A Deployment starts new pods only when its pod template changes. The template holds the ConfigMap's name, not its values. So a changed ConfigMap is not a change to the Deployment. An environment variable is a copy, taken at the start. Change the ConfigMap, and nothing that already runs will know.

So what if the settings were files instead? A ConfigMap can also be mounted as a volume: every key becomes a file in a folder. I added that to the deployment, as a folder called config. And I mounted the API version a second time, as a single file, with subPath. Changing the template started a rollout: a new pod, ready after twenty-two seconds. The deploy video showed what happens in those seconds. The new pod copied the ConfigMap as it was when it started, so it answered version two. Inside the new pod, I listed the mounted folder, to see what's really there. API version, log path, student name: one file for each key. Then I changed the ConfigMap again, to version three. A small script of mine applied it, then read four places every second, for three minutes: the file, the subPath file, the environment variable, and the app's answer. How long until the mounted file shows version three? About eighty seconds. Here is the run, three minutes squeezed into a few seconds. Applied at two seconds. The file switched to three at eighty-one. And after three minutes, the subPath file, the environment variable and the app still said version two. Mounted files do follow the ConfigMap, but not right away. The kubelet, the Kubernetes agent on every node, refreshes them on its own schedule. By default it checks once a minute, and it reads the ConfigMap from its own cache. So expect a minute or two, not a second. This series runs one Azure service at a time, for real. Subscribe to follow the series. And how does a file change without breaking a read halfway? I listed the folder again, with the details. The files are links into a hidden folder, called dot dot data. The kubelet writes a complete new folder, then switches that one link. That's why the subPath file never changed. subPath mounts the file itself, as it was at the start, not the link. So the switch never reaches it. And the app? It reads its version from the environment variable, once, at startup. The file changed under it, and the app never looked. A mounted file only helps if your code reads the file, and reads it again. Files follow the ConfigMap, a minute or so later. Your app follows only if it reads them again.

So how does version three reach the app? The fix is one command: kubectl rollout restart. I ran it on the deployment. Restarted. Twenty-two seconds later, the new pod was ready. Then I asked the app again, the same way as before. Version three. And the subPath file said three as well. rollout restart doesn't touch the ConfigMap. I asked the deployment what the restart wrote into its pod template. One annotation: restartedAt, with the time of the restart. To the Deployment, that is a change. So it starts a new pod, and the new pod copies the current settings when it starts. A restart is the update. Change the ConfigMap, then restart the deployment.

To sum it up, before the naming trick: environment variables are copied when the container starts, and never again. Mounted files follow the ConfigMap, a minute or so later, except with subPath. And your app sees a change only when it reads again, or when a restart starts a new pod.

And the naming trick? I created a new ConfigMap for the next settings, called api-config-v4, marked it immutable, and pointed the deployment at that name. The name is part of the pod template. So the deployment rolled out on its own, and the app answered version four. Every settings change is now a revision. So I ran rollout undo. Then I asked the app again. Version three. The old settings came back with the old template. And the immutable one can't be changed by mistake. I tried to apply a new value to it anyway. Forbidden: field is immutable. And you don't rename by hand. Kustomize, built into kubectl, can add a hash of the contents to the name, and update the deployment to match. New settings, new name.

The video on screen deploys to Azure Container Apps, which has the same trap with secrets, and its own fix. 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