Exclude reproducible framework code from Mautic/Nextcloud PVC backups

Mautic (node_modules/, vendor/) and Nextcloud (apps/, core/, dist/,
3rdparty/, lib/) share one PVC mount with their real user data, and were
backing up ~700MB-1GB of app/vendor code that's identical to what ships in
the image, alongside the actual user data. Excluded both via tar --exclude
in the PVC capture step: Mautic backup drops from ~185MB to ~90MB
compressed, Nextcloud from ~379MB to ~107MB.

Verified the "the image lays these down fresh on start" assumption before
relying on it — it does NOT hold for either app as a naive exclude:
  - Mautic's entrypoint has no logic at all to reconstruct node_modules/
    vendor if missing (confirmed empty by reading /entrypoint.sh).
  - Nextcloud's entrypoint only rsyncs from /usr/src/nextcloud when
    image_version > installed_version (read from version.php). Since
    version.php itself isn't excluded, a restored version.php already
    matches the running image's version, so that path never fires.

So restore-k8s-apps.sh now explicitly re-seeds these dirs from the app's
own live image (both images bake them in at /var/www/html independently of
any volume mount) via a loader pod with the PVC mounted at a different
path, after the normal PVC-data restore and before scaling the app back
up. If that re-seed fails, the app is deliberately left at 0 replicas
instead of coming back up broken (missing vendor/autoload.php is a hard
crash, not a degraded state) — found this the hard way in testing when a
90s wait timed out mid image-pull and scale-up proceeded anyway with
vendor/ missing; fixed by gating scale-up on the re-seed outcome and
raising the timeout to 240s with imagePullPolicy: IfNotPresent (the image
is already on-node, pulled off the live Deployment spec).

Verified end-to-end: real backup + restore of Mautic on the live cluster,
confirming size drop, vendor/autoload.php present post-restore, app
serving 200s, and all 433,681 leads intact via the DB restore.

Also found and left unfixed (pre-existing, unrelated): secret.yaml apply
during restore can hit a resourceVersion conflict from kubectl apply
against a captured manifest — the mautic-secrets apply failed on this
restore test with a benign "object has been modified" error since the
secret already existed with correct values; app was unaffected. Separate
bug from this change, flagging for later.
This commit is contained in:
root
2026-08-21 11:07:02 +02:00
parent 72dba7a3a4
commit fca77d92b5
2 changed files with 95 additions and 4 deletions

View File

@@ -96,6 +96,15 @@ declare -A SECRET_NAME=( [n8n]=n8n-secrets [odoo]=odoo-secrets [mautic]=ma
declare -A DB_USER_KEY=( [n8n]=DB_POSTGRESDB_USER [odoo]=POSTGRES_USER [mautic]=MYSQL_USER [nextcloud]=POSTGRES_USER [frappe]="" )
declare -A DB_PASS_KEY=( [n8n]=DB_POSTGRESDB_PASSWORD [odoo]=POSTGRES_PASSWORD [mautic]=MYSQL_ROOT_PASSWORD [nextcloud]=POSTGRES_PASSWORD [frappe]=MARIADB_ROOT_PASSWORD )
# Reproducible framework/vendor code that lives on the same PVC as real user
# data for these two apps (single shared mount, no separate volume). Both
# images bake these dirs into their own filesystem layer independent of any
# volume — Mautic has no logic to reconstruct them, so restore-k8s-apps.sh
# re-seeds them straight from the app's own image instead of from backup.
# Never pull request/exclude changes here without updating the matching
# RECONSTRUCT_DIRS table in restore-k8s-apps.sh.
declare -A TAR_EXCLUDES=( [mautic]="node_modules vendor" [nextcloud]="apps core dist 3rdparty lib" )
ALL_APPS="n8n odoo mautic nextcloud frappe"
BACKUP_DATE=$(date +%Y%m%d_%H%M%S)
@@ -361,9 +370,15 @@ for app in $ALL_APPS; do
# ---- 4. App-data PVC contents via kubectl exec (live tar, app-data only,
# never raw filesystem/PVC-path access — §5 sandbox-divergence rule) ----
echo -n " 📦 PVC data ($pvc_path) ... "
exclude_args=()
if [ -n "${TAR_EXCLUDES[$app]:-}" ]; then
for _excl_dir in ${TAR_EXCLUDES[$app]}; do
exclude_args+=(--exclude="./$_excl_dir")
done
fi
echo -n " 📦 PVC data ($pvc_path${exclude_args:+, excl: ${TAR_EXCLUDES[$app]}}) ... "
kubectl exec -n "$ns" "deploy/${app_deploy}" -c "$app_ctr" -- \
tar czf - -C "$pvc_path" . 2>"$APP_DIR/pvc-data.err" \
tar czf - "${exclude_args[@]}" -C "$pvc_path" . 2>"$APP_DIR/pvc-data.err" \
> "$APP_DIR/pvc-data.tar.gz"
if [ -s "$APP_DIR/pvc-data.tar.gz" ]; then
echo "✅ ($(du -h "$APP_DIR/pvc-data.tar.gz" | cut -f1))"