Понимание и выполнение сценариев отработки отказа и переключения PostgreSQL

Узнайте, когда использовать переключение или отработку отказа PostgreSQL, как проверить безопасность реплики и как избежать расщепления мозга во время событий высокой доступности.

Понимание и выполнение сценариев отработки отказа и переключения PostgreSQL

Разница между отработкой отказа и переключением PostgreSQL не является академической, когда вы держите пейджер. Переключение запланировано: у вас все еще есть работоспособный первичный сервер, вы можете остановить запись и выбрать лучший момент для перемещения роли записи на резервный сервер. Отработка отказа — это то, что вы делаете, когда первичный сервер исчез, недоступен или небезопасен для продолжения обслуживания трафика.

Это одно различие меняет все. Во время переключения ваша основная задача — терпение: убедитесь, что резервный сервер догнал первичный до повышения. Во время отработки отказа ваша основная задача — изоляция: убедитесь, что старый первичный сервер не может продолжать принимать записи после повышения резервного сервера. Большинство неприятных инцидентов с высокой доступностью PostgreSQL происходят из-за спешки в выполнении одной из этих двух задач.

Основы репликации: Фундамент высокой доступности

Высокая доступность PostgreSQL строится на потоковой репликации, где один сервер выступает в роли Первичного (или Мастера), а один или несколько серверов — в роли Резервных (или Реплик). Первичный сервер передает записи журнала предзаписи (WAL) на резервные серверы для поддержания их синхронизации.

Для эффективного управления этими ролями необходимы определенные настройки конфигурации как на первичном, так и на резервных узлах:

Критические настройки конфигурации

Эти настройки определяют, как работает репликация и как узлы идентифицируют друг друга:

  • wal_level: Должен быть установлен в replica или выше (в идеале logical, если используются инструменты, требующие логического декодирования) на Первичном сервере.
  • max_wal_senders: Определяет максимальное количество одновременных соединений отправителей WAL. Рассчитывайте на все физические резервные серверы, базовые резервные копии и инструменты репликации, которые могут подключаться одновременно.
  • hot_standby: Должен быть установлен в on в файле postgresql.conf резервного сервера, чтобы разрешить запросы только на чтение во время репликации.
  • synchronous_commit: Управляет моментом подтверждения транзакции. Он обеспечивает более высокую надежность только при правильной настройке синхронной репликации; сам по себе он не делает резервный сервер актуальным.
  • primary_conninfo: Устанавливается на резервном сервере и содержит информацию для подключения к текущему Первичному серверу (хост, порт, пользователь, пароль).

Лучшая практика: Поместите стабильную конечную точку перед PostgreSQL, например, HAProxy, PgBouncer за виртуальным IP, запись обнаружения служб или абстракцию служб вашей платформы. Приложения не должны знать, какой узел является первичным сегодня.

Переключение: Запланированный переход

Переключение — это контролируемый, плавный процесс, при котором активный Первичный узел намеренно выводится из эксплуатации, а назначенный Резервный узел повышается до его роли. Эта процедура обычно используется для планового обслуживания, обновления версий или замены оборудования.

Шаги для контролируемого переключения

Цель переключения — обеспечить нулевую потерю данных путем ожидания репликации всех текущих транзакций перед повышением.

  1. Остановите запись на текущем Первичном сервере: Первый шаг — предотвратить фиксацию новых транзакций на текущем Первичном сервере. Это часто достигается установкой default_transaction_read_only = on или временным отключением клиентских подключений.
  2. Дождитесь завершения репликации: Убедитесь, что назначенный Резервный сервер получил и применил все оставшиеся записи WAL от Первичного сервера. Вы можете проверить задержку репликации с помощью pg_stat_replication на Первичном сервере или изучив статус восстановления резервного сервера.
  3. Инициируйте повышение резервного сервера: Выполните команду для повышения выбранного резервного сервера до роли Первичного. Конкретная команда зависит от используемого инструмента управления (например, pg_ctl promote или команда менеджера кластера).
  4. Перенастройте старый Первичный сервер: После успешного повышения резервного сервера старый Первичный сервер должен быть перенастроен для следования за новым Первичным сервером в качестве резервного. Это включает обновление его primary_conninfo.
  5. Перенаправьте приложения: Обновите балансировщик нагрузки или пул соединений, чтобы направлять трафик на новый Первичный сервер.

Практический контрольный список переключения обычно выглядит более обыденно, чем драматично. Объявите короткую паузу записи, остановите фоновые рабочие процессы, которые продолжают запись, переведите приложение в режим обслуживания или опустошите пул записи, а затем проверьте позицию репликации. На старом первичном сервере pg_stat_replication показывает, получил ли резервный сервер WAL и сбросил ли его на диск. На резервном сервере pg_last_wal_receive_lsn() и pg_last_wal_replay_lsn() помогают увидеть, только ли прибыл WAL или он уже был воспроизведен.

Не повышайте резервный сервер только потому, что он подключен. Резервный сервер может быть подключен и все еще отставать на секунды или минуты, если он воспроизводит большую транзакцию, ожидает ввода-вывода диска или восстанавливается после паузы в сети. Для запланированного переключения вы хотите, чтобы воспроизведение догнало до повышения. Если на резервном сервере выполняются сеансы только для чтения, также проверьте, не задерживают ли долго выполняющиеся запросы воспроизведение WAL.

После повышения проверьте роль напрямую:

SELECT pg_is_in_recovery();

Повышенный узел должен вернуть false. Пониженный узел, после того как он был перестроен или перенастроен как резервный, должен вернуть true.

Сторона приложения заслуживает такого же внимания. Перед переключением узнайте, как клиенты обнаруживают писатель. Если они подключаются к DNS-имени, поймите TTL DNS и кешируют ли клиенты адреса дольше, чем ожидалось. Если они подключаются через PgBouncer, решите, нужно ли приостановить пул, перезагрузить его или перезапустить. Если вы используете HAProxy, убедитесь, что проверка работоспособности проверяет статус записи, а не только то, открыт ли порт 5432. Резервный сервер с работающим PostgreSQL не является допустимой целью для записи.

Мне также нравится записывать точку отката. Перед повышением вы обычно можете остановиться, возобновить запись на старом первичном сервере и попробовать снова позже. После повышения откат становится новой сменой роли, а не простой отменой. Это не означает, что повышение опасно; это означает, что оператор должен знать, на какой стороне линии он находится.

Отработка отказа: Реагирование на чрезвычайные ситуации

Отработка отказа — это немедленная, реактивная процедура, запускаемая, когда текущий Первичный сервер неожиданно выходит из строя (например, сбой оборудования, разделение сети, ошибка программного обеспечения) и не может быть быстро восстановлен.

Отработка отказа по своей сути несет более высокий риск потери данных, поскольку нет гарантии, что последние несколько зафиксированных транзакций успели передаться на резервные серверы до сбоя.

Выполнение аварийной отработки отказа

Процедуры отработки отказа предназначены для скорости и восстановления, часто с использованием специализированных инструментов для автоматизации повышения.

  1. Определите состояние старого Первичного сервера: Убедитесь, что исходный Первичный сервер действительно недоступен, а не испытывает временную проблему с сетью (это предотвращает опасные сценарии «расщепления мозга»).
  2. Выберите лучший резервный сервер: Выберите резервный сервер с наименьшей задержкой репликации (тот, который наиболее продвинут в потоке WAL).
  3. Повысьте резервный сервер: Немедленно повысьте выбранный резервный сервер с помощью команды повышения (pg_ctl promote).
  4. Обработайте потерю данных (при необходимости): Если в кластере используется асинхронная репликация, данные, потерянные на отказавшем Первичном сервере, возможно, придется восстановить вручную или просто принять, в зависимости от толерантности приложения.
  5. Перенастройте бывший Первичный сервер: После восстановления исходного Первичного сервера его необходимо очистить, переинициализировать (часто требуется базовая резервная копия с нового Первичного сервера) и настроить для следования за новым Первичным сервером.

Сложная часть отработки отказа — это не ввод pg_ctl promote. Сложная часть — это решение, что старый первичный сервер должен считаться небезопасным, пока не доказано обратное. Если старый первичный сервер все еще работает, но отключен от приложения или от резервного сервера, вы можете получить расщепление мозга: два доступных для записи сервера PostgreSQL, принимающие разные истории. Как только это произойдет, PostgreSQL не объединит истории за вас. Вас ждет ручное согласование данных или восстановление одной стороны из резервной копии.

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

После отработки отказа ожидайте очистки. Если старый первичный сервер вернется, не направляйте его небрежно на новый первичный сервер в надежде, что он догонит. Он может содержать WAL, принадлежащий старой временной линии. Во многих средах самый безопасный путь — pg_rewind, если выполнены предварительные условия, или свежая базовая резервная копия с нового первичного сервера, если нет.

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

Используйте ту же дисциплину для резервного копирования. Как только в кластере появится новый первичный сервер, убедитесь, что резервное копирование и архивирование WAL теперь следуют за этим первичным сервером. Отработка отказа, которая восстанавливает сервис, но молча останавливает резервное копирование, — это только половина восстановления.

Инструменты для безопасного повышения: Repmgr vs. Patroni

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

Repmgr (Менеджер репликации)

repmgr — это легкий инструмент, который помогает регистрировать узлы, отслеживать репликацию и выполнять контролируемые смены ролей. Точные команды зависят от версии и топологии кластера, но общий шаблон таков:

  • Переключение: Выполните запланированное repmgr standby switchover с резервного сервера, который должен стать первичным, после подтверждения работоспособности репликации.
  • Отработка отказа: Разрешите repmgrd выполнять автоматическую отработку отказа только в том случае, если поведение изоляции и кворума/наблюдателя понято и протестировано.

Patroni

Patroni использует распределенные хранилища консенсуса (такие как etcd, ZooKeeper или Consul) для управления состоянием кластера, автоматически выбирая новый Первичный сервер при обнаружении сбоя. Patroni в значительной степени автоматизирует как переключения, так и отработки отказа через вызовы API или операторы Kubernetes, значительно сокращая ручное вмешательство.

Пример использования Patroni (концептуальная команда повышения):

# Запуск переключения через REST API Patroni
curl -X POST http://patroni-api-endpoint/switchover -H "Content-Type: application/json" -d '{"target": "standby_node_name"}'

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

Сводка различий

Особенность Переключение (Плановое) Отработка отказа (Аварийная)
Причина Обслуживание, обновление, административное решение Сбой первичного сервера (крах, отключение)
Риск потери данных Близок к нулю (при правильном выборе времени) От среднего до высокого (зависит от режима репликации)
Ожидаемое время простоя Короткое, контролируемое время простоя Немедленное, реактивное время простоя
Подготовка Требует предварительной координации и подтверждения синхронизации WAL Требует немедленных действий и опоры на работоспособность резервного сервера

Небольшой сборник инструкций, который вы можете адаптировать

Для запланированного переключения компактный сборник инструкций может выглядеть так:

  1. Убедитесь, что выбранный резервный сервер работоспособен и воспроизводит WAL.
  2. Приостановите запись приложений и фоновые задания.
  3. Убедитесь, что воспроизведение репликации догнало.
  4. Повысьте резервный сервер с помощью инструмента высокой доступности.
  5. Переместите конечную точку записи.
  6. Убедитесь, что pg_is_in_recovery() возвращает false на новом первичном сервере.
  7. Перестройте или перемотайте старый первичный сервер как резервный.
  8. Возобновите запись и следите за ошибками, репликацией и количеством подключений.

Для отработки отказа порядок меняется:

  1. Убедитесь, что первичный сервер отказал или небезопасен.
  2. Изолируйте старый первичный сервер.
  3. Выберите наиболее продвинутый резервный сервер.
  4. Повысьте его с помощью инструмента высокой доступности.
  5. Переместите конечную точку записи один раз.
  6. Убедитесь, что запись работает на новом первичном сервере.
  7. Проверьте реплики, слоты, резервные копии и архивирование WAL.
  8. Повторно вводите старый первичный сервер только через перемотку или перестройку.

Команды различаются в зависимости от инструментов, но свойства безопасности — нет. Один доступный для записи первичный сервер, известное состояние репликации, протестированная маршрутизация клиентов и чистый способ вернуть отказавшие узлы.

Прежде чем доверять любой конструкции высокой доступности, отрепетируйте оба пути в среде, отличной от производственной. Учения по переключению должны доказать, что приложения чисто переподключаются, старый первичный сервер может снова стать резервным, а мониторинг следует за новой ролью. Учения по отработке отказа должны доказать нечто более строгое: отказавший первичный сервер изолирован, резервный сервер, выбранный для повышения, является лучшим доступным кандидатом, конечная точка записи приложения перемещается один раз, а старый первичный сервер не может присоединиться без перемотки или перестройки.

Самые безопасные команды высокой доступности PostgreSQL рассматривают отработку отказа как протестированный операционный рабочий процесс, а не как героическую команду, введенную во время простоя.