homelab-brain/arakava-news/scripts/wp-mirror-sync.sh
Homelab Cursor f0013a9fd6 fix(wp-mirror): Cron-PATH und Tar-Fehlerbehandlung im Nacht-Sync
Cron.d lieferte kein /usr/sbin, pct wurde nicht gefunden (tägl. ABORT seit 29.06.).
2026-07-29 19:44:59 +02:00

154 lines
4.9 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# Pull WordPress CT101 (pve-hetzner) → CT600 wp-mirror (pve-mu-3)
# CT101 impact: read-only mysqldump (--single-transaction) + read-only tar. No restarts.
# Logs: Host /var/log/wp-mirror-sync.log + CT600 /var/log/wp-mirror-sync.log → Loki (Promtail)
set -euo pipefail
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
HETZNER_HOST="${HETZNER_HOST:-root@100.88.230.59}"
HETZNER_CT="${HETZNER_CT:-101}"
MIRROR_CT="${MIRROR_CT:-600}"
STAGING="${STAGING:-/opt/wp-mirror-sync/staging}"
LOG="${LOG:-/var/log/wp-mirror-sync.log}"
LOG_CT="/var/log/wp-mirror-sync.log"
LOCK_FILE="/var/run/wp-mirror-sync.lock"
FAILOVER_FLAG="/opt/wp-mirror-sync/FAILOVER_ACTIVE"
MIRROR_SITEURL="http://192.168.178.142"
MYSQL_USER="wordpress"
MYSQL_PASS="astral66"
MYSQL_DB="wordpress"
WP_VOL="/var/lib/docker/volumes/wordpress_wp_data/_data"
MIRROR_RUNNING=0
log() {
local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $*"
echo "$msg" | tee -a "$LOG"
if [[ "$MIRROR_RUNNING" == "1" ]]; then
echo "$msg" | pct exec "$MIRROR_CT" -- tee -a "$LOG_CT" >/dev/null 2>&1 || true
fi
}
ensure_mirror_ct() {
if [[ "$(pct status "$MIRROR_CT" 2>/dev/null | awk '{print $2}')" != "running" ]]; then
pct start "$MIRROR_CT"
fi
for _ in $(seq 1 30); do
if pct exec "$MIRROR_CT" -- test -d /var/log 2>/dev/null; then
MIRROR_RUNNING=1
pct exec "$MIRROR_CT" -- touch "$LOG_CT" 2>/dev/null || true
return 0
fi
sleep 2
done
return 1
}
ct101_http_ok() {
local code
code="$(ssh -o BatchMode=yes -o ConnectTimeout=15 "$HETZNER_HOST" \
"pct exec ${HETZNER_CT} -- curl -sS -o /dev/null -w '%{http_code}' --max-time 15 http://127.0.0.1/" 2>/dev/null || echo 000)"
[[ "$code" == "200" || "$code" == "301" ]]
}
tar_sync_files() {
rm -rf "${STAGING}/wpfiles"
mkdir -p "${STAGING}/wpfiles"
ssh -o BatchMode=yes -o ConnectTimeout=30 "$HETZNER_HOST" \
"ionice -c3 nice -n 19 pct exec ${HETZNER_CT} -- tar c -C ${WP_VOL} \
--exclude=wp-content/cache \
--exclude=wp-content/debug.log \
--exclude=wp-content/upgrade \
. 2>/dev/null" | tar x -C "${STAGING}/wpfiles"
}
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
log "SKIP: another sync is running"
exit 0
fi
if [[ -f "$FAILOVER_FLAG" ]]; then
log "SKIP: ${FAILOVER_FLAG} present (manual failover)"
exit 0
fi
if ! command -v pct >/dev/null 2>&1; then
log "ABORT: must run on Proxmox host with pct (pve-mu-3)"
exit 1
fi
mkdir -p "$STAGING" "$(dirname "$LOG")"
log "Checking CT101 health..."
if ! ct101_http_ok; then
log "ABORT: CT101 does not respond on http://127.0.0.1/ (expected HTTP 200/301)"
exit 1
fi
log "Starting CT600 (Promtail/Loki)..."
if ! ensure_mirror_ct; then
log "ABORT: CT600 did not become ready"
exit 1
fi
log "START sync CT101 → CT600"
DB_FILE="${STAGING}/wordpress.sql.gz"
log "DB dump (single-transaction, read-only)..."
if ! ssh -o BatchMode=yes -o ConnectTimeout=30 "$HETZNER_HOST" \
"ionice -c3 nice -n 19 pct exec ${HETZNER_CT} -- docker exec wordpress-mysql mysqldump \
-u${MYSQL_USER} -p${MYSQL_PASS} \
--single-transaction --quick --skip-lock-tables \
--add-drop-table ${MYSQL_DB} 2>/dev/null" | gzip > "${DB_FILE}.tmp"; then
log "ABORT: mysqldump failed"
rm -f "${DB_FILE}.tmp"
exit 1
fi
mv "${DB_FILE}.tmp" "$DB_FILE"
log "DB dump size: $(du -h "$DB_FILE" | awk '{print $1}')"
log "File sync via tar (read-only, ~58 min)..."
if ! tar_sync_files; then
log "ABORT: file sync via tar failed"
exit 1
fi
log "Files staged: $(du -sh "${STAGING}/wpfiles" | awk '{print $1}')"
for _ in $(seq 1 45); do
if pct exec "$MIRROR_CT" -- docker info >/dev/null 2>&1; then
break
fi
sleep 2
done
pct exec "$MIRROR_CT" -- docker stop wordpress-app 2>/dev/null || true
log "Import DB into CT600..."
gunzip -c "$DB_FILE" | pct exec "$MIRROR_CT" -- docker exec -i wordpress-mysql \
mysql -u"$MYSQL_USER" -p"$MYSQL_PASS" "$MYSQL_DB"
log "Import files into CT600..."
tar c -C "${STAGING}/wpfiles" . 2>/dev/null | pct exec "$MIRROR_CT" -- tar x -C "$WP_VOL"
log "Reset mirror idle siteurl/home..."
pct exec "$MIRROR_CT" -- docker exec wordpress-mysql mysql -u"$MYSQL_USER" -p"$MYSQL_PASS" "$MYSQL_DB" -e \
"UPDATE wp_options SET option_value='${MIRROR_SITEURL}' WHERE option_name IN ('siteurl','home');"
pct exec "$MIRROR_CT" -- docker start wordpress-app
sleep 5
POSTS="$(pct exec "$MIRROR_CT" -- docker exec wordpress-mysql mysql -u"$MYSQL_USER" -p"$MYSQL_PASS" -N -e \
"SELECT COUNT(*) FROM wp_posts WHERE post_status='publish' AND post_type='post';" "$MYSQL_DB" 2>/dev/null)"
LAST="$(pct exec "$MIRROR_CT" -- docker exec wordpress-mysql mysql -u"$MYSQL_USER" -p"$MYSQL_PASS" -N -e \
"SELECT MAX(post_date) FROM wp_posts WHERE post_type='post';" "$MYSQL_DB" 2>/dev/null)"
log "Mirror verified: ${POSTS} published posts, latest ${LAST}"
if ! ct101_http_ok; then
log "WARN: CT101 health check failed AFTER sync — investigate immediately"
exit 1
fi
log "Stopping CT600 (standby)..."
pct stop "$MIRROR_CT"
MIRROR_RUNNING=0
log "DONE"