Ga naar hoofdinhoud

Objectopslag (S3)

Een library-directory hoeft geen schijf te zijn. Elke S3-compatibele bucket — AWS S3 of een zelfgehoste MinIO, Garage of Ceph RGW — kan een library bevatten, en één S3-directory kan door meerdere nodes tegelijk bediend worden: elke node die hem configureert scant, analyseert, transcodeert en streamt hem. Daarbovenop kunnen de afgeleide bestanden (artwork, geëxtraheerde ondertitels, podcastdownloads) en de HLS-transcode-uitvoer ook naar de bucket, zodat elke node dezelfde cache en dezelfde segmenten serveert.

Drie dingen staan los van elkaar en zijn elk optioneel:

FunctiePropertyWat het doet
S3-library-directoryapp.ister.disk.directories[n].s3-connectionmedia in een bucket-prefix, gekoppeld aan N nodes
Gedeelde cacheapp.ister.server.cache-s3-connectionartwork, ondertitels, podcastdownloads in de bucket voor elke library op deze node
Gedeelde transcode-opslagapp.ister.server.tmp-s3-connectionHLS-playlists en -segmenten gepubliceerd naar de bucket, teruggelezen door elke node

Connecties

Credentials en endpoints staan in benoemde connecties, alleen in de configuratie — nooit in de database:

app.ister.s3.connections[0].name=minio
app.ister.s3.connections[0].endpoint=http://minio:9000 # leeg = AWS
app.ister.s3.connections[0].region=us-east-1
app.ister.s3.connections[0].bucket=ister
app.ister.s3.connections[0].path-style=true # vereist door de meeste zelfgehoste servers
app.ister.s3.connections[0].access-key=…
app.ister.s3.connections[0].secret-key=…

Omgevingsvariabelen werken zoals altijd (APP_ISTER_S3_CONNECTIONS_0_NAME, …). Eén connectie is één bucket; een tweede bucket is een tweede connectie. Laat het endpoint leeg voor AWS; blijven de sleutels leeg, dan wordt de gewone credential-keten van de SDK gebruikt (instance profile, omgevingsvariabelen, ~/.aws).

Een S3-library-directory

In plaats van een path noemt een directory een connectie en, optioneel, een key-prefix:

app.ister.disk.directories[1].name=shows-s3
app.ister.disk.directories[1].library=shows
app.ister.disk.directories[1].s3-connection=minio
app.ister.disk.directories[1].prefix=media/shows

De objecten onder de prefix volgen dezelfde indelingsregels als een schijf (Libraries en media-indeling, Naamconventies); de "mappen" zijn gewoon key-segmenten. Rijen leggen het object vast als s3://bucket/key.

Nodes koppelen

Een S3-directory heeft geen eigenaar-node. De eerste node die ermee start maakt hem aan; elke andere node die dezelfde naam met dezelfde connectie en prefix opsomt wordt eraan gekoppeld en neemt zijn deel van het werk — alle gekoppelde nodes lezen de queues van de directory, en RabbitMQ geeft elk bericht aan één van hen. Een scan wordt één keer gestart en draait op de node die hem oppakt (een databaselock houdt twee overlappende scans uit elkaar). Een node waarvan de regel dezelfde naam naar een andere bucket of prefix laat wijzen weigert te starten, net als een node die een lokaal path configureert voor een naam die het cluster als S3 kent.

Clients kunnen een S3-bestand vanaf elke gekoppelde node streamen. De node leest het object met byte-ranges en proxiet het — S3 hoeft nooit bereikbaar te zijn vanaf de client, en standaard ook niet vanaf ffmpeg: ffmpeg leest via het eigen /mediaFile/{id}/download van de node over het loopback-adres. app.ister.s3.ffmpeg-direct=true geeft ffmpeg in plaats daarvan presigned S3-URL's, wat de hop door de node scheelt maar de bucket blootstelt aan de transcoderende hosts.

Helper-nodes (Multi-node) zijn voor S3-directories niet nodig: een node koppelen is de manier om capaciteit toe te voegen. Een helper die een S3-directory onder app.ister.helper.disks opsomt leest hem via een gekoppelde node, zoals bij een schijf.

Alles wat een echt bestand nodig heeft — de epub- en comic-lezer, PDF-rendering, ondertitel-OCR — downloadt het object eenmalig naar app.ister.s3.local-copy-dir (standaard onder de tmp-map) en houdt het als LRU-cache, begrensd door app.ister.s3.local-copy-max-bytes (2 GiB).

Gedeelde cache

app.ister.server.cache-s3-connection=minio
app.ister.server.cache-s3-prefix=cache

Hiermee schrijft de node elk afgeleid bestand naar s3://bucket/cache/… in plaats van naar zijn CACHE_DIR: episode-stills, gedownloade posters, ingebedde en epub-covers, geëxtraheerde .srt-bestanden, podcastdownloads. Het geldt voor alle libraries op de node, schijven inbegrepen. De directory wordt eenmalig geregistreerd als <clusternaam>-s3-cache en elke node die ermee geconfigureerd is wordt gekoppeld, dus podcastdownloads en cache-gescopete events zijn ook gedeeld werk.

De node-lokale cachemap wordt niet gemigreerd: haar rijen blijven geserveerd door de node die ze bezit, alleen nieuwe bestanden gaan naar de bucket. De dagelijkse cache-opschoning veegt ook de gedeelde cache (één node per run, bepaald door een databaselock), met dezelfde dry-run-vlag.

Gedeelde transcode-opslag

app.ister.server.tmp-s3-connection=minio
app.ister.server.tmp-s3-prefix=tmp

Het encoderen gebeurt nog steeds in de lokale TMP_DIR van de node die de pass draait — de segment-muxer van ffmpeg wil een filesystem — maar elk voltooid segment, elke playlist en elke voltooiingsmarkering wordt gepubliceerd naar s3://bucket/tmp/{mediaFileId}/…, en een node die afspelen bedient en een bestand lokaal mist leest het daarvandaan terug. Een client kan dus op node A beginnen met afspelen terwijl node B de pass draait, en een bestand dat gisteren op welke node dan ook getranscodeerd is speelt vandaag op elke node meteen. De node-naar-node-segmentupload van de multi-node-opzet met schijven wordt niet gebruikt als dit aanstaat.

De opslag wordt samen met de lokale tmp-opschoning geveegd (zelfde schema, min-age en dry-run): de gepubliceerde map van een mediabestand verdwijnt als het bestand weg is of als er binnen het venster niets meer voor gepubliceerd is.

Lokaal ontwikkelen

docker-compose-local.yml bevat een MinIO (minio, console op poort 9001, minioadmin/minioadmin) plus een one-shot die de bucket ister aanmaakt; het uitgecommentarieerde environment-blok op de server-service wijst er een library naartoe. De integratietests draaien dezelfde server via Testcontainers.

Verder lezen

  • Multi-node — gekoppelde nodes naast eigenaar-nodes en helpers
  • Configuratie — elke property in één tabel