WAL-G: การสำรองข้อมูลและการกู้คืนของ PostgreSQL DBMS

เป็นที่ทราบกันมานานแล้วว่าการสำรองข้อมูลลงใน SQL dumps (โดยใช้ pg_dump หรือ pg_dumpall) ไม่ใช่ความคิดที่ดี หากต้องการสำรองข้อมูล PostgreSQL DBMS ควรใช้คำสั่งจะดีกว่า pg_basebackupซึ่งสร้างสำเนาไบนารีของบันทึก WAL แต่เมื่อคุณเริ่มศึกษากระบวนการทั้งหมดในการสร้างสำเนาและการกู้คืนคุณจะเข้าใจว่าคุณต้องเขียนรถสามล้ออย่างน้อยสองสามคันเพื่อให้สิ่งนี้ใช้งานได้และไม่ทำให้คุณเจ็บปวดทั้งด้านบนและด้านล่าง เพื่อบรรเทาความทุกข์ WAL-G จึงได้รับการพัฒนา

วอล-จี เป็นเครื่องมือที่เขียนด้วยภาษา Go สำหรับการสำรองและกู้คืนฐานข้อมูล PostgreSQL (และล่าสุดคือ MySQL/MariaDB, MongoDB และ FoundationDB). รองรับการทำงานกับพื้นที่เก็บข้อมูล Amazon S3 (และแอนะล็อก เช่น Yandex Object Storage) รวมถึง Google Cloud Storage, Azure Storage, Swift Object Storage และเพียงแค่กับระบบไฟล์ การตั้งค่าทั้งหมดมีขั้นตอนง่ายๆ แต่เนื่องจากบทความเกี่ยวกับเรื่องนี้กระจัดกระจายไปทั่วอินเทอร์เน็ต จึงไม่มีคู่มือวิธีใช้ฉบับสมบูรณ์ที่จะรวมขั้นตอนทั้งหมดตั้งแต่ต้นจนจบ (มีโพสต์หลายบทความในHabré แต่ยังพลาดไปหลายจุด)

WAL-G: การสำรองข้อมูลและการกู้คืนของ PostgreSQL DBMS

บทความนี้เขียนขึ้นเพื่อจัดระบบความรู้ของฉันเป็นหลัก ฉันไม่ใช่ DBA และฉันสามารถแสดงออกเป็นภาษาของคนธรรมดาที่ไหนสักแห่งได้ ดังนั้นจึงยินดีรับการแก้ไข!

ฉันทราบว่าทุกสิ่งด้านล่างนี้เกี่ยวข้องและทดสอบกับ PostgreSQL 12.3 บน Ubuntu 18.04 คำสั่งทั้งหมดจะต้องดำเนินการในฐานะผู้ใช้ที่มีสิทธิ์

การติดตั้ง

ในขณะที่เขียนบทความนี้ WAL-G เวอร์ชันเสถียรคือ v0.2.15 (มีนาคม 2020). นี่คือสิ่งที่เราจะใช้ (แต่ถ้าคุณต้องการสร้างมันขึ้นมาเองจากสาขาหลัก ที่เก็บ github ก็มีคำแนะนำทั้งหมดสำหรับสิ่งนี้). ในการดาวน์โหลดและติดตั้งคุณต้องทำ:

#!/bin/bash

curl -L "https://github.com/wal-g/wal-g/releases/download/v0.2.15/wal-g.linux-amd64.tar.gz" -o "wal-g.linux-amd64.tar.gz"
tar -xzf wal-g.linux-amd64.tar.gz
mv wal-g /usr/local/bin/

หลังจากนี้ คุณต้องกำหนดค่า WAL-G ก่อน จากนั้นจึงกำหนดค่า PostgreSQL เอง

การตั้งค่า WAL-G

สำหรับตัวอย่างการจัดเก็บข้อมูลสำรอง จะใช้ Amazon S3 (เพราะมันใกล้กับเซิร์ฟเวอร์ของฉันมากกว่าและการใช้งานก็ถูกมาก). หากต้องการใช้งาน คุณต้องมี "บัคเก็ต s3" และคีย์การเข้าถึง

บทความก่อนหน้านี้ทั้งหมดเกี่ยวกับ WAL-G ใช้การกำหนดค่าโดยใช้ตัวแปรสภาพแวดล้อม แต่ในรุ่นนี้ การตั้งค่าต่างๆ สามารถพบได้ ไฟล์ .walg.json ในโฮมไดเร็กตอรี่ของผู้ใช้ postgres หากต้องการสร้างให้รันสคริปต์ทุบตีต่อไปนี้:

#!/bin/bash

cat > /var/lib/postgresql/.walg.json << EOF
{
    "WALG_S3_PREFIX": "s3://your_bucket/path",
    "AWS_ACCESS_KEY_ID": "key_id",
    "AWS_SECRET_ACCESS_KEY": "secret_key",
    "WALG_COMPRESSION_METHOD": "brotli",
    "WALG_DELTA_MAX_STEPS": "5",
    "PGDATA": "/var/lib/postgresql/12/main",
    "PGHOST": "/var/run/postgresql/.s.PGSQL.5432"
}
EOF
# обязательно меняем владельца файла:
chown postgres: /var/lib/postgresql/.walg.json

ให้ฉันอธิบายเล็กน้อยเกี่ยวกับพารามิเตอร์ทั้งหมด:

  • WALG_S3_PREFIX – เส้นทางไปยังบัคเก็ต S3 ของคุณที่จะอัปโหลดข้อมูลสำรอง (คุณสามารถไปที่รูทหรือไปที่โฟลเดอร์)
  • AWS_ACCESS_KEY_ID – รหัสการเข้าถึงใน S3 (ในกรณีของการกู้คืนบนเซิร์ฟเวอร์ทดสอบ คีย์เหล่านี้ต้องมีนโยบายแบบอ่านอย่างเดียว! ซึ่งมีการอธิบายโดยละเอียดเพิ่มเติมในส่วนการกู้คืน);
  • AWS_SECRET_ACCESS_KEY – รหัสลับในที่เก็บข้อมูล S3
  • WALG_COMPRESSION_METHOD – วิธีการบีบอัด ควรใช้ Brotli ดีกว่า (เนื่องจากนี่คือค่าเฉลี่ยสีทองระหว่างขนาดสุดท้ายกับความเร็วการบีบอัด/คลายการบีบอัด)
  • WALG_DELTA_MAX_STEPS – จำนวน “เดลต้า” ก่อนที่จะสร้างการสำรองข้อมูลทั้งหมด (ประหยัดเวลาและขนาดของข้อมูลที่ดาวน์โหลด แต่อาจทำให้กระบวนการกู้คืนช้าลงเล็กน้อยดังนั้นจึงไม่แนะนำให้ใช้ค่าขนาดใหญ่)
  • พีจีดาต้า – เส้นทางไปยังไดเร็กทอรีด้วยข้อมูลฐานข้อมูลของคุณ (คุณสามารถค้นหาได้ด้วยการรันคำสั่ง pg_lsclusters);
  • ผี – การเชื่อมต่อกับฐานข้อมูล ด้วยการสำรองข้อมูลในเครื่อง ควรทำผ่านซ็อกเก็ตยูนิกซ์ตามตัวอย่างนี้

พารามิเตอร์อื่น ๆ สามารถพบได้ในเอกสารประกอบ: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

การกำหนดค่า PostgreSQL

เพื่อให้ผู้จัดเก็บภายในฐานข้อมูลอัปโหลดบันทึก WAL ไปยังคลาวด์และกู้คืนจากบันทึกเหล่านั้น (หากจำเป็น) คุณต้องตั้งค่าพารามิเตอร์หลายตัวในไฟล์กำหนดค่า /etc/postgresql/12/main/postgresql.conf. สำหรับผู้เริ่มต้นเท่านั้น คุณต้องแน่ใจไม่มีการตั้งค่าใดด้านล่างเป็นค่าอื่นใด ดังนั้นเมื่อมีการโหลดการกำหนดค่าใหม่ DBMS จะไม่ขัดข้อง คุณสามารถเพิ่มพารามิเตอร์เหล่านี้ได้โดยใช้:

#!/bin/bash

echo "wal_level=replica" >> /etc/postgresql/12/main/postgresql.conf
echo "archive_mode=on" >> /etc/postgresql/12/main/postgresql.conf
echo "archive_command='/usr/local/bin/wal-g wal-push "%p" >> /var/log/postgresql/archive_command.log 2>&1' " >> /etc/postgresql/12/main/postgresql.conf
echo “archive_timeout=60” >> /etc/postgresql/12/main/postgresql.conf
echo "restore_command='/usr/local/bin/wal-g wal-fetch "%f" "%p" >> /var/log/postgresql/restore_command.log 2>&1' " >> /etc/postgresql/12/main/postgresql.conf

# перезагружаем конфиг через отправку SIGHUP сигнала всем процессам БД
killall -s HUP postgres

คำอธิบายของพารามิเตอร์ที่จะตั้งค่า:

  • วอล_เลเวล – จำนวนข้อมูลที่จะเขียนในบันทึก WAL, “แบบจำลอง” – เขียนทุกอย่าง;
  • archive_mode – เปิดใช้งานการดาวน์โหลดบันทึก WAL โดยใช้คำสั่งจากพารามิเตอร์ archive_command;
  • archive_command – คำสั่งสำหรับเก็บถาวรบันทึก WAL ที่เสร็จสมบูรณ์
  • archive_timeout – การเก็บถาวรบันทึกจะดำเนินการเฉพาะเมื่อเสร็จสิ้นเท่านั้น แต่หากเซิร์ฟเวอร์ของคุณเปลี่ยนแปลง/เพิ่มข้อมูลเพียงเล็กน้อยลงในฐานข้อมูล ก็สมเหตุสมผลแล้วที่จะตั้งค่าขีดจำกัดที่นี่ในไม่กี่วินาที หลังจากนั้นคำสั่งการเก็บถาวรจะถูกเรียกใช้โดยบังคับ (ฉันเขียนลงฐานข้อมูลอย่างเข้มข้นทุก ๆ วินาที ดังนั้นฉันจึงตัดสินใจว่าจะไม่ตั้งค่าพารามิเตอร์นี้ในการใช้งานจริง);
  • คำสั่ง restore_ – คำสั่งเพื่อกู้คืนบันทึก WAL จากการสำรองข้อมูลจะถูกใช้หาก “การสำรองข้อมูลแบบเต็ม” (การสำรองข้อมูลพื้นฐาน) ขาดการเปลี่ยนแปลงล่าสุดในฐานข้อมูล

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับพารามิเตอร์เหล่านี้ทั้งหมดในการแปลเอกสารอย่างเป็นทางการ: https://postgrespro.ru/docs/postgresql/12/runtime-config-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 ได้:

#!/bin/bash

echo "15 4 * * *    /usr/local/bin/wal-g backup-push /var/lib/postgresql/12/main >> /var/log/postgresql/walg_backup.log 2>&1" >> /var/spool/cron/crontabs/postgres
# задаем владельца и выставляем правильные права файлу
chown postgres: /var/spool/cron/crontabs/postgres
chmod 600 /var/spool/cron/crontabs/postgres

ในตัวอย่างนี้ กระบวนการสำรองข้อมูลจะเริ่มทุกวันเวลา 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 น. โดยจะลบทุกอย่าง (การสำรองข้อมูลทั้งหมด เดลต้า และ WAL) ยกเว้นสำเนาในช่วง 30 วันที่ผ่านมา แต่จะเหลือข้อมูลสำรองอย่างน้อยหนึ่งรายการ ไปยัง ระบุวันที่เพื่อให้จุดใดจุดหนึ่ง หลังจาก วันที่รวมอยู่ใน PITR

กำลังกู้คืนจากข้อมูลสำรอง

ไม่มีความลับที่กุญแจสำคัญในการสร้างฐานข้อมูลที่ดีคือการเรียกคืนและตรวจสอบความสมบูรณ์ของข้อมูลภายในเป็นระยะ ฉันจะบอกวิธีการกู้คืนโดยใช้ WAL-G ให้คุณทราบในส่วนนี้ และเราจะพูดถึงการตรวจสอบในภายหลัง

ควรสังเกตแยกต่างหาก ในการกู้คืนในสภาพแวดล้อมการทดสอบ (ทุกอย่างที่ไม่ใช่การใช้งานจริง) คุณต้องใช้บัญชีอ่านอย่างเดียวใน S3 เพื่อไม่ให้เขียนทับข้อมูลสำรองโดยไม่ตั้งใจ ในกรณีของ WAL-G คุณต้องตั้งค่าสิทธิ์ต่อไปนี้สำหรับผู้ใช้ S3 ในนโยบายกลุ่ม (ผลกระทบ: อนุญาต): 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 magic ชิ้นเล็ก ๆ ไว้ด้านล่าง ดังนั้นในกรณีที่เกิดปัญหาในการกู้คืนสคริปต์จะขัดข้องด้วยรหัสทางออกที่ไม่เป็นศูนย์ ในตัวอย่างนี้ มีการตรวจสอบ 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) แต่อย่างน้อยก็จำเป็นต้องตรวจสอบข้อมูลและดัชนีในฐานข้อมูล

หากต้องการตรวจสอบข้อมูล ก็เพียงพอที่จะเรียกใช้ผ่านดัมพ์ แต่จะดีกว่าเมื่อสร้างฐานข้อมูลที่คุณเปิดใช้งานเช็คซัม (การตรวจสอบข้อมูล):

#!/bin/bash

if ! su - postgres -c 'pg_dumpall > /dev/null'
then
    echo 'pg_dumpall failed'
    exit 125
fi

เพื่อตรวจสอบดัชนี - มีอยู่ โมดูลแอมเช็คเรามาลองสืบค้น 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

สรุป

ฉันอยากจะแสดงความขอบคุณต่อ Andrey Borodin สำหรับความช่วยเหลือในการเตรียมสิ่งพิมพ์และขอขอบคุณเป็นพิเศษสำหรับการมีส่วนร่วมในการพัฒนา WAL-G!

นี่เป็นการสรุปบันทึกนี้ ฉันหวังว่าฉันจะสามารถถ่ายทอดความง่ายในการตั้งค่าและศักยภาพมหาศาลในการใช้เครื่องมือนี้ในบริษัทของคุณได้ ฉันได้ยินมามากมายเกี่ยวกับ WAL-G แต่ก็ไม่เคยมีเวลามากพอที่จะนั่งลงและคิดออก และหลังจากที่ฉันนำไปใช้ที่บ้าน บทความนี้ก็ออกมาจากฉัน

แยกเป็นที่น่าสังเกตว่า WAL-G ยังสามารถทำงานร่วมกับ DBMS ต่อไปนี้:

  • MySQL / MariaDB;
  • MongoDB;
  • FoundationDB;
  • และเมื่อพิจารณาจากความมุ่งมั่นแล้ว คาดว่าจะมีอีกหลายคน!

ที่มา: will.com

เพิ่มความคิดเห็น