Docker: деплой master-slave конфигурации PostgreSQL
В предыдущем материале я рассказывал о проекте для автоматизации деплоя Docker контейнеров, разработка которого стартовала в начале этого года. Прошло несколько месяцев, Fabricio был значительно улучшен и доработан, и сегодня я хочу рассказать об одном из последних нововведений — об автоматическом деплое master-slave конфигураций для PostgreSQL.
Запуск PostgreSQL в контейнерах — не самая популярная идея, и тому есть разумное объяснение: ни к чему добавлять дополнительные сетевые задержки к и без того довольно загруженному сервису. Но существует ряд случаев когда такое решение все же можно применить. Например, когда вы полностью доверяете Docker БД не испытывает серьезных нагрузок, но при этом важна возможность дублирования/реплицирования хранимых данных на несколько серверов. Либо просто для тестирования и отладки настроек перед применением их на боевых серверах.
Чтобы не утомлять читателя (и пользователя) большим количеством текстовой информации, я решил, что неплохо было бы уже привести «живые» примеры использования Fabricio на реально работающих контейнерах — согласитесь — лучше один раз увидеть.
Пример конфигурации master-slave для PostgreSQL реализован на трех виртуальных машинах, в которых установлен Docker. Создание и первоначальная настройка этих виртуальных машин полностью автоматизированы при помощи Vagrant, поэтому сложностей с запуском примера быть не должно.
Реализованные сценарии
Наиболее важным сценарием в случае с master-slave конфигурацией, несомненно, является восстановление работоспособности БД после отказа мастер (ведущего) сервера. Как правило, на боевых системах это настраивается при помощи систем постоянного мониторинга существующих серверов и автоматического переключения/исключения вышедших из строя хостов — failover. Например, для этих целей можно использовать популярный сегодня инструмент pgpool-II. Однако, настройка таких систем — задача не из тривиальных (да и полагаться полностью на автоматику решаются не все), поэтому часто можно встретить конфигурации, которые обходятся без автоматического восстановления после сбоя. Если сбой в таких системах все же присходит, то устраняется он, как правило, в ручном, либо полуавтоматическом режиме путём восстановления БД из резервной копии и/или переключением конфигов приложения на адрес нового мастера БД.
Fabricio предлагает полу-автоматический способ восстановления работоспособности после сбоя мастера. Для этого требуется выполнить всего одну команду, предварительно убедившись, что вышедший из строя сервер был заменён на новый, либо удален из настроек конфигурации деплоя:
fab --parallel db
Мало чем отличается от описанного выше варианта, сценарий добавления в конфигурацию нового ведомого (slave) сервера, для этого достаточно прописать адрес этого сервера в списке хостов Fabricio и заново запустить команду деплоя. Начальное состояние БД при этом будет скопировано с актуального ведущего (master) сервера.
[vagrant@192.168.1.85] Executing task 'update'
[vagrant@192.168.1.86] Executing task 'update'
[vagrant@192.168.1.87] Executing task 'update'
[vagrant@192.168.1.85] Found master: 192.168.1.85
[vagrant@192.168.1.85] download: <- /data/postgresql.conf
[vagrant@192.168.1.85] /data/postgresql.conf not changed
[vagrant@192.168.1.85] download: <- /data/pg_hba.conf
[vagrant@192.168.1.86] Waiting for master info (10 seconds)...
[vagrant@192.168.1.85] /data/pg_hba.conf not changed
[vagrant@192.168.1.85] run: docker inspect --type container postgres
[vagrant@192.168.1.87] Waiting for master info (10 seconds)...
[vagrant@192.168.1.85] run: docker inspect --type image postgres:9.6
[vagrant@192.168.1.85] run: docker start postgres
[vagrant@192.168.1.86] download: <- /data/recovery.conf
[vagrant@192.168.1.87] download: <- /data/recovery.conf
[vagrant@192.168.1.87] /data/recovery.conf not changed
[vagrant@192.168.1.86] /data/recovery.conf not changed
[vagrant@192.168.1.87] download: <- /data/postgresql.conf
[vagrant@192.168.1.86] download: <- /data/postgresql.conf
[vagrant@192.168.1.87] /data/postgresql.conf not changed
[vagrant@192.168.1.86] /data/postgresql.conf not changed
[vagrant@192.168.1.86] download: <- /data/pg_hba.conf
[vagrant@192.168.1.87] download: <- /data/pg_hba.conf
[vagrant@192.168.1.87] /data/pg_hba.conf not changed
[vagrant@192.168.1.86] /data/pg_hba.conf not changed
[vagrant@192.168.1.87] run: docker inspect --type container postgres
[vagrant@192.168.1.86] run: docker inspect --type container postgres
[vagrant@192.168.1.87] run: docker inspect --type image postgres:9.6
[vagrant@192.168.1.86] run: docker inspect --type image postgres:9.6
[vagrant@192.168.1.87] run: docker start postgres
[vagrant@192.168.1.86] run: docker start postgres
[vagrant@192.168.1.87] No changes detected, update skipped.
[vagrant@192.168.1.85] No changes detected, update skipped.
[vagrant@192.168.1.86] No changes detected, update skipped.
Done.
Disconnecting from vagrant@127.0.0.1:2222... done.
Disconnecting from vagrant@127.0.0.1:2200... done.
Disconnecting from vagrant@127.0.0.1:2201... done.
Для тестовых нужд Fabricio поддерживает также разворачивание конфигурации «с нуля», выбирая мастера случайным образом из списка доступных хостов.
Особенности реализации
Автоматическое определение мастера требует запуска Fabricio в режиме параллельного выполнения, который по-умолчанию отключён. Именно для этого в команде деплоя применяется опция --parallel.
Если хотя бы у одного из ведомых есть своя непустая папка с данными (определяется по наличию файла PG_VERSION), автоматический выбор (promotion) нового мастера по-умолчанию не производится (скрипт завершается с соответствующей ошибкой). И хотя процедура эта вполне безопасна, все же рекомендуется ознакомиться с алгоритмом выбора нового мастера прежде, чем включать эту опцию. А выбирается мастер в таком случае случайным образом среди тех хостов, у которых имеются хоть какие-то данные, при этом на «пустые» хосты (то есть не имеющие своей БД) данные будут скопированы уже с нового мастера.
Также без специального указания не работает откат к предыдущему состоянию, из-за того, что master-slave конфигурация вообще не предусматривает наличие такого сценария — ведь автоматически мастер не починится, а если произошла какая-то ошибка, то ее лучше чинить вручную, а не полагаться на автоматику. Если опция отката все же будет включена, то будет использоваться логика отката, унаследованная от родительского класса (одиночной конфигурации PostgreSQL) — возврат предыдущих конфигов и/или предыдущего контейнера (или контейнеров) в зависимости от того, что было обновлено во время последнего удачного деплоя.
Дальнейшие планы
В самом ближайшем будущем, скорее всего до конца года, будет реализована поддержка режима Swarm в Docker версии 1.12 или выше. Что даст возможность при помощи Fabricio деплоить не только отдельные контейнеры, но сразу целые сервисы с автоматическим масштабированием и отказоустойчивостью.
После внедрения решения для Swarm, будет логично заняться поддержкой Kubernetes и/или Mesos. Но отдельной задачи на это пока нет, и все будет зависеть от сложности реализации.