## Why `minio/minio:RELEASE.2025-07-23T15-54-02Z` no longer exists. The Docker Hub repository `minio/minio` returns **404 for every tag** (checked: the pinned tag and `latest`), and MinIO has moved its images behind registry authentication (`quay.io/minio/minio` → 401, `registry.min.io/minio/minio` → auth required, `dl.min.io` binaries → 410 Gone). The tenant therefore sat in `ImagePullBackOff` — 44,229 attempts over 6d23h — and the chain from there is: ``` minio tenant pod not ready (1/2) -> headless Service minio-cluster-hl has no endpoints -> CoreDNS: lookup minio-cluster-hl.storage.svc.cluster.local -> no such host -> Vault S3 storage init fails at startup -> vault-0 CrashLoopBackOff, 1943 restarts, ~7 days ``` ## Control checks (so this is not a network story) | check | result | | --- | --- | | `alpine:3.20` pulled on talos-cp-1 | Succeeded — docker.io works | | `quay.io/prometheus/node-exporter:v1.8.2` on the same node | Succeeded — public quay works | | `minio/minio` (docker.io) | HTTP 404, repo removed | | old tag `RELEASE.2024-06-13T20-48-48Z` with `IfNotPresent` | not cached on the node | ## Change Pin `spec.image` to `coollabsio/minio:RELEASE.2025-10-15T17-29-55Z` — a public mirror, and a newer upstream release than the previous pin. Verified by pulling it on talos-cp-1 and running it: the image carries `/usr/bin/docker-entrypoint.sh` (the layout the operator expects, which runs `minio server … --console-address :9090`) and reports `minio version RELEASE.2025-10-15T17-29-55Z`. ## Caveat This is a community mirror, not MinIO's official image, because the official ones now require credentials. If MinIO registry credentials are available, add an imagePullSecret and switch back to `quay.io/minio/minio`. Supply-chain decision, flagged deliberately. ## After merge (ArgoCD, not kubectl) The storage app tracks `k8s/infra/minio`, so the sync rolls the tenant; then: ``` kubectl get pods -n storage # minio-cluster-az-a-0 -> 2/2 kubectl get pods -n iam | grep vault # vault-0 -> Running (storage init succeeds) ``` Residual risk, unrelated to this fix: the tenant's 4 x 25Gi volumes are Longhorn-backed, and Longhorn currently fails to create engines for new volume attaches cluster-wide. If the restarted pod cannot attach, that is a second, separate fault.Reviewed-on: #53 Co-authored-by: rock <[email protected]>
133 lines
5.2 KiB
YAML
133 lines
5.2 KiB
YAML
apiVersion: minio.min.io/v2
|
|
kind: Tenant
|
|
metadata:
|
|
name: minio-cluster
|
|
namespace: storage
|
|
labels:
|
|
app: minio
|
|
annotations:
|
|
# Let the operator own bucket/user provisioning declaratively.
|
|
prometheus.io/path: /minio/v2/metrics/cluster
|
|
prometheus.io/port: "9000"
|
|
prometheus.io/scrape: "true"
|
|
spec:
|
|
# Was minio/minio:RELEASE.2025-07-23T15-54-02Z — that Docker Hub repository no longer
|
|
# exists (404 for every tag), so the tenant sat in ImagePullBackOff and Vault's S3
|
|
# backend could not resolve minio-cluster-hl. Official images now need registry auth
|
|
# (quay.io/minio/minio returns 401), so this pins a public mirror of the same upstream
|
|
# release; swap it for the official image once MinIO registry credentials exist.
|
|
image: coollabsio/minio:RELEASE.2025-10-15T17-29-55Z
|
|
|
|
# Disable operator auto-TLS: MinIO serves plain HTTP internally on 9000.
|
|
# External TLS is terminated at nginx ingress (wildcard riotpiao-com-tls cert).
|
|
# Without this, MinIO auto-generates self-signed certs and serves HTTPS-only
|
|
# on 9000, which breaks plain-HTTP internal clients like Vault's S3 backend
|
|
# (they hang waiting for a TLS handshake that never completes on an HTTP request).
|
|
requestAutoCert: false
|
|
|
|
# Root credentials. v5 pods read `configuration` — a Secret whose `config.env`
|
|
# key holds shell `export MINIO_ROOT_USER=...` lines. Created out-of-band
|
|
# (SOPS), see minio-secrets.enc.yaml. NOTE: the operator health-monitor logs a
|
|
# cosmetic "empty tenant credentials" warning (it greps for legacy
|
|
# access_key/secret_key keys) — MinIO itself authenticates fine; ignore it.
|
|
configuration:
|
|
name: minio-creds
|
|
|
|
# ── Single pool on the sole storage/scheduling node (talos-cp-1, az-a) ──────
|
|
# Per the 3-CP topology only talos-cp-1 is schedulable and holds Longhorn, so
|
|
# MinIO is a single-server tenant. 4 volumes give erasure-coded durability
|
|
# (MinIO's minimum for parity) on that one node.
|
|
pools:
|
|
- name: az-a
|
|
servers: 1
|
|
volumesPerServer: 4
|
|
|
|
affinity:
|
|
nodeAffinity:
|
|
requiredDuringSchedulingIgnoredDuringExecution:
|
|
nodeSelectorTerms:
|
|
- matchExpressions:
|
|
- key: topology.kubernetes.io/zone
|
|
operator: In
|
|
values: [az-a]
|
|
|
|
tolerations:
|
|
- key: node-role.kubernetes.io/control-plane
|
|
operator: Exists
|
|
effect: NoSchedule
|
|
|
|
resources:
|
|
requests:
|
|
cpu: 250m
|
|
memory: 512Mi
|
|
limits:
|
|
cpu: "1"
|
|
memory: 1Gi
|
|
|
|
volumeClaimTemplate:
|
|
metadata:
|
|
name: data
|
|
spec:
|
|
accessModes:
|
|
- ReadWriteOnce
|
|
storageClassName: longhorn
|
|
resources:
|
|
requests:
|
|
storage: 25Gi
|
|
|
|
# ── Declarative buckets (operator creates on first boot) ────────────────────
|
|
buckets:
|
|
- name: loki-chunks
|
|
- name: loki-ruler
|
|
- name: loki-admin
|
|
- name: vault
|
|
- name: paperless
|
|
|
|
# Metrics are exposed at /minio/v2/metrics; scrape via a hand-rolled
|
|
# ServiceMonitor in the monitoring stack rather than operator auto-wiring
|
|
# (prometheusOperator:true makes the operator hunt for Prometheus in ns
|
|
# 'default' and fail the reconcile).
|
|
|
|
# Public hostnames the tenant serves (S3 + console via the cluster ingress).
|
|
# Must match actual ingress backends: the "minio" Ingress (host
|
|
# minio.riotpiao.com) routes to minio-cluster-console:9090, and "minio-api"
|
|
# (host minio-api.riotpiao.com) routes to minio:9000. Previously these were
|
|
# swapped, so MinIO's own console-domain validation rejected the OIDC
|
|
# callback arriving on minio.riotpiao.com's Host header.
|
|
features:
|
|
domains:
|
|
minio:
|
|
- https://minio-api.riotpiao.com
|
|
console: https://minio.riotpiao.com
|
|
|
|
# ── OIDC via Authentik (server-side env, valid in v2 schema) ────────────────
|
|
# Use in-cluster URL for config fetch (pod→authentik); browser redirects use
|
|
# public URLs embedded in the OIDC metadata response (issuer stays public).
|
|
env:
|
|
- name: MINIO_IDENTITY_OPENID_CONFIG_URL
|
|
# Must use external URL — well-known response contains external issuer/jwks_uri.
|
|
# MinIO validates issuer in JWT matches well-known issuer. Internal URL = mismatch.
|
|
# Hairpins through ingress-nginx but stays in-cluster.
|
|
value: "https://authentik.riotpiao.com/application/o/minio/.well-known/openid-configuration"
|
|
- name: MINIO_IDENTITY_OPENID_CLIENT_ID
|
|
value: "minio"
|
|
- name: MINIO_IDENTITY_OPENID_CLIENT_SECRET
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: minio-oidc
|
|
key: MINIO_IDENTITY_OPENID_CLIENT_SECRET
|
|
- name: MINIO_IDENTITY_OPENID_CLAIM_NAME
|
|
value: "groups"
|
|
- name: MINIO_IDENTITY_OPENID_CLAIM_PREFIX
|
|
value: ""
|
|
- name: MINIO_IDENTITY_OPENID_REDIRECT_URI
|
|
value: "https://minio.riotpiao.com/oauth_callback"
|
|
- name: MINIO_IDENTITY_OPENID_DISPLAY_NAME
|
|
value: "Authentik"
|
|
- name: MINIO_IDENTITY_OPENID_SCOPES
|
|
value: "openid,profile,email,groups"
|
|
- name: MINIO_BROWSER_LOGIN_ANIMATION
|
|
value: "off"
|
|
- name: MINIO_BROWSER_REDIRECT_URL
|
|
value: "https://minio.riotpiao.com"
|