Container Agent Pool: Azure-Pipelines-Agents auf Container Apps Jobs¶
Diese Seite richtet sich an das Plattform-Team. Sie beschreibt das Addon cae-agents der Shared Services: einen selbst gehosteten Azure-Pipelines-
Agent-Pool, der auf Azure Container Apps jobs läuft. Agents starten pro eingereihtem pipeline-Job, führen genau diesen einen Job in einem frischen
Container aus und werden danach entfernt. Wenn nichts in der Warteschlange steht, läuft nichts, und es entstehen keine Kosten.
Die Sicht des Workload-Teams (wie du den Pool nutzt und welche Grenzen er hat) steht unter Build Agents im Netzwerk. Das Netzwerkdesign für Agents steht unter Build-Agent-Zugriff auf Spokes. Wer den Pool betreibt, hängt von deinem Betriebsmodell ab.
Referenzdesign, dein Deployment kann abweichen
Die folgenden Fakten stammen aus dem Terraform-Code, den Container-Image-Dateien und der README einer Referenzimplementierung. Namen, CIDR-Bereiche und die Tools im Image sind Beispiele.
Warum Container-basierte Agents¶
| Container-basiert (dieses Addon) | VM-basierter Pool | |
|---|---|---|
| Kosten | Du zahlst für die Sekunden, die ein Job läuft. Null im Leerlauf. | VMs laufen, egal ob pipelines laufen |
| Skalierung | Von 0 bis zu einem Maximum, pro eingereihtem Job | Feste Größe oder langsameres VM-Scale-out |
| Isolation | Ein frischer Container pro Job, kein Zustand bleibt zurück | Geteilter Zustand, außer die VM wird neu aufgesetzt |
| Wartung | Kein OS-Patching von VMs. Ein neues Image ist ein neuer tag. | Patching, Neustarts oder Re-Imaging |
| Startzeit | Etwa 50 Sekunden warm, 3 bis 4 Minuten kalt | Sofort bei Standby-Agents, langsam bei neuen VMs |
Das Container-Image enthält keinen Docker-Daemon, und der Container läuft nicht als root. Workloads, die einen Docker- Daemon oder root brauchen, brauchen einen anderen Pool (siehe Shared services).
So funktioniert es¶
flowchart TD
q["Pipeline job queued in the agent pool"] --> k["KEDA scaler<br/>polls the pool queue every 10 s"]
k --> j["Container Apps job execution<br/>(one per queued job)"]
j --> pull["Pull image from the registry<br/>(managed identity, AcrPull)"]
pull --> s["start.sh"]
s --> login["az login --identity<br/>token for Azure DevOps"]
login --> reg["Download agent, register in the pool"]
reg --> run["run.sh --once: run one job"]
run --> unreg["Remove the agent from the pool"]
unreg --> done["Container exits"]
Ein Placeholder-Agent wird nach dem Deployment einmal registriert. Ohne ihn hat der Pool nicht die Capability Agent.Version,
die von Microsoft gehostete Agents standardmäßig haben, und eingereihte Jobs werden nie einem Agent zugeordnet.
Was der Code deployt¶
| Ressource | Namensmuster | Zweck |
|---|---|---|
| Ressourcengruppe | rg-agent-pool-<workload>-<postfix> |
Enthält die Agent-Ressourcen |
| User-assigned managed identity | id-container-agent-<workload> |
Zieht das Image und spricht mit Azure DevOps |
| Rollenzuweisung | AcrPull auf der Registry | Erlaubt der Identität, das Agent-Image zu ziehen |
| Container Apps environment | cae-agent-<workload> |
Im spoke-Subnet, mit internem Load Balancer, Logs in den Log Analytics workspace des spokes. Die Identität wird auch auf dem environment gesetzt, weil die Control Plane den Registry-Zugriff beim Erstellen eines Jobs validiert. |
| Container Apps job (Agent) | caj-agent-<workload> |
Ereignisgesteuert. Eine Ausführung pro eingereihtem pipeline-Job. |
| Container Apps job (Placeholder) | caj-placeholder-agent-<workload> |
Manuell ausgelöst, registriert den Placeholder-Agent |
| Azure-DevOps-Agent-Pool | container_agent_pool_name |
Pool auf Organisationsebene. Auto-Provisioning und Auto-Update sind aus. |
| Service-Principal-Entitlement | die managed identity | Lizenzstufe express in Azure DevOps |
| Security-Rollenzuweisung | die managed identity | Administrator auf dem Agent-Pool, damit die Identität Agents registrieren und entfernen kann |
Einstellungen des Agent-Jobs im Code:
| Einstellung | Wert |
|---|---|
| Trigger | KEDA scaler azure-pipelines, Polling-Intervall 10 s, Ziel-Queue-Länge 1 pro Ausführung, authentifiziert mit der managed identity |
| Skalierung | container_agent_min_replicas (Default 0) bis container_agent_max_replicas (Default 25) gleichzeitige Ausführungen |
| Größe | 1 vCPU, 2 GiB Speicher pro Container |
| Zeitlimit | replica_timeout_in_seconds = 1800: Eine Ausführung wird nach 30 Minuten gestoppt. Retry-Limit 1. |
| Workload-Profil | container_agent_workload_profile, Default Consumption |
| Umgebungsvariablen | AZP_URL, AZP_POOL und MANAGED_IDENTITY_CLIENT_ID. Der Placeholder-Job setzt zusätzlich AZP_PLACEHOLDER, AZP_RANDOM_AGENT_SUFFIX und AZP_AGENT_NAME. |
Das 30-Minuten-Limit gilt für jeden pipeline-Job auf diesem Pool. Sag es den Workload-Teams, oder nutze für lange Jobs einen anderen Pool.
Variablen¶
| Variable | Bedeutung |
|---|---|
spoke_config |
Ressourcengruppe, virtuelles Netzwerk, Subnet für die Umgebung und Log Analytics workspace des spokes, jeweils per Name |
container_agent_pool_name |
Name des Azure-DevOps-Pools |
container_agent_azp_url |
URL der Azure-DevOps-Organisation |
container_agent_location und location |
Azure-Region |
container_agent_image_tag |
Tag des Images azp-agent in der Registry |
azure_container_registry |
Name, Ressourcengruppe und subscription einer bestehenden Registry, die das Image enthält |
workload_name |
Teil der Ressourcennamen (Default ccap) |
resource_postfix |
Teil des Ressourcengruppennamens |
container_agent_min_replicas, container_agent_max_replicas |
Skalierungsgrenzen (standardmäßig 0 und 25) |
container_agent_workload_profile |
Workload-Profil des environments und der Jobs |
Voraussetzungen¶
Azure
- Ein spoke-Subnet für das Container Apps environment, delegiert an
Microsoft.App/environments. Ohne die Delegation schlägt das Deployment des environments fehl. Dimensioniere das Subnet für die maximale Anzahl gleichzeitiger Agents. - Eine Registry mit dem Agent-Image, bei der der Admin-User deaktiviert ist. Die Referenz braucht mindestens das SKU Standard. Der Pool authentifiziert sich nur mit der managed identity, und das module weist ihr AcrPull zu.
- Der Log Analytics workspace des spokes.
- Firewall-Zugriff, siehe Netzwerk.
Azure DevOps
- Eine Service Connection, die Agent-Pools verwalten darf.
- Ein Personal Access Token für den Azure-DevOps-Terraform-Provider, abgelegt in der Variable Group der pipeline. Er wird nur genutzt, während Terraform läuft, um den Pool zu erstellen und der Identität ihre Lizenz und Rolle zu geben, nicht von den Agents zur Laufzeit. Er braucht die Scopes Agent Pools (Read & Manage) und Member Entitlement Management (Read & Write). Ein Personal Access Token läuft ab: Nimm ihn in dein Secret-Monitoring auf (Secret watcher).
- Verknüpfe nach dem Deployment den Pool auf Organisationsebene mit jedem project, das ihn nutzt: Project settings, Pipelines, Agent pools, Add pool, Existing pool.
Netzwerk¶
Im Referenzdesign ist der spoke mit einem Virtual-WAN-hub verbunden, und der hub kündigt dem spoke eine Default Route (0.0.0.0/0) an.
Der gesamte Internet-Traffic des Environment-Subnets läuft über die hub-Firewall.
| Anforderung | Detail |
|---|---|
| Erforderliche Firewallregel | Der CIDR des Environment-Subnets muss in einer IP group stehen, die eine Regel nutzt, die ausgehendes TCP 443 erlaubt. In der Referenz ist das die IP group der selbst gehosteten Agents, siehe Build-Agent-Zugriff auf Spokes. |
| Benötigte Ziele | Der Microsoft-Entra-Anmeldeendpunkt (login.microsoftonline.com), die Registry (*.azurecr.io), der Azure-DevOps-Service und sein Agent-Download-Host sowie Azure Blob Storage (*.blob.core.windows.net) |
| Optional | Service Endpoints Microsoft.ContainerRegistry und Microsoft.Storage auf dem Subnet. Image Pulls und Blob-Traffic umgehen dann die Firewall, was die Verarbeitungskosten der Firewall und die Pull-Latenz senkt. Nicht nötig, wenn die Firewallregel den Traffic erlaubt. |
| HTTP (Port 80) | Im Referenzdesign von der Firewall blockiert. Der Image-Build kann deshalb nicht apt-get nutzen, das standardmäßig HTTP verwendet. |
Ohne die Regel schlägt terraform apply mit unable to pull image using managed identity fehl, weil das environment beim Erstellen eines Jobs einen Live-
Test-Pull ausführt. Ausführungen schlagen mit ImagePullFailure fehl.
Die Regel muss in der deployten Firewall-Policy stehen
Es reicht nicht, den CIDR im Konfigurations-Repository zur IP group hinzuzufügen. Prüfe die deployte IP group.
Das Container Apps environment nutzt einen internen Load Balancer (internal_load_balancer_enabled = true). Agents verbinden sich nur
ausgehend mit Azure DevOps, das ist für die pipeline also keine Einschränkung.
Das Agent-Image¶
Das Image wird aus den Dateien in infra/cae-agents/azp-agent-in-docker/ gebaut:
| Punkt | Detail |
|---|---|
| Basis | Ein Microsoft-Devcontainer-Basisimage auf Ubuntu 22.04, aus der Microsoft Container Registry gezogen. Es bringt bereits curl, git, jq, unzip, sudo, wget, rsync und openssh-client mit. |
| Ergänzt | tini, Azure CLI (installiert mit pip) und PowerShell (eine fixierte Version aus dem Release-Archiv), alles über HTTPS |
| User | Ein Non-root-User agent |
| Entry Point | start.sh |
start.sh meldet sich mit az login --identity unter der Client-ID in MANAGED_IDENTITY_CLIENT_ID an, fordert ein Token für Azure
DevOps an, lädt das neueste Agent-Paket herunter, registriert den Agent (--replace), führt ihn mit --once aus und entfernt den Agent, wenn
der Container stoppt. Der Identity-Endpunkt von Container Apps wird als Umgebungsvariablen injiziert, und az login --identity
nutzt ihn, nicht den VM-Metadata-Endpunkt. Im Placeholder-Modus registriert es den Agent und beendet sich, ohne einen Job auszuführen.
Das Dockerfile im Repository hat windows im Namen, baut aber ein Linux-Image. Das Repository hat außerdem eine ältere Datei
mit dem Suffix _old.
So aktualisierst du das Image: Baue einen neuen tag und pushe ihn in die Registry, setze container_agent_image_tag und wende an. Halte das
Image klein, denn der Cold Start enthält das Ziehen des Images (in der Referenz etwa 490 MB). Die Tools im Image sind nur die oben genannten:
Workload-Teams installieren andere Tools in ihrer pipeline (Build Agents).
Deployment¶
- Stelle sicher, dass die Voraussetzungen erfüllt sind und der Image-tag in der Registry existiert.
- Führe die pipeline der Shared Services mit
deployCAEAgentsaus (Shared services). Der plan nutzt die Variablendatei des Addons. Das apply erstellt die Ressourcen. - Die pipeline startet danach den Placeholder-Job (
az containerapp job start). Wenn du anders deployst, starte ihn einmal von Hand. - Verknüpfe den Pool mit den projects (Voraussetzungen).
- Führe eine Test-pipeline auf dem Pool aus und prüfe die Agent-Informationen, die Tools und das Token der managed identity.
Startzeit und Kosten¶
| Szenario | Typische Zeit | Grund |
|---|---|---|
| Cold Start (erster Job, Node recycelt) | 3 bis 4 Minuten | Vollständiger Image Pull und Agent-Download (etwa 100 MB) |
| Warm Start (Node wiederverwendet) | Etwa 50 Sekunden | Image-Layer auf dem Node gecacht, nur die Registrierung ist neu |
Aufeinanderfolgende Jobs profitieren von einem warmen Node. Wenn Cold Starts nicht akzeptabel sind, setze container_agent_min_replicas auf 1. Ein
Container läuft dann immer, zu konstanten Kosten.
Sicherheit¶
- Jeder Job läuft in einem frischen Container, sodass Zugangsdaten und Dateien nicht von Job zu Job übernommen werden.
- Agents authentifizieren sich an der Registry und an Azure DevOps mit der managed identity, nicht mit Schlüsseln. Das module weist der Identität in Azure nur AcrPull zu. Was ein Job in Azure darf, kommt wie bei anderen Pools aus der Service Connection der pipeline (Build-Agent-Zugriff auf Spokes).
- Die Identität ist Administrator auf dem Pool. Begrenze, wer sonst Rechte auf dem Pool hat (Azure DevOps).
- Nutze für Production-Deployments einen separaten Pool, wie in den Design-Hinweisen empfohlen.
Fehlerbehebung¶
| Symptom | Wahrscheinliche Ursache und Aktion |
|---|---|
terraform apply schlägt mit unable to pull image using managed identity fehl, oder Ausführungen schlagen mit ImagePullFailure fehl |
Die Firewall blockiert das Subnet. Prüfe, dass der CIDR in der deployten IP group steht, zum Beispiel mit az network ip-group show --name <ip group> --resource-group <hub resource group> --subscription <hub-subscription-id> --query ipAddresses. Prüfe die Logs unten. |
| Nach dem Placeholder-Job erscheinen keine Agents im Pool | Prüfe den Status der letzten Ausführung des Placeholder-Jobs. Wenn sie fehlgeschlagen ist, prüfe die Logs. Wenn sie erfolgreich war, prüfe, dass container_agent_azp_url keinen abschließenden Schrägstrich hat und der Pool mit dem project verknüpft ist. |
| The pool does not exist or has not been authorized for use | Der Pool ist nicht mit dem project der pipeline verknüpft |
| Jobs bleiben in der Warteschlange und werden nie zugewiesen | Der Placeholder-Agent fehlt, der Pool hat daher keine Capability Agent.Version. Starte den Placeholder-Job. |
| Eine Ausführung wird nach 30 Minuten gestoppt | Das Zeitlimit des Jobs. Kürze den Job oder nutze einen anderen Pool. |
| Ein Job kann nicht starten, der Job ist suspendiert | Azure suspendiert einen Container Apps job nach wiederholten Fehlern. Behebe die Ursache und setze ihn dann fort, siehe unten. |
Frage die System-Logs des environments im Log Analytics workspace des spokes ab:
ContainerAppSystemLogs_CL
| where TimeGenerated > ago(30m)
| project TimeGenerated, Reason_s, Log_s
| order by TimeGenerated asc
Einen suspendierten Job fortsetzen:
az rest --method POST \
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<resource group>/providers/Microsoft.App/jobs/<job name>/resume?api-version=2023-05-01"
Verwandte Themen¶
- Build Agents im Netzwerk
- Build-Agent-Zugriff auf Spokes
- Shared services: was deployt wird und wie
- Plattform: Deployment-Tooling und Job Runners (die eigenen Runner der Plattform)