API-Design für eine Multi-Cloud Management Plattform: Das Abstraktionsproblem lösen
Eine API für einen einzelnen Cloud Service Provider zu bauen ist schon anspruchsvoll. Eine API für eine Multi-Cloud Management Platform (MCMP) zu bauen, die AWS, Azure, GCP und weitere Provider unter einer einheitlichen Oberfläche vereint, bringt eine ganz eigene Herausforderung mit sich: die Abstraktion über fundamental unterschiedliche Provider-Konzepte hinweg. Dieser Beitrag beschreibt, wie man dieses Problem strukturiert angeht, mit konkreten Beispielen für Architektur, Datenmodell und Implementierung.
Das Kernproblem: Gleiche Sache, andere Namen, andere Fähigkeiten
Jeder Cloud-Provider modelliert dieselben Grundkonzepte unterschiedlich. Eine virtuelle Maschine heißt bei AWS “Instance”, bei Google “Compute Engine Instance”, bei DigitalOcean “Droplet”, bei einem europäischen Provider vielleicht “Server” oder “vServer”. Die verfügbaren Parameter, Limits und Zusatzfunktionen unterscheiden sich dabei ebenso wie die Terminologie.
Ein konkretes Beispiel für die Diskrepanz:
| Konzept | AWS | Azure | GCP |
|---|---|---|---|
| VM-Größe | Instance Type (t3.medium) | VM Size (Standard_D2s_v3) | Machine Type (e2-medium) |
| Netzwerk-Isolation | VPC | Virtual Network (VNet) | VPC Network |
| Objekt-Storage | S3 Bucket | Blob Container | Cloud Storage Bucket |
| Persistente Identität | IAM Role | Managed Identity | Service Account |
Eine MCMP-API muss diese Unterschiede verstecken, ohne dabei die Fähigkeiten der einzelnen Provider zu sehr zu beschneiden. Das ist der eigentliche Kern der Architektur.
Das Adapter-Pattern als Lösung
Das etablierte Muster dafür ist eine Provider-Abstraktionsschicht, die zwischen der einheitlichen API und den einzelnen Cloud-Providern vermittelt:
┌──────────────────────────────┐
│ Einheitliche MCMP-API │ ← z.B. POST /v1/instances
│ (OpenAPI-Spezifikation) │
└───────────────┬───────────────┘
│
┌────────┴─────────┐
│ Abstraction / │ ← übersetzt generische Requests
│ Provider-Layer │ in providerspezifische API-Calls
└────────┬─────────┘
┌─────────────┼─────────────┐
┌──▼───┐ ┌───▼───┐ ┌───▼───┐
│ AWS │ │ Azure │ │ GCP │ ← jeweils ein eigener
│Driver│ │Driver │ │Driver │ "Driver" / "Plugin"
└──────┘ └───────┘ └───────┘
Jeder Provider-Driver implementiert dasselbe Interface. In Pseudo-Code (angelehnt an TypeScript) sieht das etwa so aus:
interface ComputeDriver {
createInstance(spec: GenericInstanceSpec): Promise<OperationHandle>;
getInstance(id: string): Promise<GenericInstance>;
deleteInstance(id: string): Promise<OperationHandle>;
listInstances(filter: InstanceFilter): Promise<GenericInstance[]>;
getCapabilities(): DriverCapabilities;
}
class AwsComputeDriver implements ComputeDriver {
async createInstance(spec: GenericInstanceSpec) {
const ec2Params = this.mapToEc2RunInstances(spec);
const result = await this.ec2Client.runInstances(ec2Params);
return this.toOperationHandle(result);
}
// ...
}
Dieses Prinzip ist nicht neu erfunden. Es ist im Kern dasselbe Muster, das Terraform-Provider, der Kubernetes Cloud-Controller-Manager oder Crossplane verwenden. Es lohnt sich, diese Projekte als Referenz zu studieren, bevor man eine eigene Abstraktionsschicht entwirft.
Capability-Negotiation
Ein Punkt, der oft übersehen wird: Nicht jeder Provider unterstützt jede Funktion. GPU-Instanzen, bestimmte Storage-Klassen oder Spot-Pricing sind nicht überall verfügbar. Statt das im Frontend hart zu kodieren, sollte jeder Driver seine Capabilities deklarativ zurückgeben:
{
"provider": "gcp",
"capabilities": {
"spot_instances": true,
"gpu_instances": true,
"live_migration": true,
"max_instance_count": 10000
}
}
Die MCMP-API kann dann bei einer Anfrage, die eine nicht unterstützte Funktion nutzen will, einen klaren, früh abgefangenen Fehler zurückgeben (422 Unprocessable Entity mit Hinweis auf die fehlende Capability), statt einen kryptischen Provider-Fehler durchzureichen.
Kleinster gemeinsamer Nenner vs. Provider-Extensions
Die zentrale Design-Frage lautet: Wie viel gemeinsame Funktionalität bildet man ab, und wie viel providerspezifische Extras lässt man zu?
Ein reiner “kleinster gemeinsamer Nenner”-Ansatz führt dazu, dass die API die interessantesten Features der einzelnen Provider gar nicht nutzbar macht. Der bewährte Kompromiss besteht aus zwei Ebenen:
- Ein generisches Feld-Set, das über alle Provider hinweg funktioniert (CPU, RAM, Storage-Größe, Region, Tags)
- Ein
extensions- odermetadata-Feld pro Provider, über das providerspezifische Parameter durchgereicht werden können, ohne die generische Struktur zu verwässern
Beispiel für eine Request-Struktur, die beide Ebenen kombiniert:
{
"name": "web-server-01",
"region": "eu-central",
"size": { "vcpus": 4, "memory_gb": 16 },
"provider": "aws",
"provider_extensions": {
"aws": {
"placement_group": "cluster-a",
"ebs_optimized": true
}
}
}
So bleibt die API für einfache Use Cases provider-agnostisch, während fortgeschrittene Nutzer providerspezifische Fähigkeiten weiterhin nutzen können. Wichtig dabei: provider_extensions sollte niemals Pflichtfeld sein und die generische API muss auch ganz ohne dieses Feld voll funktionsfähig bleiben, sonst verschiebt sich die Komplexität nur in ein anderes Feld, statt sie zu lösen.
Ressourcenmodell einer MCMP
Über die klassischen CSP-Ressourcen (Compute, Storage, Networking, IAM) hinaus kommen bei einer MCMP typischerweise weitere Bereiche hinzu, die den eigentlichen Mehrwert der Plattform ausmachen:
- Provider-Verwaltung: Onboarding neuer Cloud-Accounts, Credential-Management pro Provider
- Kostenaggregation: Vereinheitlichte Kostenübersicht über alle Provider hinweg, oft das zentrale Verkaufsargument einer MCMP
- Orchestrierung/Blueprints: Templates, mit denen sich ganze Infrastruktur-Stacks providerübergreifend deployen lassen, ähnlich wie bei Terraform oder CloudFormation, aber providerunabhängig
- Policy & Governance: Regeln, die über alle Provider hinweg durchgesetzt werden (z. B. “keine Instanzen außerhalb der EU”)
- Vereinheitlichtes Monitoring: Metriken und Logs verschiedener Provider in einem gemeinsamen Format
Credential-Management im Detail
Das Onboarding eines Cloud-Accounts ist ein sicherheitskritischer Vorgang, der besondere Sorgfalt verdient:
- Credentials sollten niemals im Klartext in der eigenen Datenbank liegen, sondern verschlüsselt (z. B. über ein KMS wie AWS KMS, HashiCorp Vault oder Azure Key Vault)
- Wo möglich, sollten kurzlebige, delegierte Rollen genutzt werden statt statischer Access-Keys, etwa AWS IAM Cross-Account-Roles per
AssumeRole, Azure Managed Identities oder GCP Workload Identity Federation - Jeder Cloud-Account-Eintrag sollte einen Health-Check-Status besitzen (z. B.
credentials_valid,credentials_expired,insufficient_permissions), damit Nutzer proaktiv informiert werden, bevor eine Operation fehlschlägt
{
"cloud_account_id": "ca-771a",
"provider": "azure",
"auth_method": "managed_identity",
"status": "healthy",
"last_verified_at": "2026-07-27T09:00:00Z",
"granted_permissions": ["compute.read", "compute.write", "network.read"]
}
Kostenaggregation
Die Kostenaggregation über Provider hinweg ist technisch anspruchsvoller, als es zunächst wirkt, weil jeder Provider unterschiedliche Abrechnungsgranularität und -zyklen hat (AWS Cost Explorer, Azure Cost Management, GCP Billing Export, jeweils mit eigenem Datenmodell und eigener Latenz). Ein bewährter Ansatz:
- Ein Ingestion-Layer pro Provider, der die jeweiligen Billing-Exports/APIs periodisch abruft
- Eine normalisierte Kostentabelle mit gemeinsamen Dimensionen (
provider,account_id,resource_id,service_category,cost_usd,period_start,period_end) - Ein Tagging-Mapping, damit providerspezifische Tags (AWS Cost Allocation Tags, Azure Tags, GCP Labels) auf ein gemeinsames Tag-Schema abgebildet werden können
Da Billing-Daten oft mit 24 bis 48 Stunden Verzögerung ankommen, sollte die API das Alter der Daten transparent ausweisen (data_as_of Timestamp), statt Echtzeit-Genauigkeit vorzutäuschen.
Asynchronität wird durch Multi-Cloud noch wichtiger
Bei einer einzelnen CSP-API dauert eine Operation Sekunden bis Minuten. Bei einer MCMP, die im Hintergrund mehrere Provider-APIs orchestriert, können Operationen deutlich länger dauern und aus mehreren Teilschritten bestehen (z. B. VM erstellen bei Provider A, dann Netzwerk-Peering bei Provider B konfigurieren). Das Operation-Pattern aus der CSP-Welt bleibt hier essenziell, wird aber komplexer:
{
"operation_id": "op-4471",
"status": "running",
"steps": [
{ "name": "create_instance_aws", "provider": "aws", "status": "done" },
{ "name": "configure_network_azure", "provider": "azure", "status": "running" },
{ "name": "attach_storage_gcp", "provider": "gcp", "status": "pending" }
]
}
Ein detailliertes Sub-Status-Modell hilft Nutzern zu verstehen, in welchem Provider gerade was passiert. Das ist besonders wichtig, wenn ein Schritt fehlschlägt und man nachvollziehen muss, welcher Teil der Multi-Provider-Operation betroffen ist.
Orchestrierungsmuster: Saga statt verteilter Transaktion
Da es keine echte verteilte Transaktion über mehrere Cloud-Provider-APIs hinweg geben kann, empfiehlt sich das Saga-Pattern: Jeder Schritt hat eine definierte Kompensationsaktion, die ausgeführt wird, falls ein späterer Schritt fehlschlägt.
Schritt 1: VM bei AWS erstellen → Kompensation: VM löschen
Schritt 2: Netzwerk bei Azure peeren → Kompensation: Peering entfernen
Schritt 3: Storage bei GCP anhängen → Kompensation: (kein Rollback nötig, letzter Schritt)
Schlägt Schritt 2 fehl, wird automatisch die Kompensation von Schritt 1 ausgeführt, damit keine verwaisten Ressourcen (und damit unerwartete Kosten) zurückbleiben. Diese Kompensationslogik sollte pro Provider-Driver mitgeliefert werden, analog zur eigentlichen Erstellungslogik.
Fehlerbehandlung über Provider-Grenzen hinweg
Jeder Cloud-Provider hat eigene Fehlercodes und -formate. Eine MCMP-API muss diese in ein einheitliches Schema übersetzen (z. B. nach RFC 9457, dem aktuellen Standard, der RFC 7807 ablöst), sollte dabei aber die ursprüngliche Provider-Fehlermeldung nicht verschlucken:
{
"type": "https://api.example.com/errors/provider-error",
"title": "Provider operation failed",
"status": 502,
"detail": "AWS returned InsufficientInstanceCapacity",
"provider": "aws",
"provider_error_code": "InsufficientInstanceCapacity",
"trace_id": "9c31-77af-..."
}
So behält der Client die Möglichkeit, provider-spezifisch zu reagieren, ohne dass die generische API-Struktur aufgebrochen wird. Sinnvoll ist außerdem eine Fehler-Taxonomie, die zwischen folgenden Kategorien unterscheidet, damit Clients automatisiert reagieren können:
transient: Retry mit Backoff sinnvoll (z. B. Rate-Limit beim Provider)capacity: Provider hat aktuell keine Ressourcen frei, ggf. anderen Provider/Region vorschlagenpermission: Credential-Problem, erfordert manuelles Eingreifenvalidation: Anfrage selbst war fehlerhaft, Retry ohne Änderung ist sinnlos
Policy & Governance als API-First-Feature
Ein oft unterschätzter Baustein: Governance-Regeln sollten selbst über die API verwaltbar sein, nicht nur über eine UI-Konfiguration. Beispiel für ein Policy-Objekt:
{
"policy_id": "pol-889",
"name": "eu-only-deployment",
"rule": {
"condition": "region NOT IN ['eu-central', 'eu-west']",
"action": "deny"
},
"scope": { "applies_to": "all_projects" }
}
Solche Policies werden idealerweise vor dem Absenden einer Operation an den jeweiligen Provider ausgewertet (Fail-Fast), nicht erst nach dem Rückmelden eines Provider-Fehlers.
Fazit
Der entscheidende Unterschied zwischen einer CSP-API und einer MCMP-API liegt nicht in den Grundprinzipien guter API-Gestaltung, denn Idempotenz, Pagination und Versionierung gelten für beide gleichermaßen. Der Unterschied liegt in der zusätzlichen Abstraktionsschicht, die Provider-Vielfalt in eine konsistente Schnittstelle übersetzt, ohne die Fähigkeiten der einzelnen Provider zu verschütten, ergänzt um Sagas für providerübergreifende Operationen, sicheres Credential-Management und eine belastbare Kostenaggregation. Wer sich früh an etablierten Vorbildern wie Terraform-Providern oder Crossplane orientiert, spart sich viele Designfehler, die sonst erst nach dem Onboarding des dritten oder vierten Cloud-Providers sichtbar werden.
Kommentare
Zum Anzeigen und Schreiben von Kommentaren wird die selbst gehostete Software Comentario nachgeladen. Dabei wird ein technisch notwendiges Cookie gesetzt, siehe Datenschutzerklärung.