API-Design für eine Multi-Cloud Management Plattform: Das Abstraktionsproblem lösen

2026-07-27

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:

KonzeptAWSAzureGCP
VM-GrößeInstance Type (t3.medium)VM Size (Standard_D2s_v3)Machine Type (e2-medium)
Netzwerk-IsolationVPCVirtual Network (VNet)VPC Network
Objekt-StorageS3 BucketBlob ContainerCloud Storage Bucket
Persistente IdentitätIAM RoleManaged IdentityService 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:

  1. Ein generisches Feld-Set, das über alle Provider hinweg funktioniert (CPU, RAM, Storage-Größe, Region, Tags)
  2. Ein extensions- oder metadata-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:

Credential-Management im Detail

Das Onboarding eines Cloud-Accounts ist ein sicherheitskritischer Vorgang, der besondere Sorgfalt verdient:

{
  "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:

  1. Ein Ingestion-Layer pro Provider, der die jeweiligen Billing-Exports/APIs periodisch abruft
  2. Eine normalisierte Kostentabelle mit gemeinsamen Dimensionen (provider, account_id, resource_id, service_category, cost_usd, period_start, period_end)
  3. 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:

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.

← Zurück zur Übersicht

Kommentare