Live off-site Postgres copy on 247 replaces the nightly pg_dump; backup retention in deploy.sh
- ops/pgbackup: SSH tunnel + pg_receivewal (slot zareian_247) + daily verified pg_basebackup (keep 7) and a restore check, all run on 78.38.35.247 as systemd units - /api/health reports offsiteBackup (slot active/lag/lost) and warns; OFFSITE_WAL_SLOT setting - removed statuslog backup_db/backup_verify and their health warnings - deploy.sh keeps 3 pre-deploy dumps, 3 local patches, drops manual backups after 30 days - docs: operations backup section, security item 14 closed, CLAUDE.md status Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
28
ops/pgbackup/basebackup.sh
Executable file
28
ops/pgbackup/basebackup.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
# نسخهی کاملِ روزانه از Postgresِ ۲۴۹، روی ۲۴۷. هفت نسخهی آخر میماند و WALِ قدیمیتر از قدیمیترینِ
|
||||
# آنها پاک میشود؛ پس بازیابی تا هر لحظه از هفت روزِ اخیر ممکن است و حجم محدود میماند.
|
||||
# --checkpoint=spread و --max-rate بار روی ۲۴۹ را کم نگه میدارند (آنجا سرورِ اصلی است).
|
||||
# نسخه تا pg_verifybackup قبولش نکند جای قبلیها را نمیگیرد و چیزی پاک نمیشود.
|
||||
set -euo pipefail
|
||||
ROOT=/home/administrator/zareian-pgbackup
|
||||
BIN=/usr/lib/postgresql/14/bin
|
||||
KEEP=7
|
||||
|
||||
cd "$ROOT"
|
||||
umask 077
|
||||
ts=$(date +%Y%m%d-%H%M%S)
|
||||
rm -rf base/.inprogress-*
|
||||
tmp="base/.inprogress-$ts"
|
||||
|
||||
"$BIN/pg_basebackup" -h 127.0.0.1 -p 15440 -U zareian_replicator --no-password \
|
||||
-D "$tmp" -Fp -X stream --checkpoint=spread --max-rate=20M
|
||||
"$BIN/pg_verifybackup" "$tmp"
|
||||
mv "$tmp" "base/$ts"
|
||||
|
||||
ls -1d base/2* | head -n -"$KEEP" | xargs -r rm -rf
|
||||
oldest=$(ls -1d base/2* | head -1)
|
||||
start_wal=$(sed -n 's/^START WAL LOCATION: .* (file \([0-9A-F]*\))$/\1/p' "$oldest/backup_label")
|
||||
[ -n "$start_wal" ] || { echo "START WAL در $oldest/backup_label پیدا نشد" >&2; exit 1; }
|
||||
"$BIN/pg_archivecleanup" wal "$start_wal"
|
||||
|
||||
echo "ok base/$ts ($(du -sh "base/$ts" | cut -f1)), kept $(ls -1d base/2* | wc -l), wal since $start_wal ($(du -sh wal | cut -f1))"
|
||||
51
ops/pgbackup/restore-check.sh
Executable file
51
ops/pgbackup/restore-check.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
# آزمونِ بازیابی روی خودِ ۲۴۷: آخرین نسخهی کامل + همهی WALِ رسیده را در پوشهی موقت بالا میآورد
|
||||
# (Postgresِ موقت روی سوکتِ داخلِ همان پوشه، بیپورتِ شبکه؛ به Postgresِ دیگرانِ این ماشین دست نمیزند)،
|
||||
# شمارشِ جدولهای کاربر/ورود/پرداخت و آخرین زمانها را چاپ میکند و همهچیز را پاک میکند.
|
||||
# همان پرسش را روی ۲۴۹ بزنید و مقایسه کنید. با آرگومان، تا یک لحظه برمیگردد:
|
||||
# restore-check.sh '2026-10-10 16:30:00+03:30'
|
||||
# با KEEP=1 پوشه و Postgresِ موقت میمانند (برای بازیابیِ واقعی: از آن pg_dump بگیرید، بعد pg_ctl stop و rm).
|
||||
set -euo pipefail
|
||||
ROOT=/home/administrator/zareian-pgbackup
|
||||
BIN=/usr/lib/postgresql/14/bin
|
||||
TARGET="${1:-}"
|
||||
|
||||
latest=$(ls -1d "$ROOT"/base/2* | tail -1)
|
||||
work=$(mktemp -d "$ROOT/restore-check.XXXXXX")
|
||||
if [ "${KEEP:-0}" != 1 ]; then
|
||||
trap '"$BIN/pg_ctl" -D "$work/data" -m immediate stop >/dev/null 2>&1 || true; rm -rf "$work"' EXIT
|
||||
fi
|
||||
|
||||
cp -a "$latest" "$work/data"
|
||||
mkdir "$work/wal"
|
||||
cp "$ROOT"/wal/* "$work/wal/"
|
||||
# فایلِ در حالِ نوشتن (.partial) هم دادهی واقعی است؛ برای بازیابی نامِ کامل میگیرد.
|
||||
for f in "$work"/wal/*.partial; do [ -e "$f" ] && mv "$f" "${f%.partial}"; done
|
||||
|
||||
# postgresql.conf و pg_hba.conf روی اوبونتو در /etc اند، نه در پوشهی داده؛ برای نمونهی موقت ساخته میشوند.
|
||||
cat > "$work/data/postgresql.conf" <<CONF
|
||||
listen_addresses = ''
|
||||
unix_socket_directories = '$work'
|
||||
restore_command = 'cp $work/wal/%f %p'
|
||||
recovery_target_action = 'promote'
|
||||
${TARGET:+recovery_target_time = '$TARGET'}
|
||||
CONF
|
||||
echo 'local all all trust' > "$work/data/pg_hba.conf"
|
||||
: > "$work/data/postgresql.auto.conf"
|
||||
touch "$work/data/recovery.signal"
|
||||
|
||||
"$BIN/pg_ctl" -D "$work/data" -l "$work/log" -w -t 600 start >/dev/null
|
||||
for _ in $(seq 120); do
|
||||
[ "$("$BIN/psql" -h "$work" -U postgres -d zareian_mathapp -Atc 'select pg_is_in_recovery()')" = f ] && break
|
||||
sleep 1
|
||||
done
|
||||
|
||||
echo "base: $(basename "$latest") target: ${TARGET:-end of WAL}"
|
||||
"$BIN/psql" -h "$work" -U postgres -d zareian_mathapp -c "
|
||||
select 'users' what, count(*)::text n, max(created_at)::text latest from accounts_user
|
||||
union all select 'login_events', count(*)::text, max(created_at)::text from accounts_loginevent
|
||||
union all select 'orders', count(*)::text, max(created_at)::text from payment_order
|
||||
union all select 'transactions', count(*)::text, max(created_at)::text from payment_transaction
|
||||
union all select 'status_changes', count(*)::text, max(created_at)::text from statuslog_statuschangelog;"
|
||||
[ "${KEEP:-0}" = 1 ] && echo "kept: $work (psql -h $work -U postgres -d zareian_mathapp)"
|
||||
exit 0
|
||||
10
ops/pgbackup/zareian-pg-basebackup.service
Normal file
10
ops/pgbackup/zareian-pg-basebackup.service
Normal file
@@ -0,0 +1,10 @@
|
||||
# روی سرورِ بکاپ (۲۴۷). یک نسخهی کاملِ روزانه؛ بازیابی = آخرین نسخه + WALِ بعدش تا هر لحظه.
|
||||
[Unit]
|
||||
Description=Zareian MathApp: daily base backup from 249
|
||||
Requires=zareian-pg-tunnel.service
|
||||
After=zareian-pg-tunnel.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=administrator
|
||||
ExecStart=/home/administrator/zareian-pgbackup/bin/basebackup.sh
|
||||
9
ops/pgbackup/zareian-pg-basebackup.timer
Normal file
9
ops/pgbackup/zareian-pg-basebackup.timer
Normal file
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Zareian MathApp: daily base backup from 249
|
||||
|
||||
[Timer]
|
||||
OnCalendar=*-*-* 04:30:00
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
19
ops/pgbackup/zareian-pg-tunnel.service
Normal file
19
ops/pgbackup/zareian-pg-tunnel.service
Normal file
@@ -0,0 +1,19 @@
|
||||
# روی سرورِ بکاپ (۲۴۷). تونلِ SSH از 127.0.0.1:15440ِ اینجا به Postgresِ ما روی ۲۴۹ (127.0.0.1:5440).
|
||||
# کلید در authorized_keysِ ۲۴۹ با restrict,port-forwarding,permitopen="127.0.0.1:5440" محدود است:
|
||||
# با آن نه شِل باز میشود نه پورتِ دیگری. ترافیک در شبکهی داخلی (192.168.200.x) میماند.
|
||||
[Unit]
|
||||
Description=Zareian MathApp: SSH tunnel to the app Postgres on 249
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
User=administrator
|
||||
ExecStart=/usr/bin/ssh -N -i /home/administrator/.ssh/zareian_pgbackup \
|
||||
-o BatchMode=yes -o ExitOnForwardFailure=yes \
|
||||
-o ServerAliveInterval=15 -o ServerAliveCountMax=3 \
|
||||
-L 127.0.0.1:15440:127.0.0.1:5440 administrator@192.168.200.164
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
19
ops/pgbackup/zareian-pg-wal.service
Normal file
19
ops/pgbackup/zareian-pg-wal.service
Normal file
@@ -0,0 +1,19 @@
|
||||
# روی سرورِ بکاپ (۲۴۷). هر تغییرِ دیتابیسِ ۲۴۹ را لحظهای در wal/ مینویسد (pg_receivewal، ابزارِ خودِ Postgres).
|
||||
# --synchronous: هر تکه همان لحظه روی دیسکِ اینجا fsync میشود. اپ روی ۲۴۹ منتظرِ اینجا نمیماند
|
||||
# (synchronous_standby_names خالی است)، پس قطعیِ ۲۴۷ هیچ پرداختی را کند یا متوقف نمیکند.
|
||||
# اسلاتِ zareian_247 روی ۲۴۹ نگه میدارد که در قطعی چیزی جا نیفتد؛ سقفش max_slot_wal_keep_size=1GB است.
|
||||
[Unit]
|
||||
Description=Zareian MathApp: live WAL stream from 249
|
||||
After=zareian-pg-tunnel.service
|
||||
BindsTo=zareian-pg-tunnel.service
|
||||
|
||||
[Service]
|
||||
User=administrator
|
||||
ExecStart=/usr/lib/postgresql/14/bin/pg_receivewal -h 127.0.0.1 -p 15440 -U zareian_replicator \
|
||||
--no-password --slot=zareian_247 --synchronous --no-loop \
|
||||
-D /home/administrator/zareian-pgbackup/wal
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user