Використання consul для service discovery і інших задач, записки програміста

Багато хто чув, що є така штука під назвою Consul, створена в HashiCorp, компанії, яка подарувала світові Vagrant. Packer і цілий ряд інших чудових речей. Хтось навіть знає, що Consul призначений для service discovery, як і, наприклад, etcd або ZooKeeper. Але далеко не всім відомо, що крім service discovery також Consul має безліч інших можливостей, наприклад, вбудований моніторинг сервісів, розподілені локи, і інші. У цій замітці ми познайомимося з Consul, а також навчимося користуватися хоч і не всім, але істотною частиною його функціоналу.

Коротко про головне

Коли говорять «service discovery», мають на увазі, що є якась система, яка знає, де який сервіс знаходиться. Наприклад, ви використовуєте PostgreSQL. У вас є кілька Шардена бази даних, у кожного Шарда є майстер і безліч реплік. Коли падає майстер, вручну або автоматично проводиться фейловер, в результаті якого одна з колишніх реплік стає майстром. Іншими словами, роль майстра є перехідною. Крім того, число реплік не обов'язково є постійним. Так ось, service discovery - це така штука, яка може відповісти вашому додатку на питання «а хто зараз є майстром у Шарда N?» Або «дай мені список всіх реплік Шарда N».

Звичайно, можна тупо зберігати всю цю інформацію в конфігах. Але в разі, коли серверів і додатків на них багато, варіант з service discovery стає зручніше. Як мінімум, тому що всі дані лежать в одному місці, і тому не роз'їжджаються, а також тому що конфігурація оновлюється сильно швидше, ніж при заливці конфігов на все сервера по scp. Також при використанні кластерних платформ на зразок Kubernetes, Mesos або Nomad ви не знаєте заздалегідь, на яких саме машинах які сервіси піднімаються і скільки їх зараз, плюс вони можуть часто переїжджати з однієї машини на іншу. А значить, ви ніколи їх не знайдете, не маючи під рукою рішення на кшталт Consul.

Саме Consul для вирішення завдання service discovery пропонує REST API. Також він має вбудований DNS сервер, що дозволяє використовувати його навіть в додатках, які нічого не знають ні про яке Consul. При цьому Consul вміє масштабироваться на кілька датацентрів і на відміну, скажімо, від etcd, працює не тільки на Linux і MacOS, але також і на FreeBSD, і навіть на Windows. Ще вселяє довіру, що HashiCorp тестує Consul за допомогою Jepsen.

У Consul інформація про існуючі сервісах називається каталогом. Каталог зберігається на декількох серверах Consul, які спілкуються між собою за допомогою протоколу Raft. Крім серверів також існують агенти. Агенти встановлюються на всіх машинах, де крутяться сервіси, і повідомляють серверам Consul'а поточний стан як машини, так і сервісів на ній, використовуючи протокол gossip. Таким чином, якщо машина падає, або одному з сервісів на ній стає погано, сервера Consul'а дізнаються про це, і інформація про відповідні сервісах перестає віддаватися клієнтам. Самі сервера також можуть бути використані в якості агентів.

Крім service discovery і моніторингу в Consul також є багато іншого корисного функціоналу. З деяким ми ще встигнемо познайомимося далі.

Установка і настройка Consul

Всі описані дії були перевірені мною на Ubuntu 14.04 LTS, запущеної в трьох LXC контейнерах. Швидше за все, на інших дистрибутивах Linux і версіях Ubuntu ситуація буде мало чим відрізнятися. Далі передбачається, що всі машини знаходяться в мережі 10.0.3 / 24.

Consul написаний на мові Go і поширюється у вигляді єдиного бінарники (і вихідних кодів. Само собою зрозуміло). На жаль, компанія HashiCorp не надає готових пакетів для Debian / Ubuntu або CentOS / RHEL. Тут добра людина створив репозиторій, використовуючи який, можна самостійно зібрати deb-пакет. Також вдалося знайти PPA. хоч і не з найновішою версією Consul. Однак ніщо не заважає поставити Consul, використовуючи цей PPA, а потім при необхідності підмінити бінарник.

sudo apt-get update
sudo apt-get install software-properties-common
sudo apt-add-repository ppa: bcandrea / consul
sudo apt-get update
sudo apt-get install consul consul-web-ui dnsutils curl jq

Якщо хочемо самий свіжак, також виконуємо команди на зразок таких:

sudo apt-get install unzip
wget https: // releases.hashicorp.com / path / to / consul.zip
unzip consul_VER_linux_amd64.zip
sudo service consul stop
sudo mv / usr / bin / consul / usr / bin / consul.backup
sudo mv consul / usr / bin / consul

Правимо файл /etc/consul.d/20-agent.json якось так:

"Server": true,
"Datacenter": "dc1",
"Bootstrap_expect": 3,
"Data_dir": "/ opt / consul",
"Log_level": "INFO"
>

... роблячи тим самому Consul сервером, а не агентом.

sudo service consul restart

На інших нодах робимо так само. Потім об'єднуємо їх до властер:

consul join 10.0.3.223 10.0.3.224

curl -s http: // 10.0.3.224 8500 / v1 / catalog / service / postgresql-replica \
| jq.

[
"ModifyIndex": 804,
"CreateIndex": 802,
"Node": "postgresql-slave",
"Address": "10.0.3.223",
"ServiceID": "postgresql-replica-1",
"ServiceName": "postgresql-replica",
"ServiceTags": [
"Postgresql"
],
"ServiceAddress": "10.0.3.223",
"ServicePort": 5432,
"ServiceEnableTagOverride": false
>,
"ModifyIndex": 842,
"CreateIndex": 841,
"Node": "postgresql-slave-2",
"Address": "10.0.3.224",
"ServiceID": "postgresql-replica-2",
"ServiceName": "postgresql-replica",
"ServiceTags": [
"Postgresql"
],
"ServiceAddress": "10.0.3.224",
"ServicePort": 5432,
"ServiceEnableTagOverride": false
>
]

Сказати по правді, не до кінця розумію, чому потрібні два поля - Address і ServiceAddress. Підозрюю, це на випадок, якщо ми хочемо моніторити нашим агентами сторонні сервіси.

Список сервісів на заданій машині:

curl -s http: // 10.0.3.224 8500 / v1 / catalog / node / postgresql-slave | jq.

"Services": "postgresql-replica-1": "ModifyIndex": 804,
"CreateIndex": 802,
"EnableTagOverride": false,
"Port": 5432,
"Address": "10.0.3.223",
"Tags": [
"Postgresql"
],
"Service": "postgresql-replica",
"ID": "postgresql-replica-1"
>,
"Consul": "ModifyIndex": 72,
"CreateIndex": 4,
"EnableTagOverride": false,
"Port": 8300,
"Address": "",
"Tags": [],
"Service": "consul",
"ID": "consul"
>
>,
"Node": "ModifyIndex": 804,
"CreateIndex": 4,
"Address": "10.0.3.223",
"Node": "postgresql-slave"
>
>

Видалення сервісу з агента:

curl http: // localhost 8500 / v1 / agent / service / deregister / postgresql-master

Перевірити, чи живий сервіс, можна так:

curl -s http: // localhost 8500 / v1 / health / service / postgresql-replica | jq.

Відповідь в цьому випадку приходить досить великий, тому тут він не наводиться.

Service discovery за допомогою DNS

dig @ 127.0.0.1 -p 8600 postgresql-replica.service.consul

;; QUESTION SECTION:
; Postgresql-replica.service.consul. IN A

;; ANSWER SECTION:
postgresql-replica.service.consul. 0 IN A 10.0.3.223
postgresql-replica.service.consul. 0 IN A 10.0.3.224

Можна запросити SRV запис, щоб у відповіді були ще й номери портів:

dig srv @ 127.0.0.1 -p 8600 postgresql-replica.service.consul

Відповідь в цьому випадку:

;; QUESTION SECTION:
; Postgresql-replica.service.consul. IN SRV

;; ANSWER SECTION:
postgresql-replica.service.consul. 0 IN SRV 1 + 1 5432
postgresql-slave-2.node.dc1.consul.
postgresql-replica.service.consul. 0 IN SRV 1 + 1 5432
postgresql-slave.node.dc1.consul.
;; ADDITIONAL SECTION:
postgresql-slave-2.node.dc1.consul. 0 IN A 10.0.3.224
postgresql-slave.node.dc1.consul. 0 IN A 10.0.3.223

Нуль в обох ответах- це TTL, тобто DNS-відповідь не повинен кешуватися.

Ви могли помітити, що вище при отриманні інформації про сервіси Consul не сказав нам, чи працюють взагалі зараз ці сервіси, чи ні.

Отримати інформацію про стан сервісу і його хоста можна через таку ручку:

curl -s http: // 10.0.3.224 8500 / v1 / health / service / postgresql-master | jq.

[
"Checks": [
"ModifyIndex": 13148,
"CreateIndex": 13148,
"Node": "postgresql-master",
"CheckID": "serfHealth",
"Name": "Serf Health Status",
"Status": "passing",
"Notes": "",
"Output": "Agent alive and reachable",
"ServiceID": "",
"ServiceName": ""
>,
"ModifyIndex": 13155,
"CreateIndex": 13150,
"Node": "postgresql-master",
"CheckID": "service: postgresql-master",
"Name": "Service 'postgresql-master' check",
"Status": "critical",
"Notes": "",
"Output": "9.5 / main (port 5432): down \ n",
"ServiceID": "postgresql-master",
"ServiceName": "postgresql-master"
>
],
"Service": "ModifyIndex": 13155,
"CreateIndex": 13150,
"EnableTagOverride": false,
"Port": 5432,
"Address": "10.0.3.245",
"Tags": [
"Postgresql"
],
"Service": "postgresql-master",
"ID": "postgresql-master"
>,
"Node": "ModifyIndex": 13155,
"CreateIndex": 13148,
"Address": "10.0.3.245",
"Node": "postgresql-master"
>
>
]

В даному прикладі сервіс лежить.

А так можна дізнатися стан хоста і сервісів на ньому:

curl -s http: // 10.0.3.224 8500 / v1 / health / node / postgresql-slave- 2 | jq.

[
"ModifyIndex": 12827,
"CreateIndex": 12827,
"Node": "postgresql-slave-2",
"CheckID": "serfHealth",
"Name": "Serf Health Status",
"Status": "passing",
"Notes": "",
"Output": "Agent alive and reachable",
"ServiceID": "",
"ServiceName": ""
>,
"ModifyIndex": 13147,
"CreateIndex": 12832,
"Node": "postgresql-slave-2",
"CheckID": "service: postgresql-replica-2",
"Name": "Service 'postgresql-replica' check",
"Status": "passing",
"Notes": "",
"Output": "9.5 / main (port 5432): online, recovery \ n",
"ServiceID": "postgresql-replica-2",
"ServiceName": "postgresql-replica"
>
]

Спробуйте повпускали сервіси або хости і подивитися, як відповідні хелсчекі змінюють свій стан з passing на critical. У веб-панелі при цьому хости і сервіси змінюють свій колір з зеленого на жовтогарячий. Цікаво, що коли сервіс лежить, він все так же продовжує віддаватися при запиті каталогу через REST API, хоча з DNS-відповіді сервіс випилюється. Це поведінку можна виправити, додавши в запит аргумент? Passing.

Інформація про кластер і leader election

Consul дозволяє подивитися, хто зараз є в кластері:

curl -s http: // localhost 8500 / v1 / status / peers | jq.

А так можна отримати дуже розгонисту інформацію про агента, що містить, крім іншого, IP поточної машини:

curl -s http: // localhost 8500 / v1 / agent / self | jq '.Member.Addr'

Виникає закономірне бажання спробувати отримати leader election за допомогою цього API для свого застосування в 20 рядків коду на Python. Але це навряд чи вдала ідея. По-перше, тому що легко наплодити гонок і отримати в один момент часу двох лідерів. Вирішувати цю проблему потрібно шляхом очікування ACK від мажоріті кластера, до того ж в ACK слід включати номер поточного term'а в Raft'е чи іншої лічильник. По-друге, по-хорошому в цьому випадку необхідно при виконанні будь-якої дії перевіряти, чи є ми як і раніше лідером.

Як вірно повідомив мені @ sum3rman. куди простіше використовувати інший підхід, імовірно званий leader lease. Раз у нас вже є KV-сховище з підтримкою CAS, ми можемо просто писати в нього, що машина така-то є лідером до закінчення такого-то часу. Поки лідер жива й здорова, він може періодично продовжувати цей час. Якщо лідер помре, його швидко хтось підмінить. У такому варіанті досить синхронізувати на машинах час за допомогою ntpd і при виконанні лідером будь-якої дії перевіряти, що у нього в запасі достатньо часу, щоб завершити цю дію.

Ще один цікавий варіант leader election поверх Consul описаний в самій документації Consul'а. Він покладається на вже знайомий нам механізм хелсчеков, а також сесії. Сесії по суті представляють собою розподілені локи, автоматично звільняються з TTL або при падінні сервісу. В рамках цієї замітки сесії ми не розглядаємо. Але можливість ця вельми цікава, і я всіляко рекомендую з нею познайомитися.

Зауваження про безпеку

Візьміть до уваги, що за замовчуванням в Consul не використовується будь-яке шифрування трафіку. Також, отримавши доступ лише до однієї машині вашого кластера, можна досить просто влаштувати DoS всього програми.

Тут, правда, слід зробити ряд застережень. По-перше, все залежить від того, як ви використовуєте Consul. Наприклад, якщо він використовується тільки як KV сховище, доступ до нього дуже легко обмежити за допомогою Nginx і iptables. По-друге, має право на життя точка зору, що отримавши доступ до одного з фронтендів, зловмисник швидше за все отримає доступ і до БД, а значить зможе влаштувати DoS, просто записавши в цю БД дурницю. Тому на цьому етапі захищатися вже пізно, так навіщо створювати собі зайві незручності?

У будь-якому випадку, всі ці питання виходять за рамки цього поста, але в ув'язненні ви знайдете пару посилань по темі.

висновок

А чи використовуєте ви Consul і якщо так, то як враження?

Доповнення: Реальний приклад використання Consul ви можете знайти в замітці Stolon: створюємо кластер PostgreSQL з автофейловером.