Конфигурация сервисов c Consul Key-Value Store

Как люди обычно конфигурируют приложения? Некоторые, например, передают аргументы через командную строку. Ещё можно задать переменные окружения или просто указать на файл с настройками. Да чего уж там, пока никто не видит, опцию-другую можно и прямо в код прошить.

Но как быть с такой ситуацией. Есть у нас распределённое приложение: куча хостов, сервисы создаются, приходят в онлайн, уходят, и все с разными версиями и непонятными адресами. И вдруг нам нужно их всех переконфигурировать. Ничего такая задачка? С монолитным приложением можно было бы просто зайти на хост и поколдовать там. А как быть с распределённым?

Раздаём конфигурацию с Consul

Consul — это такой масштабируемый и высоко-надёжный инструмент, который делает жизнь разработчика распределённых приложений легче по многим фронтам. Он поможет сервисам найти друг друга, проверит, отзываются ли они на запросы, поработает DNS сервисом и даже предоставит хранилище типа ключ-значение всем желающим. А такое хранилище — очень удобное место для хранения разного рода настроек. К тому же, в комплекте с Consul можно получить набор утилит, которые умеют синхронизировать это хранилище и с локальными конфигурационными файлами, и с переменными окружения. На лету и без SMS. То есть мы можем привязать конфигурацию отдельных сервисов к Consul-агенту, и раздавать им настройки из общего хранилища.

Установка

Consul работает на всех основных платформах и скачивается в виде архива из одного-единственного экзешника. Даже устанавливать ничего не надо. Образ для Docker тоже имеется. После скачивания и распаковки можно запустить его с такими параметрами:

Это запустит агент Consul на локальной машие в режиме сервера. -dev флаг укажет ему работать в тестовом режиме, в котором Consul не оставит после себя никаких следов.

У Consul-агента есть и вэб-UI, который можно найти на порту 8500:

Consul UI

Хранилище ключ-значение

Как я уже упомянул, одна из фич консула — это хранилище вида ключ-значение (key-value store). Ключи — обычные строки — можно упорядочить в иерархию, как в файловой системе, просто разделяя компоненты пути косой чертой. Например, db/config/max-connections:

consul key-value

Манипуляция данными

Создавать, читать и удалять данные можно как через вэб-UI, так и через RESTful API, утилиту consul kv, или многочисленные API клиенты.

Чтобы прочитать одно значение через HTTP, нужно сделать обычный GET запрос по адресу /v1/kv + ключ:

В ответном JSON значение будет закодировано в Base64, но оно точно такое же, что и в UI. Я проверял.

Простым GET запросом можно получить вообще всё:

Для создания новой записи нужен уже PUT запрос:

Команда выше создаст пару ключ-значение, в которой ключ — web/config/experimental, а значение — enabled.

Consul: добавить ключ

PUT запрос отвечает и за редактирование. Предсказуемо, удалить что-нибудь можно через HTTP DELETE.

Если к HTTP душа не лежит, можно манипулировать значениями через consul kv. Там и синтаксис лаконичнее, и Base64 нет:

Long polling для получения обновлений

Допустим, решили мы сделать приложение, которые получает свои настройки через HTTP из Consul. Если настройки могут меняться, а приложение способно применить их без перезагрузки, то стоит делать такие запросы регулярно, чтобы не пропустить обновления. Но насколько регулярно их стоит делать? Всё-таки выбор правильного интервала — та ещё задача. Слишком частые запросы могут отправить сервер в страну вечной охоты, особенно если клиентов много. Слишком редкие могут сильно опоздать с новостями.

Но есть и другой способ — long polling. Мы делаем серверу настроек запрос на обновлённое значение, а он ответит на него только тогда, когда значение действительно изменится.

Как это сделать. Отправим-ка ещё один запрос в Consul, но в этот раз посмотрим, какие HTTP заголовки придут нам в ответе.

Вместе со стандартными заголовками Consul прислал несколько собственных, в том числе и X-Consul-Index. Если значение этого заголовка указать в запросе (например GET ../config/max-connections?index=5007), то сервер ответит тогда, когда запрашиваемое значение изменится, либо произойдёт таймаут. По-умолчанию, это пять минут, но можно передать и своё значение через URL в wait= параметре. Максимальное значение таймаута — 10 минут.

С long polling можно свести количество запросов к минимуму, и при этом получать обновления практически в реальном времени.

Обновляем конфигурационные файлы напрямую из Consul

Допустим, у нас есть какой-нибудь config.json с такими настройками внутри:

Удивительное совпадение, но в Consul key-value хранилище были точно такие же ключи. Вот было бы здорово, если бы этот файл генерировался напрямую оттуда. Оказывается, это можно очень легко сделать при помощи consul-template.

consul-template — это ещё одна скачиваемая утилита от того же разработчика. Она умеет делать классную штуку — получать на вход шаблон файла настроек и выдавать на выходе его заполненную версию. К тому же её можно запустить в цикле, и тогда она будет тихо ждать где-нибудь в фоне, и как только в KV хранилище что-то изменится, тут же обновит файл настроек. Практически без задержки.

Простые подстановки

Возвращаясь назад к config.json файлу, создадим-ка шаблон для него, например, config.tpl, и положим в него следующее:

То есть это практически такой же config.json, но значения в нём заменены на плейсхолдеры.

Теперь, если натравить на него consul-template:

Получится актуальный файл настроек. Сильное колдунство: шаблонизатор подключился к локальному Consul-агенту, выяснил текущие значения настроек, создал новый файл, и завершился. Кстати, подключиться он мог и к удалённому Consul серверу.

Если убрать -once  параметр, то consul-template останется работать в фоне и поддерживать файл настроек в актуальном состоянии. Просто и при этом гениально. Можно запускать сервисы пачками, конфигурировать всех одновременно, и даже не знать, сколько их там всего.

Сложные подстановки

Но кроме подстановки значений consul-template может делать задачи и посложнее. Например, что если мы хотим создать файл настроек, в котором лежат все параметры, найденные в KV?

Можно использовать функцию ls, чтобы получить список всех ключей (например, внутри db/config/) и затем просто пропустить их через цикл:

Получится практически тот же самый config.json, но с лишней запятой на последней строке. Её можно было бы убрать использовав {{ if }} , но для JavaScript лишняя запятая не проблема, так что зачем усложнять пример.

Кроме того, что consul-template рендерит файлы настроек, его ещё можно попросить и запускать сервис, которому тот был предназначен. И не только запускать, но и отправлять сигнал на перезапуск, если настройки изменились. Сигнал, кстати, можно подстроить под конкретный сервис.

Итого

Конфигурация коллекции сервисов, которые живут где-то в облаке, — весьма нетривиальная задача. То есть задать начальную конфигурацию, конечно, не проблема, но изменить её на лету сразу в нескольких местах — та ещё задача. А Consul и consul-template могут её решить. Можно использовать Consul Key-Value хранилище как репозиторий настроек и научить сервисы их запрашивать. Или вообще можно попросить consul-template делать это за нас, и тогда всё облако можно будет централизованно реконфигурировать практически без задержки, не меняя при этом ни строчки существующего кода.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *