Перейти к основному содержимому

Практикум DR — стенд и бэкап

На этом шаге вы поднимаете учебный PostgreSQL в Docker, наполняете базу тестовыми заказами, создаёте логический бэкап через pg_dump и копируете dump в offsite-хранилище. Offsite в lab — отдельная папка на другом диске, другая машина или bucket в облаке; главное — это не тот же Docker volume pgdata, где лежат живые данные. Параллельно вы начнёте runbook — документ, по которому в шаге 3 выполните restore drill.


1. PostgreSQL в Docker

Контейнер pg-dr-lab слушает порт 5433 на хосте (чтобы не конфликтовать с локальным Postgres на 5432). Данные persist в named volume pgdata.

docker run -d --name pg-dr-lab -e POSTGRES_PASSWORD=lab -e POSTGRES_DB=shop \
-v pgdata:/var/lib/postgresql/data -p 5433:5432 postgres:16-alpine

Проверка, что контейнер жив:

docker ps --filter name=pg-dr-lab
docker exec pg-dr-lab pg_isready -U postgres

Тестовые данные имитируют простой интернет-магазин — таблица orders с двумя строками. Время created_at пригодится позже для демонстрации RPO gap (заказы после dump не восстановятся).

docker exec -it pg-dr-lab psql -U postgres -d shop -c \
"CREATE TABLE orders(id serial PRIMARY KEY, item text, created_at timestamptz default now());
INSERT INTO orders(item) VALUES ('widget'), ('gadget');"
docker exec pg-dr-lab psql -U postgres -d shop -c "SELECT id, item, created_at FROM orders;"

Запишите в runbook версию образа (postgres:16-alpine), имя volume (pgdata) и пароль lab — в production пароль берут из секрет-хранилища, в lab допустим явный текст.


2. Логический бэкап и формат dump

pg_dump — утилита PostgreSQL для создания логической копии базы: схема, данные, индексы в переносимом файле. Флаг -Fc (custom format) даёт сжатый бинарный dump, который восстанавливают через pg_restore с опциями --clean, параллельным -j и выборочным restore объектов.

ФорматКомандаПлюсы для DR
Custom -Fcpg_dump -FcСжатие, pg_restore, гибкий restore
Plain SQLpg_dump -FpЧитаемость, медленнее, один большой SQL
Directory -Fdpg_dump -FdПараллельный dump/restore на больших базах

Создайте две папки на хосте: backup-local (рабочая копия рядом с lab) и backup-offsite (симуляция удалённого хранилища).

mkdir -p ./backup-local ./backup-offsite
docker exec pg-dr-lab pg_dump -U postgres -Fc shop > ./backup-local/shop-$(date +%Y%m%d-%H%M).dump
cp ./backup-local/shop-*.dump ./backup-offsite/

Проверьте размер и целостность файла:

ls -lh ./backup-offsite/
file ./backup-offsite/shop-*.dump

Offsite simulation: папка backup-offsite на другом диске, sync в S3 через aws s3 cp, rclone copy на второй VPS или Yandex Object Storage — любой вариант, где копия переживёт удаление volume pgdata. Копия dump только в backup-local на том же SSD, что и Docker data root, правило 3-2-1 не выполняет.


3. Стратегия расписания бэкапов

Частота pg_dump напрямую задаёт RPO при отсутствии WAL. Таблица ориентиров для pet-проекта:

РасписаниеRPO (худший случай)Нагрузка на CPU/IO
Раз в сутки 03:00До 24 часовМинимальная
Каждые 6 часовДо 6 часовНизкая
Каждый часДо 1 часаСредняя на активной базе

Для учебного магазина с двумя заказами достаточно cron каждые 6 часов. Для production с заказами каждую минуту RPO "15 минут" потребует либо hourly dump, либо архива WAL (8.11/10).

Пример строки cron на хосте (идея, пути подставьте свои):

# 0 */6 * * * pg_dump ... && aws s3 cp ... s3://my-dr-bucket/shop/

После каждого успешного dump фиксируйте timestamp в runbook или в метке имени файла shop-20250615-0300.dump.


4. Offsite в облаке и пример стоимости

Для pet-проекта offsite часто — object storage с lifecycle на cold tier. Пример monthly cost при 6 dump в сутки, размер одного dump 50 МБ после -Fc:

СтатьяРасчётОриентир
Хранение 30 дней × 6 × 50 МБ ≈ 9 ГБS3 Standard ~$0.023/ГБ~$0.21/мес
Тот же объёмGlacier Instant Retrieval~$0.04/мес
PUT запросы 180/мескопейкименее $0.01

Egress при restore из bucket в инциденте — отдельная строка (первые гигабайты часто дешевле основного трафика). Сравнение tier и алерты — FinOps для pet-проекта. Смысл FinOps здесь — не переплачивать за Standard tier для файлов, которые читают раз в квартал на учении.

Команда загрузки в S3 (после локального dump):

aws s3 cp ./backup-local/shop-$(date +%Y%m%d-%H%M).dump \
s3://my-dr-bucket/shop/ --storage-class GLACIER_IR

Аналог для Yandex Cloud — yc storage s3 cp с bucket в другом folder или регионе.


5. Retention — сколько dump хранить

Хранить один последний файл рискованно: битый dump обнаружится только на restore. Минимум для pet-проекта — 7 daily + 4 weekly или правило "последние 30 файлов". Lifecycle в bucket автоматически удаляет объекты старше N дней и экономит storage.


6. Черновик runbook

Запишите в RUNBOOK-dr-shop.md или wiki следующие блоки.

Контекст. База shop, контейнер pg-dr-lab, порт 5433, offsite ./backup-offsite/ или s3://my-dr-bucket/shop/.

Команда restore (полная версия в шаге 3):

docker run -d --name pg-dr-lab-new -e POSTGRES_PASSWORD=lab -e POSTGRES_DB=shop \
-v pgdata-new:/var/lib/postgresql/data -p 5433:5432 postgres:16-alpine
DUMP=$(ls -t ./backup-offsite/*.dump | head -1)
docker exec -i pg-dr-lab-new pg_restore -U postgres -d shop --clean --if-exists < "$DUMP"

On-call — ваш Telegram или email pet-проекта.

Целевые метрики — RTO 1 час, RPO 6 часов (пример; подставьте свои из шага 1).

Last successful test restore — дата пустая до первого учения; после шага 3 впишите фактическую дату и RTO в минутах.

Runbook — документ, который читают в 3 ночи во время инцидента. Короткие команды, без лишней теории.


7. Скрипт backup pipeline (идея)

Объедините dump, checksum и upload в один shell-скрипт backup-shop.sh. Cron вызывает только его — меньше шансов забыть cp в offsite.

#!/bin/bash
set -euo pipefail
STAMP=$(date +%Y%m%d-%H%M)
LOCAL="./backup-local/shop-${STAMP}.dump"
OFFSITE="./backup-offsite/shop-${STAMP}.dump"
docker exec pg-dr-lab pg_dump -U postgres -Fc shop > "$LOCAL"
sha256sum "$LOCAL" > "${LOCAL}.sha256"
cp "$LOCAL" "${LOCAL}.sha256" ./backup-offsite/
# aws s3 cp "$LOCAL" "s3://my-dr-bucket/shop/${STAMP}.dump"
echo "OK ${STAMP}" >> ./backup-offsite/backup.log

После upload проверьте sha256sum -c на копии в offsite. Контрольная сумма ловит обрезанные файлы при нестабильной сети.


8. Мониторинг успеха бэкапа

Cron молча падает, если диск полон. Минимальный мониторинг для pet-проекта:

СигналКак проверитьДействие
Файл свежийfind backup-offsite -mtime -1Алерт если старше 7 h при cron 6 h
Размер dumpне 0 байтАлерт в Telegram bot
Строка в logtail backup.logНет OK за сутки — разбор

Интеграция с Prometheus practicum — следующий уровень; для lab достаточно email от cron MAILTO=.


9. Логический и физический бэкап — когда переходить

КритерийОстаёмся на pg_dumpДобавляем physical + WAL
Размер БДдо ~50 ГБсотни ГБ и выше
RPOчасыминуты
Downtime restoreминуты–часы приемлемынужен PITR
Командаодин человекDBA / SRE

Practicum закрывает левую колонку. При росте проекта план миграции стратегии фиксируют в runbook: "при 100k заказов — включить WAL archive по 8.11/10".


10. Troubleshooting pg_dump

СимптомПричинаРешение
pg_dump: error: connection refusedКонтейнер downdocker start pg-dr-lab
Dump 0 байтРедирект до готовности БДpg_isready перед dump
disk fullLocal backup без rotationRetention + lifecycle
Долгий dumpБольшая таблица без vacuumVACUUM ANALYZE, -Fd parallel

12. Docker Compose для lab (альтернатива docker run)

Для повторяемого стенда сохраните docker-compose.dr-lab.yml:

services:
pg-dr-lab:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: lab
POSTGRES_DB: shop
ports:
- "5433:5432"
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:

Команды docker compose up -d и docker compose down -v (осторожно: -v удаляет volume — только в lab). Runbook фиксирует, используете ли raw docker run или compose.


13. Проверка после pg_dump

docker exec pg-dr-lab psql -U postgres -d shop -c "SELECT count(*) FROM orders;"
pg_restore --list ./backup-offsite/shop-*.dump | grep -c TABLE

Число строк orders на primary и наличие TABLE DATA в dump — быстрая sanity check перед offsite copy.


14. Wal-G и облако (обзор)

Wal-G пушит physical backup и WAL в S3/GCS — следующий уровень после practicum. Для pet-проекта с RPO в минутах изучите 8.11/10. Пока RPO в часах, pg_dump + offsite проще и дешевле в поддержке.

ИнструментТипOffsiteТипичный pet
pg_dumplogicalmanual/S3 cpда
Wal-Gphysical+WALS3 nativeпри росте
pg_probackupphysicalcatalogenterprise РФ

15. Сеть и firewall для restore

При restore на новом VPS откройте порт 5433 только для app и admin IP. Dump скачивают по HTTPS из bucket; Postgres наружу без TLS в lab допустим локально, в production — через VPN или SSL.


11. Чек перед переходом к катастрофе

Убедитесь, что в backup-offsite лежит свежий .dump, pg_restore --list на файле не падает с ошибкой (опционально), runbook сохранён. Тогда переходите к учебной катастрофе и restore.


16. Документирование в git

Коммитьте RUNBOOK-dr-shop.md, backup-shop.sh и docker-compose.dr-lab.yml в репозиторий infra. История git показывает, когда менялись команды restore — полезно при post-incident. Секреты production в git не кладут; используйте .env.example с placeholder.


17. RTO budget по шагам runbook

Шаг runbookBudget (lab)Комментарий
T0 fix0 mindate
Destroy volume1 mindocker rm
Pull image0–5 mincache
pg_restore1–10 minsize dump
App smoke2 mincurl health
Total5–20 minцель 1 h

Если сумма шагов > целевого RTO — автоматизируйте script из шага 3.


Полный walkthrough шага 2

# 1. Каталоги
mkdir -p ./backup-local ./backup-offsite
cd ~/dr-lab

# 2. Postgres
docker rm -f pg-dr-lab 2>/dev/null || true
docker volume rm pgdata 2>/dev/null || true
docker run -d --name pg-dr-lab -e POSTGRES_PASSWORD=lab -e POSTGRES_DB=shop \
-v pgdata:/var/lib/postgresql/data -p 5433:5432 postgres:16-alpine
sleep 5
docker exec pg-dr-lab pg_isready -U postgres

# 3. Данные
docker exec pg-dr-lab psql -U postgres -d shop -c \
"CREATE TABLE IF NOT EXISTS orders(id serial PRIMARY KEY, item text, created_at timestamptz default now());
INSERT INTO orders(item) VALUES ('widget'), ('gadget') ON CONFLICT DO NOTHING;"
docker exec pg-dr-lab psql -U postgres -d shop -c "SELECT * FROM orders;"

# 4. Dump
STAMP=$(date +%Y%m%d-%H%M)
docker exec pg-dr-lab pg_dump -U postgres -Fc shop > "./backup-local/shop-${STAMP}.dump"
cp "./backup-local/shop-${STAMP}.dump" "./backup-offsite/"
sha256sum "./backup-offsite/shop-${STAMP}.dump" > "./backup-offsite/shop-${STAMP}.dump.sha256"

# 5. Verify
ls -lh ./backup-offsite/
pg_restore --list "./backup-offsite/shop-${STAMP}.dump" | grep -c "TABLE DATA"
echo "OK ${STAMP}" >> ./backup-offsite/backup.log

Ожидаемый SELECT — 2 строки widget, gadget. pg_restore --list — count > 0.


Скрипт backup-shop.sh — полная версия

#!/bin/bash
set -euo pipefail
STAMP=$(date +%Y%m%d-%H%M)
LOCAL="./backup-local/shop-${STAMP}.dump"
OFFSITE="./backup-offsite/shop-${STAMP}.dump"
CONTAINER="${PG_CONTAINER:-pg-dr-lab}"

docker exec "$CONTAINER" pg_isready -U postgres
docker exec "$CONTAINER" pg_dump -U postgres -Fc shop > "$LOCAL"
test -s "$LOCAL" || { echo "FAIL: empty dump"; exit 1; }
sha256sum "$LOCAL" > "${LOCAL}.sha256"
cp "$LOCAL" "${LOCAL}.sha256" ./backup-offsite/
# aws s3 cp "$LOCAL" "s3://my-dr-bucket/shop/${STAMP}.dump" --storage-class GLACIER_IR
echo "OK ${STAMP}" >> ./backup-offsite/backup.log
pg_restore --list "$OFFSITE" >/dev/null
echo "Backup OK: $OFFSITE ($(du -h "$OFFSITE" | cut -f1))"

Запуск:

chmod +x backup-shop.sh
./backup-shop.sh

Ожидаемый вывод — Backup OK: ./backup-offsite/shop-....dump (NNK).


Cron setup

# crontab -e
# 0 */6 * * * cd /home/user/dr-lab && ./backup-shop.sh >> ./backup-offsite/cron.log 2>&1

Проверка после 6 часов:

tail -3 ./backup-offsite/backup.log
find ./backup-offsite -name '*.dump' -mtime -1

S3 offsite walkthrough

aws s3 cp ./backup-offsite/shop-latest.dump s3://my-dr-bucket/shop/ --storage-class GLACIER_IR
aws s3 ls s3://my-dr-bucket/shop/
# restore drill:
# aws s3 cp s3://my-dr-bucket/shop/shop-20250615-0300.dump ./backup-offsite/

Расширенное устранение неполадок

СимптомОжидаемоеРешение
connection refusedpg_isready acceptingdocker start pg-dr-lab
dump 0 bytesfile size > 0pg_isready перед dump
disk fulldf -h OKretention + delete old
corrupt archive--list OKновый dump
permission denied offsitecp OKchmod папки backup-offsite

Чек-лист завершения шага 2

#КритерийКомандаОжидание
1Container Updocker pspg-dr-lab
2Orders existSELECT2+ rows
3Dump in offsitels backup-offsite.dump файл
4Checksumsha256sum -cOK
5--list OKpg_restore --listбез corrupted
6Runbook v0.1файл существуетпуть offsite

Дальше — катастрофа и restore.


Предупреждения безопасности

  1. Пароль lab не в public git — .env.example с placeholder.
  2. S3 bucket — block public access, encryption SSE.
  3. Dump содержит данные — IAM least privilege.
  4. backup.log не логируйте connection strings.
  5. docker volume rm только в lab.