В первой части мы подробно рассмотрели какие проблемы и задачи ставит перед нами распределенная архитектура приложения. Мы определили какие инструменты мы можем использовать для решения этих проблем и отметили важность реализации discovery на начальном этапе проекта. А также, выбрали Consul основным приложением на базе которого мы будем рассматривать реализацию discovery-сервиса.
В заключительной части мы рассмотрим как Consul работает с протоколом DNS, разберем основные запросы к HTTP API, посмотрим какие виды Health Checks мы можем использовать и, конечно, разберем для чего нужен K/V storage. И что самое важное, ближе познакомимся с некоторыми особенностями на практике.
Интерфейс DNS
Consul может отвечать на запросы по протоколу DNS, при этом можно использовать любой DNS-клиент для запросов. DNS-интерфейс доступен для компонентов на локальном хосте на порту 8600. Помимо прямых запросов к Consul, можно прописать его как resolver в системе и прозрачно использовать его для разрешения имен, проксируя все внешние запросы к вышестоящему “полноценному” DNS-серверу и разрешая запросы в приватной зоне .consul самостоятельно.
Для реализации примитивной DNS-балансировки в случае присутствия в каталоге нескольких сервисов с одинаковым именем и разными IP-адресами Consul случайно перемешивает IP-адреса в ответе.
Помимо прямого запроса на разрешение доменного имени в рамках кластера можно выполнять поиск (lookup). Поиск может быть выполнен как для сервиса (service lookup), так и для узла кластера (node lookup).
Формат доменного имени при DNS-запросе в рамках consul-кластера жестко определен и не подлежит изменению.
Узел кластера
Это обычный DNS-запрос, который вернет IP-адрес узла кластера по его имени (имя узла задается при старте агента при помощи параметра — node). Рассмотрим формат имени узла для DNS-запроса:
[node].node[.datacenter].[domain]
- [node] — обязательная часть, имя узла;
- .node — указатель на то, что мы выполняем node lookup;
- [.datacenter] — необязательная часть, имя датацентра (consul “из коробки” может предоставлять discovery для нескольких датацентров в рамках одного кластера. По умолчанию используется имя dc1. Если имя датацентра не указано, то будет использован текущий ДЦ. То есть тот, в рамках которого запущен агент, к которому выполняется запрос);
- .[domain] — обязательная часть, приватный домен Consul первого уровня. Имеет значение
.consul
по умолчанию.
Таким образом, доменное имя для узла с именем, например, nodeservice, будет выглядеть так:
nodeservice.node.consul.
Как мы видим название датацентра пропущенно, но имя можно построить еще и так:
nodeservice.node.dc1.consul.
Несколько узлов с одинаковым именем в рамках одного ДЦ не допускаются.
Сервис
Запрос на поиск сервиса по имени выполняется на всех узлах кластера. В отличии от запроса на разрешение имени узла, запрос на поиск сервиса предоставляет больше возможностей. Кроме, собственно, IP-адреса сервиса (то есть А-записи) вы можете выполнить запрос на получение SRV-записи и узнать порты, на которых запущен сервис.
Вот так выглядит обычный запрос на поиск всех узлов, на которых запущен сервис с именем rls
:
root@511cdc9dd19b:~# dig @127.0.0.1 -p 8600 rls.service.consul.
; <<>> DiG 9.9.5-3ubuntu0.7-Ubuntu <<>> @127.0.0.1 -p 8600 rls.service.consul.
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26143
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;rls.service.consul. IN A
;; ANSWER SECTION:
rls.service.consul. 0 IN A 172.17.0.2
rls.service.consul. 0 IN A 172.17.0.3
;; Query time: 4 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Thu Feb 18 07:23:00 UTC 2016
;; MSG SIZE rcvd: 104
Из этого ответа можно увидеть, что в составе кластера присутствуют два узла, на которых запущен сервис с именем rls
и то, что DNS-интерфейс Consul вернул IP-адреса всех узлов. Если мы повторим запрос несколько раз, то увидим, что записи периодически меняются местами, то есть первое место не закреплено за первым найденным сервисом. Это и есть пример простой DNS-балансировки, о которой мы говорили выше.
Если мы запросим SRV-запись, то ответ будет таким:
root@511cdc9dd19b:/# dig @127.0.0.1 -p 8600 rls.service.consul. SRV
; <<>> DiG 9.9.5-3ubuntu0.7-Ubuntu <<>> @127.0.0.1 -p 8600 rls.service.consul. SRV
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 8371
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 2
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;rls.service.consul. IN SRV
;; ANSWER SECTION:
rls.service.consul. 0 IN SRV 1 1 80 agent-two.node.dc1.consul.
rls.service.consul. 0 IN SRV 1 1 80 agent-one.node.dc1.consul.
;; ADDITIONAL SECTION:
agent-two.node.dc1.consul. 0 IN A 172.17.0.3
agent-one.node.dc1.consul. 0 IN A 172.17.0.2
;; Query time: 5 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Thu Feb 18 07:39:22 UTC 2016
;; MSG SIZE rcvd: 244
В ANSWER SECTION
перечислены доменные имена узлов в формате, требуемом Consul (обратите внимание, узлов, но не сервисов!) и порты, на которых запущен запрашиваемый сервис. IP-адреса узлов (и, соответственно, сервисов) перечислены в ADDITIONAL SECTION
ответа.
Формат имени сервиса для DNS-запроса выглядит так:
[tag.][service].service[.datacenter].[domain]
- [tag.] — необязательная часть. Используется для фильтрации сервиса по тегам. Если у нас есть сервисы с одинаковым именем, но разными тегами, то добавление названия тега поможет отфильтровать выдачу;
- [service] — обязательная часть, имя сервиса;
- .service — указатель на то, что мы выполняем service lookup;
- [.datacenter] — необязательная часть, имя датацентра;
- .[domain] — обязательная часть, приватный домен Consul первого уровня.
Таким образом, сервис с именем nginx и имеющий tag названный web, можно представить доменом:
web.nginx.service.consul
SRV-запросы на поиск сервисов в соответствии с RFC-2782
Помимо “обычного” построения доменного имени мы можем построить его по более строгим правилам RFC-2782 для выполнения запроса на получение SRV-записи. Формат имени выглядит так:
_service._tag.service[.datacenter].[domain]
Название сервиса и tag имеют underscore (_) в виде префикса. (В оригинальном RFC вместо tag должно стоять название протокола, это сделано для предотвращения коллизий при запросе).
В случае использования имени в формате RFC-2782, сервис с именем nginx и имеющий tag названный web, будет выглядеть так:
_web._nginx.service.consul
Ответ будет точно таким же, как и в случае “простого” запроса:
root@511cdc9dd19b:/# dig @127.0.0.1 -p 8600 _rls._rails.service.consul. SRV
; <<>> DiG 9.9.5-3ubuntu0.7-Ubuntu <<>> @127.0.0.1 -p 8600 _rls._rails.service.consul. SRV
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 26932
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 2
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;_rls._rails.service.consul. IN SRV
;; ANSWER SECTION:
_rls._rails.service.consul. 0 IN SRV 1 1 80 agent-one.node.dc1.consul.
_rls._rails.service.consul. 0 IN SRV 1 1 80 agent-two.node.dc1.consul.
;; ADDITIONAL SECTION:
agent-one.node.dc1.consul. 0 IN A 172.17.0.2
agent-two.node.dc1.consul. 0 IN A 172.17.0.3
;; Query time: 6 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Thu Feb 18 07:52:59 UTC 2016
;; MSG SIZE rcvd: 268
По умолчанию, все доменные имена в рамках Consul имеют TTL = 0, то есть совсем не кешируются. Нужно иметь это в виду.
HTTP API
HTTP REST API является основным средством управления кластером Сonsul и предоставляет очень широкий диапазон возможностей. В рамках API реализовано 10 endpoints, каждый из которых предоставляет доступ к конфигурации определенного функционального аспекта Consul. Подробное описание всех edpoints есть в документации Consul, а мы кратко опишем каждый из них для представления о возможностях API:
- acl — контроль доступа;
- agent — управление агентом Consul;
- catalog — управление узлами и сервисами кластера;
- coordinate — сетевые координаты;
- event — пользовательские события;
- health — проверки доступности;
- kv — Key/Value хранилище;
- query — подготовленные запросы;
- session — сессии;
- status — статус системы.
acl
Как можно понять из названия, acl управляет контролем доступа к сервисам Consul. Мы можем регулировать доступ на получение и изменение данных о сервисах, узлах, пользовательских событиях, а также управлять доступом к k/v-хранилищу.
agent
Управление локальным агентом Consul. Все операции, доступные на этом endpoint, затрагивают данные локального агента. Можно получить информацию о текущем состоянии агента, его роли в кластере, а также получить доступ к управлению локальными сервисами. Изменения, выполненные над локальными сервисами, будут синхронизированы со всеми узлами кластера.
catalog
Управление глобальным реестром Consul. Здесь сосредоточена работа с узлами и сервисами. В рамках этого endpoint можно регистрировать и отключать сервисы и, в случае работы с сервисами, использование этого раздела более предпочтительно нежели работа через agent
. Работа через catalog
проще, понятнее и способствует анти-энтропии.
coordinate
Consul использует сетевую томографию для вычисления сетевых координат. Эти координаты используются для построения эффективных маршрутов в рамках кластера и многих полезных функций, таких как, например, поиск ближайшего узла с заданным сервисом или переключение на ближайший датацентр в случае аварии. Функции API в этом разделе используются только для получения информации о текущем состоянии сетевых координат.
event
Обработка пользовательских событий. Пользовательские события используются для выполнения каких-либо действий в рамках кластера: например для автоматического деплоя, перезапуска сервисов, запуска определенных скриптов или иных действий в рамках процесса оркестрации.
health
Проверка текущего состояния узлов и сервисов. Данный endpoint используется только для чтения и возвращает текущее состояние узлов и сервисов, а также списки выполняемых проверок.
kv
Этот endpoint имеет только один метод и используется для управления данными в распределенном key/value-хранилище, предоставленным Consul. Единственный метод в этом endpoint выглядит так:
/v1/kv/[key]
Разница в обработке заключается в методе запроса. GET вернет значение по ключу, PUT сохранит новое значение или перезапишет старое, а DELETE удалит запись.
query
Управление подготовленными запросами (Prepared queries). Подобные запросы позволяют выполнять сложные манипуляции над конфигурацией Consul и могут быть сохранены и выполнены позже. Сохраненным запросам присваивается уникальный ID. C его помощью запрос может быть выполнен в любое время без необходимости повторной подготовки.
session
Механизм сессий в Consul используется для построения распределенных блокировок. Сессии представляют собой связующий слой между узлами, выполняемыми проверками и k/v-хранилищем. У каждой сессии есть имя и оно может быть сохранено в хранилище. Имя используется для реализации блокировок в рамках последовательных действий с узлами и сервисами в конкурентном режиме. Механизм работы сессий описан в документации Consul.
status
Этот endpoint используется для получении информации о статусе кластера. Здесь можно узнать текущего лидера и получить информацию обо всех участниках кластера.
Health Checks
Ранее мы говорили про равномерное распределение нагрузки с помощью DNS, а теперь рассмотрим механизм проверки состояния узлов и сервисов. Health check — это периодически выполняемая операция, по результатам которой можно определить состояние проверяемой системы. По факту это автоматический мониторинг, который поддерживает состояние кластера в работоспособном состоянии, вычищает неработающие узлы и сервисы и возвращает их в работу по факту восстановления работоспособности. Consul поддерживает несколько видов проверок:
- Script check — запуск определенного скрипта на определенном узле с заданной периодичностью. В зависимости от кода выхода (любой отличный от нуля код будет означать, что проверка не прошла) включает или выключает узел или сервис.
- HTTP Check — проверка, которая пытается загрузить указанный URL, и в зависимости от кода ответа включает или выключает проверяемый объект (любые 2xx — все нормально, код 429 Too Many Requests генерирует предупреждение, остальные коды говорят об ошибке).
- TCP Check — проверка, которая пробует установить tcp-соединение с заданным интервалом к указанному адресу и порту. Невозможность установить соединение означает, что проверка не пройдена.
- TTL Check — проверка, которая должна периодически обновляться через HTTP API. Суть ее в том, что если некий сервис не обновил эту проверку в рамках определенного интервала, то он помечается как неработающий. Это пассивная проверка, то есть сервис сам должен периодически отчитываться о том, что он работает. Если за заданный интервал отчет не поступил, то проверка считается не пройденной.
- Docker Check — проверка для сервисов, работающих в docker-контейнерах. Consul, используя Docker Exec API, может выполнить скрипт, находящийся внутри контейнера. Результат проверки будет зависеть от кода выхода, любой отличный от нуля “провалит” проверку.
K/V storage
Хранилище, предоставляемое Consul является распределенной key-value базой данных и может использоваться для сохранения любых данных, доступных для любого участника кластера (в соответствии с правилами ACL, конечно же). Сервисы могут сохранять в этом хранилище данные, которые необходимы для других участников кластера. Это могут быть значения конфигурационных опций, результаты каких-либо вычислений или, как мы указали выше, k/v-хранилище может использоваться для реализации распределенных блокировок при помощи механизма сессий. Использование k/v-хранилища позволит нам сделать кластер более эффективным и уменьшить процент ручного вмешательства. Сервисы могут корректировать свое состояние в зависимости от информации в хранилище, гарантированно предоставленным кластером. Обратите внимание: не стоит сохранять в это хранилище какие-либо данные, связанные с бизнес-логикой ваших сервисов. Хранилище, предоставляемое Consul, используется для хранения и распространения метаинформации о состоянии участников кластера, а не о данных, которые они обрабатывают.
Заключение
Трудно переоценить роль discovery-сервиса в процессе построения распределенной архитектуры на больших проектах. Consul отлично подходит на эту роль. Продукт развивается и не стоит на месте, реализовано много полезного функционала, необходимого для безболезненного обслуживания системы с большим количеством компонентов. Кроме того, Consul написан на Go и распространяется в виде одного исполняемого файла, что делает процесс его обновления и поддержки очень удобным.
Автор: LogPacker