Настройка и восстановление PostgreSQL с WAL: подробное руководство

Настройка и восстановление PostgreSQL с WAL: подробное руководство

Картинка к публикации: Настройка и восстановление PostgreSQL с WAL: подробное руководство

Введение в WAL

В самом начале стоит понять, что такое Write-Ahead Logging (WAL), и почему он так важен для PostgreSQL. Когда мы говорим о целостности базы данных, мы подразумеваем, что при различных сбоях – будь то отключение электричества, сбой в работе диска или сбой в сети – данные остаются максимально доступными и непротиворечивыми. Для обеспечения такой надёжности PostgreSQL использует механизм WAL.

Что такое Write-Ahead Logging (WAL)

WAL (Write-Ahead Logging) — это журнал, в котором фиксируются все изменения данных перед тем, как они окончательно применяются к самим файлам базы. То есть, прежде чем обновление или вставка «попадут» на диск, PostgreSQL записывает «описание» этого изменения в лог-файл WAL. Это можно сравнить с черновиком или заметками, которые мы делаем перед тем, как внести исправления в финальный документ.

Зачем это нужно?

  • Целостность данных. При сбое система может «дочитать» лог, понять, какие данные были изменены, и восстановить их до корректного состояния.
  • Непрерывность работы. Благодаря тому, что сам WAL-файл достаточно быстр в записи (он по сути представляет собой последовательный журнал), основная часть нагрузки при записи не ложится на конечные файлы базы, и работа с данными продолжается эффективнее.

Основные виды бэкапов

При администрировании PostgreSQL обычно используют два основных способа резервного копирования (бэкапа):

  1. Логические дампы (Logical backups):
    • Выполняются с помощью утилиты pg_dump или pg_dumpall.
    • Сохраняют структуру БД (DDL) и данные (DML) в виде SQL-скриптов или пользовательских двоичных форматов.
    • Удобны тем, что позволяют переносить схему и данные на другие версии PostgreSQL, легко восстанавливать отдельные таблицы или схему.
    • Основной минус – при большом объёме данных создание дампа может занимать значительное время и потреблять много ресурсов.
  2. Физические копии (Physical backups):
    • Представляют собой копирование файловой структуры самой базы данных (каталог data, где хранятся все файлы PostgreSQL, включая WAL-логи).
    • Чаще всего выполняются с помощью утилит вроде pg_basebackup, а в случае резервного копирования через Docker – с помощью настроенных волумов и инструментов архивации.
    • Физический бэкап точнее передаёт состояние БД, чем логический дамп, но и объём данных может быть намного больше. Кроме того, физическую копию сложнее переносить на иную версию PostgreSQL (например, с 12 на 14), поэтому следует внимательно учитывать совместимость версий.

Оба способа имеют право на жизнь и обычно применяются в зависимости от ситуации. Однако в большинстве случаев рекомендуют сочетать дампы и физические бэкапы.

Зачем сочетать регулярные дампы с WAL

WAL – это «чёрный ящик» или «журнал изменений», который позволяет откатить или доиграть операции, совершённые между резервными копиями. Представим классическую ситуацию: мы делаем бэкап (дамп) раз в день. Но в промежутке между созданием дампа и моментом сбоя в базу вносились изменения (новые заказы, транзакции, записи и т.д.). В итоге, если восстанавливаться только из дампа, мы потеряем все изменения за сутки.

Как тут помогает WAL?

  • Пока база работает, PostgreSQL непрерывно записывает все изменения в WAL-файлы.
  • Если включена архивация WAL (о ней поговорим в следующих главах), эти файлы сохраняются в отдельном, безопасном месте (например, на удалённом сервере или в облаке).
  • После сбоя мы можем сначала развернуть базу из свежего дампа, а потом «доиграть» (replay) WAL-журналы за период после создания этого дампа. Это вернёт базу в состояние вплоть до момента сбоя.

Таким образом, комбинация регулярных дампов (логических или физических) и архивации WAL позволяет добиться:

  1. Быстрого восстановления «большинства» данных за счёт дампа.
  2. Восполнения пропущенных изменений за счёт «доигрывания» WAL.

На практике эта схема даёт оптимальный баланс между затратами на хранение бэкапов и гарантированным сохранением данных. Если вдруг ломается хранилище, где крутится PostgreSQL, мы восстанавливаемся из самой свежей копии (дамп или физический бэкап), а затем применяем WAL, которые накопились между моментом создания копии и сбоем.

Подготовка окружения

Во второй главе разберёмся, как настроить Docker Compose для работы с PostgreSQL и подготовить базовую конфигурацию, необходимую для дальнейшей настройки WAL. Особое внимание уделим правильной структуре каталогов (volumes), чтобы в дальнейшем без проблем сохранять файлы WAL и резервные копии.

Создание и настройка docker-compose.yml с PostgreSQL

Ниже приведён пример базового docker-compose.yml, который можно использовать в качестве отправной точки. Он поднимает единственный сервис с PostgreSQL и монтирует каталог для хранения данных и логов снаружи контейнера (через volume). Это не только облегчает резервное копирование, но и гарантирует, что данные сохранятся, даже если контейнер будет пересоздан.

services:
  postgres:
    image: postgres:14
    container_name: my_postgres
    restart: always
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: mydatabase
    volumes:
      - postgres_data:/var/lib/postgresql/data
      # При необходимости можно добавить другой volume для логов,
      # но по умолчанию PostgreSQL пишет логи в stdout
      # - postgres_logs:/var/log/postgresql
    ports:
      - "5432:5432"

volumes:
  postgres_data:
    # Локальный volume, управляется Docker’ом. Можно настроить
    # type, name и другие параметры, если нужно.

Ключевые моменты в примере

  1. Image: используем официальный образ postgres:14. Можно выбрать другую версию, но стоит убедиться, что все параметры, о которых мы будем говорить далее, присутствуют именно в этой версии.
  2. Environment: задаём пользователя, пароль и имя базы по умолчанию. Это удобный минимальный набор параметров.
  3. Volumes:
    • postgres_data указывает, что все файлы БД (включая WAL-файлы) сохраняются не внутри контейнера, а на хостовой машине (в Docker-managed volume).
    • При пересоздании контейнера данные из postgres_data не удалятся, что крайне важно для целостности и постоянного хранения WAL.
  4. Ports: прокидываем порт 5432 для доступа к СУБД извне.

Такой файл docker-compose.yml можно поместить в любой каталог на хосте. После этого достаточно запустить:

docker-compose up -d

и контейнер с PostgreSQL будет готов к работе.

Краткий обзор основных параметров PostgreSQL, важных для WAL

Хотя многие параметры PostgreSQL можно настроить через переменные окружения или с помощью дополнительного конфига, есть несколько ключевых опций, связанных с WAL, о которых важно знать заранее:

  1. wal_level
    • Определяет уровень детализации записей в WAL.
    • По умолчанию для современных версий PostgreSQL (начиная с 10) значение wal_level уже установлено в replica, что достаточно для большинства кейсов, включая репликацию и архивирование.
    • Если нужно будет более тонко управлять архивацией или репликацией, возможно понадобится изменить на logical или оставить как есть.
  2. archive_mode
    • Включает (on) или выключает (off) режим архивации WAL.
    • По умолчанию off, так что для полноценной системы бэкапов и восстановления нужно будет установить archive_mode = on.
  3. archive_command
    • Команда, которая вызывается PostgreSQL для копирования каждого завершённого WAL-файла в архив.
    • Например: archive_command = 'cp %p /path/to/archive/%f'.
    • Про неё поговорим подробнее в следующей главе, когда будем настраивать архивацию WAL.
  4. max_wal_size, min_wal_size
    • Параметры, задающие минимально и максимально допустимые размеры WAL в структуре данных PostgreSQL.
    • В большинстве случаев можно оставлять значения по умолчанию, но при очень высоконагруженной системе можно увеличить max_wal_size для избежания частых чекпоинтов.
  5. wal_compression
    • Указывает, сжимать ли WAL-файлы «на лету».
    • Это может быть полезно, если размер WAL велик и есть желание экономить место.

Важно понимать, что не все параметры удобно задавать через переменные окружения (например, POSTGRES_INITDB_ARGS или через дополнительный файл конфигурации). Иногда проще подготовить свой postgresql.conf, а затем смонтировать его через volume в /var/lib/postgresql/data или /etc/postgresql/postgresql.conf внутри контейнера. Но чаще всего базовых параметров, переданных через переменные окружения (или дополнительных SQL-скриптов при инициализации), бывает достаточно.

Структура каталогов в контейнере и снаружи (volumes)

При работе с PostgreSQL через Docker важно понимать, где физически хранятся данные и, в частности, WAL-файлы. По умолчанию в контейнере PostgreSQL использует каталог:

/var/lib/postgresql/data

В нём находится как основная директория базы, так и:

  • Папка pg_wal (ранее pg_xlog), где живут файлы WAL.
  • Конфигурационные файлы (при инициализации).
  • Вся структура таблиц и служебные файлы.

Когда мы монтируем postgres_data:/var/lib/postgresql/data, Docker сохраняет эти данные в специальном месте на хосте (или внешнем драйвере volume). Таким образом, при остановке или удалении контейнера:

  • Данные (включая WAL) не удаляются автоматически.
  • Вы можете бэкапить их стандартными средствами или копировать целиком папку, если нужно сделать «физическую копию» базы.

Если требуется отдельное место для логов или архива, можно добавить ещё один volume, смонтировав его на хостовом каталоге:

volumes:
  postgres_logs:
    # ...

services:
  postgres:
    # ...
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./logs:/var/log/postgresql

Тогда логи из PostgreSQL можно будет изучать напрямую на хосте в папке ./logs. Для WAL-архивов в конфигурации PostgreSQL можно указать каталог (например, /var/lib/postgresql/data/archives), а затем смонтировать туда отдельный volume. В следующих главах мы разберём детальнее, как с этим работать.

Основные нюансы при подготовке окружения

  1. Сохраняйте все, что нужно бэкапить, за пределами контейнера.
    Использование volume – наиболее правильный способ. Если контейнер удалится, данные останутся на месте, а вы сможете в любой момент пересоздать контейнер, не потеряв при этом базу.
  2. Выбирайте стабильный образ PostgreSQL.
    Как правило, лучше использовать обновлённый мажорный релиз (например, 14 или 15), чтобы получить все новые функции и патчи. Но если у вас Production-система, убедитесь, что нужная версия поддерживается, а её поведение вам известно.
  3. Храните конфигурационные файлы вместе с данными или подключайте отдельным volume.
    Часто удобнее иметь свой postgresql.conf, pg_hba.conf или папку с SQL-скриптами начальной инициализации. Это позволит быстрее вносить правки в конфигурацию, особенно касающиеся WAL, без необходимости пересобирать образ.
  4. Учитывайте размер диска.
    Убедитесь, что на хосте достаточно места для хранения как основных данных, так и потенциала для архивации WAL, особенно если база активно записывает много данных. Слишком маленький диск может привести к переполнению volume и остановке работы контейнера.

На этом этапе мы имеем базовый docker-compose.yml и общее понимание того, как PostgreSQL хранит данные и WAL. В дальнейших главах настроим параметры для архивации WAL, чтобы иметь полноценную систему восстановления данных, комбинируя бэкапы и «доигрывание» логов.

Настройка PostgreSQL

Итак, мы создали базовое окружение Docker и кратко рассмотрели, какие параметры PostgreSQL влияют на WAL. Теперь настал момент настроить систему так, чтобы она могла архивировать WAL-файлы и использовать их для последующего восстановления.

Основные конфигурационные параметры

Для полноценной работы с WAL нужно включить и правильно настроить несколько ключевых параметров в postgresql.conf (либо передать их через переменные окружения при запуске контейнера):

  1. wal_level
    • Отвечает за уровень детализации записей в WAL.
    • Распространённые значения:
      • replica (стоит по умолчанию у современных версий PostgreSQL)
      • logical (если планируете логическую репликацию)
    • Для большинства случаев архивации и обычной репликации достаточно replica.
  2. archive_mode
    • Включает или выключает архивацию WAL-файлов.
    • При работе с бэкапами и восстановлением из WAL нужно ставить on.
  3. archive_command
    • Задаёт команду, с помощью которой PostgreSQL копирует каждый завершённый WAL-файл в место постоянного хранения.
    • Например:

      archive_command = 'cp %p /var/lib/postgresql/data/archives/%f'

      где %p – путь к WAL-файлу в рабочем каталоге, а %f – его имя.

    • Подразумевается, что в директории /var/lib/postgresql/data/archives будет достаточно места.
    • Для удалённой архивации можно использовать scp, rsync и другие инструменты, при условии корректной настройки доступа.
  4. archive_timeout (необязательно)
    • Заставляет PostgreSQL «принудительно» завершать WAL-файл через указанный интервал времени (даже если он ещё не достигнут конца).
    • Удобно, если нужно более предсказуемое расписание архивации, особенно при низкой нагрузке на базу.
  5. wal_log_hints (для некоторых вариантов репликации и standby)
    • Обычно не требуется, но может понадобиться, если планируете использовать реплику в качестве "promote" без полного перебора pages.
  6. max_wal_size, min_wal_size
    • Настройки, регулирующие, сколько WAL-файлов PostgreSQL может хранить до следующего чекпоинта.
    • При больших объёмах записи и высоких требованиях к производительности может понадобиться увеличение max_wal_size.

Настройка конфигурационных файлов и их размещение в Docker

Способ 1: Настройка через переменные окружения

В Docker-образе PostgreSQL предусмотрен механизм инициализации с помощью переменных окружения и SQL-скриптов. Но в случае с параметрами вроде archive_mode или archive_command проще использовать собственный конфигурационный файл.

Способ 2: Использование собственного postgresql.conf

Наиболее распространённая практика — указать в docker-compose.yml дополнительный volume, который «накрывает» дефолтный postgresql.conf или монтирует собственный файл в папку с конфигами:

services:
  postgres:
    image: postgres:14
    container_name: my_postgres
    environment:
      POSTGRES_USER: myuser
      POSTGRES_PASSWORD: mypassword
      POSTGRES_DB: mydatabase
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - archives:/var/lib/postgresql/data/archives
      - ./my_postgresql.conf:/etc/postgresql/postgresql.conf
      - ./pg_hba.conf:/etc/postgresql/pg_hba.conf
    ports:
      - "5432:5432"
    command: >
      -c 'config_file=/etc/postgresql/postgresql.conf'
      -c 'hba_file=/etc/postgresql/pg_hba.conf'

volumes:
  postgres_data:
  archives:

В таком случае структура папок на хосте может выглядеть так:

my_project/
  ├─ docker-compose.yml
  ├─ my_postgresql.conf
  └─ pg_hba.conf (опционально)

Сохраним дефолтные настройки postgresql.conf выполнив команду:

docker run --rm postgres:14 cat /usr/share/postgresql/postgresql.conf.sample > my_postgresql.conf

Изменим my_postgresql.conf раскомментировав, к примеру, следующие строки:

# WAL настройки
wal_level = replica                 # Уровень детализации WAL
archive_mode = on                   # Включить архивацию
archive_command = 'cp %p /var/lib/postgresql/data/archives/%f' # Команда для архивирования WAL
archive_timeout = 300               # Таймаут архивации WAL (5 минут)
max_wal_size = 2GB                  # Максимальный размер WAL
min_wal_size = 80MB                 # Минимальный размер WAL
wal_buffers = 16MB                  # Буферы для WAL-записей (оптимально для больших нагрузок)

# Настройки автосохранения
checkpoint_timeout = 5min           # Частота выполнения checkpoint
checkpoint_completion_target = 0.7  # Целевой процент завершения checkpoint до таймаута
checkpoint_warning = 30s            # Предупреждение, если checkpoint выполняется слишком часто

# Оптимизация параллельных операций
effective_cache_size = 512MB        # Оценка кэша системы (обычно 50%-75% от RAM)
default_statistics_target = 100     # Статистика для планировщика запросов

В самом контейнере при запуске PostgreSQL прочитает настройки из /etc/postgresql/postgresql.conf, которые мы смонтировали. Путь к этому файлу указываем через опцию -c 'config_file=...' в docker-compose.yml.

Файл pg_hba.conf отвечает за контроль доступа (авторизация). Он напрямую не влияет на WAL, но при переносе собственной конфигурации стоит убедиться, что в нём сохранены корректные настройки для вашего окружения (например, чтобы пользователь myuser мог подключаться по нужному IP-адресу).
Пример:

# Разрешить локальное подключение для всех пользователей
local   all             all                                     trust

# Разрешить подключение по сети с использованием MD5-авторизации
host    all             all             0.0.0.0/0               md5
host    all             all             ::/0                    md5

В случае Docker’а полезно указывать host all all 172.17.0.0/16 md5 (или свой диапазон) — зависит от сетевых настроек Compose.

Проверка корректности настроек и перезапуск контейнера

Шаг 1: Создаём директорию для архивов

Перед тем как запускать контейнер, убедитесь, что есть каталог для архивных WAL-файлов, если не создан volume как в нашем примере.

Шаг 2: Запуск или перезапуск контейнера

Если вы уже запустили контейнер раньше, нужно перезапустить его, чтобы применились новые настройки:

docker-compose down
docker-compose up -d

При этом проверьте логи:

docker-compose logs -f postgres

Ищите строки, указывающие, что archive_mode включён (archive_mode = on) и что команда archive_command установлена.

Шаг 3: Проверка параметров внутри PostgreSQL

Подключитесь к контейнеру PostgreSQL с помощью psql: Используйте следующую команду для подключения к базе данных mydatabase:

docker exec -it my_postgres psql -U myuser -d mydatabase

Если хотите подключиться через встроенную базу postgres, к примеру чтоб создать mydatabase, используйте:

docker exec -it my_postgres psql -U myuser -d postgres

Затем выполните команду:

\l

Это отобразит список всех доступных баз данных.

Если база данных mydatabase отсутствует, создайте её:

CREATE DATABASE mydatabase;

Выполните команды для проверки параметров: Внутри интерактивной оболочки psql выполните следующие SQL-запросы:

SHOW wal_level;
SHOW archive_mode;
SHOW archive_command;

Шаг 4: Проверка архивации WAL

Для теста можно создать или изменить данные в базе, затем дождаться, когда PostgreSQL «свернёт» WAL-файл и запустит archive_command. В папке archives (в volume или на хосте) должен появиться файл с именем наподобие 00000001000000000000000A.

docker exec -it my_postgres ls -l /var/lib/postgresql/data/archives

Если видите, что файлы появляются — настройка архивации WAL работает. Если нет, смотрите логи PostgreSQL: иногда из-за прав на запись или неверного пути PostgreSQL не может выполнить cp %p /.../%f.

Таким образом, мы включили архивацию WAL, указали, куда копируются файлы, и проверили корректность настроек. На следующем этапе разберём, как организовать хранение этих архивов, эффективно управлять их объёмом и впоследствии использовать их для восстановления данных между моментами создания дампов.

Организация архивации WAL

В этой главе рассмотрим, куда и как можно сохранять WAL-файлы, какие инструменты и команды используются для их архивации, а также познакомимся с механизмами сжатия, ротации и мониторинга. Правильно настроенная архивация позволит нам в дальнейшем «доигрывать» операции при восстановлении из дампа — без потери важных изменений.

Способы хранения WAL-файлов

1. Локальный диск

  • Преимущества: простота организации (достаточно указать команду копирования в локальную папку), быстрый доступ к файлам, отсутствие зависимости от сети.
  • Недостатки: при выходе из строя локального диска теряется и архив. Также может быстро закончиться место, если объём WAL велик.

2. Сетевое хранилище (NFS, SMB и пр.)

  • Преимущества: архив хранится отдельно от основного диска сервера БД, что повышает надёжность. Можно подключать несколько серверов к одному хранилищу.
  • Недостатки: нужна стабильная сеть с достаточной пропускной способностью. При сбоях в сети процесс архивации может прерываться.

3. Облачные хранилища (S3, GCS и др.)

  • Преимущества: данные «живут» в облаке, обычно есть встроенные механизмы надёжного хранения и репликации.
  • Недостатки: возможна сложность интеграции (нужно использовать специальные утилиты типа s3cmd, rclone или писать свой скрипт). Задержки на сеть могут быть выше, чем при локальном или сетевом хранилище.

4. Удалённый сервер (через scp, rsync)

  • Преимущества: простая реализация, достаточно иметь доступ по SSH; архив лежит отдельно от текущего сервера БД.
  • Недостатки: нужно убедиться, что на удалённом сервере настроены права и есть достаточно места для хранения. При проблемах с сетью архивация будет «подвисать» или прерываться.

На практике многие начинают с локального диска (или Docker volume) для отладки и тестирования, а потом переводят архив на более надёжное место: например, сетевую шару или S3.

Настройка archive_command

Чтобы PostgreSQL «отдавал» WAL-файлы в архив, нужно в postgresql.conf (или через переменные окружения) задать archive_command. Простейший вариант (локальный архив):

archive_mode = on
archive_command = 'cp %p /var/lib/postgresql/data/archives/%f'

Здесь:

  • %p — это путь к исходному WAL-файлу.
  • %f — имя файла (обычно выглядит как 00000001000000000000000A).
  • /var/lib/postgresql/data/archives/ — каталог, куда мы складываем файлы. Он может быть локальной директорией или смонтированным volume.

Распространённые ошибки и способы их устранения

  1. Отсутствие каталога для архивов
    • Если папка archives/ не существует или у PostgreSQL нет прав на запись в неё, то при каждом завершении WAL-файла в логах будут появляться ошибки вида could not create file /... Permission denied.
    • Решение: убедиться, что директория создана и права выставлены корректно. В Docker-контейнере папка должна существовать внутри контейнера, если вы не монтируете её снаружи.
  2. Перезапись уже существующих файлов
    • По умолчанию команда cp %p /archives/%f перезапишет файл, если он уже там есть. Это может быть проблемой в редких случаях (например, если архивируемся с нескольких нод репликации).
    • Решение: добавить проверку наличия файла, например:

      archive_command = 'test ! -f /archives/%f && cp %p /archives/%f'

      Так мы не затираем уже существующий WAL-файл.

  3. Сеть недоступна (при удалённом хранении)
    • Если используем scp или rsync на удалённый сервер, при отсутствии сети PostgreSQL будет пытаться снова и снова выполнить команду, «зависая» на архивации.
    • Решение: обеспечивать стабильный доступ к сети, использовать дополнительные ключи rsync --partial --append, настраивать SSH-ключи для автоматического подключения. При серьёзных сбоях следить за логами PostgreSQL.
  4. Недостаточно места
    • При избыточной нагрузке и большом количестве WAL-файлов архив может быстро заполняться. Когда директория переполнена, PostgreSQL не сможет завершить запись WAL-файлов, что может привести к критическим ошибкам.
    • Решение: регулярно очищать или ротационно перемещать старые файлы, следить за объёмами и расширять хранилище.

Механизмы сжатия, ротации и мониторинга

1. Сжатие (compression)

Чтобы не захламлять хранилище гигабайтами WAL-файлов, можно их сжимать. Например, с помощью gzip:

archive_command = 'cp %p /var/lib/postgresql/data/archives/%f && gzip /var/lib/postgresql/data/archives/%f'

Или комбинированная команда:

archive_command = 'gzip -c %p > /var/lib/postgresql/data/archives/%f.gz'

Главное — помнить, что при восстановлении нужно будет распаковывать эти файлы (PostgreSQL сам не умеет работать с .gz-файлами «напрямую»). В сценариях восстановления нужно прописать путь или использовать скрипты, которые будут «догонять» WAL-файлы в распакованном виде.

Варианты сжатия

  • gzip: баланс скорости и сжатия, почти повсеместно доступен.
  • bzip2 или xz: более сильное сжатие, но выше нагрузка на процессор.
  • zstd: современный алгоритм, даёт хорошее соотношение скорость/сжатие, но требует установки соответствующей утилиты.

2. Ротация архивов

Хранить все WAL-файлы бесконечно невозможно: они могут занять терабайты места. Поэтому необходимо выстроить политику ротации:

  • Автоматическая ротация: если у вас настроена логическая репликация или потоковая реплика, возможно, часть WAL уже не нужна. Иногда используют pg_archivecleanup, который удаляет WAL-файлы, не нужные для определённой точки восстановления.
  • Внешние скрипты: можно использовать cron-задачи для удаления старых файлов (например, старше N дней), если у вас принято хранить их только за этот период.
  • Логические дампы: при наличии «золотого» дампа (или физической копии) на определённую дату хранить сверхдревние WAL-файлы нет смысла, т.к. восстанавливаться далее этой даты вы всё равно не будете.

3. Мониторинг состояния архивации

Чтобы не пропустить момент, когда что-то пошло не так, стоит мониторить:

  • Логи PostgreSQL: при каждом копировании WAL-файла PostgreSQL пишет запись в лог. Если там появляются повторяющиеся ошибки, нужно реагировать незамедлительно.
  • Размер каталога с архивами: если объём внезапно вырос — возможно, из-за пиковых нагрузок или по ошибке не удаляются старые файлы.
  • Количество файлов: если раз в день делается дамп, то объём WAL между дампами должен быть понятен и предсказуем. Слишком много новых файлов может говорить о «шторме» транзакций или о циклических ошибках.

Можно подключить системы мониторинга (Prometheus, Zabbix, ELK, Grafana и пр.), следить за состоянием диска, скоростью роста каталога, появлением ошибок в логах.

Таким образом, в правильной схеме архивации мы стремимся хранить WAL на надёжном носителе (локальном, сетевом или облачном), сжимать (при необходимости) файлы ради экономии пространства и регулярно «подчищать» старые логи, без которых уже не будет восстанавливаться база. В следующих главах мы подробно рассмотрим, как непосредственно использовать эти файлы для восстановления между моментами создания дампа и точкой сбоя, то есть как «доигрывать» транзакции из WAL.

Процесс восстановления

В предыдущих главах мы рассмотрели, как настроить PostgreSQL для записи и архивации WAL-файлов. Теперь разберём конкретные шаги, которые позволяют «доиграть» операции (транзакции) между моментом создания дампа и точкой сбоя. Основная идея в том, что дамп восстанавливает базовое состояние, а затем с помощью WAL-файлов мы восстанавливаем все изменения, произошедшие после создания этого дампа.

Шаг 1: Подготовка свежего дампа

Чтобы иметь точку начала для восстановления из WAL, нам нужен актуальный дамп (логический или физический). Предположим, что регулярно (например, раз в сутки или несколько раз в день) мы делаем логический дамп при помощи pg_dump:

pg_dump -U myuser -d mydatabase > /backup/pg_dumps/mydatabase_20250101.sql

Или делаем физическую копию через pg_basebackup (которая сохраняет структуру файловой системы БД). В любом случае у нас должен быть «срез» состояния базы на момент создания бэкапа.

Шаг 2: Подготовка каталога для восстановления

Когда случается сбой, нам нужно создать новую чистую среду (новую папку с данными PostgreSQL) и «развернуть» туда наш бэкап. В Docker-контейнере обычно это выглядит так:

  1. Останавливаем контейнер с PostgreSQL (если он ещё работает).
  2. Очищаем или создаём новую папку (volume) для базы данных:

    docker-compose down
    # Допустим, мы сохраняем данные в volume postgres_data
    # Если нужно, можно пересоздать volume:
    docker volume rm my_project_postgres_data
    docker volume create my_project_postgres_data
  3. Запускаем контейнер с новым (пустым) volume:

    docker-compose up -d

    Теперь в /var/lib/postgresql/data у нас чистая файловая система PostgreSQL.

Шаг 3: Восстановление из дампа

Вариант 1: Логический дамп (.sql или custom-формат)

  1. Подключаемся к базе (которая сейчас пуста) с помощью psql:

    psql -U myuser -d mydatabase -h 127.0.0.1 -p 5432 < /backup/pg_dumps/mydatabase_20250101.sql

    или, если дамп в custom-формате, используем pg_restore:

    pg_restore -U myuser -d mydatabase -h 127.0.0.1 -p 5432 /backup/pg_dumps/mydatabase_20250101.dump
  2. Дожидаемся окончания восстановления. Теперь в базе данные соответствуют состоянию на момент, когда делался дамп.

Вариант 2: Физическая копия (через pg_basebackup)

  1. Распаковываем физическую копию в папку data (или соответствующую папку volume):

    pg_basebackup -D /var/lib/postgresql/data_restored -U myuser -Fp -Xs -P
  2. Запускаем PostgreSQL, указав путь к этой директории в Docker (или копируя файлы в нужную папку volume).

При физическом бэкапе у нас уже есть «живое» состояние всей файловой системы базы, включая pg_wal, но для полноценного восстановления может понадобиться дополнительная настройка recovery-файлов — об этом в следующих шагах.

Шаг 4: Применение WAL для восстановления данных между моментом создания дампа и точкой сбоя

Теперь самое важное: «доиграть» (replay) все WAL-файлы, которые накопились после создания дампа. Логика следующая:

  1. PostgreSQL при старте переходит в режим восстановления (recovery) и начинает поочерёдно читать архивные WAL-файлы.
  2. Все изменения, зафиксированные в WAL, применяются к базе.
  3. Как только доходят до нужной точки (обычно до последнего доступного WAL-файла перед сбоем), восстанавливаемая база становится актуальной.

Настройка режима восстановления

Исторически в PostgreSQL был файл recovery.conf, где прописывалось, откуда брать WAL для восстановления. Начиная с 12-й версии, эти параметры переехали в postgresql.conf или standby.signal/recovery.signal. Однако логика осталась примерно той же:

  • restore_command — команда, которую PostgreSQL использует, чтобы брать нужный WAL-файл из архива. Аналогична archive_command, но в обратную сторону.
  • recovery_target_time или recovery_target_xid (опционально) — если нужно восстановиться к конкретному моменту времени или к конкретной транзакции.
  • recovery_target_action — что делать после достижения нужной точки (остановить или продолжить работу).

Пример фрагмента в postgresql.conf, который настраивается для восстановления:

# Включаем режим восстановления (PostgreSQL 12+)
restore_command = 'cp /var/lib/postgresql/data/archives/%f %p'

# Если хотим «поймать» момент времени, до которого делаем recovery:
# recovery_target_time = '2025-01-01 10:30:00'

# Когда восстановление достигнет точки, перестать читать WAL и «промоутиться» в обычный режим
recovery_target_action = 'promote'

В более старых версиях PostgreSQL (до 12) можно было создать файл recovery.conf в папке с данными и прописать там:

restore_command = 'cp /var/lib/postgresql/data/archives/%f %p'
recovery_target_action = 'promote'

Размещение конфигурации в Docker

  1. Создайте или отредактируйте дополнительный файл конфигурации, например recovery.conf (для PostgreSQL < 12) или включите эти параметры напрямую в postgresql.conf (для PostgreSQL ≥ 12). 
  2. При запуске контейнера в вашем docker-compose.yml можно указать:

    services:
      postgres:
        # ...
        volumes:
          - ./postgresql.conf:/etc/postgresql/postgresql.conf
          - ./my_recovery_signal_file:/var/lib/postgresql/data/recovery.signal
        command: >
          -c 'config_file=/etc/postgresql/postgresql.conf'

    Зачем нужен ./my_recovery_signal_file

    Файл recovery.signal служит сигналом для PostgreSQL, что база данных должна быть запущена в режиме восстановления. Это необходимо в следующих случаях:

    1. Восстановление из архива WAL: PostgreSQL использует restore_command, чтобы последовательно восстанавливать изменения из архивных WAL-файлов.
    2. Point-in-time recovery (PITR): Если задан параметр recovery_target_time, PostgreSQL остановит восстановление на указанном моменте времени.
    3. Репликация: Если вы настраиваете реплику базы данных, режим восстановления необходим для синхронизации с мастером.

       

  3. Убедитесь, что при старте базы нет конфликтующих настроек (например, archive_mode=on и одновременно попытка восстановиться).

Запуск и проверка

После того как PostgreSQL стартует в режиме восстановления, он будет последовательно копировать WAL-файлы командой restore_command. В логах должны появляться сообщения вида:

LOG:  starting point-in-time recovery to 2025-01-01 10:30:00
LOG:  restored log file "00000001000000000000000A" from archive
LOG:  consistent recovery state reached

Когда все нужные файлы будут обработаны, база «промоутится» в обычное рабочее состояние.

Шаг 5: Тонкости и частые подводные камни

  1. Совпадение версий
    • Дамп, WAL-файлы и восстанавливаемая версия PostgreSQL должны совпадать (или быть совместимы). Если у вас дамп сделан на PostgreSQL 14, а восстанавливаете на 15, могут возникать проблемы с физической копией. Для логического дампа это менее критично, но всё же лучше избегать больших «скачков» версий.
  2. Чистота данных в каталоге
    • Если в каталоге данных уже лежат какие-то «старые» файлы от другой установки PostgreSQL, возможно, восстановление пройдёт некорректно или не начнётся вовсе. Убедитесь, что папка чистая, прежде чем разворачивать в неё бэкап.
  3. Количество WAL-файлов
    • Если в архиве не хватает нужных WAL-файлов, то восстановиться до последнего состояния не получится. Убедитесь, что архивирование велось непрерывно и все файлы с момента создания дампа до сбоя присутствуют.
    • Если какого-то WAL-файла нет, PostgreSQL выдаст ошибку could not restore file ... и придётся искать обходные пути (например, отменить восстановление после последнего имеющегося файла).
  4. Конфликтующие настройки
    • В современных версиях (12+) recovery.conf больше не используется, и сам файл вызовет ошибку при старте PostgreSQL. Нужно использовать сигнальные файлы recovery.signal или standby.signal и прописывать параметры восстановления в postgresql.conf.
  5. Права доступа и пути
    • При использовании Docker зачастую проблемы возникают из-за неправильных путей внутри контейнера. Проверяйте, соответствует ли путь в restore_command реальному пути к архиву. Если архив монтируется извне, нужно убедиться, что PostgreSQL внутри контейнера «видит» именно тот каталог, который вы указали.

Таким образом, полный процесс восстановления выглядит так:

  1. Развернуть новое окружение или очистить существующее.
  2. Импортировать свежий дамп (логический или физический).
  3. Указать в конфигурации пути к архиву WAL и команды для его копирования.
  4. Запустить PostgreSQL в режиме восстановления, проверить логи и дождаться сообщения о завершении recovery.
  5. Получить базу в актуальном состоянии с учётом всех транзакций, которые были совершены после создания дампа.

Практические примеры

В этой главе мы рассмотрим конкретные команды и способы проверки, которые помогут убедиться, что наша конфигурация бэкапов и WAL работает без сбоев. Также поговорим о полезных лайфхаках и том, куда двигаться дальше, чтобы углублять знания в теме резервирования и масштабирования.

Примеры Docker-команд и команд psql для бэкапа

Ниже — несколько команд, которые можно запускать в стандартном окружении Docker с PostgreSQL.

1. Создание логического дампа

# Допустим, у нас контейнер называется my_postgres
# Создаём дамп базы mydatabase, пользователь myuser
docker exec -it my_postgres \
  pg_dump -U myuser mydatabase > /backup/pg_dumps/mydatabase_$(date +%Y%m%d_%H%M).sql
  • docker exec -it my_postgres — заходим внутрь контейнера.
  • pg_dump -U myuser mydatabase — команда для бэкапа.
  • > /backup/pg_dumps/... — перенаправляем вывод в файл (предполагается, что папка /backup/pg_dumps/ смонтирована как volume или проброшена наружу).

Важно: Проверьте, что внутри контейнера действительно есть каталог /backup/pg_dumps. Иначе перенаправляйте вывод на хосте, используя что-то вроде docker exec my_postgres pg_dump ... > backup.sql.

2. Создание физического бэкапа при помощи pg_basebackup

docker exec -it my_postgres \
  pg_basebackup -U myuser -D /backup/physical_backup -Fp -Xs -P
  • -D /backup/physical_backup — каталог для сохранения физических файлов базы.
  • -Fp — формат Plain, копирование файлов.
  • -Xs — включение репликационных слотов (или стриминг WAL) в процессе.
  • -P — показывает прогресс.

После выполнения команды в /backup/physical_backup будет готовая файловая структура, которую можно «поднять» вместо /var/lib/postgresql/data при восстановлении.

3. Восстановление логического дампа

# Допустим, хотим восстановить в ту же базу (mydatabase),
# в том же контейнере
docker exec -i my_postgres \
  psql -U myuser -d mydatabase < /backup/pg_dumps/mydatabase_20250101.sql
  • psql -U myuser -d mydatabase < ... — применяем SQL-скрипт дампа к существующей базе.

Если нужно «чистое» восстановление (например, сбросить базу перед заливкой дампа), можно предварительно удалить/создать заново базу командой:

DROP DATABASE mydatabase;
CREATE DATABASE mydatabase;

И только потом залить дамп.

Примеры восстановления с WAL

  1. Проверка наличия архивированных WAL-файлов

    ls /var/lib/postgresql/data/archives

    Если всё работает, вы увидите файлы формата 00000001000000000000000A (и так далее).

  2. Проверка параметров архивации в работающем контейнере

    docker exec -it my_postgres psql -U myuser -c "SHOW archive_mode;"
    docker exec -it my_postgres psql -U myuser -c "SHOW archive_command;"
    docker exec -it my_postgres psql -U myuser -c "SHOW wal_level;"

    Убедитесь, что archive_mode = on, archive_command не пуст, а wal_level соответствует replica или logical.

  3. Симуляция сбоя и восстановление
    • Останавливаем контейнер и меняем volume (или его очищаем).
    • Запускаем контейнер с пустой базой.
    • Заливаем последний дамп.
    • Указываем путь к архивным WAL-файлам в параметре restore_commandpostgresql.conf или recovery.conf для версий < 12).
    • Перезапускаем контейнер и ждём, пока PostgreSQL «доиграет» WAL.
    • Проверяем, что нужные данные (созданные после дампа) появились.

Полезные советы и лайфхаки

1. Регулярное тестовое восстановление

  • Отрабатывайте план B: как только настроили бэкап и WAL — пробуйте на тестовом окружении развернуть базу из этих копий. Лишь проверив реальное восстановление, можно быть уверенным, что бэкап не окажется бесполезным.
  • График тестов: раз в неделю или месяц делайте «учебную» процедуру восстановления, чтобы в случае ЧП не тратить время на изучение логов и документации.

2. Ротация старых WAL-файлов

  • Используйте механизмы вроде pg_archivecleanup, чтобы автоматически удалять WAL-файлы, которые больше не нужны (если у вас нет целей откатываться на очень старые точки).
  • В больших системах накопление тысяч WAL-файлов может занимать много места и усложнять мониторинг.

3. Мониторинг и оповещения

  • Логи PostgreSQL: анализируйте логи на предмет ошибок архивации. Если archive_command возвращает ненулевой код, в логах появятся соответствующие сообщения.
  • Сторонние инструменты: подключите Prometheus/Grafana или другую систему мониторинга, чтобы отслеживать использование дискового пространства, появление новых WAL-файлов и т.п.

4. Автоматизация бэкапов

  • Cron-задачи: внутри контейнера (или на хосте) можно прописать cron, который вызывает pg_dump или pg_basebackup в нужное время. Логи работы cron нужно куда-то записывать, чтобы отслеживать возможные ошибки.
  • Docker-подход: некоторые предпочитают отдельный контейнер, который «подключается» к базе и делает бэкап, выкладывая результат в S3, локальную папку или сетевое хранилище.

5. Проверка совместимости версий

  • При переходе на новую версию PostgreSQL (например, с 14 на 15) заранее убедитесь, что физический бэкап и WAL взаимно совместимы. Логический дамп обычно переносится проще, но всё же внимательно изучайте Release Notes.

6. Хранение бэкапов вне сервера

  • Если вся система (сервер/хост Docker) выходит из строя, локальные бэкапы не спасут. Поэтому всегда держите бэкапы (дампы и WAL-архивы) как минимум в другом дата-центре или другом физическом месте.

Что дальше изучать и в каком направлении двигаться

  • Потоковая репликация (Streaming Replication): позволяет иметь «горячий» стендбай-сервер, который уже получает WAL-файлы в реальном времени. В случае сбоя можно быстро переключиться на реплику.
  • Логическая репликация: помогает избирательно реплицировать таблицы или даже отдельные данные в другие кластеры.
  • Шардирование, Patroni, Stolon и другие менеджеры кластеров: для больших нагрузок и высоких требований к доступности используют дополнительные инструменты, которые упрощают администрирование нескольких узлов PostgreSQL.
  • Автоматизированные инструменты бэкапа: например, Barman или WAL-G, которые берут на себя ротацию, мониторинг и экономию дискового пространства.

Таким образом, на основе всех рассмотренных глав можно собрать рабочую систему: от базового Docker Compose-файла и настройки archive_mode до тестирования восстановления при сбое. Регулярные проверки, отслеживание логов и своевременная ротация WAL-файлов станут залогом надёжности и помогут избежать критических потерь данных.


Читайте также:

ChatGPT
Eva
💫 Eva assistant

Выберите способ входа