Eine private API braucht ein Gateway vor Ort? Ein selbst gehostetes API-Management-Gateway bereitstellen
Ein selbst gehostetes Gateway läuft in der Nähe deiner API. Konfiguriert wird es weiterhin in Azure API Management. Eine Lagerverwaltungs-API kann beispielsweise im Netzwerk des Lagers bleiben. Lokale Anwendungen rufen das Gateway vor Ort auf. Es wendet APIM-Richtlinien an und leitet Anfragen an die API weiter. Diese Anfragen müssen nicht über das von Azure gehostete Gateway laufen.
Das Gateway ist ein Container, den du betreibst. Azure liefert die Konfiguration; du kümmerst dich um Laufzeitumgebung, Netzwerk, Updates und Verfügbarkeit. Der Gateway-Eintrag im Portal allein startet noch keinen Container.
In diesem Lab stellt dein Computer den privaten Standort dar:
Azure-APIM-Konfiguration → lokaler Gateway-Container
curl auf deinem Computer → localhost:8080 → Gateway → privater API-Container
Das Backend liefert eine kleine JSON-Antwort in einem Docker-Netzwerk und veröffentlicht keinen Port auf dem Host. Das Gateway veröffentlicht nur 127.0.0.1:8080. Andere Computer können darüber keine Verbindung herstellen. Das demonstriert lokale Konnektivität und ist kein Authentifizierungskonzept für den Produktivbetrieb.
Lab vorbereiten
Du brauchst Docker mit Linux-Containern, ein Terminal mit Bash und curl auf demselben Computer. Nutze Terminal unter macOS/Linux oder ein WSL-Bash-Terminal mit aktivierter Docker-Integration unter Windows. Führe alle Shell-Befehle dort aus, nicht in Azure Cloud Shell. Prüfe zuerst docker info: Der Befehl muss eine laufende Docker-Engine erreichen.
Das Gateway-Image benötigt Linux x86-64. Auf ARM-Computern wie einem Apple-Silicon-Mac muss Docker amd64-Emulation unterstützen. Der folgende Befehl fordert diese Architektur ausdrücklich an. Ohne Emulation brauchst du einen x86-64-Rechner mit Docker.
Die bisherige Consumption-APIM-Instanz unterstützt diese Gateway-Konfiguration nicht. Erstelle eine separate Instanz im Tarif Developer (klassisch). Developer ist für Entwicklung und Tests gedacht und verursacht Kosten, solange die Instanz bereitgestellt ist – auch bei gestoppten lokalen Containern. Prüfe den Preis im Portal und lösche die Instanz nach dem Lab. Ein Kubernetes-Cluster oder eine Azure-VM ist für diesen Ansatz nicht nötig.
APIM-Instanz erstellen
Öffne im Azure-Portal API Management services → Create:
Subscription: CloudTrips TEST
Resource group: rg-cloudtrips-selfhosted-test-neu (neu)
Region: North Europe
Resource name: apim-ctselfhosted-neu (muss weltweit eindeutig sein)
Organization name: CloudTrips
Administrator email: deine E-Mail-Adresse
Pricing tier: Developer (klassisch)
Verwende öffentliche Konnektivität ohne VNet-Integration. Erstelle den Dienst und warte, bis er bereit ist. Die Bereitstellung einer dedizierten APIM-Instanz kann länger dauern. Die bisherige Consumption-Instanz bleibt separat.
Privates Backend starten
Das lokale Lab liegt unter examples/apim-selfhosted in deinem cloudtrips-Projekt; die Befehle setzen ~/Development/cloudtrips als Projektpfad voraus (passe ihn bei Bedarf an). Führe im Bash-Terminal deines Computers Folgendes aus:
mkdir -p ~/Development/cloudtrips/examples/apim-selfhosted/backend
cd ~/Development/cloudtrips/examples/apim-selfhosted
printf '%s\n' '{"service":"private-orders","location":"local-docker","status":"ok"}' > backend/status.json
docker network create ct-apim-local
docker run -d --name ct-private-api \
--network ct-apim-local \
--mount "type=bind,src=$PWD/backend,dst=/usr/share/nginx/html,readonly" \
nginx:stable-alpine
ct-private-api ist der Docker-DNS-Name des Backends. Container im Netzwerk ct-apim-local erreichen es unter http://ct-private-api. Ohne -p veröffentlicht das Backend keinen Host-Port. Das Gateway erreicht es über das gemeinsame Docker-Netzwerk.
Falls ein Container oder Netzwerk bereits existiert, prüfe docker ps -a und docker network ls. Verwende deine vorhandene Lab-Umgebung oder entferne nur die genannten Lab-Ressourcen, bevor du die Befehle erneut ausführst.
API in Azure definieren
Öffne in der neuen APIM-Instanz APIs → APIs → Add API → HTTP (die Option für eine leere HTTP-API):
Display name: CloudTrips Private Orders
Name: ct-private-orders
Web service URL: http://ct-private-api
API URL suffix: private-orders
Erstelle die API. Wähle unter Settings → URL scheme / Protocols HTTP oder HTTP and HTTPS und speichere: Der lokale Test ruft http://127.0.0.1:8080 auf; eine reine HTTPS-API passt nicht zu dieser Anfrage und liefert 404. Lass unter Settings die Products leer, setze Subscription required → Off und speichere. Subscription required → Off bedeutet, dass kein APIM-Abonnementschlüssel erforderlich ist; dein Azure-Abonnement und die Abrechnung bleiben davon unberührt. Die Loopback-Bindung beschränkt den Gateway-Zugriff auf deinen Computer. Konfiguriere in einer echten Umgebung TLS und Client-Authentifizierung, bevor andere Rechner darauf zugreifen können.
Erstelle unter Design → Add operation:
Display name: Get status
Name: get-status
Method: GET
URL: /status.json
Speichere. Öffne All operations → policy code editor und trage ein:
<policies>
<inbound>
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
<set-header name="X-CloudTrips-Gateway" exists-action="override">
<value>self-hosted</value>
</set-header>
</outbound>
<on-error>
<base />
</on-error>
</policies>
Speichere. Der Antwort-Header zeigt später, dass das Gateway die Richtlinie angewendet hat. Die JSON-Datei im Backend enthält diesen Header nicht.

ct-private-api wird innerhalb von Docker aufgelöst. Das Azure-gehostete Gateway kann dieses lokale Backend nicht erreichen. Teste deshalb über die lokale URL unten und nicht über die standardmäßige Azure-Gateway-URL im Portal.
Gateway registrieren und starten
Öffne in der neuen APIM-Instanz Deployment + infrastructure → Self-hosted gateways (oder Gateways) → Add:
Name: gw-ctlocal
Region: local-lab
Description: CloudTrips local Docker gateway
APIs: CloudTrips Private Orders
Region ist hier eine beschreibende Standortangabe. Dadurch wird keine Azure-Ressource in dieser Region bereitgestellt. Ordne CloudTrips Private Orders ausdrücklich zu. Eine nicht zugeordnete API liefert über dieses Gateway 404.
Öffne gw-ctlocal → Deployment (gegebenenfalls unter Settings). Wähle Docker, verwende/erzeuge ein Token mit ausreichender Laufzeit für das Lab und lade env.conf herunter. Diese Datei enthält die Zugangsdaten des Gateways für den Konfigurationsabruf. Sie ist kein API-Schlüssel für Clients. Halte sie privat und aus Screenshots und Git heraus.
Verschiebe env.conf nach ~/Development/cloudtrips/examples/apim-selfhosted. Führe in Bash aus:
cd ~/Development/cloudtrips/examples/apim-selfhosted
chmod 600 env.conf
docker run -d --name ct-local-gateway \
--platform linux/amd64 \
--network ct-apim-local \
-p 127.0.0.1:8080:8080 \
--env-file ./env.conf \
mcr.microsoft.com/azure-api-management/gateway:v2
docker ps
docker logs --tail 30 ct-local-gateway
Der Befehl nutzt die Portal-Konfigurationsdatei, verbindet das Gateway mit dem Backend-Netzwerk und veröffentlicht dessen HTTP-Port auf localhost. Das Tag v2 folgt den Updates der Gateway-Version 2 und eignet sich für dieses Lab. Verwende im Produktivbetrieb eine getestete, festgelegte Image-Version.
Der Container benötigt ausgehenden HTTPS-Zugriff auf
Aktualisiere Overview des Gateways in Azure, bis eine verbundene, fehlerfreie Instanz beziehungsweise ein aktueller Heartbeat angezeigt wird. Der erste Konfigurationsabruf und die Statusmeldung benötigen etwas Zeit.

Lokale Route testen
Führe auf demselben Computer aus:
curl -i --max-time 30 http://127.0.0.1:8080/private-orders/status.json
Erwartet werden HTTP 200, der Header X-CloudTrips-Gateway: self-hosted (die Groß-/Kleinschreibung kann abweichen) und:
{"service":"private-orders","location":"local-docker","status":"ok"}
Damit siehst du, dass das lokale Gateway das Backend erreicht und die in Azure konfigurierte Richtlinie angewendet hat. Du hast nicht die APIM-Gateway-Adresse unter azure-api.net aufgerufen.
Ändere für einen kurzen Konfigurationstest in Azure unter All operations den Header-Wert zu self-hosted-updated. Speichere, warte auf die Synchronisierung und führe curl erneut aus. Der geänderte Antwort-Header zeigt, dass die Konfiguration von Azure zum lokalen Gateway gelangt, während das Backend lokal bleibt.

Wenn der Aufruf fehlschlägt
- Connection refused: Prüfe Docker,
docker ps, die Container-Logs und ob Port 8080 bereits belegt ist. Führe curl auf dem Docker-Host aus, nicht in Cloud Shell. - 404: Die API muss HTTP erlauben, nicht nur HTTPS. Prüfe anschließend die API-Zuordnung zu gw-ctlocal, das Suffix private-orders und die Operation GET /status.json. Warte auf die Synchronisierung.
- 401: Bei dieser neuen API muss Subscription required auf Off stehen. Dieses Lab nutzt nicht die vorherige OAuth-API oder deren Richtlinien.
- 502/anderer Backend-Fehler: Beide Container müssen in ct-apim-local sein. Die Backend-URL lautet http://ct-private-api, nicht localhost. Im Gateway-Container bezeichnet localhost das Gateway selbst.
- Gateway nicht fehlerfrei: Prüfe
docker logs --tail 50 ct-local-gateway, Token-Ablauf, env.conf, DNS und ausgehende HTTPS-Verbindungen. Lade bei abgelaufenen Zugangsdaten eine neue Datei herunter und erstelle den Gateway-Container neu. Veröffentliche beim Teilen von Logs niemals das Token.
Aufräumen
Im lokalen Bash-Terminal:
docker rm -f ct-local-gateway ct-private-api
docker network rm ct-apim-local
rm -f ~/Development/cloudtrips/examples/apim-selfhosted/env.conf
Lösche auch weitere heruntergeladene Kopien von env.conf. Das kleine Verzeichnis mit der Backend-JSON-Datei kannst du entfernen, sobald du es nicht mehr brauchst.
Lösche anschließend in Azure rg-cloudtrips-selfhosted-test-neu. Nur den Gateway-Eintrag oder die lokalen Container zu entfernen, beendet die Kosten der Developer-APIM-Instanz nicht. Deine frühere APIM-Instanz liegt in einer anderen Ressourcengruppe.