Приветствую тебя, дорогой читатель! Моя первая статья на Хабр посвящена простому и, надеюсь, полезному решению, сделавшим для меня сбор метрик в Prometheus с разнородных серверов удобным. Я затрону некоторые подробности, в которые многие могли не погружаться, эксплуатируя Prometheus, и поделюсь своим подходом по организации в нём легковесного service discovery. Для этого понадобится: Prometheus, HashiCorp Consul, systemd, немного кода на Bash и осознание происходящего. Если интересно узнать, как все это связано и как оно работает, добро пожаловать под кат.  Мое знакомство с Prometheus произошло, когда возникла необходимость собирать метрики с кластера Kubernetes. Пробежавшись по материалам в интернете стало понятно, что работать с Prometheus и его pull-моделью, очень удобно, когда он самостоятельно узнаёт о сервисах, с которых необходимо собирать метрики. Для настройки Prometheus под Kubernetes в конфигурационном файле prometheus.yml есть директива kubernetes_sd_configs. Она отвечает за коммуникацию с kube-apiserver с целью получения IP-адресов и метаинформации о pod'ах в кластере с которых нужно собирать метрики. С первого взгляда картинка не сильно понятная, но с документацией и в несколько экспериментов разобраться можно. Помимо role=pod есть и другие роли у kubernetes_sd_configs. Наблюдая, как эффективно Prometheus узнает о сервисах в Kubernetes, например, о DaemonSet prometheus/node_exporter, сразу появилось желание реализовать аналогичный подход для сбора метрик и мониторинга доступности сервисов вне кластера Kubernetes: node_exporter, Zookeeper, Kafka, ClickHouse, CEPH, Elasticsearch, Tarantool ... Писать каждый раз targets в static_configs при добавлении нового сервера в текущий кластер Kafka или OSD в CEPH ну совсем не про удобное управление инфраструктурой. А еще это нужно не забывать делать. Да, можно все автоматизировать, например через Ansible, но что делать, если на некоторое время отключить один CEPH OSD для обслуживания. Тогда нужно еще и автоматизировать временное выведение этого сервиса из конфига prometheus.yml. В итоге получается огромная куча слоев автоматизации, которые тоже нужно не забывать запускать.  Именно слоев, потому что одна такая автоматизация порождает необходимость в другой. Так еще ко всему этому сильно разрастается prometheus.yml, в котором нужно не просто перечислять сервисы, а еще делить их на разные job_name для удобного доступа к метрикам. Тут легко прикинуть сколько строк в prometheus.yml нам создаст один кластер Kafka из 6 брокеров. А если кластеров 3. И добавим к ним еще 3 кластера ClickHouse, в каждом по 4 ноды. А уж про CEPH и вообще говорить страшно. А еще на каждом сервере не по одному сервису, с которого нужно собирать метрики — про prometheus/node_exporter не надо забывать. Представив всю свою инфраструктуру в количественной мере можно прикинуть и размер prometheus.yml при использовании только static_configs. Kubernetes Documentation, Configuration Best Practices, General Configuration Tips. *Админы почему-то очень любят хранить в конфигах строки по-умолчанию в виде комментариев, создавая из файла конфигурации настоящую свалку. Не нужно этого делать! В файле конфигурации должны быть только важные строки, отвечающие за работу приложения. Файл, который шел в инсталляционном пакете, можно оставить рядом, добавив ему постфикс .default или .original. А можно вообще ничего не хранить рядом и в файл конфигурации, в шапке, положить 2-3 полезные ссылки на описание этого файла или его содержимое по-умолчанию во внешнем источнике: документация, GitHub репозиторий проекта. Совсем не сложно сделать файл конфигурации удобным и читаемым для коллег и себя самого, в будущем.* Нужна была максимально простая и понятная автоматизация управления сервисами в prometheus.yml. Самое знакомое что сразу бросалось в глаза из документации Prometheus — consul_sd_configs. То есть, Prometheus, посредством HashiCorp Consul, может узнавать о сервисах с которых нужно собирать метрики и заодно мониторить их доступность. Например: Будучи уже знакомым с Consul, я знал, что сам он не узнает о сервисах даже на тех машинах, на которых запущен его agent. Через его простое HTTP API сервис нужно зарегистрировать в Consul, а еще его можно разрегестрировать. Ведь бывает, что сервис больше никому не нужен: попробовали, и не подошел. В полном объеме я никогда не использовал Consul. Возникали только задачи, с которыми Consul отлично справлялся частью своих функций: KV-хранилище, HashiCorp Vault, кластеризация Traefik. И, например, никогда не нужен был его DNS. Вот и сейчас задача стояла не усложнить себе жизнь, запуская на каждом сервере по дополнительном сервису в виде Consul agent. Достаточно запустить один инстанс Consul, который будет принимать запросы в HTTP API и к нему будет обращаться Prometheus за списком адресов, по которым расположены те или иное сервисы. Идеально, если это все будет по HTTPS, хотя сеть и так закрытая. На такой волне стало понятно, что уже имеющийся в Kubernetes StatefulSet Consul, обеспечивающий кластерную работу Traefik, можно использовать для service discovery в Prometheus. И даже Ingress в локальную сеть у него уже был, так как было пару моментов использования Consul’s web UI. Бонусом Traefik обеспечивал ему HTTPS-транспорт с сертификатом от Let`s Encrypt через DNS Challenge. На этом этапе у меня имелся Prometheus, который готов забрать из Consul адреса сервисов и начать собирать с них метрики, и инстанс Consul, в котором можно регистрировать сервисы. Возникает задача автоматизировать децентрализованную регистрацию сервисов во время запуска на любом сервере инфраструктуры. И на помощь приходит простой Bash. В своей инфраструктуре я давно использую иммутабельную CoreOS Container Linux. Было даже дело рассказать об этой ОС на DevOpsConf Russia 2018. Я и по сей день использую эту ОС как основную для запуска сервисов в эфемерных Docker контейнерах, оборачивая в systemd.service. Но только теперь это Flatcar Linux, разработчики которой подхватили идею и не дали ей погибнуть, обеспечив полную совместимость с CoreOS Container Linux. А перейти с CoreOS на Flatcar можно простым обновлением системы! Все сервисы на моём сервере — это systemd.service. А systemd — это очень мощный инструмент, который активно развивается и решает очень много задач в Linux. И есть в systemd.service, в секции , такие параметры как ExecStartPost, ExecStop. Они-то и помогут мне с регистрацией и разрегистрацией сервиса в Consul. Опишу сразу на примере юнита prometheus-node-exporter.service. Этот сервис примечателен тем, что запускается абсолютно на каждом сервере и ему одному в static_configs можно было бы выделить больше 100 строк. Как раз вот тут появляется некий /opt/bin/consul-service. У него всего пара переменных системного окружения и несколько обязательных аргументов, которые я опишу поподробней. Переменные окружения: Обязательные аргументы: Использование consul-service в systemd.service оказалось очень практичным и преподнесло один очень приятный бонус. systemd.service во время старта, а именно ExecStartPost с consul-service register, регистрирует сервис в Consul, и Prometheus сразу же может забирать с него метрики, а также мониторить его доступность. Но самое удобное, когда нужно ненадолго оставить сервис или перезагрузить сервер. В этот момент выполняются все ExecStop, в том числе consul-service deregister, и сервис разрегистрируется. Consul и следом Prometheus о нем забывают, и тогда нет никакого лишнего оповещения о недоступности намеренно остановленного сервиса. Особенно это практично, когда работы проводятся в ночное время одним инженером, а другой в этот момент в ответе за Service Availability и получает пугающие его оповещения о недоступности сервиса. Но если сервис упал, и никто специально для проведения работ его не останавливал, и разрегистрации сервиса не было, то он будет под мониторингом со всеми вытекающими. Очень важным моментом в работе consul-service является правильно установленные hostname сервера и search (домены поиска) в resolv.conf. Этот же адрес должен присутствовать в локальном DNS-сервере, что как минимум best practice при работе с серверами в локальной среде. Если вы до сих пор работаете со своими серверами в локальной сети по IP, то самое время присвоить себе тоже какой-нибудь идентификатор и обязать коллег обращаться к вам только по нему. И больше ни на какие обращения не реагировать. Так будет справедливо и быстро осознается ценность имени. consul-service написан на чистом Bash в ~60 строчек. В зависимостях у него только хорошо известный всем cURL и Bash. Уверен, что админ без опыта разработки бегло разберется в нескольких строчках этого скрипта. Лицензия этого макро-решения MIT, поэтому если появится необходимость доработать его под свои нужды, не стесняйтесь. Тот самый scrape_configs в prometheus.yml для prometheus/node_exporter, запущенном на всех серверах, лаконичен и не зависит никак от количества серверов. tags: [prod] тут отвечает за выборку инстансов только production среды. Это как раз тот самый аргумент -e в consul-service, который по факту превращается в один из тегов сервиса при попадании в Consul. На мои сервера с Flatcar Linux доставка скрипта в /opt/bin осуществляется посредством Ansible. Playbook всего на пару tasks. Но это реализуется и любым другим инструментом, хоть строчкой в crontab. О том как Ansible, а точнее Python, попадает и работает на серверах Flatcar Linux думаю, можно рассказать в будущих статьях. В планах посвятить целый цикл статей про иммутабельную ОС Flatcar Linux. Наверное, можно считать всю эту конструкцию неким велосипедом. Но в эксплуатации и передачи коллегам такого велосипеда на поддержку стало сразу ясно, что он простой как «рама+колеса+педали+руль», и ездить на нем удобно. И почти в обслуживании не нуждается.
А лучшей похвалой этой статье станет мысль читающего — “Интересное решение! Надо попробовать!”. Development & Operations & Management