• English icon
  • German icon
Erklärvideo

Gleicher Tag, anderes Image: Azure Container Registry erklärt

· Reihe: Container auf Azure

azureazure-container-registrydockeracr-tasksdevops

Das Video ist auf Englisch.

Eine Anwendung skaliert hoch. Ein neuer Server startet, zieht wie alle anderen inference-api:latest und führt anderen Code aus. Niemand hat etwas deployt: Jemand hat ein neues Image unter demselben Tag gepusht.

Dieses Erklärvideo zeigt, wie Azure Container Registry (ACR) Images speichert, warum ein Tag keine Version ist, wie ACR Tasks Images in der Cloud baut und aktuell hält, und ein Schema für Tags und Aufräumen, mit dem Produktion vorhersagbar bleibt.

Das Wichtigste

  • Ein Tag ist ein Etikett, das wandern kann. Ein Digest ist ein Fingerabdruck, der sich nie ändert. Wer einen vorhandenen Tag erneut pusht, verschiebt ihn auf das neue Image; das alte Image bleibt in der Registry, ohne Tag.
  • ACR Tasks baut in Azure. az acr build lädt den Quellcode hoch, baut das Image und pusht es, ohne Docker auf dem eigenen Rechner. Tasks können außerdem bei einem Commit, nach Zeitplan oder bei einem Patch des Base Images neu bauen.
  • Stabile Tags für Base Images, eindeutige Tags für Deployments. latest bleibt aus der Produktion heraus, und das deployte Image wird gesperrt.
  • acr purge löscht die Tags, die Ihr Filter auswählt, nicht nur Images ohne Tag. Für verwaiste Images allein gibt es --untagged-only, und jeder neue Purge-Befehl läuft zuerst mit --dry-run.

Wie Images gespeichert werden

EbeneWas sie enthältBeispiel
Registryalles, hinter einer Adresse: dem Login-Servermyacr.azurecr.io
Repositoryalle Versionen eines Images; Schrägstriche gruppieren Repositories nach Team oder Umgebungproduction/inference-api
Artefaktein Container-Image, ein Helm-Chart oder ein anderes OCI-Artefaktinference-api:v1.2.0

Berechtigungen lassen sich auf ein Repository oder ein Namespace-Präfix beschränken, sodass ein Team nur seine eigenen Images erreicht.

Ein Image ist ein Stapel aus Layern. Jede Dockerfile-Anweisung, die Dateien ändert, fügt einen hinzu. Layer werden nach ihrem Inhalt gespeichert: Teilen sich zehn Images denselben Python-Base-Layer, speichert die Registry ihn einmal, und ein Server, der ihn schon hat, lädt ihn nicht erneut herunter. Obendrauf liegt das Manifest. Es listet alle Layer auf und wird über einen Digest identifiziert, einen SHA-256-Hash.

Die drei Tarife (Basic, Standard, Premium) pushen und pullen gleich. Premium ergänzt Funktionen für größere Umgebungen, etwa Georeplikation in andere Regionen und Private Endpoints.

Tags und Digests

Ein Tag wie v1.2.0 oder latest ist ein lesbarer Name. Ein Image kann mehrere Tags tragen, zum Beispiel 1.2.0 und stable. Aber ein Tag kann wandern: Wird ein neues Image mit einem vorhandenen Tag gepusht, springt der Tag auf das neue Image. Genau das ist dem neuen Server oben passiert. Und fehlt der Tag ganz, nimmt Docker latest.

Ein Digest wird aus dem Inhalt berechnet und kann deshalb nie auf ein anderes Image zeigen:

# Per Tag: worauf der Tag gerade zeigt
docker pull myacr.azurecr.io/inference-api:v1.2.0
# Per Digest: immer genau dieses Image
docker pull myacr.azurecr.io/inference-api@sha256:<digest>

Tags sind für Menschen. Digests sind für Garantien.

Builds in der Cloud mit ACR Tasks

Lokal bauen und pushen funktioniert, bis fünf Entwickler dasselbe Image auf fünf Laptops bauen. Ein Quick Task verlegt den Build nach Azure:

az acr build --registry myacr \
--image inference-api:v1.0.0 .

Das letzte Argument ist der Build-Kontext: ein lokaler Ordner, ein Git-Repository oder ein Remote-Tarball. Der Befehl lädt ihn hoch, führt den Docker-Build in Azure aus und pusht das Ergebnis, wie docker build plus docker push. Jeder Build läuft in derselben Umgebung, und ACR Tasks baut Images für Linux, Windows und ARM.

Ein schneller Smoke-Test startet einen Container aus der Registry in der Cloud und zeigt seine Ausgabe:

az acr run --registry myacr \
--cmd '$Registry/inference-api:v1.0.0 python --version' \
/dev/null

/dev/null heißt „kein Quellkontext“. $Registry steht für Ihre Registry; ohne sie würde der Image-Name auf Docker Hub gesucht.

Builds, die von selbst starten

Ein mit az acr task create angelegter Task kann von selbst starten:

TriggerStartet einen Build, wennGut zu wissen
Quellcodeein Commit in einem GitHub- oder Azure-DevOps-Repository ankommtBuilds für Pull Requests sind möglich, standardmäßig aus
Timerein Cron-Ausdruck zutrifftder Zeitplan ist in UTC
Base Imagedas Base Image Ihrer Anwendung aktualisiert wirdstandardmäßig an; der Task lernt das Base Image beim ersten Lauf
az acr task create --registry myacr --name build-api \
--image 'inference-api:{{.Run.ID}}' \
--context https://github.com/org/api.git#main \
--file Dockerfile --git-access-token $PAT \
--schedule "0 0 * * *"

Der Base-Image-Trigger spart die meiste Arbeit. Bekommt python:3.12-slim einen Sicherheitspatch, ist jedes darauf gebaute Image veraltet; ACR Tasks baut sie automatisch neu. Das funktioniert mit Base Images in derselben Registry, in einer anderen Azure Container Registry oder in öffentlichen Repositories auf Docker Hub und in der Microsoft Container Registry. Ein Base Image in einer Azure-Registry löst den Rebuild sofort aus; öffentliche Base Images werden alle 10 bis 60 Minuten geprüft. Verfolgt wird nur das Base Image der letzten Stage.

Eine Bedingung: Das Base Image muss einen stabilen Tag behalten, etwa 3.12-slim. Kommt der Patch unter einem neuen Tag, wird nichts ausgelöst.

Für mehrere Schritte wird ein Task in YAML beschrieben. Dieser baut das Image, führt darin die Tests aus und pusht erst am Ende:

version: v1.1.0
steps:
- build: -t $Registry/inference-api:$ID .
- cmd: $Registry/inference-api:$ID python -m pytest
- push:
- $Registry/inference-api:$ID

Die Schritte laufen nacheinander, und ein fehlgeschlagener Schritt lässt den ganzen Lauf scheitern. Ein Image mit roten Tests wird also nie gepusht. $ID ist die Run-ID: Jeder Build bekommt seinen eigenen, eindeutigen Tag.

Tags für die Produktion

Stabile TagsEindeutige Tags
Wiederverwendet?ja, sie wandern zu jeder neuen Versionnie
Beispiele1 (neueste 1.x), 1.1 (neuester Patch von 1.1)Build-ID, Zeitstempel, Commit-Hash
Richtig fürBase Images: Patches fließen einDeployments: Jeder Server zieht dasselbe Image, ein Rollback ist der vorherige Tag

Die Build-ID ist meist der beste eindeutige Tag, denn sie führt direkt zum Pipeline-Lauf mit Logs und Testergebnissen. Ein Commit-Hash kann sich wiederholen: Ein Rebuild wegen des Base Images nutzt denselben Commit.

Die Regel: stabile Tags für Base Images, eindeutige Tags für Deployments, und latest bleibt aus der Produktion heraus.

Danach schützen Sie, was läuft. Ein gesperrtes Image lässt sich nicht versehentlich überschreiben oder löschen, bis es wieder entsperrt wird:

az acr repository update --name myacr \
--image inference-api:v1.2.0 --write-enabled false

Aufräumen, ohne die Produktion zu löschen

Jeder verschobene Tag hinterlässt ein Image ohne Tag, und mit der Zeit füllen diese verwaisten Images die Registry und die Rechnung. Das Werkzeug zum Aufräumen ist acr purge (in Preview). Es läuft als Task in der Registry, bei Bedarf oder nach Zeitplan.

Der häufige Fehler: Der Filter wählt Tags aus. Purge löscht jeden passenden Tag, der älter ist als --ago, und --untagged entfernt zusätzlich die Images ohne Tag. --filter 'inference-api:.*' --untagged --ago 30d löscht also jeden Tag in diesem Repository, der älter als 30 Tage ist, auch den in Produktion, sofern er nicht gesperrt ist.

Nur die verwaisten Images, und zuerst als Vorschau:

az acr run --registry myacr \
--cmd "acr purge --untagged-only --ago 7d --dry-run" \
/dev/null

Im Premium-Tarif löscht eine Aufbewahrungsrichtlinie (Retention Policy) Images ohne Tag automatisch nach einer festgelegten Zahl von Tagen (noch in Preview). Eine Warnung für beides: Ein System, das per Digest zieht, braucht womöglich ein Image ohne Tag, und das Aufräumen löscht Images ohne Tag. Eindeutige Tags vermeiden das Problem.

Weiterführende Links

Video-Transkript (Englisch)

Your app scales out. A new server starts and pulls the same image as the others: inference-api, tag latest. But the new server runs different code than the rest. Nobody deployed anything. Someone just pushed.

In this video, you will see how Azure Container Registry stores images, why a tag is not the same as a version, how to build images in the cloud and keep them patched, and a tagging scheme that keeps production predictable.

Start with the structure. Azure Container Registry is a private registry for container images, run by Azure. At the top is the registry itself. It has one address, called the login server: your registry name, followed by azurecr.io. Inside the registry are repositories. A repository holds all versions of one image, for example inference-api. Repository names can contain slashes, like production/inference-api, or ml-team/model-server. These namespaces group images by team or environment. And access can be granted per repository, so a team only reaches its own images. Inside a repository are the artifacts: container images, but also Helm charts and other OCI artifacts. Now look inside one image. It is a stack of layers. Each instruction in your Dockerfile that changes files adds a layer. Layers are stored by their content. So when ten images share the same Python base layer, the registry keeps that layer only once. And a server that already has the layer doesn't download it again. On top of the layers sits the manifest. It lists every layer of the image, and it has a digest: a SHA-256 hash that identifies it. Registries come in three tiers: Basic, Standard and Premium. Pushing and pulling work the same in all of them. Premium adds features for larger setups, like geo-replication to other regions and private endpoints.

Now the most important idea in this video. A tag is a sticky note. A digest is a fingerprint. A tag, like version 1.2.0 or latest, is a readable name for an image. One image can carry several tags at once, for example 1.2.0 and stable. But tags can move. When you push a new image with a tag that already exists, the tag jumps to the new image. The old image stays in the registry, but now no tag points to it. It is untagged. That is exactly what happened at the start of this video. Someone pushed a new image as latest. The new server pulled latest a few minutes later, and got the new image. There is one more trap. If you leave out the tag completely, Docker uses latest automatically. A digest works differently. It is calculated from the content, so it can never point to another image. Pull by digest, with an @ sign and the SHA-256 hash, and you get exactly the same image every time. So: tags are for people. Digests are for guarantees.

So how do images get into the registry? The classic way is to build on your own machine with Docker, and push. That works, until five developers build the same image on five different laptops. ACR Tasks moves the build into Azure. The simplest form is a quick task, with one command: az acr build. You give it the registry, an image name with a tag, and a build context. The context is where your source files are: a local folder, a Git repository, or a remote tarball. The command uploads the context, runs the Docker build in Azure, and pushes the result to your registry. Like docker build plus docker push, but you don't need Docker on your machine at all. Every build runs in the same environment, no matter who starts it. And ACR Tasks can build images for Linux, Windows and ARM. You can also run a container from your registry in the cloud. az acr run takes an image and a command, for example python --version, and shows you the output. A quick smoke test before anything is deployed.

Quick tasks run when you call them. A task created with az acr task create can also start on its own. There are three kinds of triggers. First, a source code trigger. Point the task at a Git repository in GitHub or Azure DevOps, and every commit starts a build. Builds for pull requests are possible too, but they are off by default. Second, a timer. A cron expression in UTC, for example every night at midnight. And third, the trigger that saves the most work: the base image trigger. Your Dockerfile starts with a FROM line, for example python, tag 3.12-slim. When that base image gets a security patch, every image built on top of it is out of date. ACR Tasks learns the base image of your app the first time the task runs. From then on, when the base image is updated, it rebuilds your app automatically. This works when the base image is in the same registry, in another Azure container registry, or in a public repository on Docker Hub or the Microsoft Container Registry. A base image in an Azure registry triggers the rebuild right away. Public base images are checked every ten to sixty minutes. One condition matters: the base image must keep a stable tag, like 3.12-slim. If the patch arrives under a new tag, nothing is triggered. For more than one step, you describe the task in YAML. A multi-step task can build the image, run the tests inside it, and push it as the last step. A failed step fails the whole run, so an image with failing tests is never pushed. And the run ID gives every build its own unique tag.

That brings us back to tags, because there are two kinds, and each one has its job. Stable tags are reused. Think of them as version channels. Tag 1 always points to the newest version 1. Tag 1.1 points to the newest patch of 1.1. Stable tags are right for base images, because they let patches flow in. That is exactly what the base image trigger needs. Unique tags are never reused. Every push gets a new one: a build ID, a timestamp, or a commit hash. The build ID is usually the best choice, because it leads straight back to the pipeline run, with its logs and test results. A commit hash can repeat, because a base image rebuild uses the same commit. Unique tags are right for deployments. Every server pulls exactly the same image, and a rollback is just the previous tag. So the rule is simple: stable tags for base images, unique tags for deployments. And latest stays out of production. One more step protects what is running. Lock the deployed image by setting write-enabled to false. Now nobody can overwrite or delete that tag by accident, until you unlock it.

Every moved tag leaves something behind: an untagged image. Over time, these orphans fill your registry and your bill. The cleanup tool is acr purge. It runs as a task inside your registry, on demand or on a schedule. But read the filter carefully, because this is a common mistake. The filter selects tags. Purge deletes every matching tag that is older than the age you set. The untagged option removes untagged images in addition to those tags. So a filter for all tags in inference-api, with an age of thirty days, deletes every tag in that repository that is older than thirty days. Including the one in production, unless it is locked. If you only want the orphans, use the --untagged-only option. And run every new purge command with --dry-run first. It shows what would be deleted, without deleting anything. On the Premium tier, a retention policy can do this for you. It deletes untagged images automatically after a number of days you choose. It is still in preview. One warning: if a system pulls images by digest, the image it needs might have no tag at all, and cleanup deletes untagged images. Unique tags avoid that problem.

To sum it up. A registry holds repositories, and repositories hold images built from shared layers. A tag is a sticky note that can move. A digest is a fingerprint that never changes. ACR Tasks builds in the cloud, and rebuilds on a commit, on a schedule, or when the base image is patched. Stable tags for base images, unique tags for deployments, and a lock on what runs in production. And clean up with care, because purge deletes the tags your filter selects.

Now you know where your images live, and how to keep them under control. 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