Kai Ole Hartwig
Medien aus S3 in Kubernetes?

caddy-fs-s3cache — Dateien aus S3 ausliefern, ohne für jedes Bild dreimal zu fragen.

Viele Websites speichern Bilder und Downloads nicht mehr auf der Festplatte des Servers, sondern in einem Objektspeicher wie Amazon S3. Der Webserver Caddy kann solche Dateien direkt von dort ausliefern. Mit dem bisher verfügbaren Modul kostet das pro Datei drei Anfragen an S3, und beim nächsten Aufruf beginnt alles von vorn. caddy-fs-s3cache holt eine Datei einmal, behält eine Kopie auf der lokalen Platte und beantwortet alle weiteren Aufrufe von dort. Nur in Abständen fragt es bei S3 nach, ob sich die Datei geändert hat.

  • Was es ist: ein Erweiterungsmodul für Caddy (caddy.fs.s3cache), das an die Stelle des bisherigen S3-Moduls tritt
  • Für wen: Betreiber, die Caddy oder FrankenPHP einsetzen und ihre Medien in S3 halten, zum Beispiel in Kubernetes
  • Technik: Go-Modul · Caddy 2.11+ · Go 1.26+ · MIT-Lizenz · Quellcode auf GitHub

TL;DR — Passt das zu meinem Setup?

Nutze es wenn …

Nutze es nicht wenn …

Aufwand

Eine Zeile beim Bau von Caddy und ein Block in der Konfiguration. Zugangsdaten für S3 kommen aus der üblichen AWS-Kette, in Kubernetes aus der Identität des Pods. In der Konfigurationsdatei steht kein Passwort.

Warum drei Anfragen pro Datei, und wie der Cache sie spart

Das Problem

Caddys Dateiserver behandelt S3 wie eine Festplatte. Für jede Datei fragt er erst nach, ob sie existiert und wie groß sie ist. Dann öffnet er sie. Zum Schluss springt er an den Anfang zurück, um sie auszuliefern. Eine Festplatte erledigt das in Mikrosekunden. S3 kennt kein Zurückspringen, also wird die Datei ein zweites Mal geholt. Drei Anfragen, jede 30 bis 40 Millisekunden, für ein winziges Bild. Und beim nächsten Aufruf wieder von vorn.

Die Lösung

caddy-fs-s3cache merkt sich, was es schon geholt hat. Die erste Anfrage lädt die Datei und legt sie auf die lokale Platte. Existenz, Größe, Öffnen und Ausliefern kommen danach von dort. Nach einer einstellbaren Frist, standardmäßig eine Minute, fragt das Modul bei S3 nach, ob sich die Datei geändert hat. Unverändert heißt eine kurze Antwort ohne Download.

SituationAnfragen an S3
Datei kürzlich geholtkeine
Datei älter als die Frist, aber unveränderteine kurze Nachfrage, kein Download
Datei neu oder geändertein Download
Datei existiert nichteine Nachfrage, danach 30 Sekunden lang keine
Viele Besucher rufen eine neue Datei gleichzeitig abein Download für alle

Fällt S3 aus, liefert Caddy weiter aus, was es schon hat, und schreibt eine Warnung ins Protokoll. Nur eine Datei, die nie geholt wurde, meldet dann einen Serverfehler statt eines falschen „nicht gefunden“.

Gemessen im Betrieb

Gemessen auf einer TYPO3-Website in Kubernetes. Gleiche Server, gleiche Dateien, einmal mit dem bisherigen Modul und einmal mit caddy-fs-s3cache. Die Zeiten sind die Antwortzeit des Webservers selbst, ohne den Weg zum Browser.

Was abgerufen wurdebishermit Cache
Kleines Bild (1,8 kB), schon im Cache72 bis 100 ms3 bis 5 ms
Kleines Bild, erster Abruf250 ms44 ms
Mittleres Bild (92 kB), schon im Cache70 bis 180 ms4 bis 6 ms
Datei, die es nicht gibt40 ms12 bis 27 ms
Erster Abruf über den vorgeschalteten Cache-Proxy70 bis 88 ms1 bis 12 ms

Zur Einordnung: Ein einzelner S3-Zugriff aus dem Pod dauert in derselben Region 28 bis 42 Millisekunden. Drei davon ergaben die 70 bis 100 Millisekunden. Der Cache macht daraus den Preis einer lokalen Datei.

Medien ohne FUSE-Mount aus S3 ausliefern? Ich helfe bei Caddy-Build, Caddyfile und der Pod-Identität in Kubernetes. Anfragen →

Einbau und Konfiguration

Für Betreiber, die Caddy oder FrankenPHP selbst bauen. Zwei Schritte.

1. Modul beim Bau einbinden

 

xcaddy build --with github.com/ohartwig/caddy-fs-s3cache@v1.0.1

 

Bei FrankenPHP steht die Zeile als weiteres --with neben github.com/dunglas/frankenphp.

2. Dateisystem in der Caddyfile anlegen

Der erste Block benennt den Bucket und den Cache. Der zweite liefert alles unter /media/ daraus aus.

 

{
    filesystem media s3cache {
        bucket my-media-bucket
        region eu-north-1
        cache_dir /var/cache/caddy/media
        cache_max_size 512MiB
        revalidate_after 60s
        negative_ttl 30s
        keepalive 25s
    }
}

example.org {
    handle /media/* {
        route {
            uri strip_prefix /media
            root * .
            fs media
            file_server {
                pass_thru
            }
            respond "Not Found" 404
        }
    }
}

 

Die Optionen

OptionStandardBedeutung
bucketPflichtName des S3-Buckets
region, endpoint, use_path_styleaus der AWS-KonfigurationAWS-Region; eigener Endpunkt und Pfad-Adressierung für S3-kompatible Speicher wie MinIO oder RustFS
prefixleerNur ein Unterordner des Buckets wird sichtbar
cache_dirtemporäres VerzeichnisWo die Kopien liegen; wird beim Start geleert
cache_max_size512MiBObergrenze für den Cache; die am längsten nicht genutzten Dateien weichen zuerst
revalidate_after60sSo lange gilt eine Kopie ohne Nachfrage bei S3
negative_ttl30sSo lange bleibt eine fehlende Datei fehlend, ohne neue Nachfrage
keepalive25sHält die Verbindung zu S3 im Hintergrund offen, damit der erste Abruf nach einer Pause nicht auf den Verbindungsaufbau wartet; off schaltet ab
max_idle_conns16Wie viele Verbindungen zu S3 offen gehalten werden

Rechte

Das Modul braucht Leserechte auf die Dateien im Bucket (s3:GetObject). Ist keepalive an, zusätzlich das Recht, den Bucket zu sehen (s3:ListBucket). Zugangsdaten stehen nicht in der Caddyfile. Sie kommen wie bei allen AWS-Werkzeugen aus Umgebungsvariablen, der AWS-Konfigurationsdatei oder in Kubernetes aus der Identität des Pods.

Häufige Fallstricke

Nächster Schritt

Medien aus S3 ohne Netzlaufwerk ausliefern?

Wenn jedes Bild deiner Website drei Anfragen an S3 kostet oder ein eingehängtes Netzlaufwerk im Weg steht, hilft caddy-fs-s3cache. Ich unterstütze beim Bau von Caddy, bei der Konfiguration und bei der Pod-Identität in Kubernetes.

Einbau besprechen →

Oder direkt schreiben: mail@ole-hartwig.eu