Azure IoT Operations auf Arc-enabled Kubernetes - Troubleshooting Guide

Azure IoT Operations auf Arc-enabled Kubernetes - Troubleshooting Guide

Einleitung

Du hast Azure IoT Operations auf einem Azure Arc-enabled Kubernetes Cluster deployed - und auf einmal läuft etwas nicht so, wie es soll. Pods crashen, der MQTT Broker antwortet nicht, die Verbindung zu Arc ist unterbrochen, oder die OPC UA Assets senden keine Daten mehr.

Willkommen in der Realität von Edge-Deployments.

Troubleshooting in einer verteilten IoT-Umgebung ist komplex, weil viele Schichten zusammenwirken: Kubernetes, Azure Arc Agents, das IoT Operations Control Plane, der MQTT Broker, Zertifikate, Netzwerk-Policies und die Cloud-seitigen Azure-Services. Ein Problem auf einer Ebene kann sich als Symptom auf einer ganz anderen Ebene zeigen.

In diesem Post zeige ich dir:

  • Die richtigen Diagnose-Tools für Azure IoT Operations (nicht nur kubectl logs)
  • Die häufigsten Fehlerkategorien und wie du sie erkennst
  • Konkrete kubectl- und az-cli-Befehle für die Fehleranalyse
  • Observability mit Azure Monitor, Prometheus und Grafana aufbauen
  • Best Practices, damit viele Fehler erst gar nicht auftreten

Das wichtigste Tool zuerst: az iot ops check

Bevor du anfängst, manuell Logs zu durchsuchen, solltest du diesen Befehl kennen. Er ist der schnellste Einstieg in die Diagnose:

az iot ops check

az iot ops check wertet deinen Azure IoT Operations Deployment auf Health, Konfiguration und Nutzbarkeit aus. Er prüft alle AIO-Komponenten systematisch und gibt dir eine strukturierte Übersicht über gefundene Probleme.

Für ein detaillierteres Ergebnis:

az iot ops check --as-object  # JSON-Output für Scripting
az iot ops check --context <dein-kubectl-context>  # bei mehreren Clustern

Support-Bundle erstellen

Wenn das Problem tiefer liegt oder du Microsoft Support einbeziehen möchtest, erstellt dieser Befehl ein vollständiges Diagnose-Paket:

az iot ops support create-bundle

Das Bundle ist ein ZIP-Archiv mit Logs, Events, Custom Resource Status und Konfigurationen aller AIO-Komponenten. Ideal für die Kommunikation mit dem Support oder für eine gründliche Offline-Analyse.

💡 Voraussetzung: Azure CLI ab Version 2.53.0 und die azure-iot-ops CLI-Extension müssen installiert sein:

az extension add --upgrade --name azure-iot-ops

Kategorie 1: Azure Arc Agent Probleme

Symptome

  • Der Cluster taucht nicht im Azure Portal auf
  • az connectedk8s connect schlägt fehl oder hängt
  • Arc-Agents crashen oder starten nicht

Diagnose

Alle Azure Arc Agents laufen als Pods im Namespace azure-arc. Prüfe deren Status:

kubectl get deployments,pods -n azure-arc

Die Ausgabe sollte ungefähr so aussehen - alle Pods mit Running und vollständiger READY-Anzahl:

NAME                                         READY   STATUS    RESTARTS   AGE
pod/cluster-metadata-operator-...            2/2     Running   0          3d
pod/clusterconnect-agent-...                 3/3     Running   0          3d
pod/clusteridentityoperator-...              2/2     Running   0          3d
pod/config-agent-...                         1/2     Running   0          3d
pod/controller-manager-...                   2/2     Running   0          3d
pod/extension-manager-...                    3/3     Running   0          3d
pod/kube-aad-proxy-...                       2/2     Running   0          3d
pod/metrics-agent-...                        2/2     Running   0          3d
pod/resource-sync-agent-...                  2/2     Running   0          3d

Pods im Status CrashLoopBackOff oder Pending - das sind die Kandidaten:

kubectl describe pod <pod-name> -n azure-arc
kubectl logs <pod-name> -n azure-arc --previous  # Logs des letzten Crashes

Häufige Ursachen und Lösungen

Problem: clusterconnect-agent oder config-agent im CrashLoopBackOff

Prüfe, ob das MSI-Zertifikat vorhanden ist:

kubectl get secret -n azure-arc -o yaml | grep name:

Du solltest azure-identity-certificate in der Liste sehen. Fehlt es, ist die System-Assigned Managed Identity nicht korrekt eingerichtet. Lösung: Arc-Deployment löschen und neu verbinden:

az connectedk8s delete --name <cluster-name> --resource-group <rg>
az connectedk8s connect --name <cluster-name> -l <region> --resource-group <rg> \
  --enable-oidc-issuer --enable-workload-identity

Problem: kube-aad-proxy Pod fehlt oder startet nicht

Prüfe, ob das kube-aad-proxy-certificate Secret vorhanden ist:

kubectl get secret -n azure-arc -o yaml | grep kube-aad-proxy

Fehlt es: Arc-Deployment löschen und mit einem anderen Cluster-Namen neu verbinden.

Problem: Outbound Connectivity - Arc kann Azure nicht erreichen

Arc Agents benötigen ausgehende Verbindungen zu einer Reihe von Endpunkten. Prüfe, ob folgende Domains aus dem Cluster erreichbar sind:

# Von einem Pod im Cluster testen:
kubectl run curl-test --image=curlimages/curl --restart=Never --rm -it -- \
  curl -v https://management.azure.com

Pflicht-Endpunkte für Azure Arc:

  • management.azure.com
  • login.microsoftonline.com
  • mcr.microsoft.com
  • *.servicebus.windows.net (für Cluster Connect)
  • <region>.obo.arc.azure.com:8084

Wenn ein Proxy im Einsatz ist, muss er beim az connectedk8s connect angegeben werden:

az connectedk8s connect --name <cluster-name> --resource-group <rg> \
  --proxy-http http://proxy:port \
  --proxy-https http://proxy:port \
  --proxy-skip-range 169.254.169.254,10.0.0.0/8

Problem: UnauthorizedNamespaceError beim AIO-Deployment

Microsoft.ExtendedLocation resource provider does not have the required
permissions to create a namespace on the cluster.

Das Custom Locations Feature ist nicht korrekt aktiviert. Lösung:

export OBJECT_ID=$(az ad sp show --id bc313c14-388c-4e7d-a58e-70017303ee3b --query id -o tsv)
az connectedk8s enable-features -n <cluster-name> -g <rg> \
  --custom-locations-oid $OBJECT_ID \
  --features cluster-connect custom-locations

Kategorie 2: Azure IoT Operations Deployment schlägt fehl

Diagnose: Der AIO check-Befehl zuerst

az iot ops check

Danach, für spezifische Services:

az iot ops check --svc broker     # Nur MQTT Broker prüfen
az iot ops check --svc dataflow   # Nur Data Flows prüfen
az iot ops check --svc opcua      # Nur OPC UA Connector prüfen

Häufige Ursachen und Lösungen

Problem: LinkedAuthorizationFailed

The client has permission to perform action ... however, it does not have
permission to perform action(s) Microsoft.Authorization/roleAssignments/write

Der deploying Principal hat keine ausreichenden Berechtigungen. Für das AIO-Deployment wird die Rolle Azure IoT Operations Onboarding benötigt, die Microsoft.Authorization/roleAssignments/write beinhaltet.

az role assignment create \
  --assignee <principal-id> \
  --role "Azure IoT Operations Onboarding" \
  --scope /subscriptions/<sub-id>/resourceGroups/<rg>

Problem: MQTT Broker Deployment schlägt wegen Ressourcenmangel fehl

kubectl describe pod -l app=aio-broker -n azure-iot-operations
# Ausgabe prüfen auf: Insufficient memory / Insufficient cpu

Der Broker benötigt je nach Cardinality-Konfiguration erhebliche Ressourcen. Mindestanforderung für AIO: 4 vCPUs, 10 GB RAM. Prüfe den tatsächlichen Verbrauch:

kubectl top nodes
kubectl top pods -n azure-iot-operations

Problem: Namespace bleibt beim Löschen in Terminating hängen

Nie direkt den Namespace löschen! AIO Custom Resources haben Finalizers, die das blockieren. Immer den offiziellen Weg gehen:

az iot ops delete --name <instance-name> --resource-group <rg>

Kategorie 3: MQTT Broker Probleme

Diagnose

# Status aller Broker-Pods
kubectl get pods -n azure-iot-operations -l app=aio-broker

# Listener-Status anzeigen
kubectl get brokerlistener -n azure-iot-operations

# Details zu einem spezifischen Listener
kubectl describe brokerlistener <listener-name> -n azure-iot-operations

# Broker-Logs (Frontend)
kubectl logs -l app=aio-broker-frontend -n azure-iot-operations --tail=100

# Health Manager Logs (wichtig für TLS/Zertifikat-Probleme)
kubectl logs -l app=health-manager -n azure-iot-operations --tail=100

Häufige Ursachen und Lösungen

Problem: AllBrokersDown Fehler in Data Flow Logs

Global error: AllBrokersDown

Der Data Flow konnte 4-5 Minuten lang keine Nachrichten verarbeiten. Prüfe:

  1. Sind die richtigen Topic-Namen konfiguriert?
  2. Laufen alle Broker-Pods?
  3. Ist der BrokerListener korrekt konfiguriert?
kubectl get pods -n azure-iot-operations -l app=aio-broker
kubectl get brokerlistener -n azure-iot-operations -o yaml

Problem: TLS-Verbindung schlägt fehl - server certificate not found

kubectl logs -l app=health-manager -n azure-iot-operations | grep -i certificate

Typische Meldung: Server certificate server-cert-secret not found. Awaiting creation of secret.

Das referenzierte Kubernetes Secret für das TLS-Zertifikat existiert nicht. Prüfe:

kubectl get secret -n azure-iot-operations | grep tls

Fehlende Secrets entweder manuell erstellen (manuelles TLS) oder cert-manager konfigurieren (automatisches TLS).

Problem: MQTT-Verbindung von extern wird abgelehnt

Checkliste in dieser Reihenfolge prüfen:

# 1. Externe IP vorhanden?
kubectl get service -n azure-iot-operations

# 2. TCP-Verbindung erreichbar?
nc -zv <external-ip> 8883

# 3. TLS-Zertifikat prüfen
openssl s_client -connect <external-ip>:8883 -CAfile ca.crt

# 4. Listener-Konfiguration korrekt?
kubectl describe brokerlistener -n azure-iot-operations

# 5. BrokerAuthentication verknüpft?
kubectl get brokerauthentication -n azure-iot-operations -o yaml

Problem: Clients werden nach einiger Zeit getrennt

Azure IoT Operations trennt Clients automatisch, wenn deren Credentials ablaufen:

  • SAT-Clients (Kubernetes Service Account Token): Token läuft ab
  • X.509-Clients: Client-Zertifikat abgelaufen
  • Custom Auth-Clients: Ablaufzeit vom Custom Auth Server bestimmt

MQTT v5 Clients können sich mit einem neuen Credential re-authentifizieren, ohne die Verbindung zu trennen:

# Neues SAT-Token für Tests generieren
kubectl create token <service-account> -n azure-iot-operations --audience <audience>

Kategorie 4: OPC UA Connector Probleme

Diagnose

# OPC UA Connector Pods
kubectl get pods -n azure-iot-operations -l app.kubernetes.io/component=akri-agent

# Logs des OPC UA Connectors
kubectl logs -l app.kubernetes.io/component=aio-opc -n azure-iot-operations --tail=100

# Asset-Status in der Operations Experience prüfen
# https://iotoperations.azure.com

Häufige Ursachen und Lösungen

Problem: BadSecurityModeRejected - OPC UA Server Verbindung schlägt fehl

Der Connector versucht, sich mit einem OPC UA Server zu verbinden, der nur unsichere Endpoints ohne Security-Policy anbietet. Zwei Optionen:

Option A - Security explizit auf None setzen (nur für Tests!):

Im Device-Konfiguration in der Operations Experience oder per YAML:

additionalConfiguration:
  securityMode: none
  securityPolicy: "http://opcfoundation.org/UA/SecurityPolicy#None"

Option B - OPC UA Server um einen sicheren Endpoint erweitern und Certificate Trust konfigurieren. Das ist der empfohlene Weg für Produktionsumgebungen.

Problem: OPC PLC Simulator sendet keine Daten nach Device-Erstellung

Bekanntes Issue: Der Simulator erfordert, dass nicht vertrauenswürdige Server-Zertifikate automatisch akzeptiert werden. In der Operations Experience beim Device Endpoint die Option "Automatically accept untrusted certificates" aktivieren.

⚠️ Diese Einstellung nur in Entwicklungsumgebungen verwenden!

Problem: Resource Sync funktioniert nicht - Akri entdeckt keine Geräte

az iot ops enable-rsync -n <instance-name> -g <resource-group>

Wenn der CLI-User keine Berechtigung hat, die OID des K8 Bridge Service Principals abzufragen:

# OID manuell ermitteln
az ad sp list --display-name "K8 Bridge" --query "[0].appId" -o tsv

# Dann mit expliziter OID
az iot ops enable-rsync --k8-bridge-sp-oid <oid> -n <instance-name> -g <rg>

Kategorie 5: Azure Key Vault und Secret Management

Diagnose

# Secret Provider Class Status
kubectl get secretproviderclass -n azure-iot-operations

# Fehler beim Secret-Mount
kubectl describe pod <pod-name> -n azure-iot-operations | grep -A 10 Events

Problem: SecretNotFound in Key Vault

rpc error: ... GET https://<vault>.vault.azure.net/secrets/<name>/...
RESPONSE 404: SecretNotFound

Das Secret, das AIO synchronisieren soll, existiert nicht in Azure Key Vault. Das Secret muss vor der AIO-Konfiguration in Key Vault angelegt werden.

Problem: Key Vault Secrets Officer Berechtigungen fehlen

Wenn in der Operations Experience Secrets oder Zertifikate nicht hinzugefügt werden können:

az role assignment create \
  --assignee <entra-id-user-or-sp> \
  --role "Key Vault Secrets Officer" \
  --scope /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault-name>

Kategorie 6: Netzwerk und Firewall

Pflicht-Ports für Azure IoT Operations

Folgende Ports müssen ausgehend aus dem Cluster erreichbar sein:

Protokoll Port Zweck
HTTPS 443 Azure Management, MCR, ACR
AMQP 5671 Azure Service Bus / Event Hubs
MQTT+TLS 8883 External MQTT Clients (Inbound)
HTTPS 8084 <region>.obo.arc.azure.com (Arc Cluster Connect)

Für die ausgehenden Endpunkte von IoT Operations gelten zusätzlich die Arc-enabled Kubernetes Network Requirements.

Netzwerk-Debugging

# DNS-Auflösung im Cluster testen
kubectl run dns-test --image=busybox --restart=Never --rm -it -- \
  nslookup management.azure.com

# Ausgehende Verbindungen prüfen
kubectl run curl-test --image=curlimages/curl --restart=Never --rm -it -- \
  curl -v https://management.azure.com

# Netzwerk-Policy prüfen
kubectl get networkpolicy -n azure-iot-operations
kubectl get networkpolicy -n azure-arc

Observability aufbauen: Azure Monitor + Prometheus + Grafana

Das beste Troubleshooting beginnt, bevor Fehler auftreten. Azure IoT Operations unterstützt eine vollständige Observability-Stack aus Prometheus-Metriken, Container Insights Logs und Grafana-Dashboards.

Setup in 3 Schritten

Schritt 1: Azure-Ressourcen erstellen

# Azure Monitor Workspace für Metriken
az monitor account create \
  --name <workspace-name> \
  --resource-group <rg> \
  --location <region>

# Azure Managed Grafana
az grafana create \
  --name <grafana-name> \
  --resource-group <rg>

# Log Analytics Workspace für Container Insights
az monitor log-analytics workspace create \
  -g <rg> \
  -n <logs-workspace-name>

Schritt 2: Metrics Collection am Arc-Cluster aktivieren

# Prometheus-Metriken aktivieren
az k8s-extension create \
  --name azuremonitor-metrics \
  --cluster-name <cluster-name> \
  --resource-group <rg> \
  --cluster-type connectedClusters \
  --extension-type Microsoft.AzureMonitor.Containers.Metrics \
  --configuration-settings \
    azure-monitor-workspace-resource-id=<monitor-workspace-id> \
    grafana-resource-id=<grafana-id>

# Container Insights Logs aktivieren
az k8s-extension create \
  --name azuremonitor-containers \
  --cluster-name <cluster-name> \
  --resource-group <rg> \
  --cluster-type connectedClusters \
  --extension-type Microsoft.AzureMonitor.Containers \
  --configuration-settings \
    logAnalyticsWorkspaceResourceID=<log-analytics-id>

Schritt 3: OpenTelemetry Collector deployen

AIO exportiert Metriken über OpenTelemetry. Installiere den Collector mit Helm:

helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts
helm repo update
helm upgrade --install aio-observability open-telemetry/opentelemetry-collector \
  -f otel-collector-values.yaml \
  --namespace azure-iot-operations

Nach dem Setup: Im AIO GitHub Repository gibt es fertige Grafana Dashboard-JSON-Dateien für AIO, die du direkt importieren kannst.

Nützliche Log-Queries in Azure Log Analytics

// AIO Fehler der letzten Stunde
ContainerLog
| where TimeGenerated > ago(1h)
| where Namespace == "azure-iot-operations"
| where LogEntry contains "error" or LogEntry contains "Error"
| project TimeGenerated, ContainerName, LogEntry
| order by TimeGenerated desc

// MQTT Broker Verbindungsfehler
ContainerLog
| where TimeGenerated > ago(1h)
| where ContainerName contains "aio-broker"
| where LogEntry contains "connection" or LogEntry contains "auth"
| project TimeGenerated, ContainerName, LogEntry
| order by TimeGenerated desc

// Arc Agent Events
KubeEvents
| where TimeGenerated > ago(1h)
| where Namespace == "azure-arc"
| where Reason != "Pulled" and Reason != "Started"
| project TimeGenerated, Name, Reason, Message
| order by TimeGenerated desc

Best Practices: Fehler von vornherein vermeiden

✅ 1. AIO immer mit az iot ops delete deinstallieren

Niemals den azure-iot-operations Namespace direkt löschen. Die Custom Resources haben Finalizers, die den Namespace in einem dauerhaften Terminating-Zustand hinterlassen können:

# Richtig:
az iot ops delete --name <instance-name> --resource-group <rg>

# Falsch:
kubectl delete namespace azure-iot-operations  # ❌

✅ 2. Default-BrokerListener niemals anfassen

Der Default-Listener (ClusterIP, Port 18883) ist für die interne Kommunikation zwischen AIO-Komponenten reserviert. Für externe Verbindungen immer einen neuen Listener erstellen.

✅ 3. az iot ops check in CI/CD integrieren

Nach jedem Deployment oder Update ausführen, um Probleme früh zu erkennen:

az iot ops check --as-object | jq '.status'

✅ 4. Netzwerk-Anforderungen vor dem Deployment prüfen

Azure Arc und AIO benötigen ausgehende Verbindungen zu einer Reihe von Azure-Endpunkten. Führe vor dem Deployment einen Connectivity-Check durch:

az connectedk8s troubleshoot --name <cluster-name> --resource-group <rg>

✅ 5. Ressourcen-Anforderungen realistisch planen

Mindestanforderungen für AIO auf einem Produktionscluster:

  • RAM: 16 GB (10 GB für AIO)
  • CPU: 4 vCPUs
  • Für Multi-Node mit Fault Tolerance: 32 GB RAM, 8 vCPUs empfohlen

✅ 6. Observability von Anfang an aktivieren

Nicht erst aktivieren, wenn es brennt. Der Observability-Stack aus Azure Monitor, Prometheus und Grafana sollte Teil jedes AIO-Deployments sein.

✅ 7. Auto-Upgrade deaktivieren

Beim initialen Arc-Connect --disable-auto-upgrade setzen und Updates kontrolliert manuell durchführen:

az connectedk8s connect --name <cluster> --resource-group <rg> \
  --disable-auto-upgrade

Schnell-Referenz: Die wichtigsten Debugging-Befehle

# ═══ AIO Gesamt-Health ═══
az iot ops check

# ═══ Support Bundle erstellen ═══
az iot ops support create-bundle

# ═══ Arc Agents ═══
kubectl get pods -n azure-arc
kubectl logs <pod> -n azure-arc

# ═══ AIO Pods ═══
kubectl get pods -n azure-iot-operations
kubectl top pods -n azure-iot-operations

# ═══ MQTT Broker ═══
kubectl get brokerlistener -n azure-iot-operations
kubectl logs -l app=aio-broker-frontend -n azure-iot-operations --tail=100
kubectl logs -l app=health-manager -n azure-iot-operations --tail=100

# ═══ Kubernetes Events (sehr nützlich!) ═══
kubectl get events -n azure-iot-operations --sort-by='.lastTimestamp'
kubectl get events -n azure-arc --sort-by='.lastTimestamp'

# ═══ Custom Resource Status ═══
kubectl get instance -n azure-iot-operations -o yaml
kubectl get broker -n azure-iot-operations -o yaml
kubectl get brokerlistener -n azure-iot-operations -o yaml

# ═══ Zertifikate ═══
kubectl get certificate -n azure-iot-operations
kubectl get secret -n azure-iot-operations | grep tls

Fazit

Troubleshooting in Azure IoT Operations folgt einer klaren Logik: von oben nach unten. Beginne immer mit az iot ops check für einen schnellen Überblick, dann arbeite dich durch die Schichten - Arc Agents, AIO Control Plane, MQTT Broker, Konnektoren, Netzwerk.

Die häufigsten Fehlerquellen in der Praxis:

  • Berechtigungen: RBAC-Rollen fehlen bei Deployment oder Secret-Zugriff
  • Netzwerk: Ausgehende Verbindungen zu Azure-Endpunkten sind geblockt
  • Zertifikate: TLS-Zertifikate fehlen, sind abgelaufen oder enthalten nicht die richtige SAN
  • Ressourcen: Zu wenig RAM/CPU für den MQTT Broker

Und das Wichtigste: Observability nicht als Nachgedanken behandeln. Azure Monitor, Prometheus und Grafana sind keine Add-ons - sie sind die Grundlage dafür, dass du Probleme erkennst, bevor sie zum Ausfall werden.


Weiterlesen

Praxisprojekt: End-to-End Deployment mit Azure Arc-enabled Kubernetes: GitOps, Policy und Monitoring

Praxisprojekt: End-to-End Deployment mit Azure Arc-enabled Kubernetes: GitOps, Policy und Monitoring

Einleitung Freitagabend, 22:30 Uhr. Maren, Ops-Leiterin bei einem mittelständischen Logistikdienstleister, sitzt mit dem Laptop auf der Couch. In ihrem Slack explodiert es: Der Label-Service im Lager Hamburg druckt seit einer Stunde falsche Versandlabels. DHL-Pakete bekommen DPD-Labels. Die Nachtschicht stapelt Pakete, die nicht raus können. Maren weiß, was jetzt kommt.

Von Tim Steiner