В предыдущем материале я рассказывал о проекте для автоматизации деплоя 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: <file obj> <- /data/postgresql.conf
[vagrant@192.168.1.85] /data/postgresql.conf not changed
[vagrant@192.168.1.85] download: <file obj> <- /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: <file obj> <- /data/recovery.conf
[vagrant@192.168.1.87] download: <file obj> <- /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: <file obj> <- /data/postgresql.conf
[vagrant@192.168.1.86] download: <file obj> <- /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: <file obj> <- /data/pg_hba.conf
[vagrant@192.168.1.87] download: <file obj> <- /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. Но отдельной задачи на это пока нет, и все будет зависеть от сложности реализации.
Поделиться с друзьями
-->

Комментарии (5)


  1. shuron
    16.11.2016 22:51

    Увидев про сложный сценарий с базой данных в Докере, зашел прочитать о само интересном, куда и как вы пишите данные базы данных и что происходит при релокации контейнеров. Но не увидел…


    1. renskiy
      16.11.2016 23:17
      +1

      Спасибо за комментарий. Давайте попробуем разобраться.

      куда и как вы пишите данные базы данных

      Собственно за данные отвечает сама БД, именно она решает куда писать данные, точнее это определяется переменной окружения контейнера PGDATA (которую мы указываем при создании контейнера). Далее, мы подключаем к контейнеру volume с папкой на хосте (снаружи контейнера), где данные будут хранится на самом деле, то есть не внутри контейнера.
      и что происходит при релокации контейнеров

      Этот вопрос я не совсем понял. Но в любом случае, данные БД никуда не релоцируются, они сохраняются даже при полном уничтожении контейнера (благодаря volume). А физический перенос данных при добавлении нового slave производится при помощи стандартной утилиты pg_basebackup, которая производит всю необходимую работу по переносу данных с одного сервера на другой.


      1. shuron
        17.11.2016 19:09

        В «нормальном»* кластере совершенно не хочется полагаться на доступнось какого-то определенного нода и знать его в лицо…
        Если работать с контенерами то очень быстро имеет смысл не полагаться на топологию кластера (покраней мере на конкретные ноды)
        Видимо в вашем примере вы полагаетесь на доступность конкретного хоста/нода/виртульной машины.

        Но представьте боевой кластер с 10 или 100 машинами… с нормальным скедьюлером.
        Ваш контейнер пишет данные через volume в хост «номер 7» потому что скедьюлер его там стартанул…

        Так что происходит с вашими данными если машина номер 7 не отвечает минут 5 и контенер с мастером стартнул где-то в на машине 99?
        Что происходит если машина не должна возвратится?

        В этом то весь интерес… при поднятии каких-либо баз данных в «нормальном» контеннерном класстер, очень сильно не хочется собирать данные на ких-то хостах…
        Хотя может это и просто по началу…


        1. renskiy
          17.11.2016 19:59

          Вы правы насчет контейнеров, которые не хранят состояние. Работая с такими контейнерами действительно не хочется полагаться на работоспособность одного из сотни, или даже тысячи хостов. И очень здорово здесь может помочь Swarm. Скажу немного заранее, но уже сейчас в соответствующей ветке, где идет разработка поддержки режима Swarm, реализована возможность безболезненно пропускать отвалившиеся и неработающие хосты. По сути, для работы Fabricio нужен в этом случае только активный менеджер, отказоустойчивость которого гарантируется роем (Swarm).

          Но в случае же с БД хранится не просто состояние, а целая база, которая может насчитывать терабайты и терабайты данных. Перекидывать такой объем данных даже в случае отказа — весьма накладное дело. Именно поэтому количество хостов БД в любых конфигурациях, как правило, строго ограничено и не превышает в большинстве случаев 10-ти серверов (хотя возможны и другие варианты, но не с реляционными БД).

          Подводя итог, в Fabricio контейнеры работают в «strict» режиме, то есть любая ошибка с невозможностью поднять контейнер рассматривается как фатальная и запрещающая дальнейшую работу (до тех пор, пока ошибка не будет исправлена). Это же касается и master-slave конфигурации, для которой это актуальнее даже в бОльшей степени, потому что цена ошибки в таких конфигурациях стоит очень дорого для того, чтобы полностью полагаться на автоматику.


          1. shuron
            17.11.2016 23:19

            Но в случае же с БД хранится не просто состояние, а целая база, которая может насчитывать терабайты и терабайты данных. Перекидывать такой объем данных даже в случае отказа — весьма накладное дело. Именно поэтому количество хостов БД в любых конфигурациях, как правило, строго ограничено и не превышает в большинстве случаев 10-ти серверов (хотя возможны и другие варианты, но не с реляционными БД).

            Ну в том то и дело… Есть ли смысл вообще базу данных в докер пихать…