Docker swarm mode (режим рою)

swarm
На хабрі вже писали про Docker swarm mode (режим рою), який є новою фічею версії 1.12. Дана опція внесла невелику плутанину в голови тих, хто знайомий з окремою реалізацією Docker Swarm, що мала поширення раніше і не відрізнялася зручністю налаштування та використання. Однак, після додавання Swarm в коробку з Docker все стало набагато простіше, очевидніше та функціональніше.

Активація Docker swarm mode

У режимі swarm усі ноди поділяються на два типи: manager та worker. При цьому повноцінний кластер може обходитися без робочих нод взагалі, тобто менеджери за замовчуванням є також робітниками.

Серед менеджерів завжди є один, який на даний момент є лідером кластера. Усі керуючі команди, які виконуються на інших менеджерах, автоматично перенаправляються на нього.

режим

Для включення режиму swarm достатньо вибрати хост, який буде початковим лідером у майбутньому кластері, і виконати на ньому лише одну команду:

Після того, як «рій» ініціалізований, він уже готовий для запуску на ньому будь-якої кількості сервісів. Щоправда, стан такого кластера буде неконсистентним (консистентний стан досягається за кількості менеджерів щонайменше 3). І звичайно ні про яке масштабування і стійкість до відмови в цьому випадку мови також бути не може. Для цього до кластера потрібно підключити ще хоча б дві ноди, що управляють. Дізнатися про те, як це зробити, можна, виконавши на лідері наступні команди:

Додавати і видаляти ноди можна будь-якої миті життя кластера — це жодним чином не впливає на його працездатність.

Створення сервісу

Створення сервісу Docker принципово не відрізняється від створення контейнера:

Відмінності, як правило, полягають урізний набір опцій. Наприклад, сервіс не має опції--volume, але є опція--mount— ці опції дозволяють підключати до контейнерів локальні ресурси нод, але роблять це по-різному.

Оновлення сервісу

Тут починається найбільша відмінність роботи контейнерів від кластера контейнерів (сервісу). Зазвичай, щоб оновити контейнер, доводиться зупиняти поточний і запускати новий. Це призводить хоч і до незначного, але існуючого часу простою вашого сервісу (якщо ви не переймалися тим, щоб обробляти такі ситуації за допомогою інших інструментів).

При використанні сервісу з кількістю реплік щонайменше 2 простої сервісу здебільшого не приходить. Це досягається завдяки тому, що Docker оновлює контейнери сервісу по черзі. Тобто в один і той же час завжди є хоча б один працюючий контейнер, який може обслужити запит користувача.

Для оновлення (у тому числі додавання та видалення) властивостей сервісу, які можуть мати кілька значень (наприклад,--publishабо--label), Docker пропонує використовувати спеціальні опції, що закінчуються суфіксами-addта-rm:

Видалення деяких опцій, однак, менш тривіальне і часто залежить від самої опції:

Докладно про кожну опцію можна дізнатися в описі командиdocker service update.

Масштабування та балансування

сервісу

Для розподілу запитів між нодами Docker використовується схема званаingress load balacing. Суть цього механізму полягає в тому, що на яку б із нід не прийшов запит користувача, він спочатку пройде через внутрішній механізм балансування, а потім буде перенаправлений на ту ноду, яка в даний момент можеобслуговувати такий запит. Тобто будь-яка нода здатна обробити запит до будь-якого з сервісів кластера.

Масштабування сервісу Docker досягається за рахунок вказівки необхідної кількості реплік. У той момент, коли вам необхідно збільшити (або зменшити) кількість нод, які обслуговують запити від клієнта, ви просто оновлюєте властивості сервісу із зазначенням потрібного значення опції <--replicas :

У цьому випадку треба не забути попередньо переконатися, що кількість доступних нід не менша, ніж кількість реплік, які ви хочете використати. Хоча нічого страшного не станеться навіть якщо нід менше, ніж реплік - просто деякі ноди запустять у себе більше одного контейнера одного і того ж сервісу (інакше Docker намагатиметься запускати репліки одного сервісу на різних нодах).

Відмовостійкість

docker

Відмовостійкість сервісу гарантується самим Docker. Це досягається в тому числі за рахунок того, що в кластері можуть одночасно працювати кілька управляючих нід, які можуть у будь-який момент замінити лідера, що вийшов з ладу. Якщо говорити докладніше, то використовується так званий алгоритм підтримки розподіленого консенсусу Raft. Тим, хто цікавиться, рекомендую подивитися цю чудову візуальну демонстрацію: Raft в роботі.

swarm

Автоматичний деплой

Деплой нової версії програми на бойові сервери завжди супроводжується ризиком того, що щось не піде. Саме тому вважається поганою прикметою викочувати нову версію програми перед вихідними та святами. Причому, перед тривалими святами, на зразок новорічних канікул, будь-які зміни на бойовій інфраструктурі припиняються за тиждень або за два до їх початку.

Незважаючи на те, що сервіси Docker пропонують цілкомнадійний спосіб запуску та оновлення програми, досить часто швидкий відкат до попередньої версії утруднений з тієї чи іншої причини, що може спричиняти недоступність вашого сервісу користувачам протягом багатьох годин.

Найнадійніший спосіб уникнути проблем при оновленні програми -автоматизація та тестування. Саме для цього розробляються системи автоматичного деплою. Важливою частиною таких систем, як правило, є можливість швидкого оновлення та відкату до попередньої версії на будь-якій вибраній інфраструктурі.

Більшість інструментів для автоматизації деплою пропонують описувати конфігурацію за допомогою популярних мов розмітки типу XML або YAML. Деякі йдуть далі і розробляють власну мову опису таких конфігурацій (наприклад, HCL або Puppet language). Я ж не бачу необхідності йти по жодному з цих шляхів з таких причин:

  • XML/YAML ніколи не зрівняються за можливостями розширення та використання з повноцінними мовами програмування, а прагнення спростити конфігурування через використання спрощеної розмітки часто навпаки, лише все ускладнює Плюс, мало хто з програмістів захоче програмувати на XML/YAML, а конфігурування — це і є окремий випадок програмування.
  • Розробка своєї власної мови програмування - надзвичайно складний і стомлюючий процес, найчастіше невартий зусиль, що витрачаються на нього.
Тому Fabricio для опису конфігурацій використовує звичайний Python і частину надійних і перевірених часом бібліотек (серед них невідомий Fabric).

Звичайно, багато хто може заперечити з цього приводу, що не всі розробники і DevOps знають Python. Ну, по-перше, Python (як і Bash) входить у джентльменський набірскриптових мов, які повинен знати кожен, хто поважає себе DevOps (ну або майже кожен). По-друге, як це не парадоксально, знати Python практично необов'язково. На підтвердження своїх слів наводжу приклад конфігурації сервісу заснованого на Django для Fabricio:

Погодьтеся, що цей приклад не складніший, ніж аналогічний опис YAML. Людина, який володіє хоча б однією мовою програмування, розбереться в цьому конфізі без особливих проблем.

Але досить лірики.

Процес деплою

Схематично процес деплою сервісу за допомогою Fabricio виглядає так, як представлено на малюнку нижче (після виконання командиfab djangoдля описаного вище конфігу):

режим

Розглянемо кожен пункт у порядку. Для початку, відразу хочу помітити, що представлена ​​схема актуальна при включеному режимі паралельного виконання (із зазначеною опцією-parallel). Відмінність послідовного режиму лише тому, що це дії виконуються суворо послідовно.

Відразу після запуску команди деплоя послідовно починають виконувати наступні кроки:

Усі команди Fabricio (крім backup і restore) є ідемпотентними, тобто безпечними при повторному виконанні з тими самими параметрами.