WAL-G: αντίγραφα ασφαλείας και ανάκτηση του PostgreSQL DBMS

Είναι από καιρό γνωστό ότι η δημιουργία αντιγράφων ασφαλείας σε SQL dumps (χρησιμοποιώντας 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é, αλλά πολλά σημεία χάνονται εκεί).

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. Για να το δημιουργήσετε, εκτελέστε το ακόλουθο σενάριο bash:

#!/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 – τον ​​αριθμό των "δέλτα" πριν από τη δημιουργία πλήρους αντιγράφου ασφαλείας (εξοικονομούν χρόνο και το μέγεθος των δεδομένων που έχουν ληφθεί, αλλά μπορούν να επιβραδύνουν ελαφρώς τη διαδικασία ανάκτησης, επομένως δεν συνιστάται η χρήση μεγάλων τιμών).
  • PGDATA – διαδρομή προς τον κατάλογο με τα δεδομένα της βάσης δεδομένων σας (μπορείτε να το μάθετε εκτελώντας την εντολή pg_lsclusters);
  • PGHOST – σύνδεση στη βάση δεδομένων, με τοπικό αντίγραφο ασφαλείας είναι καλύτερα να το κάνετε μέσω μιας υποδοχής unix, όπως σε αυτό το παράδειγμα.

Άλλες παράμετροι μπορούν να βρεθούν στην τεκμηρίωση: https://github.com/wal-g/wal-g/blob/v0.2.15/PostgreSQL.md#configuration.

Διαμόρφωση PostgreSQL

Προκειμένου ο αρχειοθέτης μέσα στη βάση δεδομένων να ανεβάσει αρχεία καταγραφής WAL στο cloud και να τα επαναφέρει από αυτά (εάν είναι απαραίτητο), πρέπει να ορίσετε αρκετές παραμέτρους στο αρχείο διαμόρφωσης /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_level – πόσες πληροφορίες να γράψετε στα αρχεία καταγραφής του WAL, «αντίγραφο» – γράψτε τα πάντα.
  • archive_mode – ενεργοποιήστε τη λήψη των αρχείων καταγραφής WAL χρησιμοποιώντας την εντολή από την παράμετρο archive_command;
  • archive_command – εντολή για την αρχειοθέτηση ενός ολοκληρωμένου αρχείου καταγραφής WAL.
  • archive_timeout – η αρχειοθέτηση των αρχείων καταγραφής εκτελείται μόνο όταν ολοκληρωθεί, αλλά εάν ο διακομιστής σας αλλάξει/προσθέσει λίγα δεδομένα στη βάση δεδομένων, τότε είναι λογικό να ορίσετε ένα όριο εδώ σε δευτερόλεπτα, μετά το οποίο θα κληθεί αναγκαστικά η εντολή αρχειοθέτησης (Γράφω εντατικά στη βάση δεδομένων κάθε δευτερόλεπτο, οπότε αποφάσισα να μην βάλω αυτήν την παράμετρο στην παραγωγή);
  • restore_command – Η εντολή επαναφοράς του αρχείου καταγραφής WAL από ένα αντίγραφο ασφαλείας θα χρησιμοποιηθεί εάν το "πλήρες αντίγραφο ασφαλείας" (βασικό αντίγραφο ασφαλείας) δεν έχει τις τελευταίες αλλαγές στη βάση δεδομένων.

Μπορείτε να διαβάσετε περισσότερα για όλες αυτές τις παραμέτρους στη μετάφραση της επίσημης τεκμηρίωσης: https://postgrespro.ru/docs/postgresql/12/runtime-config-wal.

Ρύθμιση ενός προγράμματος δημιουργίας αντιγράφων ασφαλείας

Ό,τι και να πει κανείς, ο πιο βολικός τρόπος για να το τρέξετε είναι το cron. Αυτό θα ρυθμίσουμε για τη δημιουργία αντιγράφων ασφαλείας. Ας ξεκινήσουμε με την εντολή για τη δημιουργία πλήρους αντιγράφου ασφαλείας: στο wal-g αυτό είναι το όρισμα εκκίνησης backup-push. Αλλά πρώτα, είναι καλύτερο να εκτελέσετε αυτήν την εντολή χειροκίνητα από τον χρήστη του 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 task:

#!/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 σε αυτήν την ενότητα και θα μιλήσουμε για ελέγχους αργότερα.

Θα πρέπει να σημειωθεί ξεχωριστά ότι για επαναφορά σε δοκιμαστικό περιβάλλον (ό,τι δεν είναι παραγωγή) πρέπει να χρησιμοποιήσετε έναν λογαριασμό μόνο για ανάγνωση στο S3 για να μην αντικαταστήσετε κατά λάθος αντίγραφα ασφαλείας. Στην περίπτωση του WAL-G, πρέπει να ορίσετε τα ακόλουθα δικαιώματα για τον χρήστη S3 στην Πολιτική ομάδας (Επίδραση: Να επιτρέπεται): s3: GetObject, s3:ListBucket, s3: GetBucketLocation. Και, φυσικά, μην ξεχάσετε να ρυθμίσετε archive_mode=off στο αρχείο ρυθμίσεων 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). Αλλά τουλάχιστον, είναι απαραίτητο να ελέγξετε τα δεδομένα και τα ευρετήρια στη βάση δεδομένων.

Για να ελέγξετε τα δεδομένα, αρκεί να τα εκτελέσετε μέσα από ένα 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

Συνοψίζοντας

Θα ήθελα να εκφράσω την ευγνωμοσύνη μου στον Andrey Borodin για τη βοήθειά του στην προετοιμασία της έκδοσης και ιδιαίτερες ευχαριστίες για τη συμβολή του στην ανάπτυξη του WAL-G!

Αυτό ολοκληρώνει αυτό το σημείωμα. Ελπίζω ότι κατάφερα να μεταδώσω την ευκολία εγκατάστασης και τις τεράστιες δυνατότητες χρήσης αυτού του εργαλείου στην εταιρεία σας. Άκουσα πολλά για το WAL-G, αλλά δεν είχα ποτέ αρκετό χρόνο να καθίσω και να το καταλάβω. Και αφού το εφάρμοσα στο σπίτι μου βγήκε αυτό το άρθρο.

Ξεχωριστά, αξίζει να σημειωθεί ότι το WAL-G μπορεί επίσης να λειτουργήσει με τα ακόλουθα DBMS:

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο