Zum Inhalt

S3 Buckets & Object Storage

Überblick

Die Polycrate API verwaltet S3-Cluster (technischer Speicher-Cluster, z. B. MinIO, Ceph RadosGW) und S3-Buckets (logische Einheiten für Anwendungsdaten) mit vollständigem Lifecycle (Create, Provision, Credentials, Capacity, Delete). Buckets können über Web-UI, API oder – seit CLI 0.35.0 – deklarativ über Kubernetes Custom Resources angelegt werden.

graph LR
    User[User / K8sApp] --> Bucket[S3Bucket]
    Bucket --> Cluster[S3Cluster<br/>MinIO / RadosGW]
    Bucket --> Creds[Credentials<br/>Access/Secret Key]
    Bucket --> Capacity[Capacity-Tracking]
Konzept Zweck
S3Cluster Einer technischer Storage-Cluster mit Endpoint, Region, Provider-Typ
S3Bucket Logischer Bucket innerhalb eines Clusters mit eigenem IAM-User und Credentials
S3Region Region-String, den User im Request angeben (Polycrate löst auf S3Cluster auf)

Buckets anlegen – drei Wege

1. Web-UI

Storage → S3 Buckets → New

Feld Wert
Name eindeutiger Bucket-Name (DNS-konform)
Region gewählte Region – wird zu einem konkreten S3-Cluster aufgelöst
Organization / Workspace automatische Zuordnung oder manuell wählbar
CORS Allow All optional, öffnet CORS für Public-Use-Cases
Labels / Annotations frei nutzbar für eigene Zwecke

Polycrate erzeugt beim Speichern synchron:

  1. Bucket auf dem S3-Cluster (MinIO oder RadosGW)
  2. IAM-User mit Access-/Secret-Key (nur für diesen Bucket)
  3. Credentials-Datensatz, der verschlüsselt gespeichert wird

2. REST API

POST /api/v1/s3/buckets/
{
  "name": "my-app-data",
  "region": "eu-central",
  "organization": "11111111-2222-3333-4444-555555555555",
  "workspace": "66666666-7777-8888-9999-000000000000",
  "cors_allow_all": false
}

Fehlt region, nutzt die API die Einstellung System Config → Default S3-Bucket-Cluster. Ist auch diese nicht gesetzt: HTTP 400 mit klarer Fehlermeldung.

3. Kubernetes Custom Resource (neu mit CLI 0.35.0)

apiVersion: polycrate.io/v1alpha1
kind: S3Bucket
metadata:
  name: my-app-data
  namespace: my-app
spec:
  region: eu-central            # optional; fällt auf Default zurück
  corsAllowAll: false
  credentialsSecretName: my-app-s3   # optional; Default: <cr-name>-s3

Sobald die CR im Cluster angewendet wird:

  1. Der Polycrate Operator erzeugt den Bucket in der Polycrate API (inkl. IAM-User und Credentials)
  2. Der Access-/Secret-Key wird als Kubernetes Secret im selben Namespace abgelegt:

    apiVersion: v1
    kind: Secret
    metadata:
      name: my-app-s3
      namespace: my-app
    type: Opaque
    stringData:
      AWS_ACCESS_KEY_ID: "..."
      AWS_SECRET_ACCESS_KEY: "..."
      AWS_ENDPOINT_URL_S3: "https://minio.eu-central.example.com"
      BUCKET_NAME: "my-app-data"
    
  3. Im Status der CR erscheinen die Phasen Pending → Provisioning → Ready

  4. Beim Löschen der CR (kubectl delete s3bucket my-app-data) wird der Bucket in der API deprovisioniert und das Secret entfernt (Finalizer-Pattern)

Adopt-Pattern

Existiert in der Polycrate API bereits ein Bucket mit demselben Namen + Region, wird er adoptiert statt neu angelegt. So können bestehende Buckets nachträglich per CR verwaltet werden.

Idealer Workflow für K8sApps

S3Bucket-CRs gehören in das Manifest-Set Ihrer App (GitOps). Die App kann direkt via envFrom: secretRef die Credentials konsumieren – kein manuelles Secret-Management nötig.


Region-Auflösung

Die Region im Request (region: eu-central) wird in dieser Reihenfolge auf einen S3-Cluster aufgelöst:

  1. Organisations-Cluster: Hat die Organisation einen eigenen S3-Cluster mit dieser Region? → nehmen
  2. Platform-Service-Cluster: Gibt es einen shared Cluster mit dieser Region? → nehmen
  3. Erster verfügbarer Cluster mit dieser Region
  4. Default aus SystemConfig (wenn region ganz fehlt)
  5. Sonst: HTTP 400

Hat der aufgelöste Cluster allow_new_buckets=false (z. B. weil Kapazität erschöpft ist), wird der Request mit einer verständlichen Fehlermeldung abgelehnt.


Capacity-Tracking

Polycrate holt pro S3-Cluster regelmäßig Statistiken vom Provider (MinIO Admin API bzw. Ceph-Telemetry) und speichert sie pro Bucket:

Metrik Beschreibung
Belegung (Bytes) Live-Nutzung pro Bucket
Object Count Anzahl Objekte pro Bucket
Cluster-Belegung Aggregiert pro S3-Cluster (für Kapazitätsplanung)

Diese Werte fließen in die Pricing-Logik ein und sind über Prometheus-Metriken abrufbar.


Credentials-Zugriff

  • UI: Bucket-Detail → Tab Credentials (zeigt Access/Secret nur nach explizitem "Reveal")
  • API: GET /api/v1/s3/buckets/{id}/credentials/ — liefert Metadaten ohne Secrets
  • Via CR: automatisch als Kubernetes Secret im App-Namespace

Jeder Bucket hat einen eigenen IAM-User – Compromise eines Buckets betrifft andere Buckets nicht.

Secrets werden nur einmal zurückgegeben

access_key und secret_key erscheinen ausschließlich in der Create-Response (Bucket anlegen oder zusätzlichen Access Key erstellen). Die List- und Detail-Endpunkte liefern keine Secrets — diese werden weder gespeichert noch erneut ausliefert. Sofort nach der Erstellung sichern.

Zusätzliche Access Keys (nur Ceph/RadosGW)

Für Ceph-basierte Cluster (Provider rook-ceph) können pro Bucket mehrere Access Keys mit unterschiedlichen Rechten (read, write, readwrite) angelegt werden:

POST /api/v1/s3/buckets/{id}/credentials/create
{
  "description": "CI/CD Read-only",
  "permissions": "read"
}

Response enthält access_key und secret_key einmalig:

{
  "id": "...",
  "description": "CI/CD Read-only",
  "permissions": "read",
  "access_key": "AKIAIOSFODNN7EXAMPLE",
  "secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}

MinIO-Cluster unterstützen keine zusätzlichen Access Keys

Die credentials/create-Action steht nur für Ceph/RadosGW-Cluster bereit. MinIO-Cluster haben je Bucket genau einen Access Key (den beim Bucket-Create erzeugten). Beim Versuch an einem MinIO-Bucket antwortet die API mit 400 Bad Request.

Presigned Browser-Upload

Für direkte Browser-Uploads in S3-Buckets ohne Umweg über einen Backend-Proxy kann eine kurzlebige presigned Upload-URL angefordert werden:

POST /api/v1/s3/buckets/{id}/objects/upload-url/
Authorization: Bearer <api-key>

{
  "key": "uploads/my-file.pdf",
  "content_type": "application/pdf",
  "size": 1048576,
  "overwrite": false
}

Response:

{
  "url": "https://s3.eu-central.example.com/my-bucket/uploads/my-file.pdf?...",
  "method": "PUT",
  "headers": {
    "Content-Type": "application/pdf"
  },
  "expires_at": "2026-07-15T14:30:00Z",
  "max_bytes": 5368709120
}

Der Browser sendet danach direkt (ohne Backend) einen PUT-Request an die zurückgegebene URL. Die URL ist für expires_at gültig und auf den angegebenen Content-Type und size limitiert.

CORS

Der Browser muss CORS-Zugriff auf den S3-Endpoint haben. Das wird durch die Bucket-Einstellung CORS Allow All (cors_allow_all: true) oder eine vorkonfigurierte CORS-Policy auf dem S3-Cluster ermöglicht — unabhängig von der presigned URL.

Bucket ohne Workspace anlegen (via API)

Kunden ohne eigenen Kubernetes-Cluster können Buckets per API anlegen, ohne einen Workspace anzugeben. workspace kann in diesem Fall null sein:

POST /api/v1/s3/buckets/
Authorization: Bearer <org-api-key>

{
  "name": "my-data",
  "region": "eu-central",
  "organization_id": "11111111-2222-3333-4444-555555555555",
  "workspace": null
}

Die Organisation muss immer angegeben sein. Bei Verwendung eines Organization API Keys muss organization_id zur eigenen Organisation gehören.


Produktisierung & Abrechnung

S3Bucket ist ein productized model: beim Anlegen wird automatisch ein Default-Produkt (kind=object-storage) zugewiesen, das aus System Config → Default Products stammt. Pricing folgt dem Modell des Produkts (Flat-Rate oder mengenabhängig über Capacity-Tracking). Details: Pricing & Business Layer.


Siehe auch