feat(minio) — stockage objet S3 du homelab (brique partagée de tools)
Helm Charts / Detect changed charts (push) Successful in 16s
Helm Charts / Detect changed charts (pull_request) Successful in 15s
Helm Charts / Library charts tool (push) Has been skipped
Helm Charts / Library charts tool (pull_request) Has been skipped
MinIO / Tofu - minio IAC (pull_request) Has been skipped
MinIO / Auth with gitea for vault (pull_request) Failing after 8m32s
Helm Charts / Application charts pgcat (pull_request) Has been skipped
Helm Charts / Application charts pgcat (push) Has been skipped
Helm Charts / Detect changed charts (push) Successful in 16s
Helm Charts / Detect changed charts (pull_request) Successful in 15s
Helm Charts / Library charts tool (push) Has been skipped
Helm Charts / Library charts tool (pull_request) Has been skipped
MinIO / Tofu - minio IAC (pull_request) Has been skipped
MinIO / Auth with gitea for vault (pull_request) Failing after 8m32s
Helm Charts / Application charts pgcat (pull_request) Has been skipped
Helm Charts / Application charts pgcat (push) Has been skipped
Le fondateur : « on déploie MinIO dans le repo tools du homelab non ? » — oui, c'est bien le pattern : le dossier tools/ porte les briques PARTAGÉES (pgbouncer, clickhouse, grafana…) et le namespace tools les fait tourner ; les charts applicatifs vivent dans le repo de leur app. Décidé de longue date côté produit, jamais déployé : ADR-012 « MinIO local d'abord » (bascule R2 à 100+ utilisateurs / 10 To par mois), ADR-013 (le gratuit reste local-first, MinIO sert les paliers payants). Vérifié avant d'écrire : aucun pod ni service MinIO dans le cluster. Le chart suit la recette du repo (dépendance à la library "tool" + chart amont en SubChart, deux gardes dans templates/) : - mode STANDALONE, 1 réplique : ce qui transite est DÉRIVÉ (le master d'une vidéo reste sur l'appareil de son propriétaire, ADR-018) 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 ; - 50 Gi sur longhorn ≈ 250 h de cours au palier « travail » (360p, 3,4 Mo/min) ; ⚠ Longhorn réplique : compter ×3 sur la capacité avant d'augmenter ; - ressources bornées (512 Mi / 2 Gi) : la limite protège les voisins de tools ; - API s3.arcodange.lab + console minio.arcodange.lab (Traefik) ; - bucket kadans-videos PRIVÉ — l'accès passera par des URL signées (ADR-0002) ; - identifiants JAMAIS au dépôt : iac/ les génère dans Vault (kvv2/minio/config), le Vault Secrets Operator les matérialise, le chart les lit via existingSecret. Le SA du pod est nommé "minio" (pas le "minio-sa" amont) car le module app_roles borne l'authentification au SA portant le nom de l'app. ⚠ Le workflow minio.yaml écrit ses triggers EN TOUTES LETTRES : une ancre YAML dans un trigger Gitea Actions fait taire push ET pull_request en silence (vécu sur arcodange/kadans, issues 113→117). Les workflows plausible/crowdsec/vault de ce repo en utilisent encore — à vérifier séparément, c'est probablement pourquoi ils ne partent qu'à la main. Vérifié : helm dependency update + helm template (11 ressources rendues, SA et VaultAuth cohérents) + helm lint ✓. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01CoafGWmRVESaWX819USUUA
This commit is contained in:
@@ -0,0 +1,70 @@
|
|||||||
|
---
|
||||||
|
name: MinIO
|
||||||
|
|
||||||
|
# ⚠ Triggers écrits EN TOUTES LETTRES, sans ancre YAML (`&`/`*`).
|
||||||
|
# Une ancre dans un trigger Gitea Actions fait taire push ET pull_request —
|
||||||
|
# en silence, aucun run, aucune erreur (vécu sur arcodange/kadans, issues 113
|
||||||
|
# → 117). Les autres workflows de ce repo utilisent encore des ancres : à
|
||||||
|
# vérifier séparément, c'est probablement pour ça qu'ils ne partent qu'à la
|
||||||
|
# main (workflow_dispatch).
|
||||||
|
on:
|
||||||
|
workflow_dispatch: {}
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'minio/**/*.tf'
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'minio/**/*.tf'
|
||||||
|
|
||||||
|
# cancel any previously-started, yet still active runs of this workflow on the same branch
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.ref }}-${{ github.workflow }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
.vault_step: &vault_step
|
||||||
|
name: read vault secret
|
||||||
|
uses: https://gitea.arcodange.lab/arcodange-org/vault-action.git@main
|
||||||
|
id: vault-secrets
|
||||||
|
with:
|
||||||
|
url: https://vault.arcodange.lab
|
||||||
|
caCertificate: ${{ secrets.HOMELAB_CA_CERT }}
|
||||||
|
jwtGiteaOIDC: ${{ needs.gitea_vault_auth.outputs.gitea_vault_jwt }}
|
||||||
|
role: gitea_cicd_minio
|
||||||
|
method: jwt
|
||||||
|
path: gitea_jwt
|
||||||
|
secrets: |
|
||||||
|
kvv1/google/credentials credentials | GOOGLE_BACKEND_CREDENTIALS ;
|
||||||
|
kvv1/gitea/tofu_module_reader ssh_private_key | TERRAFORM_SSH_KEY ;
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
gitea_vault_auth:
|
||||||
|
name: Auth with gitea for vault
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
gitea_vault_jwt: ${{steps.gitea_vault_jwt.outputs.id_token}}
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Auth with gitea for vault
|
||||||
|
id: gitea_vault_jwt
|
||||||
|
run: |
|
||||||
|
echo -n "${{ secrets.vault_oauth__sh_b64 }}" | base64 -d | bash
|
||||||
|
|
||||||
|
tofu:
|
||||||
|
name: Tofu - minio IAC
|
||||||
|
needs:
|
||||||
|
- gitea_vault_auth
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
OPENTOFU_VERSION: 1.8.2
|
||||||
|
TERRAFORM_VAULT_AUTH_JWT: ${{ needs.gitea_vault_auth.outputs.gitea_vault_jwt }}
|
||||||
|
VAULT_CACERT: "${{ github.workspace }}/homelab.pem"
|
||||||
|
steps:
|
||||||
|
- *vault_step
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: prepare vault self signed cert
|
||||||
|
run: echo -n "${{ secrets.HOMELAB_CA_CERT }}" | base64 -d > $VAULT_CACERT
|
||||||
|
- name: terraform apply
|
||||||
|
uses: dflook/terraform-apply@v1
|
||||||
|
with:
|
||||||
|
path: minio/iac
|
||||||
|
auto_approve: true
|
||||||
@@ -9,3 +9,4 @@ tools:
|
|||||||
grafana: {}
|
grafana: {}
|
||||||
plausible: {}
|
plausible: {}
|
||||||
prometheus: {}
|
prometheus: {}
|
||||||
|
minio: {}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# MinIO — stockage objet S3 du homelab (3 × Raspberry Pi 5, arm64).
|
||||||
|
#
|
||||||
|
# Pourquoi ici : c'est une BRIQUE PARTAGÉE, au même titre que pgbouncer ou
|
||||||
|
# clickhouse — le namespace `tools` en héberge le serveur ; les buckets, quotas
|
||||||
|
# et identifiants d'une application vivent, eux, avec cette application.
|
||||||
|
#
|
||||||
|
# Premier consommateur : Kadans (ADR-012 « MinIO local d'abord », ADR-013
|
||||||
|
# « OPFS local-first, MinIO/R2 = paliers payants »). La bascule vers Cloudflare
|
||||||
|
# R2 est prévue par l'ADR-012 aux seuils : 100+ utilisateurs actifs, > 10 To/mois
|
||||||
|
# de bande passante, ou dispersion géographique.
|
||||||
|
#
|
||||||
|
# Mode STANDALONE assumé : 3 nœuds, mais la donnée servie ici est DÉRIVÉE (le
|
||||||
|
# master d'une vidéo reste sur l'appareil de son propriétaire — ADR-018 du front) ;
|
||||||
|
# la redondance de Longhorn suffit, l'erasure coding distribué de MinIO coûterait
|
||||||
|
# de la RAM et des IOPS que des Pi n'ont pas à dépenser pour ça.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
apiVersion: v2
|
||||||
|
name: minio
|
||||||
|
description: A Helm chart for Kubernetes
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
- name: tool
|
||||||
|
version: 0.1.0
|
||||||
|
repository: https://gitea.arcodange.lab/api/packages/arcodange-org/helm
|
||||||
|
- name: minio
|
||||||
|
version: 5.4.0
|
||||||
|
repository: https://charts.min.io/
|
||||||
|
|
||||||
|
# A chart can be either an 'application' or a 'library' chart.
|
||||||
|
#
|
||||||
|
# Application charts are a collection of templates that can be packaged into versioned archives
|
||||||
|
# to be deployed.
|
||||||
|
#
|
||||||
|
# Library charts provide useful utilities or functions for the chart developer. They're included as
|
||||||
|
# a dependency of application charts to inject those utilities and functions into the rendering
|
||||||
|
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
|
||||||
|
type: application
|
||||||
|
version: 0.1.0
|
||||||
|
appVersion: "latest"
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
# 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` (Traefik) |
|
||||||
|
| 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
|
||||||
|
|
||||||
|
1. **Appliquer l'IaC** — crée le rôle Vault et génère le mot de passe root :
|
||||||
|
workflow `MinIO` (déclenchable à la main), ou `tofu apply` dans `minio/iac`.
|
||||||
|
2. **ArgoCD** synchronise l'application (déclarée dans `chart/values.yaml`).
|
||||||
|
3. Vérifier : `kubectl -n tools get vaultstaticsecret minio` (secret matérialisé)
|
||||||
|
puis `kubectl -n tools get pods -l app=minio`.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> L'ordre compte : 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.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
terraform {
|
||||||
|
backend "gcs" {
|
||||||
|
bucket = "arcodange-tf"
|
||||||
|
prefix = "tools/minio/main"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Rôle Vault + politique pour que le VaultAuth du chart puisse lire kvv2/minio/*
|
||||||
|
# (module partagé du repo — même recette que plausible).
|
||||||
|
module "app_roles" {
|
||||||
|
source = "git::ssh://[email protected]:2222/arcodange-org/tools.git//hashicorp-vault/iac/modules/app_roles?depth=1&ref=main"
|
||||||
|
name = "minio"
|
||||||
|
service_account_namespaces = ["tools"]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Identifiants ROOT de MinIO. Générés ici, jamais choisis à la main et jamais
|
||||||
|
# écrits dans le dépôt : le chart officiel les lit dans le secret k8s
|
||||||
|
# `minio-config` (clés rootUser / rootPassword), matérialisé par le Vault
|
||||||
|
# Secrets Operator depuis ce chemin.
|
||||||
|
resource "random_password" "root" {
|
||||||
|
length = 40
|
||||||
|
special = false # les outils S3 transportent mal certains caractères en URL
|
||||||
|
}
|
||||||
|
|
||||||
|
locals {
|
||||||
|
config = {
|
||||||
|
rootUser = "kadans-root"
|
||||||
|
rootPassword = random_password.root.result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "vault_kv_secret_v2" "config" {
|
||||||
|
mount = "kvv2"
|
||||||
|
name = "minio/config"
|
||||||
|
cas = 1
|
||||||
|
data_json = jsonencode(local.config)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
vault = {
|
||||||
|
source = "vault"
|
||||||
|
version = "4.4.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "vault" {
|
||||||
|
address = "https://vault.arcodange.lab"
|
||||||
|
auth_login_jwt { # TERRAFORM_VAULT_AUTH_JWT environment variable
|
||||||
|
mount = "gitea_jwt"
|
||||||
|
role = "gitea_cicd_minio"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{{- if eq .Values.tool.kind "HelmChart" -}}
|
||||||
|
{{- include "tool.helm-chart-config.tpl" . -}}
|
||||||
|
{{- end -}}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{{- if eq .Values.tool.kind "HelmChart" -}}
|
||||||
|
{{- include "tool.helm-chart.tpl" . -}}
|
||||||
|
{{- end -}}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{{- /* Identifiants MinIO : jamais dans le dépôt. Le Vault Secrets Operator
|
||||||
|
authentifie ce chart auprès de Vault (rôle créé par iac/) puis
|
||||||
|
matérialise le secret k8s consommé via `existingSecret`. Même recette
|
||||||
|
que plausible. */ -}}
|
||||||
|
apiVersion: secrets.hashicorp.com/v1beta1
|
||||||
|
kind: VaultAuth
|
||||||
|
metadata:
|
||||||
|
name: minio
|
||||||
|
namespace: tools
|
||||||
|
spec:
|
||||||
|
vaultConnectionRef: default
|
||||||
|
method: kubernetes
|
||||||
|
mount: kubernetes
|
||||||
|
kubernetes:
|
||||||
|
role: minio
|
||||||
|
serviceAccount: minio
|
||||||
|
audiences:
|
||||||
|
- vault
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{{- /* kvv2/minio/config → secret `minio-config` (clés rootUser / rootPassword,
|
||||||
|
les deux noms exigés par le chart officiel). Le mot de passe est généré
|
||||||
|
par Terraform (iac/), jamais choisi à la main. */ -}}
|
||||||
|
apiVersion: secrets.hashicorp.com/v1beta1
|
||||||
|
kind: VaultStaticSecret
|
||||||
|
metadata:
|
||||||
|
name: minio
|
||||||
|
namespace: tools
|
||||||
|
spec:
|
||||||
|
type: kv-v2
|
||||||
|
mount: kvv2
|
||||||
|
path: minio/config
|
||||||
|
destination:
|
||||||
|
name: minio-config
|
||||||
|
create: true
|
||||||
|
refreshAfter: 30s
|
||||||
|
vaultAuthRef: minio
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
minio: &minio_config
|
||||||
|
# Image officielle MinIO — multi-arch, arm64 inclus (les nœuds sont des Pi 5).
|
||||||
|
image:
|
||||||
|
repository: quay.io/minio/minio
|
||||||
|
pullPolicy: IfNotPresent
|
||||||
|
|
||||||
|
# STANDALONE : un seul serveur, un seul volume. La donnée servie ici est
|
||||||
|
# DÉRIVÉE (le master reste chez l'utilisateur) et Longhorn réplique déjà le
|
||||||
|
# volume ; l'erasure coding distribué coûterait de la RAM que les Pi n'ont pas
|
||||||
|
# à dépenser pour ça. `replicas` est ignoré en standalone.
|
||||||
|
mode: standalone
|
||||||
|
replicas: 1
|
||||||
|
|
||||||
|
# Le rôle Vault du module `app_roles` borne l'authentification au SERVICE
|
||||||
|
# ACCOUNT nommé comme l'app (`bound_service_account_names = [minio]`) : on
|
||||||
|
# aligne donc le SA du pod sur ce nom, plutôt que le « minio-sa » par défaut
|
||||||
|
# du chart amont — un SA pour le pod, le même pour Vault, rien à réconcilier.
|
||||||
|
serviceAccount:
|
||||||
|
create: true
|
||||||
|
name: minio
|
||||||
|
|
||||||
|
# Identifiants JAMAIS dans le dépôt : le secret est matérialisé par le
|
||||||
|
# Vault Secrets Operator depuis kvv2/minio/config (voir resources/ et iac/).
|
||||||
|
# Le chart lit `.data.rootUser` et `.data.rootPassword` de ce secret.
|
||||||
|
existingSecret: minio-config
|
||||||
|
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
storageClass: longhorn
|
||||||
|
accessMode: ReadWriteOnce
|
||||||
|
# 50 Gi ≈ 250 heures de cours au palier « travail » de Kadans (360p,
|
||||||
|
# 3,4 Mo/min — ADR-018 du front). Longhorn réplique ce volume sur les
|
||||||
|
# nœuds : compter ×3 sur la capacité du cluster avant d'augmenter.
|
||||||
|
size: 50Gi
|
||||||
|
|
||||||
|
# Des Pi 5 à 8 Go partagés avec le reste de `tools` : on borne franchement.
|
||||||
|
# MinIO standalone est frugal ; la limite protège les voisins, pas MinIO.
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
memory: 512Mi
|
||||||
|
cpu: 100m
|
||||||
|
limits:
|
||||||
|
memory: 2Gi
|
||||||
|
|
||||||
|
# API S3 (les applications parlent ici).
|
||||||
|
ingress:
|
||||||
|
enabled: true
|
||||||
|
ingressClassName: traefik
|
||||||
|
path: /
|
||||||
|
hosts:
|
||||||
|
- s3.arcodange.lab
|
||||||
|
|
||||||
|
# Console d'administration (humains).
|
||||||
|
consoleIngress:
|
||||||
|
enabled: true
|
||||||
|
ingressClassName: traefik
|
||||||
|
path: /
|
||||||
|
hosts:
|
||||||
|
- minio.arcodange.lab
|
||||||
|
|
||||||
|
# Buckets créés au déploiement. `versioning: false` assumé : ces objets sont
|
||||||
|
# DÉRIVÉS et re-générables depuis le master local — versionner doublerait le
|
||||||
|
# stockage pour un filet dont on n'a pas besoin.
|
||||||
|
buckets:
|
||||||
|
- name: kadans-videos
|
||||||
|
policy: none # privé : l'accès passe par des URL signées (ADR-0002 du dossier)
|
||||||
|
purge: false
|
||||||
|
versioning: false
|
||||||
|
|
||||||
|
# Métriques : Prometheus (namespace `tools`) scrape déjà la façade et le
|
||||||
|
# laptop (ADR-0014 du dossier) — MinIO rejoint la même vue.
|
||||||
|
metrics:
|
||||||
|
serviceMonitor:
|
||||||
|
enabled: false # pas d'opérateur Prometheus ici : scrape par annotation
|
||||||
|
environment:
|
||||||
|
MINIO_PROMETHEUS_AUTH_TYPE: "public"
|
||||||
|
|
||||||
|
tool:
|
||||||
|
# kind: 'SubChart' or 'HelmChart', if subchart then uncomment Chart.yaml dependency, else comment and use tool library with helm chart template
|
||||||
|
kind: 'SubChart'
|
||||||
|
repo: https://charts.min.io/
|
||||||
|
chart: minio
|
||||||
|
version: 5.4.0
|
||||||
|
values: *minio_config
|
||||||
Reference in New Issue
Block a user