pgbackrest — утилита для бекапирования postgres

Thank you for reading this post, don't forget to subscribe!

рас­ши­ряю статью:

PgBackRest . мене­джер бэка­пов для postgres

Офи­ци­аль­ный user guide https://pgbackrest.org/user-guide.html

Пара­мет­ры кон­фи­гу­ра­ции https://pgbackrest.org/configuration.html (крайне реко­мен­ду­ет­ся к изучению).

На сер­ве­ре пред­ва­ри­тель­но уже дол­жен быть уста­нов­лен PostgreSQL.

 

Установка

Уста­нав­ли­ва­ем на сер­ве­ре PostgreSQL pgbackrest (в каче­стве при­ме­ра уста­нав­ли­ва­ем на сер­вер CentOS 7):

sudo yum install pgbackrest

pgbackrest есть в репо­зи­то­рии PostgreSQL, если репо­зи­то­рий не уста­нов­лен, то выполеняем:

sudo yum -y install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm

Если не уда­лось най­ти rpm пакет pgbackrest в репо­зи­то­ри­ях, то мож­но собрать бинар­ник само­сто­я­тель­но, вос­поль­зо­вав­шись инструкциями:

Ско­пи­ро­вать бинар­ник и задать ему нуж­ные пра­ва и создать необ­хо­ди­мые дирек­то­рии с выда­чей прав как напи­са­но здесь https://pgbackrest.org/user-guide-centos7.html#installation

Настройка

В зави­си­мо­сти от кон­фи­гу­ра­ции, либо созда­ем локаль­ную дирек­то­рию для бека­пов с вла­дель­цем postgres:postgres, либо мон­ти­ру­ем шару (напри­мер, /mnt/pgbak как будет опи­са­но далее в инструк­ции) с тем же вла­дель­цем postgres:postgres.

Созда­ем дирек­то­рию под бекапы:

mkdir /mnt/pgbak
chown postgres:postgres /mnt/pgbak

Созда­ем так же дирек­то­рию под логи бекапов:

mkdir /mnt/pgbak/log
chown postgres:postgres /mnt/pgbak/log

Про­ве­ря­ем что на файл кон­фи­га /etc/pgbackrest.conf есть пра­ва для чте­ния у всех поль­зо­ва­те­лей (так как вла­де­лец root), что­бы postgres мог им поль­зо­вать­ся, либо мож­но так же сде­лать вла­дель­цем это­го фай­ла postgres:postgres.

chown postgres:postgres /etc/pgbackrest.conf

Редак­ти­ру­ем файл кон­фи­га /etc/pgbackrest.conf ука­зы­вая сле­ду­ю­щие настройки:

Пара­метр process-max ука­зы­ва­ет сколь­ко ядер CPU исполь­зо­вать для бека­па (сжа­тие и копи­ро­ва­ние фай­лов), по дефол­ту он выстав­лен в зна­че­ние 1 (одно ядро). Для уве­ли­че­ния ско­ро­сти бека­па, реко­мен­ду­ет­ся выста­вить зна­че­ние пара­мет­ра в зави­си­мо­сти от име­ю­ще­го­ся на сер­ве­ре коли­че­ства ядер, но не ука­зы­вать доступ­ный мак­си­мум ядер, ина­че это может повли­ять на рабо­ту PostgreSQL. Зна­че­ние так же сто­ит выстав­лять исхо­дя из теку­щей ути­ли­за­ции CPU. Реко­мен­ду­ет­ся попро­бо­вать выста­вить process-max в треть от доступ­ных ядер и посте­пен­но уве­ли­чи­вать зна­че­ние наблю­дая общую ути­ли­за­цию CPU во вре­мя бека­пов. Кор­рект­но настро­ен­ный process-max поз­во­ля­ет зна­чи­тель­но уве­ли­чить ско­рость бека­пи­ро­ва­ния данных.

Где [global] настрой­ки все­го pgbackrest, [prodname] имя stanza и настрой­ки для бека­пов (имя при­ду­мы­ва­ем сами, напри­мер, testprod).

В repo1-path необ­хо­ди­мо ука­зать путь к дирек­то­рии бека­пов, в pg1-path дирек­то­рию PGDATA PostgreSQL.

log-path ука­зы­ва­ет в какой дирек­то­рии хра­нить лог рабо­ты pgbackrest.

start-fast=y ука­зы­ва­ет на то, что при запус­ке бека­па необ­хо­ди­мо сра­зу выпол­нить checkpoint и начать выпол­не­ние бекапа.

stop-auto=y озна­ча­ет что необ­хо­ди­мо оста­но­вить преды­ду­щий про­цесс бека­па, если тако­вой выпол­ня­ет­ся в дан­ный момент (хотя пред­по­ла­га­ет­ся что pgbackrest будет един­ствен­ным сред­ством кото­рое будет выпол­нять бека­пы на регу­ляр­ной основе).

Более подроб­но о настрой­ках и пара­мет­рах мож­но про­чи­тать в user guide и доке по пара­мет­рам конфигурации.

Теперь пра­вим кон­фиг postgresql.conf и пере­за­пус­ка­ем PostgreSQL (после это­го WAL будут архи­ви­ро­вать­ся pgbackrest в спе­ци­аль­ную дирек­то­рию archive):

После это­го созда­ем stanza:

И выпол­ня­ем ее check проверку:

В резуль­та­те в /mnt/pgbak будет созда­но дере­во под­ка­та­ло­гов с бека­па­ми инстан­са и wal логами:

Выполнение тестового бекапа

Если все ОК, выпол­ня­ем full backup (с выво­дом инфор­ма­ции о ходе бека­па в консоль):

pgBackRest не будет выпол­нять бекап, если у него не будет воз­мож­но­сти писать лог. По дефол­ту логи пишут­ся в дирек­то­рию /var/log/pgbackrest, поэто­му важ­но сле­дить что­бы в /var/log все­гда было доступ­ное место. Мож­но пере­на­зна­чить дирек­то­рию куда pgBackRest будет писать логи (как мы и сде­ла­ли в дан­ном слу­чае), для это­го необ­хо­ди­мо создать дирек­то­рию под логи, напри­мер, /mnt/pgbak/log и про­пи­сать это в кон­фи­ге /etc/pgbackrest.conf

В pgbackrest есть пара­метр checksum-page кото­рый поз­во­ля­ет включить/отключить про­вер­ку кон­троль­ных сумм стра­ниц при выпол­не­нии бекапа.

Направ­ля­ет pgBackRest на про­вер­ку кон­троль­ных сумм всех стра­ниц дан­ных при резерв­ном копи­ро­ва­нии кла­сте­ра. Этот пара­метр вклю­ча­ет­ся авто­ма­ти­че­ски, когда в кла­сте­ре вклю­че­ны кон­троль­ные сум­мы стра­ниц данных.

Сбои при про­вер­ке кон­троль­ной сум­мы не пре­ры­ва­ют резерв­ное копи­ро­ва­ние. Ско­рее, пре­ду­пре­жде­ния будут отправ­лять­ся в жур­нал (и на кон­соль с настрой­ка­ми по умол­ча­нию), а спи­сок недей­стви­тель­ных стра­ниц будет сохра­нен в мани­фе­сте резерв­ной копии.

Посмот­реть име­ю­щи­е­ся бека­пы мож­но выпол­нив команду:

Мож­но так же попро­бо­вать выпол­нить диф­фе­рен­ци­аль­ный или инкре­мен­таль­ный бека­пы, ука­зав в —type=diff или —type=incr.

Фай­лы во вре­мя бека­па по умол­ча­нию сжи­ма­ют­ся и хра­нят­ся в сжа­том виде, настрой­ка­ми сжа­тия мож­но управ­лять при помо­щи параметров:

Настройка расписания бекапов

Настра­и­ва­ем рас­пи­са­ние бека­пов в cron, напри­мер так:

[codelanguage=»bash»]vi /etc/crontab

 

#pgbackrest backups
00 01 * * 6 sudo -u postgres pgbackrest —stanza=testprod —type=full backup
00 01 * * 0-5 sudo -u postgres pgbackrest —stanza=testprod —type=incr backup[/code]

Реко­мен­ду­ет­ся изу­чить и про­те­сти­ро­вать вли­я­ние на вре­мя выполнения/нагрузку на cpu/коэффициент сжа­тия пара­мет­ра кон­фи­га compress-level https://pgbackrest.org/configuration.html#section-general/option-compress-level, его так же мож­но задать и для WAL https://pgbackrest.org/user-guide.html#quickstart/configure-archiving

Восстановление из бекапов (подготовка тестовых данных)

Для тести­ро­ва­ния вос­ста­нов­ле­ния из бека­пов созда­дим две базы test1 и test2.

Базы сей­час у нас пустые и оди­на­ко­во­го размера.

Выпол­ним full backup:

Созда­дим в каж­дой них оди­на­ко­вую таб­ли­цу но запол­ним раз­ным коли­че­ством дан­ных что­бы они отли­ча­лись по размеру:
БД test1:

БД test2:


Базы у нас теперь раз­но­го объема.
Про­те­сти­ру­ем воз­мож­но­сти восстановления.

Полное восстановление кластера

Смот­рим теку­щее время:

 

14:14:59. Пред­по­ло­жим что после это­го момен­та мы слу­чай­но уда­ли­ли базу test2:

drop database test2;

Нам надо вос­ста­но­вить­ся из бека­па что­бы вер­нуть нашу базу обрат­но. Самый про­сто спо­соб вос­ста­нов­ле­ния из бека­па это пол­ное восстановление.

Для это­го, сна­ча­ла оста­нав­ли­ва­ем PostgreSQL:

systemctl stop postgresql-13
Уда­ля­ем пол­но­стью содер­жи­мое нашей PGDATA /var/lib/pgsql/13/data, при этом сама дирек­то­рия data долж­на остать­ся, либо пере­со­зда­ем ее с теми же пра­ва­ми и владельцем:

rm -rf /var/lib/pgsql/13/data/*
Теперь в пустую PGDATA выпол­ня­ем вос­ста­нов­ле­ние из бека­па, в дан­ном слу­чае будем вос­ста­нав­ли­вать­ся на точ­ку во вре­ме­ни 14:14:59 и выво­дить про­цесс вос­ста­нов­ле­ния в консоль:

Кла­стер вос­ста­нов­лен, так же авто­ма­ти­че­ски был создан пустой файл recovery.signal и сге­не­ри­ро­ва­ны пара­мет­ры для вос­ста­нов­ле­ния в фай­ле postgresql.auto.conf:

Запус­ка­ем PostgreSQL:

systemctl start postgresql-13
PostgreSQL запу­стит­ся, выпол­нит recovery при помо­щи WAL до ука­зан­ной вре­мен­ной точ­ки и promote, после вос­ста­нов­ле­ния про­ве­ря­ем что у нас сер­вер закон­чил вос­ста­нов­ле­ние по логам:

И выпол­нив команду:

select pg_is_in_recovery();
Кото­рая долж­на вер­нуть false:

Файл recovery.signal при этом уда­лил­ся автоматически.

Под­клю­чив­шись к кла­сте­ру видим что наша бд test2 на месте в том же состо­я­нии как и была до удаления.

Вос­ста­нов­ле­ние дель­ты изме­не­ний (пере­за­пись толь­ко изме­нив­ших­ся файлов)
Если объ­ем кла­сте­ра боль­шой, то пол­ное вос­ста­нов­ле­ние может занять мно­го вре­ме­ни. Для того что­бы сэко­но­мить вре­мя и не вос­ста­нав­ли­вать все фай­лы (ведь мог­ла поме­нять­ся толь­ко из часть) для вос­ста­нов­ле­ния мож­но вос­поль­зо­вать­ся пара­мет­ром —delta, в таком слу­чае, будут вос­ста­нов­ле­ны и заме­не­ны толь­ко изме­нив­ши­е­ся файлы.

Итак, дроп­нем одну из наших баз, напри­мер, test1. Фик­си­ру­ем теку­щее вре­мя — 14:41:32. И после это­го выполняем:

drop database test1;
Что­бы ее вер­нуть, вос­ста­но­вим­ся из бека­па. Гасим postgresql:

systemctl stop postgresql-13
Но в этот раз уда­лять кла­стер не будем, а вос­поль­зу­ем­ся пара­мет­ром —delta. Выпол­ня­ем восстановление:

 

В этот раз была вос­ста­нов­ле­на толь­ко нуж­ная часть фай­лов и заня­ло это мень­ше вре­ме­ни чем пол­ное восстановление.

Запус­ка­ем PostgreSQL:

systemctl start postgresql-13
Весь про­цесс вос­ста­нов­ле­ния ана­ло­ги­чен преды­ду­ще­му, после его завер­ше­ния можем про­ве­рить что кла­стер у нас досту­пен на запись и база успеш­но восстановлена.

Вос­ста­нов­ле­ние толь­ко выбран­ных баз
pgBackRest поз­во­ля­ет при жела­нии вос­ста­но­вить толь­ко выбранную(ые) базу(ы) кла­сте­ра, либо исклю­чить базу(ы) из вос­ста­нов­ле­ния. Подроб­нее в доку­мен­та­ции, мы же, в каче­стве при­ме­ра, дроп­нем обе базы test1 и test2 и вос­ста­но­вим толь­ко базу test1.

Про­ве­ря­ем вре­мя — 14:56:54.

Выпол­ня­ем дроп обе­их баз данных:

drop database test1;
drop database test2;

Оста­нав­ли­ва­ем PostgreSQL:

systemctl stop postgresql-13

Выпол­ня­ем вос­ста­нов­ле­ние кла­сте­ра и одной базы test1 (исполь­зу­ем —delta для более быст­ро­го восстановления):

sudo -u postgres pgbackrest --stanza=testprod --delta --db-include=test1 --type=time "--target=2021-07-22 14:56:54" --target-action=promote --log-level-console=info restore

Запус­ка­ем PostgreSQL и ждем завер­ше­ния нака­та WAL логов:

systemctl start postgresql-13
Под­клю­ча­ем­ся к кла­сте­ру и про­ве­ря­ем что база test1 на месте и к ней мож­но под­клю­чить­ся и рабо­тать с ней.

При этом мы видим так же и базу test2, но при под­клю­че­нии к ней мы полу­чим ошиб­ку о том что объ­ект не суще­ству­ет. Дело в том что pgBackRest вос­ста­нав­ли­ва­ет все базы, но дан­ные зали­ва­ют­ся толь­ко в те что мы ука­за­ли в дан­ном вари­ан­те вос­ста­нов­ле­ния. Поэто­му теперь, послед­ним шагом, необ­хо­ди­мо выпол­нить дроп базы пустыш­ки test2:

drop database test2;