مدتهاست که مشخص شده است که تهیه نسخه پشتیبان به SQL dump (با استفاده از pg_dump یا pg_dumpall) ایده خوبی نیست. برای پشتیبان گیری از PostgreSQL DBMS بهتر است از دستور استفاده کنید pg_basebackup، که یک کپی باینری از گزارش های WAL ایجاد می کند. اما وقتی شروع به مطالعه کل فرآیند ایجاد یک کپی و بازیابی کنید، متوجه خواهید شد که برای این کار باید حداقل چند چرخه سه چرخه بنویسید و باعث ایجاد درد در بالا و پایین نشود. برای کاهش رنج، WAL-G ساخته شد.
WAL-G ابزاری است که در Go برای پشتیبان گیری و بازیابی پایگاه داده PostgreSQL نوشته شده است (و اخیراً MySQL/MariaDB، MongoDB و FoundationDB). از کار با ذخیره سازی Amazon S3 (و آنالوگ ها، به عنوان مثال، Yandex Object Storage)، و همچنین Google Cloud Storage، Azure Storage، Swift Object Storage و به سادگی با سیستم فایل پشتیبانی می کند. کل راهاندازی به مراحل ساده ختم میشود، اما با توجه به اینکه مقالاتی درباره آن در اینترنت پراکنده شدهاند، هیچ دستورالعمل کاملی وجود ندارد که شامل تمام مراحل از ابتدا تا انتها باشد (چندین پست در Habré وجود دارد، اما بسیاری از نکات در آنجا مغفول مانده است).
این مقاله در وهله اول برای نظام مند کردن دانش من نوشته شده است. من DBA نیستم و می توانم در جایی به زبان عامیانه صحبت کنم، بنابراین هرگونه اصلاحی پذیرفته می شود!
به طور جداگانه، توجه می کنم که همه چیز زیر مربوط است و برای PostgreSQL 12.3 در اوبونتو 18.04 آزمایش شده است، همه دستورات باید به عنوان یک کاربر ممتاز اجرا شوند.
نصب
در زمان نگارش این مقاله، نسخه پایدار WAL-G است نسخه 0.2.15 (مارس 2020). این چیزی است که ما استفاده خواهیم کرد (اما اگر میخواهید خودتان آن را از شاخه اصلی بسازید، مخزن github تمام دستورالعملهای این کار را دارد.). برای دانلود و نصب باید انجام دهید:
پس از این، ابتدا باید WAL-G و سپس خود PostgreSQL را پیکربندی کنید.
راه اندازی WAL-G
برای نمونه ای از ذخیره سازی پشتیبان، آمازون S3 استفاده خواهد شد (زیرا به سرورهای من نزدیکتر است و استفاده از آن بسیار ارزان است). برای کار با آن، به یک "سطل s3" و کلیدهای دسترسی نیاز دارید.
همه مقالات قبلی در مورد WAL-G از پیکربندی با استفاده از متغیرهای محیطی استفاده می کردند، اما با این نسخه تنظیمات را می توان در فایل walg.json در فهرست اصلی کاربر postgres. برای ایجاد آن، اسکریپت bash زیر را اجرا کنید:
WALG_S3_PREFIX – مسیر سطل S3 شما که در آن نسخههای پشتیبان آپلود میشوند (شما میتوانید به روت یا یک پوشه)
AWS_ACCESS_KEY_ID – کلید دسترسی در S3 (در صورت بازیابی در سرور آزمایشی، این کلیدها باید دارای خط مشی فقط خواندنی باشند! این با جزئیات بیشتر در بخش بازیابی توضیح داده شده است.);
AWS_SECRET_ACCESS_KEY – کلید مخفی در حافظه S3؛
WALG_COMPRESSION_METHOD - روش فشرده سازی، بهتر است از Brotli استفاده شود (زیرا این میانگین طلایی بین اندازه نهایی و سرعت فشرده سازی/فشرده سازی است).
WALG_DELTA_MAX_STEPS - تعداد "دلتاها" قبل از ایجاد یک نسخه پشتیبان کامل (آنها در زمان و اندازه داده های بارگیری شده صرفه جویی می کنند، اما می توانند روند بازیابی را کمی کند کنند، بنابراین استفاده از مقادیر زیاد توصیه نمی شود).
PGDATA – مسیر دایرکتوری با داده های پایگاه داده شما (با اجرای دستور می توانید متوجه شوید pg_lsclusters);
PGHOST – اتصال به پایگاه داده، با یک نسخه پشتیبان محلی بهتر است مانند این مثال از طریق سوکت یونیکس این کار را انجام دهید.
برای اینکه بایگانی داخل پایگاه داده لاگ های WAL را در فضای ابری آپلود کند و آنها را از آنها بازیابی کند (در صورت لزوم)، باید چندین پارامتر را در فایل پیکربندی تنظیم کنید. /etc/postgresql/12/main/postgresql.conf. فقط برای شروع باید مطمئن شویدکه هیچ یک از تنظیمات زیر روی هیچ مقدار دیگری تنظیم نشده است، به طوری که وقتی پیکربندی دوباره بارگیری می شود، DBMS خراب نمی شود. شما می توانید این پارامترها را با استفاده از:
wal_level - چه مقدار اطلاعات برای نوشتن در سیاهههای مربوط به WAL، "Replica" - نوشتن همه چیز.
archive_mode - دانلود سیاهههای WAL را با استفاده از دستور از پارامتر فعال کنید archive_command;
archive_command - دستور برای بایگانی یک گزارش کامل WAL؛
archive_timeout – بایگانی گزارشها فقط زمانی انجام میشود که تکمیل شود، اما اگر سرور شما دادههای کمی را به پایگاه داده تغییر دهد/افزودن کند، منطقی است که در اینجا یک محدودیت در چند ثانیه تعیین کنید، پس از آن دستور بایگانی به اجبار فراخوانی میشود (من هر ثانیه به شدت در پایگاه داده می نویسم، بنابراین تصمیم گرفتم این پارامتر را در تولید تنظیم نکنم);
restore_command - اگر "پشتیبان گیری کامل" (پشتیبان گیری پایه) فاقد آخرین تغییرات در پایگاه داده باشد، از دستور بازگرداندن گزارش WAL از یک نسخه پشتیبان استفاده می شود.
هر چه که می توان گفت، راحت ترین راه برای اجرای آن cron است. این همان چیزی است که ما برای ایجاد پشتیبان پیکربندی خواهیم کرد. بیایید با دستور ایجاد یک نسخه پشتیبان کامل شروع کنیم: در wal-g این آرگومان راه اندازی است پشتیبان - فشار. اما ابتدا، بهتر است این دستور را به صورت دستی از کاربر postgres اجرا کنید تا مطمئن شوید که همه چیز خوب است (و هیچ خطای دسترسی وجود ندارد):
#!/bin/bash
su - postgres -c '/usr/local/bin/wal-g backup-push /var/lib/postgresql/12/main'
آرگومان های راه اندازی مسیر دایرکتوری داده را نشان می دهد - یادآوری می کنم که می توانید با اجرا کردن آن را پیدا کنید pg_lsclusters.
اگر همه چیز بدون خطا پیش رفت و داده ها در حافظه S3 بارگیری شدند، سپس می توانید راه اندازی دوره ای را در crontab پیکربندی کنید:
در این مثال، فرآیند پشتیبان گیری هر روز از ساعت 4:15 صبح شروع می شود.
حذف بک آپ های قدیمی
به احتمال زیاد، شما نیازی به نگه داشتن مطلقاً تمام نسخه های پشتیبان از دوران مزوزوئیک ندارید، بنابراین مفید خواهد بود که به طور دوره ای فضای ذخیره سازی خود را "پاکسازی" کنید (هم "پشتیبان گیری کامل" و هم گزارش های WAL). ما همه این کارها را از طریق یک کار cron انجام خواهیم داد:
#!/bin/bash
echo "30 6 * * * /usr/local/bin/wal-g delete before FIND_FULL $(date -d '-10 days' '+%FT%TZ') --confirm >> /var/log/postgresql/walg_delete.log 2>&1" >> /var/spool/cron/crontabs/postgres
# ещё раз задаем владельца и выставляем правильные права файлу (хоть это обычно это и не нужно повторно делать)
chown postgres: /var/spool/cron/crontabs/postgres
chmod 600 /var/spool/cron/crontabs/postgres
Cron این کار را هر روز در ساعت 6:30 صبح اجرا میکند و همه چیز (پشتیبانگیری کامل، دلتاها و WALها) را به جز کپیهای 10 روز گذشته حذف میکند، اما حداقل یک نسخه پشتیبان باقی میگذارد. به تاریخ مشخص شده به طوری که هر نقطه پس از تاریخ ها در PITR گنجانده شدند.
بازیابی از پشتیبان
بر کسی پوشیده نیست که کلید یک پایگاه داده سالم، بازیابی دوره ای و تأیید صحت داده های داخل آن است. نحوه بازیابی با استفاده از WAL-G را در این بخش به شما خواهم گفت و بعداً در مورد چک ها صحبت خواهیم کرد.
باید جداگانه ذکر شود که برای بازیابی در یک محیط آزمایشی (هر چیزی که تولید نمی شود) باید از یک حساب Read Only در S3 استفاده کنید تا به طور تصادفی نسخه های پشتیبان را بازنویسی نکنید. در مورد WAL-G، باید حقوق زیر را برای کاربر S3 در Group Policy تنظیم کنید (اثر: اجازه می دهد): s3: GetObject, s3: ListBucket, s3: GetBucketLocation. و البته تنظیم را فراموش نکنید archive_mode=خاموش در فایل تنظیمات postgresql.conf، به طوری که پایگاه داده آزمایشی شما نمی خواهد بی سر و صدا پشتیبان گیری شود.
ترمیم با یک حرکت جزئی دست انجام می شود حذف تمام داده های PostgreSQL (از جمله کاربران)، بنابراین لطفاً هنگام اجرای دستورات زیر بسیار مراقب باشید.
#!/bin/bash
# если есть балансировщик подключений (например, pgbouncer), то вначале отключаем его, чтобы он не нарыгал ошибок в лог
service pgbouncer stop
# если есть демон, который перезапускает упавшие процессы (например, monit), то останавливаем в нём процесс мониторинга базы (у меня это pgsql12)
monit stop pgsql12
# или останавливаем мониторинг полностью
service monit stop
# останавливаем саму базу данных
service postgresql stop
# удаляем все данные из текущей базы (!!!); лучше предварительно сделать их копию, если есть свободное место на диске
rm -rf /var/lib/postgresql/12/main
# скачиваем резервную копию и разархивируем её
su - postgres -c '/usr/local/bin/wal-g backup-fetch /var/lib/postgresql/12/main LATEST'
# помещаем рядом с базой специальный файл-сигнал для восстановления (см. https://postgrespro.ru/docs/postgresql/12/runtime-config-wal#RUNTIME-CONFIG-WAL-ARCHIVE-RECOVERY ), он обязательно должен быть создан от пользователя postgres
su - postgres -c 'touch /var/lib/postgresql/12/main/recovery.signal'
# запускаем базу данных, чтобы она инициировала процесс восстановления
service postgresql start
برای کسانی که می خواهند روند ریکاوری را بررسی کنند، یک قطعه کوچک ماژیک bash در زیر آماده شده است تا در صورت بروز مشکل در ریکاوری، اسکریپت با یک کد خروج غیر صفر از کار بیفتد. در این مثال، 120 بررسی با فاصله زمانی 5 ثانیه (در مجموع 10 دقیقه برای بازیابی) انجام می شود تا مشخص شود که آیا فایل سیگنال حذف شده است (این بدان معنی است که بازیابی موفقیت آمیز بوده است):
#!/bin/bash
CHECK_RECOVERY_SIGNAL_ITER=0
while [ ${CHECK_RECOVERY_SIGNAL_ITER} -le 120 ]
do
if [ ! -f "/var/lib/postgresql/12/main/recovery.signal" ]
then
echo "recovery.signal removed"
break
fi
sleep 5
((CHECK_RECOVERY_SIGNAL_ITER+1))
done
# если после всех проверок файл всё равно существует, то падаем с ошибкой
if [ -f "/var/lib/postgresql/12/main/recovery.signal" ]
then
echo "recovery.signal still exists!"
exit 17
fi
پس از بازیابی موفقیت آمیز، فراموش نکنید که همه فرآیندها را دوباره شروع کنید (pgbouncer/monit و غیره).
بررسی اطلاعات پس از بازیابی
بررسی یکپارچگی پایگاه داده پس از بازیابی ضروری است تا وضعیتی با پشتیبان گیری شکسته/مخلوط ایجاد نشود. و بهتر است این کار را با هر بایگانی ایجاد شده انجام دهید، اما کجا و چگونه فقط به تخیل شما بستگی دارد (شما می توانید سرورهای جداگانه را به صورت ساعتی افزایش دهید یا در CI بررسی کنید). اما حداقل باید داده ها و شاخص های موجود در پایگاه داده را بررسی کرد.
برای بررسی داده ها کافی است آن ها را از طریق Dump اجرا کنید، اما بهتر است هنگام ایجاد پایگاه داده چک جمع ها را فعال کنید (جمع های چک داده ها):
#!/bin/bash
if ! su - postgres -c 'pg_dumpall > /dev/null'
then
echo 'pg_dumpall failed'
exit 125
fi
برای بررسی نمایه ها - وجود دارد ماژول amcheck، بیایید پرس و جوی sql را از آن بگیریم تست های WAL-G و کمی منطق حول آن بسازید:
#!/bin/bash
# добавляем sql-запрос для проверки в файл во временной директории
cat > /tmp/amcheck.sql << EOF
CREATE EXTENSION IF NOT EXISTS amcheck;
SELECT bt_index_check(c.oid), c.relname, c.relpages
FROM pg_index i
JOIN pg_opclass op ON i.indclass[0] = op.oid
JOIN pg_am am ON op.opcmethod = am.oid
JOIN pg_class c ON i.indexrelid = c.oid
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE am.amname = 'btree'
AND c.relpersistence != 't'
AND i.indisready AND i.indisvalid;
EOF
chown postgres: /tmp/amcheck.sql
# добавляем скрипт для запуска проверок всех доступных баз в кластере
# (обратите внимание что переменные и запуск команд – экранированы)
cat > /tmp/run_amcheck.sh << EOF
for DBNAME in $(su - postgres -c 'psql -q -A -t -c "SELECT datname FROM pg_database WHERE datistemplate = false;" ')
do
echo "Database: ${DBNAME}"
su - postgres -c "psql -f /tmp/amcheck.sql -v 'ON_ERROR_STOP=1' ${DBNAME}" && EXIT_STATUS=$? || EXIT_STATUS=$?
if [ "${EXIT_STATUS}" -ne 0 ]
then
echo "amcheck failed on DB: ${DBNAME}"
exit 125
fi
done
EOF
chmod +x /tmp/run_amcheck.sh
# запускаем скрипт
/tmp/run_amcheck.sh > /tmp/amcheck.log
# для проверки что всё прошло успешно можно проверить exit code или grep’нуть ошибку
if grep 'amcheck failed' "/tmp/amcheck.log"
then
echo 'amcheck failed: '
cat /tmp/amcheck.log
exit 125
fi
به طور خلاصه
من می خواهم از آندری بورودین برای کمک او در تهیه نشریه و تشکر ویژه برای کمک او در توسعه WAL-G تشکر کنم!
این یادداشت را به پایان می رساند. امیدوارم که توانسته باشم سهولت راه اندازی و پتانسیل عظیم استفاده از این ابزار را در شرکت شما منتقل کنم. من چیزهای زیادی در مورد WAL-G شنیدم، اما هرگز وقت کافی برای نشستن و کشف آن نداشتم. و بعد از اینکه آن را در خانه اجرا کردم، این مقاله از من بیرون آمد.
به طور جداگانه، شایان ذکر است که WAL-G می تواند با DBMS زیر نیز کار کند: