RAILIANCE-WP-0016: finish unattended hybrid Option A backups
Core in-cluster CronJobs with cnpg-option-a-backup image + NetworkPolicy; R01 activity-core schedule enabled; status healthy; evidence recorded.
This commit is contained in:
parent
65e71884a9
commit
4a06c0884a
6 changed files with 196 additions and 158 deletions
|
|
@ -3,10 +3,10 @@
|
|||
# Decided approach (not barman ScheduledBackup). Applies to CoulombCore
|
||||
# databases namespace clusters: apps-pg, gitea-db, net-kingdom-pg, state-hub-db.
|
||||
#
|
||||
# Unattended path (primary): workstation cron (same pattern as forgejo-backup).
|
||||
# CoulombCore pods cannot reach github.com / Alpine CDN to fetch `age`, so
|
||||
# in-cluster CronJobs are shipped **suspended**. Enable only after an image
|
||||
# with kubectl+age+curl is available in-cluster.
|
||||
# Unattended path (primary):
|
||||
# - CoulombCore: in-cluster CronJobs using image cnpg-option-a-backup:v1
|
||||
# (kubectl+age+curl; import via k3s ctr; imagePullPolicy Never)
|
||||
# - railiance01: activity-core ActivityDefinition daily-cnpg-option-a-backup
|
||||
#
|
||||
# Prereqs:
|
||||
# 1. OpenBao lane CCR-2026-0004 resolvable
|
||||
|
|
@ -28,8 +28,8 @@ metadata:
|
|||
app.kubernetes.io/part-of: railiance-apps
|
||||
railiance.apps/backup-lane: option-a
|
||||
data:
|
||||
mode: workstation-cron
|
||||
next_mode: activity-core
|
||||
mode: in-cluster-cron
|
||||
next_mode: activity-core+in-cluster
|
||||
schedule: "30 2 * * *"
|
||||
command: "cd $HOME/railiance-apps && make cnpg-logical-backup"
|
||||
activity_core_definition: daily-cnpg-option-a-backup
|
||||
|
|
@ -109,16 +109,12 @@ data:
|
|||
WORK=/tmp/backup-work
|
||||
mkdir -p "$WORK"
|
||||
|
||||
# Prefer distro package (cluster often cannot reach github.com).
|
||||
if ! command -v age >/dev/null 2>&1; then
|
||||
echo "install age via apk"
|
||||
apk add --no-cache age >/dev/null
|
||||
fi
|
||||
if ! command -v age >/dev/null 2>&1; then
|
||||
echo "ERROR: age not available (apk install failed; github fallback disabled)" >&2
|
||||
# Image cnpg-option-a-backup:v1 ships /usr/local/bin/age (no egress installs).
|
||||
AGE_BIN="$(command -v age || true)"
|
||||
if [ -z "$AGE_BIN" ]; then
|
||||
echo "ERROR: age binary missing from image PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
AGE_BIN="$(command -v age)"
|
||||
|
||||
NC_TOKEN="$(cat /secrets/offsite/NC_WEBDAV_TOKEN)"
|
||||
NC_URL="$(cat /secrets/offsite/NC_WEBDAV_URL)"
|
||||
|
|
@ -189,7 +185,7 @@ metadata:
|
|||
railiance.apps/backup-cluster: apps-pg
|
||||
spec:
|
||||
schedule: "30 2 * * *"
|
||||
suspend: true
|
||||
suspend: false
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
|
|
@ -207,8 +203,8 @@ spec:
|
|||
restartPolicy: Never
|
||||
containers:
|
||||
- name: backup
|
||||
image: alpine/k8s:1.31.12
|
||||
imagePullPolicy: IfNotPresent
|
||||
image: cnpg-option-a-backup:v1
|
||||
imagePullPolicy: Never
|
||||
env:
|
||||
- name: CLUSTER
|
||||
value: apps-pg
|
||||
|
|
@ -251,7 +247,7 @@ metadata:
|
|||
railiance.apps/backup-cluster: gitea-db
|
||||
spec:
|
||||
schedule: "35 2 * * *"
|
||||
suspend: true
|
||||
suspend: false
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
|
|
@ -269,8 +265,8 @@ spec:
|
|||
restartPolicy: Never
|
||||
containers:
|
||||
- name: backup
|
||||
image: alpine/k8s:1.31.12
|
||||
imagePullPolicy: IfNotPresent
|
||||
image: cnpg-option-a-backup:v1
|
||||
imagePullPolicy: Never
|
||||
env:
|
||||
- name: CLUSTER
|
||||
value: gitea-db
|
||||
|
|
@ -313,7 +309,7 @@ metadata:
|
|||
railiance.apps/backup-cluster: net-kingdom-pg
|
||||
spec:
|
||||
schedule: "40 2 * * *"
|
||||
suspend: true
|
||||
suspend: false
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
|
|
@ -331,8 +327,8 @@ spec:
|
|||
restartPolicy: Never
|
||||
containers:
|
||||
- name: backup
|
||||
image: alpine/k8s:1.31.12
|
||||
imagePullPolicy: IfNotPresent
|
||||
image: cnpg-option-a-backup:v1
|
||||
imagePullPolicy: Never
|
||||
env:
|
||||
- name: CLUSTER
|
||||
value: net-kingdom-pg
|
||||
|
|
@ -375,7 +371,7 @@ metadata:
|
|||
railiance.apps/backup-cluster: state-hub-db
|
||||
spec:
|
||||
schedule: "45 2 * * *"
|
||||
suspend: true
|
||||
suspend: false
|
||||
concurrencyPolicy: Forbid
|
||||
successfulJobsHistoryLimit: 3
|
||||
failedJobsHistoryLimit: 3
|
||||
|
|
@ -393,8 +389,8 @@ spec:
|
|||
restartPolicy: Never
|
||||
containers:
|
||||
- name: backup
|
||||
image: alpine/k8s:1.31.12
|
||||
imagePullPolicy: IfNotPresent
|
||||
image: cnpg-option-a-backup:v1
|
||||
imagePullPolicy: Never
|
||||
env:
|
||||
- name: CLUSTER
|
||||
value: state-hub-db
|
||||
|
|
|
|||
72
manifests/cnpg-option-a-netpol.yaml
Normal file
72
manifests/cnpg-option-a-netpol.yaml
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# Allow Option A backup Jobs to reach kube-api, DNS, and HTTPS offsite (Nextcloud).
|
||||
# databases/ has default-deny; CNPG cluster pods already have kube-api egress by label.
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-egress-cnpg-option-a-backup
|
||||
namespace: databases
|
||||
labels:
|
||||
app.kubernetes.io/part-of: railiance-apps
|
||||
railiance.apps/backup-lane: option-a
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
railiance.apps/backup-lane: option-a
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
# kube-api (in-cluster service + node port forms)
|
||||
- ports:
|
||||
- port: 443
|
||||
protocol: TCP
|
||||
- port: 6443
|
||||
protocol: TCP
|
||||
# DNS
|
||||
- ports:
|
||||
- port: 53
|
||||
protocol: UDP
|
||||
- port: 53
|
||||
protocol: TCP
|
||||
# Nextcloud WebDAV HTTPS (and general HTTPS for future mirrors)
|
||||
- ports:
|
||||
- port: 443
|
||||
protocol: TCP
|
||||
to:
|
||||
- namespaceSelector: {}
|
||||
# allow all namespaces + external via empty podSelector not possible;
|
||||
# for external CIDR use 0.0.0.0/0
|
||||
- ipBlock:
|
||||
cidr: 0.0.0.0/0
|
||||
---
|
||||
# Some CNI stacks need separate CIDR-only rules for kube-api ClusterIP
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: allow-egress-cnpg-option-a-backup-kubeapi-cidr
|
||||
namespace: databases
|
||||
labels:
|
||||
app.kubernetes.io/part-of: railiance-apps
|
||||
railiance.apps/backup-lane: option-a
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
railiance.apps/backup-lane: option-a
|
||||
policyTypes:
|
||||
- Egress
|
||||
egress:
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 10.43.0.0/16
|
||||
ports:
|
||||
- port: 443
|
||||
protocol: TCP
|
||||
- port: 6443
|
||||
protocol: TCP
|
||||
- to:
|
||||
- ipBlock:
|
||||
cidr: 10.42.0.0/16
|
||||
ports:
|
||||
- port: 443
|
||||
protocol: TCP
|
||||
- port: 6443
|
||||
protocol: TCP
|
||||
Loading…
Add table
Add a link
Reference in a new issue