# ----------------------------------------------------------------------------- # MinIO — Deployment écrit À LA MAIN, et voici pourquoi. # # ⚠⚠ LE CHART OFFICIEL `minio/minio` N'OFFRE AUCUNE SONDE DE SANTÉ. # Vérifié sur la 5.4.0 (la dernière au 2026-09-02) : aucune clé `livenessProbe` # dans ses `values.yaml`, aucun rendu de sonde dans ses `templates/`. Ce n'était # donc pas un oubli de configuration de notre part — il n'y avait rien à # configurer. On reprend le manifeste à notre charge pour pouvoir poser la sonde. # # CE QUE ÇA A COÛTÉ, MESURÉ (arcodange-org/tools#31) : # le 2026-08-29 à 14 h 11, les répliques Longhorn se perdent de vue sur le réseau # (`R/W Timeout. No response received in 8s`) ; le moteur les marque `ERR` une à # une ; à 15 h 32 le périphérique bloc disparaît sous MinIO ; à 17 h 54 **ext4 # abandonne son journal** (`comm minio: Detected aborted journal`). À partir de # là, toute écriture rend `EIO`. # # Longhorn s'est rétabli TOUT SEUL le 30 août à 11 h 50. Mais un journal ext4 # abandonné ne se répare jamais seul : il le reste jusqu'au démontage. MinIO est # donc resté assis sur un montage mort DEUX JOURS APRÈS la réparation du # stockage, pendant que Longhorn (`healthy`), les répliques (`RW`), les nœuds, # ArgoCD (`Synced, Healthy`) et le pod lui-même (`1/1 Running`, `0 restart`) # affichaient tous vert. Trois jours de panne totale du stockage objet, et pas # un seul indicateur rouge. # # ⚠ LE POD NE REDÉMARRE PAS QUAND SON SYSTÈME DE FICHIERS MEURT. Le processus # vit ; c'est tout ce que Kubernetes regardait, faute de sonde. # ----------------------------------------------------------------------------- apiVersion: apps/v1 kind: Deployment metadata: name: minio namespace: tools labels: app: minio release: minio spec: replicas: 1 # ⚠ IMMUABLE, et repris À L'IDENTIQUE de ce que le sous-chart avait posé : # changer un `selector` sur un Deployment existant fait ÉCHOUER l'application, # et ArgoCD le recréerait — donc détacherait puis rattacherait le volume. selector: matchLabels: app: minio release: minio strategy: # ⚠ `Recreate`, PAS `RollingUpdate` : le volume est ReadWriteOnce. Un # roulement voudrait deux pods à la fois, le second resterait en # `ContainerCreating` sur un `Multi-Attach error` jusqu'au timeout, et le # déploiement paraîtrait « en cours » pendant des minutes. Le chart amont # posait `maxSurge: 100%`, ce qui est le mauvais réglage pour un RWO. type: Recreate template: metadata: labels: app: minio release: minio spec: serviceAccountName: minio # --------------------------------------------------------------- # ⚠⚠ JAMAIS SUR pi2 (incident arcodange-org/tools#49, 2026-09-14). # # pi2 tourne à ~110 % de mémoire, sans swap. Le moteur Longhorn d'un # volume vit sur le nœud du POD : sur pi2, il a manqué son délai de 8 s # (`R/W Timeout`), les écritures sont revenues au noyau en # `critical medium error`, et ext4 a ABANDONNÉ son journal (04:05 puis # 07:19). Le montage mort n'est jamais démonté par un redémarrage de # conteneur : 314 redémarrages sur « drive is faulty » en 26 h. # # Même mécanisme que tools#31. Déplacer le pod déplace le moteur, et le # déplacement démonte le volume — ext4 rejoue son journal au remontage. # --------------------------------------------------------------- affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: NotIn values: [pi2] preferredDuringSchedulingIgnoredDuringExecution: # pi3 est le moins chargé en mémoire (58 % au 2026-09-15, pi1 81 %). - weight: 50 preference: matchExpressions: - key: kubernetes.io/hostname operator: In values: [pi3] securityContext: # Repris tel quel : la donnée déjà écrite sur le volume appartient à # 1000:1000. Changer ces valeurs rendrait le bucket illisible. runAsUser: 1000 runAsGroup: 1000 fsGroup: 1000 fsGroupChangePolicy: OnRootMismatch terminationGracePeriodSeconds: 30 containers: - name: minio image: "{{ .Values.minio.image.repository }}:{{ .Values.minio.image.tag }}" imagePullPolicy: {{ .Values.minio.image.pullPolicy }} command: - /bin/sh - -ce - /usr/bin/docker-entrypoint.sh minio server /export -S /etc/minio/certs/ --address :9000 --console-address :9001 env: # Identifiants JAMAIS dans le dépôt : le secret est matérialisé par # le Vault Secrets Operator depuis kvv2/minio/config. - name: MINIO_ROOT_USER valueFrom: secretKeyRef: name: {{ .Values.minio.existingSecret }} key: rootUser - name: MINIO_ROOT_PASSWORD valueFrom: secretKeyRef: name: {{ .Values.minio.existingSecret }} key: rootPassword {{- range $cle, $valeur := .Values.minio.environment }} - name: {{ $cle }} value: {{ $valeur | quote }} {{- end }} ports: - name: http containerPort: 9000 protocol: TCP - name: http-console containerPort: 9001 protocol: TCP # --------------------------------------------------------------- # LES SONDES — la raison d'être de ce fichier. # # ⚠ `/minio/health/live` NE SUFFIT PAS, et c'est mesuré : il répond # 200 sur un magasin QUI NE PEUT PLUS ÉCRIRE. C'est précisément # pourquoi la panne du 29 août est restée invisible trois jours. # # `/minio/health/cluster` vérifie le QUORUM D'ÉCRITURE — la propriété # qu'on veut réellement garder. # # SABOTAGE RELEVÉ (2026-09-02, MinIO jetable, quatre disques, les deux # moitiés dans LE MÊME POD, même image) : # # | état du magasin | /health/live | /health/cluster | # |------------------------|--------------|-----------------| # | 4 disques sains | 200 | **200** | # | quorum d'écriture perdu| **200** | **503** | # # Sabotage : `format.json` de deux disques sur quatre écrasé. La bascule # de `cluster` est survenue entre t+20 s et t+40 s. `live` n'a jamais # bougé. Les DEUX moitiés comptent : la sonde rougit quand la propriété # est violée, ET reste verte sinon. # --------------------------------------------------------------- startupProbe: # Au démarrage on interroge `live` : tant que MinIO monte, `cluster` # répond 503 pour une raison légitime, et l'interroger ici tuerait # le pod avant qu'il ait fini de démarrer. # 24 × 5 s = 2 min accordées au démarrage. httpGet: path: /minio/health/live port: 9000 periodSeconds: 5 timeoutSeconds: 5 failureThreshold: 24 readinessProbe: # Retire MinIO du Service quand il ne peut plus écrire : les clients # reçoivent un refus franc au lieu d'un dépôt qui part dans le vide. httpGet: path: /minio/health/cluster port: 9000 periodSeconds: 15 timeoutSeconds: 5 failureThreshold: 3 livenessProbe: # ⚠ C'EST CELLE-CI QUI AURAIT LEVÉ LA PANNE : le remède au journal # ext4 abandonné est un démontage, donc un redémarrage du pod. # # ⚠ 6 × 30 s = 3 MINUTES de refus SOUTENU avant de redémarrer. Cette # grappe connaît des à-coups Longhorn de quelques secondes (le # déclencheur du 29 août était un `R/W Timeout` de 8 s) : une sonde # nerveuse redémarrerait MinIO sur un hoquet. C'est l'erreur exacte # que redis a payée ici en 2026-07 — 836 échecs de sonde, 135 # redémarrages, et un `.fr` en 403 (voir redis/values.yaml). httpGet: path: /minio/health/cluster port: 9000 periodSeconds: 30 timeoutSeconds: 5 failureThreshold: 6 resources: {{- toYaml .Values.minio.resources | nindent 12 }} volumeMounts: - name: export mountPath: /export - name: minio-user mountPath: /tmp/credentials readOnly: true volumes: - name: export persistentVolumeClaim: claimName: minio - name: minio-user secret: secretName: {{ .Values.minio.existingSecret }}