Helm Charts / Detect changed charts (push) Successful in 1m6s
Helm Charts / Detect changed charts (pull_request) Successful in 1m5s
Helm Charts / Library charts tool (push) Has been skipped
Helm Charts / Library charts tool (pull_request) Has been skipped
Helm Charts / Application charts pgcat (push) Has been skipped
Helm Charts / Application charts pgcat (pull_request) Has been skipped
Sans ça, la synchronisation des vidéos ne peut pas marcher, et ce sont des faits du navigateur : une page servie en https ne peut pas émettre de requête vers http:// (contenu mixte), et `.lab` n'est pas résolvable hors du LAN. L'ingress existant est en entrypoint `web` sans TLS, sur un domaine interne — donc inutilisable depuis kadans.arcodange.fr, en déplacement COMME à la maison. Même motif que grafana/templates/ingress-public.yaml : entrypoint `web`, TLS terminé par le tunnel Cloudflare (wildcard *.arcodange.fr), middleware crowdsec. Le `.lab` interne reste inchangé. PAS de basic-auth, contrairement à kadans-public : une requête S3 porte sa propre signature (SigV4), et un défi HTTP Basic casserait le PUT présigné auquel le navigateur ne peut pas répondre. L'autorisation vient de l'URL signée et de sa durée de vie courte. CORS déclaré sur les origines EXACTES de la PWA (.fr et .lab), jamais « * » : une URL présignée qui fuiterait serait sinon rejouable depuis n'importe quel site. Sans cette liste, le préflight OPTIONS échoue et le PUT ne part même pas. ⚠ Signalé, NON mesuré : le tunnel Cloudflare plafonne probablement le corps d'une requête (~100 Mo sur les offres gratuites). À éprouver avec un vrai téléversement. Contexte utile : sur le corpus réel du fondateur (707 vidéos, ~2 ans), la durée MOYENNE est de 53 s et deux vidéos seulement dépassent 5 min — au palier « travail » de l'ADR-018, 100 Mo valent ~29 min de cours. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01CoafGWmRVESaWX819USUUA
95 lines
5.4 KiB
Markdown
95 lines
5.4 KiB
Markdown
# MinIO — stockage objet S3 du homelab
|
||
|
||
Brique **partagée** du namespace `tools`, au même titre que pgbouncer ou
|
||
clickhouse. Le serveur vit ici ; les buckets, quotas et identifiants d'une
|
||
application vivent avec cette application.
|
||
|
||
## Premier consommateur : Kadans
|
||
|
||
- [ADR-012](https://gitea.arcodange.lab/arcodange/kadans/src/branch/main/docs/adr/012-video-storage-minio-first.md)
|
||
« MinIO local d'abord » — bascule vers Cloudflare R2 prévue aux seuils :
|
||
100+ utilisateurs actifs, > 10 To/mois, ou dispersion géographique.
|
||
- [ADR-013](https://gitea.arcodange.lab/arcodange/kadans/src/branch/main/docs/adr/013-video-storage-opfs-local-first.md)
|
||
le gratuit est **local-first** (la vidéo ne quitte pas l'appareil) ; MinIO sert
|
||
les **paliers payants**.
|
||
- [ADR-018](https://gitea.arcodange.lab/arcodange/kadans/src/branch/main/docs/adr/018-qualite-video-au-transfert.md)
|
||
ce qui transite est **dérivé** (aperçu 240p ~50 Ko, travail 360p ~3,4 Mo/min) —
|
||
le master reste chez l'utilisateur. D'où le dimensionnement ci-dessous.
|
||
|
||
## Ce que ce chart pose
|
||
|
||
| | |
|
||
|---|---|
|
||
| Mode | **standalone** (1 réplique) — la donnée est dérivée et Longhorn réplique déjà le volume ; l'erasure coding distribué coûterait de la RAM que des Pi 5 n'ont pas à dépenser pour ça |
|
||
| Volume | **50 Gi** sur `longhorn` ≈ **250 h de cours** au palier « travail ». ⚠ Longhorn réplique : compter **×3** sur la capacité du cluster avant d'augmenter |
|
||
| Ressources | requests 512 Mi / 100 m · limit 2 Gi — la limite protège les voisins de `tools`, pas MinIO |
|
||
| API S3 | `s3.arcodange.lab` (interne) **et `s3.arcodange.fr`** (public, tunnel Cloudflare → entrypoint `web` + crowdsec) — voir « Pourquoi une exposition publique » |
|
||
| Console | `minio.arcodange.lab` (Traefik) |
|
||
| Bucket | `kadans-videos`, **privé** — l'accès passe par des URL signées (ADR-0002 du dossier produit) |
|
||
| Identifiants | **jamais dans le dépôt** : `iac/` les génère dans Vault (`kvv2/minio/config`), le Vault Secrets Operator les matérialise en secret `minio-config`, le chart les lit via `existingSecret` |
|
||
|
||
Le ServiceAccount du pod est nommé `minio` (et non le `minio-sa` par défaut du
|
||
chart amont) parce que le module Vault `app_roles` borne l'authentification au
|
||
SA portant le nom de l'app : un seul SA, rien à réconcilier.
|
||
|
||
## Première mise en service
|
||
|
||
L'ordre compte, et il compte **deux fois** :
|
||
|
||
1. **Workflow `Hashicorp Vault`** — MinIO doit d'abord figurer dans
|
||
`hashicorp-vault/iac/terraform.tfvars` (c'est fait) : c'est **là** que naît
|
||
le rôle CI `gitea_cicd_minio`, et non dans `minio/iac`. Sans cette étape,
|
||
le workflow MinIO échoue sur
|
||
`role "gitea_cicd_minio" could not be found` — il essaie de s'authentifier
|
||
avec un rôle que personne n'a encore créé.
|
||
2. **Workflow `MinIO`** — applique `minio/iac` : rôle Kubernetes pour le Vault
|
||
Secrets Operator, et **génération** du mot de passe root dans
|
||
`kvv2/minio/config`.
|
||
3. **ArgoCD** synchronise l'application (déclarée dans `chart/values.yaml`).
|
||
4. Vérifier : `kubectl -n tools get vaultstaticsecret minio` (secret
|
||
matérialisé) puis `kubectl -n tools get pods -l app=minio`.
|
||
|
||
> [!NOTE]
|
||
> Sans le secret `minio-config`, le pod ne démarre pas. C'est voulu — mieux
|
||
> vaut un pod en attente qu'un MinIO ouvert avec des identifiants par défaut.
|
||
|
||
|
||
## Pourquoi une exposition publique (`s3.arcodange.fr`)
|
||
|
||
La PWA Kadans est servie en `https://kadans.arcodange.fr` et téléverse ses vidéos
|
||
**directement** vers MinIO, avec des URL présignées émises par kadans-api
|
||
(kadans-api#23) : les octets ne passent jamais par l'API.
|
||
|
||
Deux raisons rendent le `.lab` inutilisable pour ça, et ce sont des faits du
|
||
navigateur, pas des préférences :
|
||
|
||
1. **Contenu mixte** — une page servie en `https` ne peut pas émettre une requête
|
||
vers `http://`. L'ingress `.lab` est en entrypoint `web` sans TLS.
|
||
2. **`.lab` n'est pas résolvable hors du LAN** — la synchronisation ne marcherait
|
||
qu'à la maison, ce qui vide de son sens « retrouver mes vidéos sur mon autre
|
||
appareil ».
|
||
|
||
**Pas de basic-auth** sur cet ingress, contrairement à `kadans-public` : une
|
||
requête S3 porte sa propre signature (SigV4). Un défi HTTP Basic casserait le PUT
|
||
présigné, auquel le navigateur ne peut pas répondre. L'autorisation vient de
|
||
l'URL signée et de sa durée de vie courte (15 min pour déposer, 1 h pour lire).
|
||
|
||
**CORS** (`MINIO_API_CORS_ALLOW_ORIGIN`) liste les origines EXACTES de la PWA —
|
||
jamais `*` : une URL présignée qui fuiterait serait sinon rejouable depuis
|
||
n'importe quel site.
|
||
|
||
### ⚠ À vérifier avant de s'y fier : la taille maximale d'une requête
|
||
|
||
Le trafic public passe par un **tunnel Cloudflare**. Les offres gratuites de
|
||
Cloudflare plafonnent la taille du corps d'une requête proxifiée (de l'ordre de
|
||
**100 Mo**) — ce plafond n'a **pas** été mesuré ici, il doit l'être avec un vrai
|
||
téléversement avant d'annoncer une limite aux utilisateurs.
|
||
|
||
Ce qu'on sait, en revanche, et qui rend le sujet peu urgent : sur le corpus réel
|
||
du fondateur (707 vidéos, ~2 ans), **la durée moyenne est de 53 secondes** et
|
||
**deux vidéos seulement dépassent 5 minutes**. Au palier « travail » de l'ADR-018
|
||
(360p ≈ 3,4 Mo/min), 100 Mo représentent ~29 minutes de cours : le corpus entier
|
||
passe très largement. Si la limite se confirme, le plafond de 200 Mio annoncé
|
||
côté API mérite d'être ramené sous celle du tunnel — mieux vaut refuser tôt, avec
|
||
une phrase claire, qu'échouer au milieu d'un téléversement.
|