feat(backup): prod shares dev's MinIO — plus the path tenancy that makes it survivable
Operator ruling: "the S3 backup location is the same as Dev." Prod's
S3_BACKUP_ENDPOINT is now http://192.168.1.9:9000 and the TBD-PROD-* placeholder
is retired. Set EXPLICITLY rather than by omitting the op — an omitted op would
inherit the same value silently, and a shared backup target must be visible in
the overlay that owns it.
SHARING THE ENDPOINT WAS NOT SAFE AS AUTHORED. Nothing about the backup PATHS
was per-environment, and enabling prod against dev's MinIO would have been the
2026-09-04 external-dns incident again, in a worse place:
VELERO — bucket `velero`, NO prefix, and both clusters run schedules named
`daily` / `daily-resources-only`. Velero SYNCS backups it finds in its BSL
into itself as Backup CRs and GARBAGE-COLLECTS them at TTL, so prod would
have adopted dev's backups and then deleted them on schedule.
CNPG — the worse one. destinationPath `s3://cnpg` with CNPG appending
serverName, which defaults to the CLUSTER NAME. Both environments have
keycloak-db, nextcloud-db and semaphore-db, so prod's Postgres would archive
WAL into the EXACT prefix dev's does. Two servers interleaving WAL into one
Barman stream CORRUPTS THE CHAIN and destroys PITR — and dev's restore is
PROVEN (R-STATE-03). Sharing an endpoint is fine; sharing a path is not.
Two knobs, authored once, environments differing only by scoped overrides
(R-ENV-02):
VELERO_BACKUP_PREFIX dev "" | prod "prod"
CNPG_BACKUP_PATH dev "s3://cnpg" | prod "s3://cnpg/prod"
THE BASE VALUES ARE DEV'S CURRENT LAYOUT ON PURPOSE. Dev's existing backups live
at the bucket root and at s3://cnpg; giving dev a prefix would ORPHAN the very
backups its proven restore depends on — the same reasoning that keeps dev's
EXTERNALDNS_TXT_OWNER_ID at `default`. Prod has no history to preserve.
*** PROD STILL HAS NO BACKUP COVERAGE — the endpoint is not reachable. ***
Measured from a throwaway pod INSIDE the prod cluster (the workstation proves
nothing about pod egress, and a workstation-side reading has already misled this
project once): curl http://192.168.1.9:9000/minio/health/live → connection timed
out after 15s. pfSense does not route prod's pod network to the dev LAN's MinIO.
OPERATOR ACTION REQUIRED. velero stays suspended; note apps-wave-0 applies GREEN
regardless — the token IS defined so strict substitution succeeds — while every
write fails at RUNTIME. A green apps-wave-0 does not mean backups work.
New `standup.sh s3check` step probes the endpoint from a throwaway in-cluster
pod and fails closed, with `--s3-endpoint <url>` to test a CANDIDATE before
authoring it into cluster-vars. That is how the unreachability above was
established rather than assumed.
VERIFIED: full CI gate PASS. flux-postbuild-substitution 254 -> 260 assertions,
and neither new knob appears in the dead-knob advisory list, so both are really
consumed. Rendered both overlays: all three CNPG destinationPaths and Velero's
prefix carry the tokens; dev resolves to its existing layout, prod to the nested
paths.
DELIVERY: prod reconciles the bootstrap Gitea's main at c8c1d29. This branch also
carries the DNS inversion, which must NOT reach prod. Delivering needs a
c8c1d29-based branch with only these commits — no urgency while the routing is
still closed.
Refs R-STATE-03, R-ENV-02, R-IDP-05.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
1 parent
e6bcecc78c
commit
b80e8eef82
8 files changed
+172
-19
No files matched your search
@@ -212,6 +212,28 @@ data:
|
||||
VELERO_BACKUP_SCHEDULE: "0 3 * * *" # cron; overlays may differ
|
||||
VELERO_RETENTION: "168h" # dev short | prod long
|
||||
|
||||
# BACKUP TENANCY — the two knobs that keep two clusters sharing ONE MinIO from destroying each
|
||||
# other's backups. Added 2026-09-05 when the operator ruled prod uses the SAME endpoint as dev.
|
||||
#
|
||||
# Sharing an endpoint is fine. Sharing a PATH is not, and nothing here was per-environment before:
|
||||
#
|
||||
# VELERO — bucket `velero`, no prefix, and BOTH clusters run schedules named `daily` and
|
||||
# `daily-resources-only`. Each cluster's Velero SYNCS backups it finds in the BSL into
|
||||
# itself as Backup CRs and GARBAGE-COLLECTS them when their TTL expires. Prod would
|
||||
# therefore adopt dev's backups and delete them on schedule.
|
||||
# CNPG — destinationPath `s3://cnpg` with CNPG appending serverName, which defaults to the
|
||||
# CLUSTER NAME. Both environments have `keycloak-db`, `nextcloud-db`, `semaphore-db`,
|
||||
# so prod's Postgres would archive WAL into the exact prefix dev's does. Two servers
|
||||
# interleaving WAL into one Barman stream CORRUPTS THE CHAIN and destroys PITR — and
|
||||
# dev's DR restore is PROVEN (R-STATE-03). This is the worst of the two.
|
||||
#
|
||||
# THE BASE VALUES ARE DEV'S CURRENT LAYOUT ON PURPOSE, and must stay that way: dev's existing
|
||||
# backups live at the bucket root / at `s3://cnpg`. Giving dev a prefix would ORPHAN the very
|
||||
# backups its proven restore depends on — the same reason dev's EXTERNALDNS_TXT_OWNER_ID stays
|
||||
# `default`. Prod, which has no history to preserve, takes the namespaced paths.
|
||||
VELERO_BACKUP_PREFIX: "" # dev: bucket root (existing layout) | prod: "prod"
|
||||
CNPG_BACKUP_PATH: "s3://cnpg" # dev: bucket root (existing layout) | prod: "s3://cnpg/prod"
|
||||
|
||||
# --- placement ---
|
||||
PLATFORM_NODE_SELECTOR_KEY: "kubernetes.io/os" # base no-op selector; prod may pin roles
|
||||
PLATFORM_NODE_SELECTOR_VALUE: "linux"
|
||||
@@ -383,10 +383,38 @@ patches:
|
||||
- op: replace
|
||||
path: /data/CRAFTY_L4_VIP
|
||||
value: "192.168.2.22"
|
||||
# Off-cluster DR target. Dev's versitygw/MinIO (192.168.1.9:9000) sits on the DEV side of the
|
||||
# network — a prod backup written there is not off-cluster DR and would mix two environments'
|
||||
# PITR streams in one bucket. Operator supplies the prod endpoint (full URL incl. scheme+port).
|
||||
# ------------------------------------------------------------------------------------------
|
||||
# OFF-CLUSTER DR TARGET — operator ruling 2026-09-05: "the S3 backup location is the same as
|
||||
# Dev." Prod therefore points at the on-Proxmox MinIO, and the TBD placeholder is retired.
|
||||
# Stated EXPLICITLY rather than omitting the op: an omitted op would inherit the same value
|
||||
# silently, and a shared backup target is exactly the kind of decision that must be visible
|
||||
# in the overlay that owns it.
|
||||
# ------------------------------------------------------------------------------------------
|
||||
# *** NOT YET REACHABLE — velero and the CNPG backups CANNOT be enabled on prod today. ***
|
||||
# Measured 2026-09-05 from a throwaway pod INSIDE the prod cluster (not from the workstation,
|
||||
# which proves nothing about pod egress): `curl http://192.168.1.9:9000/minio/health/live`
|
||||
# → connection timed out after 15s. pfSense does not route prod's pod network to the dev LAN's
|
||||
# MinIO. Until that routing exists, Velero and CNPG would install cleanly and fail every
|
||||
# backup at RUNTIME — a tier that reports healthy while no backup exists.
|
||||
# Re-test with: bootstrap/standup.sh ... --s3-endpoint http://192.168.1.9:9000 s3check
|
||||
#
|
||||
# SHARING THE ENDPOINT IS SAFE ONLY BECAUSE OF THE TWO PATH KNOBS BELOW. Nothing about the
|
||||
# paths was per-environment before this change: Velero used one unprefixed bucket, and every
|
||||
# CNPG Cluster used `s3://cnpg` with serverName defaulting to the CLUSTER NAME — and both
|
||||
# environments have keycloak-db / nextcloud-db / semaphore-db. Enabling prod against dev's
|
||||
# MinIO without these knobs would (a) let prod adopt and TTL-delete dev's Velero backups and
|
||||
# (b) interleave two Postgres servers' WAL into one Barman stream, CORRUPTING dev's chain and
|
||||
# destroying the PITR that R-STATE-03 proved. See clusters/base/cluster-vars.yaml.
|
||||
- op: replace
|
||||
path: /data/S3_BACKUP_ENDPOINT
|
||||
value: "TBD-PROD-S3-BACKUP-ENDPOINT"
|
||||
value: "http://192.168.1.9:9000"
|
||||
# Prod's Velero objects nest under `prod/`; dev keeps the bucket root where its backups already
|
||||
# are (renaming dev's path would orphan the backups its proven restore depends on).
|
||||
- op: replace
|
||||
path: /data/VELERO_BACKUP_PREFIX
|
||||
value: "prod"
|
||||
# Prod's Barman objects nest under s3://cnpg/prod/<cluster>/; dev keeps s3://cnpg/<cluster>/.
|
||||
- op: replace
|
||||
path: /data/CNPG_BACKUP_PATH
|
||||
value: "s3://cnpg/prod"
|
||||
# TODO(WS-INFRA, Phase 7): pin prod node placement once the bare-metal role labels exist.
|
||||
Reference in new issue
Block a user