Привет, Хабр! Я Максим Чудинов, инженер команды мониторинга в Cloud.ru. Одно из направлений нашей работы — обеспечение сбора логов с элементов инфраструктуры компании и их длительного хранения. Логи у нас хранятся в кластерах — их много и становится всё больше. Запускать новые и расширять существующие нужно быстро. Поэтому я разработал решение по автоматизации развертывания стека OpenSearch на виртуальных машинах с Ubuntu 20.04. О нем и расскажу в статье.

Почему мы выбрали OpenSearch


Зачем понадобилась автоматизация и именно OpenSearch? В начале 2022 года в Cloud.ru было уже 18 кластеров Elasticsearch для хранения логов — об этом я подробно рассказывал на Big Monitoring Meetup 9. Все кластеры работали на устаревшей версии 7.9, а перейти на более свежую версию, по сумме обстоятельств, мы не могли. В качестве альтернативы выбрали OpenSearch 1.3, поскольку это полный open source и форк Elasticsearch 7.10 без серьезных отличий в работе. 

Чтобы развернуть 18 однотипных кластеров OpenSearch сейчас, N-е количество в будущем и поддерживать их дальше, появилась задача автоматизировать деплой. При разработке нужно было учесть одно важное условие — описание конфигурации всех кластеров должно было храниться в одном месте, чтобы можно было разворачивать, обновлять и вносить изменения коммитом в одном репозитории.

Как форк, OpenSearch повторяет стек Elastic: кластер хранилища, фронтенд, коллектор. Для прода производитель рекомендует конфигурацию кластера от трех нод.

Ноды — это хранилище данных и основа стека. Также частью стека являются фронтенд — для удобного поиска по индексам, и коллекторы — при необходимости предобработки и работы в распределенной инфраструктуре.

Кластер из трех нод с индексом, разбитым на шарды с одной репликой
Кластер из трех нод с индексом, разбитым на шарды с одной репликой

События из журналов систем и оборудования отправляются в кластер и складываются в индексы. Индексы имеют один основной шард от нуля до нескольких его реплик. Количество реплик зависит от количества нод. Кластер сам распределяет шарды таким образом, чтобы на одной ноде в один момент времени не находились основной шард и его реплика. За счет этого обеспечивается надежность хранения, а шардирование ускоряет выполнение поисковых запросов.

Описание решения

Ansible де-факто уже давно стандартный инструмент автоматизации в IT. Так что я взял его и написал две роли с плейбуком — для настройки нод кластера роль opensearch, для фронтенда роль opensearch_dashboards. Плейбук и описание кластеров сделал в отдельном репозитории.

Основой для применения ролей служит инвентарь. Как пример, в нем перечислены хосты для двух кластеров: 

Файл инвентаря со списком хостов
Файл инвентаря со списком хостов

Сами роли применяются к группам logsearch_node и logsearch_frontend, описанным в отдельном файле.

Файл инвентаря с группами хостов
Файл инвентаря с группами хостов

Группы mon_logsearch_os1_dev и mon_logsearch_os2_dev являются объединением хостов соответствующих кластеров. Для каждой группы в переменных group_vars задаются специфичные для кластера значения. Также группы можно использовать для работы с отдельным кластером при выполнении плейбука с ключом –limit. Группа logsearch_logstash заложена на перспективу использования инвентаря для развертывания коллектора logstash. Вы можете развернуть в своей среде желаемые кластеры, указав соответствующие имена и адреса хостов. 

Конфигурация, хотя и предполагается к применению в закрытом контуре, должна быть безопасной. Поэтому между элементами кластера устанавливаются SSL-соединения. Для них нужны предварительно сгенерированные сертификаты безопасности. О том, как самому сгенерировать сертификаты, подробно описано в документации Opensearch.

Сертификаты я вынес в групповые переменные и в будущем планирую перенести их в корпоративный Vault.

Сертификаты в файле с общими для плейбука переменными
Сертификаты в файле с общими для плейбука переменными

Для каждого кластера по умолчанию используются сертификаты из общих переменных. По желанию их можно переопределить в описании кластера в словаре clusters. Словарь содержит параметры всех разворачиваемых кластеров.

Файл со словарем описания всех кластеров
Файл со словарем описания всех кластеров

Кроме сертификатов для каждого кластера указываются дистрибутив, имя кластера, имя и пароль административной учетной записи, имя и пароль учетной записи для подключения фронтенда к кластеру и т. д. Пароли заданы в открытом виде для примера. В своей инфраструктуре я указываю пароли в зашифрованном с помощью ansible_vault виде. В планах заменить шифрование на хранение в корпоративном Vault, как и сертификаты.

Куски для конфигурационных файлов internal_users.yml и roles_mapping.yml описаны в виде plain text и используются в шаблонах «как есть». В internal_users указаны дефолтные пользователи из коробочной установки, их можно полностью заменить на свои предпочтения. К сожалению, прекрасный способ настройки пользователей через API появился только в последних релизах второй версии Opensearch. Из-за этого потребуется предварительно где-то выполнить установку пакета opensearch или вытащить из tar-архива opensearch скрипты plugins/opensearch-security/tools, чтобы по этой инструкции сгенерировать hash пароля для каждого пользователя.

А дальше магия Gitlab CI/CD ?. При merge в основную ветку запускается ansible docker container. В нём выкачиваются зависимости согласно requirements.yml и выполняется плейбук от имени пользователя из переменной ${SSH_USER} с ключом из переменной ${SSH_PRIVATE_KEY}. Переменные определяются в CI/CD Settings проекта.

Сначала применяется роль opensearch к хостам группы logsearch_node. Выполняется тюнинг системных параметров целевого хоста (согласно рекомендациям разработчика), установка и настройка opensearch на целевом хосте. 

Первым таском установки выполняется скачивание дистрибутива в директорию /tmp целевого хоста. Это действие пришлось добавить после ситуации, когда модуль apt не смог выполнить установку из указанного url по причине таймаута. Площадки размещения бывают разные и не всегда с хорошими каналами связи. Поэтому сначала скачивание с помощью get_url, а затем локальная установка. 

Затем создаются директории для индексов и снапшотов и из шаблонов копируются конфигурационные файлы. Файлы с демосертификатами удаляются и из шаблонов копируются сертификаты, заданные в переменных описания кластера. Размер java heap автоматически вычисляется как половина от фактической памяти хоста (но не более 30 ГБ) и записывается в jvm.options. Затем ждет 10 секунд для запуска сервиса и удаляет из конфигурационного файла строку с указанием хостов для выбора мастер-ноды. Этот перечень нужен только один раз для процесса cluster bootstrapping и в дальнейшей жизни кластера не пригодится. Еще при первом запуске кластер сам инициализирует внутренних пользователей и роли, используя конфигурацию плагина безопасности.

Вторым этапом применяется роль opensearch_dashboards к хостам группы logsearch_frontend. Аналогично предыдущей роли выполняется тюнинг системных параметров целевого хоста согласно рекомендациям разработчика, установка и настройка opensearch-dashboards на целевом хосте. Так же и по тем же причинам, что и в роли opensearch, первым таском установки выполняется скачивание дистрибутива в директорию /tmp целевого хоста и затем локальная установка. После из шаблонов копируются конфигурационные файлы и сертификаты.

В ролях я предусмотрел тестирование с помощью molecule. Пока ручное ?. При этом разворачиваются три ноды, кластер инициализируется дефолтными параметрами, проверяется возможность авторизации и возврат статуса. Аналогично для фронтенда: всё запускается в контейнерах, а если не выполнять шаг destroy, то можно получить полноценную инсталляцию, походить по интерфейсу и попробовать настройки.

Из-за отсутствия DNS в некоторых сегментах инфраструктуры в инвентаре мы используем параметр ansible_host с IP-адресом. С molecule в контейнерах такой возможности нет — адреса выдаются произвольно и сетевой доступ выполняется по именам. А мне для конфигурации каждой ноды нужно было собрать массив IP-адресов всех нод. 

Чтобы роли работали и в тесте, и на виртуальных машинах, я написал код с формированием списка хостов в зависимости от значения ansible_host. Нативными командами ansible реализовать не получилось, поэтому я написал код на jinja2.

Формирование списка хостов кластера
Формирование списка хостов кластера

Если все переменные заданы правильно, то плейбук выполняется без ошибок и мы получаем рабочий кластер. Или набор рабочих кластеров, описанных в словаре clusters

По факту Opensearch — это стабильный готовый продукт. Вывести его из строя может только падение операционной системы или несоблюдение рекомендаций разработчиков. Но эта стабильность не отменяет обязательного мониторинга. 

Мониторинг

Я определил несколько ключевых метрик (SLI) для верхнеуровневого мониторинга состояния кластера:

  • Health status — доступность кластера и его способность принимать новые данные;

  • Indexing rate — скорость поступления событий;

  • Indexing latency — задержка записи событий;

  • Storage utilization — величина использования хранилища кластера;

  • Product Index size — размер индексов каждого продукта источника;

  • Estimated time to 15% free space — прогнозное время до достижения 15% остатка хранилища.

Для сбора метрик я использовал Zabbix и коробочный шаблон Elasticsearch, немного доработанный для целей capaсity management. Метрики собираются штатным образом через Elastisearch API. Дополнительно выполняется поиск индексов и вычисление занимаемого места на диске (через группировку по алиасу) — Product Index size. Продукт включает в себя множество источников событий, и каждый в любой момент может значительно увеличить количество генерируемых событий. Например, корпоративный кластер Confluence, Gitlab или кластер K8s для облачных сервисов. 

Мы не применяем жестких ограничений и не фильтруем события — так можно потерять действительно важные данные. Что отправить в хранилище кластера — решает команда каждого продукта. Обычно отправляют всё?. События от каждого продукта я направляю в отдельный индекс.

Поскольку хранилище не бесконечное, важно отслеживать, какой продукт и сколько места занимает. Теперь через метрику Product Index size у меня есть Logging budget: можно сразу увидеть сколько места в байтах занимают события того или иного продукта и при резком росте значения сразу понятно, в какую команду идти за разъяснениями.

Топ-10 индексов с размерами и в динамике
Топ-10 индексов с размерами и в динамике

Еще я добавил в шаблон метрику Estimated time, которая через прогнозную функцию timeleft вычисляет, сколько времени остается до 15% свободного места в кластере. Такой прогноз позволяет проактивно в рабочем порядке расширить кластер. 

Если не мешают обстоятельства, то мои метрики оказываются в коробочном шаблоне Zabbix.

Дашборд с основными SLI
Дашборд с основными SLI

Визуализацию я настроил средствами Grafana — во-первых, потому что Grafana основной рабочий инструмент дежурной смены. Во-вторых, потому что метрики разных кластеров снимаются разными инстансами Zabbix. 

С помощью разных источников данных и шаблонных наименований хостов naming convention у меня получилось сделать один динамический шаблон, который отображает SLI по каждому кластеру. Кластер выбирается из выпадающего списка. Дашборд не требует изменений при добавлении или удалении кластера. 

Выводы

Автоматизация — это здорово! Теперь с помощью плейбука я могу развернуть несколько кластеров в течение часа. И большую часть времени займет заполнение инвентаря и всех нужных переменных. Добавление нод в кластер — дело 10 минут. Внедренное решение сильно сократило время на деплой, а также привело конфигурацию всех кластеров к одному виду. Единообразие сильно сокращает временные затраты на траблшутинг.

Реализация деплоя через CI/CD принесла пользу команде. Раньше только я знал все нюансы установки и мог развернуть кластер. Теперь добавив описание нового кластера в переменных и выполнив merge request это может сделать каждый.

Конечно, роли не идеальны. К ним хотелось бы добавить валидацию переменных, автогенерацию документации и установку под другие операционные системы. Будем считать это домашним заданием?.

В планах на будущее: перенести все секреты и сертификаты в Vault, разработать и подключить роль logstash, собрать логи всех кластеров в один индекс и настроить предупреждения по аварийным событиям. Также предстоит договориться с командами разработки внутренних продуктов о формате событий приложений (contract), чтобы избежать ошибок несовпадения типов данных при индексировании.


Интересное в блоге:

Комментарии (3)


  1. D1abloRUS
    23.05.2024 12:12
    +1

    Брали бы лучше qryn + clickhouse + vector...


  1. CTPEJIKuH
    23.05.2024 12:12
    +3

    Это playbook для OpenSearch, зачем в него добавлять настройку ВМ и т.д.?
    А ВМ как развертываются? Руками?
    Монорепа -___-
    Отвечаю, очень шустро будет отрабатывать CI/CD, когда кластеров станет десятки :)
    Автор очень сильно радуется, что написал playbook для OpenSearch, при этом playbook сильно завязан на инфраструктуру cloud.ru и, соответственно, для большинства пользователей он бесполезен.


  1. post_ed
    23.05.2024 12:12

    Для сбора логов с коммутаторов кто чем пользуется?