Infra2: Consul Registrator

Пришло время рассказать про замечательный инструмент Consul Registrator. Давно его используем и очень довольны - никогда нас не подводил. Более того, в одном из проектов нам пришлось использовать не его, а упрощенный аналог 🚲 - и это оказалось очень легко реализовать. Но обо всём по порядку.


Infra1: Service Discovery, Cloud Native Proxy и деплой - первая статья цикла про различные современные штуки, которые мы используем для запуска и масштабирования приложений.

Infra2: Consul Registrator - вторая статья про замечательный инструмент.

Infra3: Мониторинг, Event Logging и сбор логов - третья статья про мониторинг.

Infra4: ...дальше видно будет...

Прелюдия

В прошлой статье мы рассказали как устроена наша внутренняя инфраструктура и общие подходы в наших продуктах. Остановились на том, как собственно происходит склейка Docker ->Consul ->Traefik после деплоя.

Docker -> Consul -> Traefik

Deploy

Как и полагается для хорошего 12 factor application наши приложения не знают ничего об инфраструктуре в которой они будут запускаться и работать. Их можно запустить в виде простого docker-контейнера, а можно с помощью оркестрации в многонодовом окружении, с масштабированием, избыточностью и прочими современными штучками. И конечно нам (как разработчикам, так и DevOps-направлению) очень не хочется настраивать вручную доменные имена, сертификаты, хелсчеки, мониторинг и прочую обвязку, которой обрастает каждый проект в продуктовом контуре. Вот что приходится настраивать:

  • Доменные имена - то, как пользователи видят ваши приложения в этих самых интернетах
  • Закрытые авторизацией внутренние и служебные адреса - метрики, админки...
  • Service Discovery для разных компонентов системы
  • Service Discovery для мониторинга (Prometheus)
  • Хелсчеки - как liveness так и readiness для Zero downtime deployment и прочих green/blue деплоев

Вот для этого всего нам и помогает Consul Registrator. При этом он просто запускается однажды на хосте и тихонько там работает и даже кушать не просит.

Consul Registrator

Registrator automatically registers and deregisters services for any Docker container by inspecting containers as they come online. Registrator supports pluggable service registries, which currently includes Consul, etcd and SkyDNS 2.

Основным понятием для данного инструмента является понятиеService - это некоторая сущность, которая "слушает порт". Если сущность "слушает порт", то к ней можно обратиться извне. Если приложение/контейнер слушает 2 порта, то это 2 (два) сервиса и так далее. В жизни бывают ситуации, когда ваши сервисы не слушаю порт - например, они сами стучатся к брокеру RabbitMQ и работают по постоянно открытому каналу, но о них чуть позже.

Service, во вселенной регистратора, обладает стандартными атрибутами - уникальным идентификатором, именем, IP-адресом, портом и метаданными (Tags иAttrs). И регистратор умеет создавать (и удалять, что немаловажно) эти самые сервисы в нескольких бэкендах:

  • Consul - тут всё просто. Consul сам имеет first-class citizen под названием Service, который проживает в Service Catalog и прозрачно понимает регистратора. Тут есть поддержка и метаданных и хелсчеков.
  • Consul KV - тут вместо каталога используется key-value store и вся информация о сервисе складывается там. Весьма ограниченный функционал для специфического использования
  • Etcd - аналогично Consul KV, но есть поддержка TTL
  • SkyDNS 2 - тот же Etcd, но информация пишется в подходящем для SkyDNS 2 формате
  • Zookeeper - использует Zookeeper но складывает всю метаинформацию о сервисе в виде JSON

Исходя из этого списка мы выбрали Consul, как наиболее подходящий под наши требования.

Данные для описания сервиса регистратор берёт из меток (label) и переменных окружения (environment) запущенного контейнера, а события о контейнерах через docker events, для чего получает управляющий сокет докера при старте:

 docker run -d 
    --net=host \
    --volume=/var/run/docker.sock:/tmp/docker.sock \
    gliderlabs/registrator:latest consul://localhost:8500

Также он периодически сканирует уже запущенные контейнеры и обрабатывает случаи, когда они были запущены до самого регистратора.

Естественно, метки и переменные окружения являются "статикой" - то есть используются только те, которые были доступны на момент запуска контейнера. Когда нам понадобилось чуть больше динамики мы завели пулл-реквест, но он так и висит, а мы «пошли другим путём».

docker-compose.yml

Теперь приведём пример docker-compose.yml файла, настоящего, который используется в проде и посмотрим как там всё описывается:

Чуть конечно пришлось подправить

В результате мы видим, что у нас есть три контейнера - web,jobs, иrufus. Собственно web-сервер, демон для выполнения отложенных задач и демон для шедулинга повторяющихся, периодических задач.

web

Этот сервис должен обслуживать HTTP-запросы пользователей, поэтому мы говорим, что его должен обслуживать Traefik и при этом использовать TLS и доменное имя web.example.com. Кроме того, сюда должен наведываться Prometheus и бдительно смотреть за состоянием. Всё это конфигурируется через метку SERVICE_TAGS - именно на неё смотрит регистратор и всё, что в ней записано, крепит к записи в Consul:

labels успешно проброшены в сервис

Далее идёт настройка liveness хелсчеков: SERVICE_CHECK_* - их может быть больше, но в данном случае достаточно одного. Эти проверки выполняет локально запущенный Consul-агент и "красит" состояние сервиса в красный 🔴 или зелёный 🟢 в зависимости от результата. А Traefik наблюдает за сервисами и как только они "зеленеют" направляет трафик на них (или снимает если сервис "краснеет")

Далее мы говорим, что Docker-демон может использовать любой свободный порт на данной машине, а Consul Registrator уже после запуска узнает, что же именно было назначено, и пропишет соответствующий порт в объект сервиса в Consul:

Случайный порт назначен для сервиса и прописан в consul

После этого идёт настройка readiness проверок, которые выполняет уже сам Docker-демон, при запуске или обновлении контейнеров.

Вишенкой тут является монтирование tmpfs в /tmp для ускорения всяких действий, которые могут происходить внутри - масштабирование картинок и масса всего, что живет в /tmp

jobs

Если не собирать метрики с сервиса, выполняющего отложенные задачи, то для контейнера регистрация в Consul не требуется. Но ведь мы очень хотим мониторить его с помощью Prometheus и поэтому мы используем плагин к Delayed Job, встраиваемся в его lifecycle и выставляем наружу /metrics. Мы работаем аналогично данному гему, но используем свой велосипед. Поскольку сервис теперь имеет HTTP доступ, то его надо регистрировать в Consul, и прометей до него доберётся самостоятельно. Для Sidekiq в том же геме есть собственный плагин.

rufus

Rufus-scheduler (out of the box) is an in-process, in-memory scheduler. It uses threads.
It does not persist your schedules. When the process is gone and the scheduler instance with it, the schedules are gone.

Этот сервис работает аналогично предыдущему и выполняет функцииCron, то есть обрабатывает постановку регулярных задач. Поскольку мы избегаем выполнять действия непосредственно в нём, то его мониторинг особо не нужен. Но чего не сделаешь ради унификации.

Бонус

В качестве ещё одного положительного качества данного инструмента хочу отметить его простоту. Однажды нам потребовалось добавить немного динамики в Consul Registrator - некоторые данные сервиса мы могли вычислить только после запуска и для реализации это функционала сделали пулл-реквест. Однако на гитхабе жизнь замерла и мы решили полностью реализовать свой сервис, аналогичный Consul Registrator, но с очень ограниченным и специфичным функционалом 🚲. Мы с чистой совестью подсмотрели реализацию в регистраторе и запилили свой сервис примерно за 2 дня. Это показательный момент. Если по какой-то причине вам не подойдет текущий функционал, или там появится баг, мешающий вашему проду, то вы без особых проблем сможете решить проблему собственными силами 💪.