Русский
Русский
English
Статистика
Реклама

Prometheus

Мониторинг эксплойтов

08.04.2021 18:23:51 | Автор: admin

В прошлой статье мы собрали данные для того чтобы мониторить эксплуатацию уязвимости в Windows Server, который имеет роль контроллера домена. В этой статье мы попытаемся понять есть ли возможность мониторить остальные классы эксплойтов с помощью инструментов ELK, Zabbix или Prometheus.

Классификация эксплойтов с точки зрения мониторинга

Уязвимость - несовершенство программного обеспечения. В простом случае это причина почему приложение не может выполнить свою задачу и завершает работу аварийно.

Можно ли все уязвимости отследить? Попробуем собрать факты воедино. Факт 1 уязвимости с точки зрения их использования для достижения целей компрометации или вывода из строя информационной системы можно условно поделить на:

  1. Уязвимости приводящие к аварийному завершению работы приложения

  2. Уязвимости позволяющие запустить дополнительные команды в уязвимой системе

Стоит отметить, что для обеих групп мы можем мониторить только последствия или зависимые события.

Первая группа уязвимостей достаточно просто мониторится системами ELK, Zabbix и Prometheus. Недоступность пострадавшей системы тут же будет выведено на общий мониторинг.

Не так всё просто со вторым типом. Дело в том, что именно эти уязвимости имеют довольно большое количество вариаций. И как правило связаны с теми подсистемами, где важен порядок и скорость действий системы. В эти подсистемы очень сложно добавить механизмы снятия информации для мониторинга.

чтобы понять насколько много этих уязвимостей их список по популярности появления в программном обеспечении можно найти здесь.

Факт 2 - всего классов уязвимостей задокументировано - 699. Интересная цифра, но это тоже верхнеуровневая абстракция, на самом деле и у этих 699 уязвимостей тоже есть вариации.

Что особенного среди 699 групп уязвимостей? Особенность заключается в том, что большую часть этих уязвимостей в силу своей природы достаточно проблематично обнаружить. К примеру, группа CWE-120. Группа уязвимостей основной смысл которых заключается в том, что программист не верно рассчитал количество памяти, которое нужно было приложению для работы с определенным объектом. В результате у атакующего есть возможность влиять на заполнение памяти процесса. В ряде случаев это может привести к выполнению передаваемых злоумышленником команд.

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

Получается память для мониторинга недоступна? На самом деле это не так. Любая система мониторинга и даже логи операционных систем могут включать информацию о том как потребляется память приложениями. Можно эти данные наблюдать в режиме реального времени с помощью различного софта типа VMMap. Софт может показывать количество страниц памяти, которое используется, освобождается, резервируется и т.д. Что же это нам дает?

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

  • понимать как работает эксплойт, идеально если будет исходный код

  • иметь возможность расширить набор событий в системных логах ОС

Тактика обнаружения на основании списка Mitre

Уязвимости сами по себе могут быть не видны на средствах мониторинга. При использовании возможностей только операционной системы. Иногда сами вендоры, видя популярность уязвимости могут создать отдельные события, которые помогают локализовать эксплуатацию уязвимости.

В случае с уязвимостью CVE-2020-1472 Microsoft добавила специальные события, которые генерируются, если система использует уязвимые алгоритмы. Нам же придется искать закономерности самостоятельно и использовать эти закономерности для мониторинга. Возможно даже придется прибегнуть к сторонним программным продуктам.

Для примера сбора таких закономерностей будем использовать эксплойт к уязвимости CVE-2020-0796. Основная проблема уязвимости - переполнение целочисленного элемента, который используется для контроля размера выделяемой приложением памяти. В сети можно найти 2 сценария использования уязвимости:

  1. Remote Code Execution

  2. Local Priveledge Escalation

Первый весьма нестабилен, выполнения команд от него добиться достаточно сложно. Однако из-за того что уязвимость находится в ядре, падение ОС будет зарегистрировано мониторингом по-умолчанию.

Попробуем разобраться со второй. Проект на github предоставляет исходный код на языке программирования C++. Попробуем локализовать основные операциии, которые выполняет эксплойт.

Инициализация её код можно найти ниже:

Создается сокет на loopback интерфейсе и 445 порту. Далее эксплойт будет создавать в специальном формате пакет и отправит его на созданный сокет. Произойдет переполнение размера выделенной памяти в ядре ОС и код перетрёт память, которая относилась к primary токену основного процесса. С этими данными будет создан новый процесс cmd.exe. Как создается этот процесс? Эти данные можно найти в исходнике:

К сожалению, среди событий логов обнаружить ничего не удалось, так как все действия работали динамически в оперативной памяти и не задействовали стандартные механизмы получения доступа к объектам. Журнал "Security" тоже не будет содержать никакой информации. Что же делать? Есть 2 варианта:

  1. Использовать IDS для loopback интерфейса

  2. Использовать Endpoint защиту

Мы выберем 3-й путь. В ОС Windows есть возможность расширить список событий, которые будет генерировать система. Это возможно сделать с помощью инструмента SysMon. Чтобы инструмент мог генерировать нужные события, ему необходим конфиг, где и перечислены необходимые данные. Есть уже готовый. Причем если мы хотим поотслеживать события с loopback интерфейсом, нужно убрать следующие строки:

...<RuleGroup name="" groupRelation="or">        <NetworkConnect onmatch="exclude">            ...            <DestinationIp condition="is">127.0.0.1</DestinationIp> <==удалить            <DestinationIp condition="begin with">fe80:0:0:0</DestinationIp> <==удалить        </NetworkConnect>    </RuleGroup>...

Все данные по событиям выкладываются в системные журналы. Для Windows 10 можно использовать вот этот путь: Applications and Services Logs\Microsoft\Windows\Sysmon\Operational.

Для установки в систему конфига и Sysmon нужно набрать следующую команду:

sysmon.exe -accepteula -i sysmonconfig-export.xml

Запустим эксплойт и заглянем в лог Sysmon:

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

P.S. Для систем на базе Linux тоже скоро будет SysMon.

Подробнее..

Интеграция Росплатформы с grafanaprometheus через consul

18.12.2020 02:10:01 | Автор: admin


Долго колебался мыслями о необходимости написания этой статьи, но все таки решился. Интерес к красивым дашбордам победил лень и отсутствие мотивации к слишком мудреным реализациям мониторинга такого типа (для микросервисных систем). Плюс накопилось желание поскорее все выложить, после полученных впечатлений в процессе изучения, но тут конечно необходимо чтобы это было понятно всем, поэтому постараюсь сдержать свои эмоции) и описать это более детально.

Былой опыт


Ранее несколько лет назад был 5 летний опыт работы с СУБД Oracle в среде RISC-овой архитектуры на базе IBM, c их очень хорошей юникс подобной ОС AIX c своим прекрасным инструментом smitty, и все это еще разворачивалось на аппаратной виртуализации PowerVM, где можно настраивать балансировку на базе двух VIOS и т.д.



За всем этим набором как-то надо было следить, особенно за БД, и у всех этих программ были свои средства мониторинга, но вдохновлял меня на тот момент самый красивый и имеющий дашборды для всех этих компонентов, инструмент под названием spotlight от компании Quest.


Дашборд Spotlight

Прошло время пришлось работать с другими технологиями, как и многие попал в течение тенденций в сторону открытого ПО, где проприетарный spotlight конечно не очень котируется. Избалованный всякими простыми, платными удобностями, сквозь негативные эмоции, пользовался средствами от разработчиков свободного ПО и иногда ностальгировал по spotlight. Например Zabbix как-то не особо привлекал к себе внимание, но как только услышал про Grafana с Prometheus, само название меня уже заинтересовало, а когда увидел дашборды то, вспомнил про Spotlight. Хотя наверно и в Zabbix можно добиться такого же эффекта или даже использовать его через тот же Grafana, но изначально как я понял без особой персонализации красивых дашбордов там нет, с таким же наборов возможностей, впрочем как и в простом Prometheus.

Соблазнительно-таинственный grafana+prometheus



Конечно главнее всего полезность мониторинга, а не его красота, но для админа и инженеров всяких кластерных систем это все рано или поздно становится рутиной, и хочется тоже как-то чувствовать современный приятный дизайн, превращая эту рутину в интересную красивую игру. Чем больше я узнавал про Grafana плюс Prometheus, тем больше меня это привлекало, особенно своими названиями, красивыми дашбордами с графиками, бесплатностью, и даже не смотря на весь на мой взгляд геморой, в реализации этой связки со ложными системами на микросервисной архитектуре, с которым мне пришлось столкнутся.



Но кто работает с открытым ПО для них эти трудности привычное дело, и они даже получают удовольствие от этого садомазохизма, где даже самые простые вещи необходимо делать самому, своими ручками. Что противоречит например таким понятиям как: клиент недолжен парится, что там скрывается под капотом, главное простота, удобство, предоставляющие надежность, быстрый оперативный результат при выполнении задач клиента и т.д. Поэтому дальнейшее описание интеграции у меня язык не поворачивается назвать удобной и простой, а красота как говорится требует жертв, но для неизбалованных любителей linux это все просто как два пальца об асфальт)



На самом деле для Grafana и Prometheus уже есть много разработанных готовых дашбордов и экспортеров метрик от различных систем, что делает его простым и удобным для использования, но когда идет речь о чем-то своеобразном то, необходимо немного заморачиваться и хорошо если разработчики постарались, написали метрики к своим сервисам, а бывает что приходится самому варганить эти метрики. Тогда уже может невольно прийти желание использовать другие средства мониторинга, например как в Росплатформе если недостаточно встроенных предлагается Zabbix, это лучше чем возится с разработкой метрик каждого сервиса под Prometheus.

Мониторинг Росплатформы



В Росплатформе конечно есть еще и свои встроенные средства мониторинга, например в веб UI для SDS vstorage или для гипервизора с виртуальными средами или для таких сервисов на экспорт как s3, iscsi.


Главный дашборд SDS-а(Р-хранилище) Росплатформы


Дашборд одной из нод кластера Росплатформы(в Р-хранилище)


Дашборд Росплатфомы для s3 в Р-хранилище


Мониторинг виртуальной машины Росплатформы(В Р-управлении виртуализации)

Есть даже CLI мониторинг SDS(Р-хранилища) сердце Росплатформы #vstorage c имякластера top

Мониторинг SDS(Р-хранилища) Росплатформы через CLI

Но когда необходим более детальный мониторинг по каждому сервису/службе то, тут уже необходимо что-то другое, а в Росплатформе как в инфраструктурной экосистеме, сервисов немало. И разработчики как оказалось работают в этом направлении и можно даже увидеть результат их деятельности, если в развернутом кластере Росплатформы посмотреть на сервисы SDS через команду #netstat tunap | grep имясервиса, где имя сервиса например cs служба чанк сервера.
И мы можем увидеть вывод:



Где есть адрес 0.0.0.0 и порт 37548 который можно прослушать через команду
#curl localhost: 37548/metrics




И мы увидим целую кучу метрик, которые как раз подходят для Prometheus и для рисования графиков с привлекательным интерфейсом в Grafana.



По мимо этих просто чудо метрик есть конечно возможность использовать обычные доступные всем экспортеры для Prometheus, потому что под капотом Росплатформы модифицированный гипервизор kvm-qemu плюс libvirt и операционная система на базе linux ядра. Разработчики Росплатформы также планируют добавить в свой репозиторий эти экспортеры с модификацией под новую версию, но можно не дожидаясь воспользоваться уже сейчас выше описанными. В этой статье попробую описать все таки как можно работать с чудо метриками отдельных сервисов так как это оказалось более сложнее чем просто готовые экспортеры.



В общем сама Grafana умеет только рисовать, а для сбора метрик она использует различные сборщики, одним из которых является выше описанный Prometheus. Он конечно сам умеет рисовать графики и имеет свой веб ui, но это далеко от Grafana.


Веб ui от Prometheus


Дашборд Grafana

Модный Prometheus как и Grafana, тоже может только видеть метрики там где они лежат, или забирать их через экспортеры, в которых описывают места вывода метрик. Все это конечно можно настроить в конфиге Prometheus, и в экспортере добавить адреса(цели), по которым будут транслироваться метрики.

Service Discovery



Изначально мне показалось, что всего этого будет достаточно чтобы замутить свой мониторинг на базе этих прекрасных инструментов, но не тут-то было. Дело в том, что например для микросервисов SDS-а нет готовых экспортеров, для каждой его службы, да и вряд ли появятся, так как сервис работающий для одного диска может появляться и исчезать, если этот диск например заменить или добавить новые диски, или сервисы s3/iscsi при масштабировании могут плодится и т.д. И что получается каждый новый сервис прописывать в экспортере или в конфиге Prometheus, где для каждого свой уникальный порт?

Можно конечно написать целую программу под это дело, но это уже другая история, и хочется как-то менее рутинным и более легким путем. Покапавшись в гугле узнал, что есть еще программы service discover и одна из самых популярных для Prometheus это Сonsul.



Насмотревшись про него видео и изучив его возможности оказалось, что в нем можно просто зарегистрировать/ отрегистрировать сервисы с их портами для последующей передачи Prometheus, но сам он конечно ничего не ищет, как это описывают на первый взгляд в многих статьях и документации этого инструмента. То есть искать он может разными способами (DNS, HTTP API, RPC) уже у себя внутри среди зарегистрированных в нем сервисах.

В результате можно вернутся к нашей команде #netstat, и выполнять эту команду через Ansible или написать скрипт под планировщик задач с помощью которого будут сканироваться наши сервисы netstat-ом. Далее каждый найденный сервис наш скрипт будет регистрировать в Сonsul командой
#curl --request PUT --data @services.json localhost:8500/v1/agent/service/register

Где файл services.json это описание сервиса в этом формате:
 {  "services":[{  "name":"cs",  "tags":["csid=1026"],  "address":"127.0.0.1",  "port":33074},{  "name":"mds",  "address":"127.0.0.1",  "tags":["mdsid=2"],  "port": 9100}]}

В данном примере описываются два сервиса это чанк сервер cs и служба метаданных SDS Росплатформы mds.
Отрегистрировать также можно устроить с помощью одного и того же скрипта, который будет проверять доступность метрик от этого сервиса по его порту и в случае пустого ответа выкидывать этот сервис из Consul по команде:
#curl --request PUT http://127.0.0.1:8500/v1/agent/service/deregister/my-service-id


Есть конечно еще путь эмулировать API Consul, чтобы Prometheus думал, что он обращается к Consul, а на самом деле к ngnix, где ему подкладывал бы в формате json список сервисов этот же скрипт. Но это уже опять другая история, близкая к разработке. Можно оставить сам консул, который идет в виде отдельно выполняемого файла, в связи с чем его можно расположить на SDS для отказоустойчивости вместо его кластерной настройки, которую также можно осуществить, но это усложняет инструкцию и выходит за рамки этого описания.

Далее после того как у нас запущен Consul с необходимыми зарегистрированными сервисами, надо установить и настроить Prometheus. Можно это сделать в виртуальной среде, а на каждой ноде только его экспортер. Например в Росплатформе он уже предустановлен в контейнере vstorage-ui управления SDS-ом(Р-хранилище), остается только установить экспортеры на ноды и прописать их в конфиге Prometheus.

В его конфиге также можно прописать правила выборки метрик далее адрес и порт Consul, и регулярные выражения с метками для фильтрации нужных значений.
После этого мы можем устанавливать Grafana можно на этом же узле или даже на клиенте на своем ноутбуке, а можно как в моем варианте в гостевой машине, где в настройках Grafana указать сборщик данных Prometheus с адресом на наш установленный и его портом.

Если пройти в раздел explore то, можно проверить нашу работу, нажав на кнопку метрики, где у вас появится меню/список с разделами метрик.



Установка настройка Consul





В выше описанной краткой инструкции я опустил настройку конфигурационного файла Prometheus,
но для начала установим и запустим сам Consul на одной из нод кластера Росплатформы(Р-виртуализации):
Можно скачать его следующей командой
#wget  https://releases.hashicorp.com/consul/1.9.1/сonsul_1.9.1_linux_amd64.zip

Распаковываем его
# unzip сonsul_1.9.1_linux_amd64.zip

B сразу можно запустить проверить
#./consul v

Для начала чтобы не заморачиваться со автоскриптом по поиску и регистрации сервисов служб SDS-а Росплатформы в Consul, описанным выше, попробуем просто создать папку с прописанными службами в файле json.
#mkdir consul.d

И внутри этой папки создадим файл
#vi services.json

Со следующим содержимом
{  "services":[{  "name":"cs",  "tags":["csid=1026"],  "address":"127.0.0.1",  "port":33074},{  "name":"mds",  "address":"127.0.0.1",  "tags":["mdsid=2"],  "port": 9100}]}

Где 1026 это id службы чанк сервера, которую можно увидеть по команде
#vstorage c имя_вашего_кластера list-services



По ней также можно увидеть mdsid

Порты можно посмотреть через #netstat tunap | grep cs или mds в строке с адресом 0.0.0.0 с протоколом tcp.
После этого можно проверить запустить наш Consul
#consul agent -dev -enable-script-checks -config-dir=./consul.d

На экран будут выводится сообщения, можно это окно закрыть consul продолжит работать в фоновом режиме, для его перезагрузки можно воспользоваться командой
#consul reload

Можно проверить работу Consul через команду
#curl localhost:8500/v1/catalog/services

Он должен вывести наши зарегистрированные сервисы



И можно еще проверить каждый сервис:


Установка настройка Prometheus





Теперь можно установить Prometheus прям на ноду чтобы пока не возится с Prometheus в vstorage-ui
#wget https://github.com/prometheus/prometheus/releases/download/v2.23.0/prometheus-2.23.0.linux-amd64.tar.gz#mkdir /etc/Prometheus#mkdir /var/lib/Prometheus#tar zxvf prometheus-2.23.0.linux-amd64.tar.gz#cd prometheus-*.linux-amd64#cp prometheus promtool /usr/local/bin/#cp -r console_libraries consoles prometheus.yml /etc/Prometheus#useradd --no-create-home --shell /bin/false Prometheus#chown -R prometheus:prometheus /etc/prometheus /var/lib/Prometheus#chown prometheus:prometheus /usr/local/bin/{prometheus,promtool}

Как запустить и прописать в автозапуск в виде сервиса смотрим здесь
Редактируем наш конфиг файл Prometheus:
#vi /etc/systemd/system/prometheus.service

global:  scrape_interval:     1m  evaluation_interval: 1malerting:  alertmanagers:  - static_configs:    - targets:      - localhost:9093rule_files:- /var/lib/prometheus/rules/*.rules- /var/lib/prometheus/alerts/*.rules  - job_name: consul    honor_labels: true    consul_sd_configs:    - server: '127.0.0.1:8500'  #адрес и порт Consul       datacenter: 'dc1'   # к какому датацентру Consul относится - опционально      scheme: http  # по какому протоколу/схеме взаимодействие    relabel_configs:    - source_labels: [__address__]      regex: (.*)[:].+      target_label: instance      replacement: '${1}'    - source_labels: [__meta_consul_service]      target_label: 'job'    - source_labels: [__meta_consul_node]      target_label: 'node'    - source_labels: [__meta_consul_tags]      regex: ',(?:[^,]+,){0}([^=]+)=([^,]+),.*'      target_label: '${1}'      replacement: '${2}' 

Здесь
Нам в помощь дока про конфиг, а в самом примере здесь некоторые строки с комментарием.
Теперь можно запустить Prometheus проверить его работоспособность
#systemctl start prometheus.service#systemctl status prometheus.service

Пройти через браузер по адресу адрес_ноды_где_установлен_Prometheus:9090


И потом пройти в меню status -> targets



И провалится например по ссылке 127.0.0.1:33074 /metrics где мы увидим наши метрики от службы чанк сервера


К каждой строке есть комментарий

Установка настройка Grafana





Далее устанавливаем grafana
Я установил у себя на ноутбуке на windows 10 и зашел через браузер по адресу localhost:3000
Далее подключился к серверу к ноде с установленным Prometheus


Теперь проходим в меню manage и создаем наш новый дашборд.


Выбираем добавить новую панель

Можно ее назвать например memory use, для того чтобы попробовать отобразить использование памяти сервера нашей выше описанной службы чанк сервер.
На вкладе query выбрать из выпадающего списка datasource Prometheus, который мы ранее настроили на наш сервер(Р-виртуализации) Росплатформы с прослушивающим портом 9090.
Далее в поле metrics мы должны вставить метрику, ее можно подобрать из списка всех метрик по описанию после слова HELP.



Находим process_swap_bytes использование swap в байтах. Еще можно взять process_resident_memory_bytes из комментария видно, что это использование памяти сервера.
И дополнительно взять process_swapin_delay_seconds задержка при передачи памяти swap в резидентную память.
В Grafana в дашборде можно создать переменную:



После этого редактируем панель


  • 1. Название панели memory use.
  • 2. Выбираем data sources в нашем случае это Prometheus.
  • 3. Добавляем описание например общий объем памяти и памяти подкачки, занятой CS, а также процент времени, затраченного на ожидание передачи памяти swap в резидентную память.
  • 4. Пишем первый запрос с именем метрики process_swap_bytes{job=cs,csid=$cs}, где указываем службу cs и переменную его id.
  • 5. Имя определения.
  • 6. Разрешение.




Добавляем еще query и прописываем туда аналогично как мы прописывали для swap,
Только в поле напротив metrics где B будет process_resident_memory_bytes{job=cs,csid="$cs"}, а в С будет instance:process_swapin_delay_seconds:rate5m{job=cs,csid="$cs"}


Здесь настраиваем цвет и шкалу графика


В результате должен получится вот такой график



На этом пока все, надеюсь это как-то поможет тем, кто интересуется настройкой своего мониторинга на базе Grafana и Prometheus плюс Consul для Росплатформы или других похожих систем.



Полезные ссылки:



Подробнее..

Перевод Сервер Prometheus и TLS

15.01.2021 16:07:39 | Автор: admin


Prometheus теперь поддерживает TLS и базовую аутентификацию для HTTP эндпоинтов.


Скрейпинг таргетов через HTTPS вместо HTTP поддерживается уже давно. Метрики можно собирать с поддержкой HTTPS, аутентификации по клиентским сертификатам и базовой аутентификации.


В прошлом году Node Exporter стал первым официальным экспортером, который нативно предоставляет метрики по HTTPS. Все подробности в предыдущем посте. На этой неделе (прим. переводчика: статья вышла 6 января 2021 года) мы встречаем Prometheus 2.24.0. В последнее время Prometheus радует нас крутыми новшествами это и TLS, и backfilling (обратное заполнение, тоже в версии 2.24) и даже переход на современный пользовательский интерфейс на React.


В этом посте мы расскажем о TLS и базовой аутентификации.


Здесь можно узнать больше о модели безопасности Prometheus и о том, как пожаловаться на уязвимости.


Если вы обнаружили в Prometheus, официальном экспортере или библиотеке какую-нибудь критическую уязвимость, не рассказывайте об этом публично, а сообщите нам, чтобы мы все исправили.


API для администрирования и управления жизненным циклом


По умолчанию Prometheus предоставляет эндпоинты API только для запроса данных. При необходимости можно включить еще эндпоинты для управления жизненным циклом (перезагрузка конфигурации, выход) и для администрирования (удаление метрик, создание снапшотов).


При этом нужно будет защитить порт Prometheus, например, с помощью аутентификации.


Защита доступа к Prometheus


Раньше между клиентами и сервером Prometheus для защиты обычно ставили обратный прокси:



С такой конфигурацией можно не только управлять доступом и шифрованием, но и автоматизировать создание сертификатов, использовать троттлинг, дополнительные средства контроля, изменения имен (mangling) и так далее. За задачи балансировщиков нагрузки и обратных прокси Prometheus не отвечает, так что продолжайте использовать обратный прокси, если вам нужны эти расширенные функции.


Если ваш сценарий попроще или нужно защитить трафик между обратным прокси и Prometheus, теперь у вас есть встроенные средства для защиты входящего HTTP-трафика.



Как настроить TLS


Посмотрим, как это работает на практике, на примере Prometheus на Linux.


Настройка рабочего каталога


Мы будем работать в отдельном каталоге:


$ mkdir ~/prometheus_tls_example$ cd ~/prometheus_tls_example

Создание TLS-сертификатов


Для начала создадим самоподписанный TLS-сертификат.


$ cd ~/prometheus_tls_example$ openssl req -new -newkey rsa:2048 -days 365 -nodes -x509 -keyout prometheus.key -out prometheus.crt -subj "/C=BE/ST=Antwerp/L=Brasschaat/O=Inuits/CN=localhost" -addext "subjectAltName = DNS:localhost"

Здесь localhost это имя хоста для сервера Prometheus.


Создается два файла: prometheus.crt и prometheus.key.


Веб-конфигурация Prometheus


Скачиваем Prometheus v2.24.0, распаковываем, переносим сертификаты, которые создали выше:


$ cd ~/prometheus_tls_example$ wget https://github.com/prometheus/prometheus/releases/download/v2.24.0/prometheus-2.24.0.linux-amd64.tar.gz$ tar xvf prometheus-2.24.0.linux-amd64.tar.gz$ cp prometheus.crt prometheus.key prometheus-2.24.0.linux-amd64$ cd prometheus-2.24.0.linux-amd64

Сейчас нужно создать новый файл конфигурации. Мы не будем настраивать TLS и аутентификацию в основном файле конфигурации prometheus.yml. Это позволит нам перечитывать отдельный файл конфигурации при каждом запросе, чтобы на лету подхватывать новые учетки и сертификаты.


Создадим файл web.yml с конфигурацией TLS:


tls_server_config:  cert_file: prometheus.crt  key_file: prometheus.key

Запускаем сервер Prometheus, указывая --web.config.file в командной строке:


$ ./prometheus --web.config.file=web.yml[...]enabled and it cannot be disabled on the fly." http2=truelevel=info ts=2021-01-05T13:27:53.677Z caller=tls_config.go:223 component=webmsg="TLS is enabled." http2=true

Если мы видим это сообщение, сервер Prometheus запущен с поддержкой TLS.


Примечание: Все параметры TLS можно менять динамически, но если уж мы включили TLS, то без перезапуска Prometheus его не отключишь.


Подробности об этом дополнительном файле конфигурации смотрите в документации.


Проверка конфигурации TLS вручную


В curl проверим конфигурацию TLS. В новом терминале запустим пару команд для теста:


$ cd ~/prometheus_tls_example$ curl localhost:9090/metricsClient sent an HTTP request to an HTTPS server.$ curl --cacert prometheus.crt https://localhost:9090/metrics[...]

Вместо --cacert prometheus.crt можно передать -k, чтобы пропустить проверку
сертификата в curl.


Конфигурация скрейпа


Настроить TLS выборочно не получится если он включен, он распространяется на все эндпоинты. Это значит, что собственные метрики Prometheus тоже будет извлекать через TLS, поэтому настроим использование HTTPS.


Изменим задание prometheus в файле prometheus.yml:


global:  scrape_interval:     15s  evaluation_interval: 15sscrape_configs:  - job_name: 'prometheus'    scheme: https    tls_config:      ca_file: prometheus.crt    static_configs:    - targets: ['localhost:9090']

Для tls_config и scheme установим https. Полный список параметров клиента tls_config см. в конфигурации Prometheus.


Перечитаем конфигурацию Prometheus:


$ killall -HUP prometheus

Откроем https://localhost:9090/targets локально в браузере и увидим https://localhost:9090/metrics в списке таргетов.


Таргет имеет статус UP? Ура! Мы настроили TLS для сервера Prometheus и теперь собираем метрики с шифрованием.


Как настроить базовую аутентификацию


Давайте пойдем еще дальше и затребуем имя пользователя и пароль. TLS здесь не обязателен, но крайне рекомендуется (настраивать его мы уже умеем).


Веб-конфигурация


Для начала создадим хэш паролей (с помощью bcrypt). Для этого используем команду htpasswd (пакет apache2-utils или httpd-tools есть в дистрибутиве; если это не продакшен, можно найти генераторы bcrypt онлайн).


$ htpasswd -nBC 10 "" | tr -d ':\n'New password:Re-type new password:$2y$10$EYxs8IOG46m9CtpB/XlPxO1ei7E4BjAen0SUv6di7mD4keR/8JO6m

Для примера возьмем пароль inuitsdemo.


Добавим пользователя в файл веб-конфигурации Prometheus web.yml:


tls_server_config:  cert_file: prometheus.crt  key_file: prometheus.keybasic_auth_users:  prometheus: $2y$10$EYxs8IOG46m9CtpB/XlPxO1ei7E4BjAen0SUv6di7mD4keR/8JO6m

Примечание: В этом файле prometheus это имя пользователя.


Если Prometheus еще запущен, введите пароль для доступа к веб-интерфейсу по адресу https://127.0.0.1:9090, иначе на странице targets для таргета будет отображаться ошибка 401 Unauthorized.



Конфигурация Prometheus


Изменим prometheus.yml, чтобы поскрейпить имя пользователя и пароль.


global:  scrape_interval:     15s  evaluation_interval: 15sscrape_configs:  - job_name: 'prometheus'    scheme: https    basic_auth:      username: prometheus      password: inuitsdemo    tls_config:      ca_file: prometheus.crt    static_configs:    - targets: ['localhost:9090']

Перезагрузим конфигурацию Prometheus сигналом SIGHUP:


$ killall -HUP prometheus

Если все работает, Prometheus снова откроет страницу targets.



Promtool


У Prometheus есть свой инструмент командной строки promtool, которым теперь можно проверять и файлы веб-конфигурации:


$ ./promtool check web-config web.ymlweb.yml SUCCESS

Используйте любой инструмент автоматизации для удобного обновления файлов web.yml.


Grafana


Grafana поддерживает все необходимые функции для подключения к серверу Prometheus. Можно указать CA (наш prometheus.crt) или пропустить проверку сертификатов.



Заключение


Это только общий обзор. В продакшене нужно будет использовать подходящий CA и продумать еще много деталей. Можно настроить клиентские сертификаты, но из соображений безопасности эта функция считается экспериментальной. Со временем поддерживаемая версия TLS может меняться, а с ней и поведение функций.


В следующие несколько месяцев мы планируем развернуть эту поддержку HTTPS по всем официальным экспортерам Prometheus и другим проектам, например, Alertmanager, Pushgateway.


Мы за безопасный мониторинг.


От редакции: Подробнее о работе с Prometheus можно узнать на курсе Слёрма Мониторинг и логирование инфраструктуры в Kubernetes. Сейчас курс находится в разработке и его можно купить по цене предзаказа.


Полезные ссылки


Prometheus 2.24.0
Модель безопасности Prometheus
Документация по конфигурации TLS-сервера (для Prometheus)
Документация по конфигурации TLS-клиента (для сервера Prometheus)

Подробнее..

Как мы собираем общие сведения о парке из Kubernetes-кластеров

16.06.2021 10:13:29 | Автор: admin

Имея в обслуживании большой (более 150) парк Kubernetes-кластеров, всегда хотелось иметь удобное представление их общего состояния, в том числе и для того, чтобы поддерживать их гомогенными. В первую очередь нас интересовали следующие данные:

  • версия Kubernetes чтобы все кластеры были on the edge;

  • версия Deckhouse (наша Kubernetes-платформа) для лучшего планирования релизных циклов;

  • количество узлов с разбивкой по типам (управляющие, виртуальные и статические) для отдела продаж;

  • количество ресурсов (CPU, memory) на управляющих узлах;

  • на какой инфраструктуре запущен кластер (виртуальные облачные ресурсы, bare metal или гибридная конфигурация);

  • какой облачный провайдер используется.

И вот каким был наш путь к тому, чтобы превратить эту потребность в наглядную реальность

Истоки и проверка концепции

В какой-то момент времени мы стали использовать Terraform для раскатки инфраструктуры в облака и вопрос отслеживания соответствия желаемых конфигураций реальности встал еще острее. Мы храним Terraform state в самих кластерах и проверку соответствия их с реальностью проверяет отдельно написанный Prometheus exporter. Хотя ранее у нас уже была информация для реагирования на изменения (через соответствующие алерты в системе управления инцидентами), хотелось ещё иметь полное представление о ситуации в отдельной аналитической системе.

Итак, изначально в качестве PoC был несложный Bash-скрипт, которым мы вручную время от времени собирали интересующие данные с K8s-кластеров по SSH. Он выглядел примерно так:

((kubectl -n d8-system get deploy/deckhouse -o json | jq .spec.template.spec.containers[0].image -r | cut -d: -f2 | tr "\n" ";") &&(kubectl get nodes -l node-role.kubernetes.io/master="" -o name | wc -l | tr "\n" ";") &&(kubectl get nodes -l node-role.kubernetes.io/master="" -o json | jq "if .items | length > 0 then .items[].status.capacity.cpu else 0 end" -r | sort -n | head -n 1 | tr "\n" ";") &&(kubectl get nodes -l node-role.kubernetes.io/master="" -o json | jq "if .items | length > 0 then .items[].status.capacity.memory else \"0Ki\" end | rtrimstr(\"Ki\") | tonumber/1000000 | floor" | sort -n | head -n 1 | tr "\n" ";") &&(kubectl version -o json | jq .serverVersion.gitVersion -r | tr "\n" ";") &&(kubectl get nodes -o wide | grep -v VERSION | awk "{print \$5}" | sort -n | head -n 1 | tr "\n" ";") &&echo "") | tee res.csvsed -i '1ideckhouse_version;mastersCount;masterMinCPU;masterMinRAM;controlPlaneVersion;minimalKubeletVersion' res.csv

(Здесь приведен лишь фрагмент для демонстрации общей идеи.)

Однако количество клиентов и кластеров росло стало ясно, что дальше так жить нельзя. Мы ведь инженеры, поэтому всё, что может быть автоматизировано, должно быть автоматизировано.

Так начался наш путь разработки волшебного агента для кластеров, который бы:

  • собирал желаемую информацию,

  • агрегировал ее,

  • отправлял в какое-то централизованное хранилище.

а заодно соответствовал каноном высокой доступности и cloud native.

Этот путь дал начало истории модуля в Kubernetes-платформе Deckhouse, развёрнутой на всех наших кластерах, и сопутствующего ему хранилища.

Реализация

Хуки на shell-operator

В первой итерации источником данных в клиентских кластерах служили Kubernetes-ресурсы, параметры из ConfigMap/Deckhouse, версия образа Deckhouse и версия control-plane из вывода kubectl version. Для соответствующей реализации лучше всего подходил shell-operator.

Были написаны хуки (да, снова на Bash) с подписками на ресурсы и организована передача внутренних values. По результатам работы этих хуков мы получали список желаемых Prometheus-метрик (их экспорт поддерживается в shell-operator из коробки).

Вот пример хука, генерирующего метрики из переменных окружения, он прост и понятен:

#!/bin/bash -efor f in $(find /frameworks/shell/ -type f -iname "*.sh"); do  source $fdonefunction __config__() {  cat << EOF    configVersion: v1    onStartup: 20EOF}function __main__() {  echo '  {    "name": "metrics_prefix_cluster_info",    "set": '$(date +%s)',    "labels": {      "project": "'$PROJECT'",      "cluster": "'$CLUSTER'",      "release_channel": "'$RELEASE_CHANNEL'",      "cloud_provider": "'$CLOUD_PROVIDER'",      "control_plane_version": "'$CONTROL_PLANE_VERSION'",      "deckhouse_version": "'$DECKHOUSE_VERSION'"    }  }' | jq -rc >> $METRICS_PATH}hook::run "$@"

Отдельно хочу обратить ваше внимание на значение метрики (параметр set). Изначально мы писали туда просто 1, но возник резонный вопрос: Как потом получить через PromQL именно последние, свежие labels, включая те series, которые уже две недели не отправлялась? Например, в том же MetricsQL от VictoriaMetrics для этого есть специальная функция last_over_time. Оказалось, достаточно в значение метрики отправлять текущий timestamp число, которое постоянно инкрементируется во времени. Вуаля! Теперь стандартная функция агрегации max_over_time из Prometheus выдаст нам самые последние значения labels по всем series, которые приходили хоть раз в запрошенном периоде.

Чуть позже к источникам данных добавились метрики из Prometheus в кластерах. Для их получения был написан еще один хук, который через curl ходил в кластерный Prometheus, подготавливал полученные данные и экспортировал их в виде метрик.

Чтобы вписаться в парадигму cloud-native и обеспечить HA агента, мы запустили его в несколько реплик на управляющих узлах кластера.

Grafana Agent

Оставалось как-то донести полученные метрики до централизованного хранилища, а также обеспечить их кэширование на стороне кластера на случай временной недоступности хранилища, связанной с его обслуживанием или модернизацией.

Выбор пал на разработку Grafana Labs, а именно Grafana Agent. Он умеет делать scrape метрик с endpointов, отправлять их по протоколу Prometheus remote write, а также (что немаловажно!) ведет свой WAL на случай недоступности принимающей стороны.

Задумано сделано: и вот приложение из shell-operator и sidecarом с grafana-agent уже способно собирать необходимые данные и гарантировать их поступление в центральное хранилище.

Конфигурация агента делается довольно просто благо, все параметры подробно описаны в документации. Вот пример нашего итогового конфига:

server:  log_level: info  http_listen_port: 8080prometheus:  wal_directory: /data/agent/wal  global:    scrape_interval: 5m  configs:  - name: agent    host_filter: false    max_wal_time: 360h    scrape_configs:    - job_name: 'agent'      params:        module: [http_2xx]      static_configs:      - targets:        - 127.0.0.1:9115      metric_relabel_configs:      - source_labels: [__name__]        regex: 'metrics_prefix_.+'      - source_labels: [job]        action: keep        target_label: cluster_uuid        replacement: {{ .Values.clusterUUID }}      - regex: hook|instance        action: labeldrop    remote_write:    - url: {{ .Values.promscale.url }}      basic_auth:        username: {{ .Values.promscale.basic_auth.username }}        password: {{ .Values.promscale.basic_auth.password }}

Пояснения:

  • Директория /data это volumeMount для хранения WAL-файлов;

  • Values.clusterUUID уникальный идентификатор кластера, по которому мы его идентифицируем при формировании отчетов;

  • Values.promscale содержит информацию об endpoint и параметрах авторизации для remote_write.

Хранилище

Разобравшись с отправкой метрик, необходимо было решить что-то с централизованным хранилищем.

Ранее у нас были попытки подружиться с Cortex, но, по всей видимости, на тот момент инженерная мысль его разработчиков не достигла кульминации: пугающая обвязка вокруг него в виде Cassandra и других компонентов не дала нам успеха. Поэтому мы данную затею отложили и, памятуя о прошлом опыте, использовать его не стали.

NB. Справедливости ради, хочется отметить, что на данный момент Cortex выглядит уже вполне жизнеспособным, оформленным как конечный продукт. Очень вероятно, что через какое-то время вернемся к нему и будем использовать. Уж очень сладко при мысли о generic S3 как хранилище для БД: никаких плясок с репликами, бэкапами и растущим количеством данных

К тому времени у нас была достаточная экспертиза по PostgreSQL и мы выбрали Promscale как бэкенд. Он поддерживает получение данных по протоколу remote-write, а нам казалось, что получать данные используя pure SQL это просто, быстро и незатратно: сделал VIEWхи и обновляй их, да выгружай в CSV.

Разработчики Promscale предоставляют готовый Docker-образ, включающий в себя PostgreSQL со всеми необходимыми extensions. Promscale использует расширение TimescaleDB, которое, судя по отзывам, хорошо справляется как с большим количеством данных, так и позволяет скейлиться горизонтально. Воспользовались этим образом, задеплоили connector данные полетели!

Далее был написан скрипт, создающий необходимые views, обновляющий их время от времени и выдающий на выход желаемый CSV-файл. На тестовом парке dev-кластеров всё работало отлично: мы обрадовались и выкатили отправку данных со всех кластеров.

Но с хранилищем всё не так просто

Первую неделю всё было отлично: данные идут, отчет генерируется. Сначала время работы скрипта составляло около 10 минут, однако с ростом количества данных это время увеличилось до получаса, а однажды и вовсе достигло 1 часа. Смекнув, что что-то тут не так, мы пошли разбираться.

Как оказалось, ходить в таблицы базы данных мимо магических оберток, предоставляемых Promscale (в виде своих функций и views, опирающихся в свою очередь на функции TimescaleDB), невероятно неэффективно.

Было решено перестать ковыряться в потрохах данных и положиться на мощь и наработки разработчиков Promscale. Ведь их connector может не только складывать данные в базу через remote-write, но и позволяет получать их привычным для Prometheus способом через PromQL.

Одним Bashем уже было не обойтись мы окунулись в мир аналитики данных с Python. К нашему счастью, в сообществе уже были готовы необходимые инструменты и для походов с PromQL! Речь про замечательный модуль prometheus-api-client, который поддерживает представление полученных данных в формате Pandas DataFrame.

В этот момент еще сильнее повеяло взрослыми инструментами из мира аналитики данных Мотивированные на пощупать интересное и доселе неизведанное, мы двинулись в этом направлении и не прогадали. Лаконичность и простота верчения этой кучей данных через Pandas DataFrame доставила массу позитивных эмоций. И по сей день поддержка полученной кодовой базы, добавление новых параметров и всевозможные правки отображения финальных данных воспринимаются как праздник программиста и не требуют большого количества времени.

Изначально мы выбрали период скрейпинга данных grafana-agentом равным одной минуте, что отразилось на огромных аппетитах конечной БД в диске: ~800 мегабайт данных в день. Это, конечно, не так много в масштабах одного кластера (~5 мегабайт), но когда кластеров много суммарный объём начинает пугать. Решение оказалось простым: увеличили период scrapeа в конфигах grafana-agentов до одного раза в 5 минут. Прогнозируемый суммарный объем хранимых данных с retentionом в 5 лет уменьшился с 1,5 Тб до 300 Гб, что, согласитесь, уже выглядит не так ужасающе.

Некоторый профит от выбора PostgreSQL как конечного хранилища мы уже получили: для успешного переезда хранилища в финальный production-кластер достаточно было отреплицировать базу. Единственное текущий недостаток пока не получилось самостоятельно собрать свой кастомный PostgreSQL с необходимыми расширениями. После пары неудачных попыток мы остались на готовом образе от разработчиков Promscale.

Получившаяся архитектура выглядит так:

Итоги и перспективы

Мы смотрим в будущее и планируем отказаться от отчётов в формате CSV в пользу красивого интерфейса собственной разработки. А по мере разработки собственной биллинговой системы начнём отгружать данные и туда для нужд отдела продаж и развития бизнеса. Но даже те CSV, что мы получили сейчас, уже сильно упрощают рабочие процессы всего Фланта.

А пока не дошли руки до фронтенда, мы сделали dashboard для Grafana (почему бы и нет, раз всё в стандартах Prometheus?..). Вот как это выглядит:

Общая сводная таблица по кластерам с Terraform-состояниямиОбщая сводная таблица по кластерам с Terraform-состояниямиРаспределение кластеров по облачным провайдерамРаспределение кластеров по облачным провайдерамРазбивка по используемым Inlet в Nginx Ingress-контроллерахРазбивка по используемым Inlet в Nginx Ingress-контроллерахКоличество podов Nginx Ingress-контроллеров с разбивкой по версиямКоличество podов Nginx Ingress-контроллеров с разбивкой по версиям

Впереди нас ждет продолжение пути автоматизации всего и вся с уменьшением необходимости ручных действий. В числе первых горячо ожидаемых плюшек переход к автоматическому применению изменений конфигураций Terraform, если таковые не подразумевают удаление каких-либо ресурсов (не являются деструктивными для кластера).

P.S.

Читайте также в нашем блоге:

Подробнее..

Рефакторинг пет проекта докеризация, метрики, тесты

17.02.2021 20:19:33 | Автор: admin

Всем привет, я php разработчик. Я хочу поделиться историей, как я рефакторил один из своих телеграм ботов, который из поделки на коленке стал сервисом с более чем 1000 пользователей в очень узкой и специфической аудитории.

Предыстория

Пару лет назад я решил тряхнуть стариной и поиграть в LineAge II на одном из популярных пиратских серверов. В этой игре есть один игровой процесс, в котором требуется "поговорить" с ящиками после смерти 4 боссов. Ящик стоит после смерти 2 минуты. Сами боссы после смерти появляются спустя 24 +/- 6ч, то есть шанс появится есть как через 18ч, так и через 30ч. У меня на тот момент была фуллтайм работа, да и в целом не было времени ждать эти ящики. Но нескольким моим персонажам требовалось пройти этот квест, поэтому я решил "автоматизировать" этот процесс. На сайте сервера есть RSS фид в формет XML, где публикуются события с серверов, включая события смерти босса.

Задумка была следующей:

  • получить данные с RSS

  • сравнить данные с локальной копией в базе данных

  • если есть разница данных - сообщить об этом в телеграм канал

  • отдельно сообщать если босса не убили за первые 9ч сообщением "осталось 3ч", и "осталось 1,5ч". Допустим вечером пришло сообщение, что осталось 3ч, значит смерть босса будет до того, как я пойду спать.

Код на php был написан быстро и в итоге у меня было 3 php файла. Один был с god object классом, а другие два запускали программу в двух режимах - парсер новых, или проверка есть ли боссы на максимальном "респе". Запускал я их крон командами. Это работало и решало мою проблему.

Другие игроки замечали, что я появляюсь в игре сразу после смерти боссов, и через 10 дней у меня на канале было около 50 подписчиков. Так же попросили сделать такое же для второго сервера этого пиратского сервиса. Задачу я тоже решил копипастой. В итоге у меня уже 4 файла с почти одинаковым кодом, и файл с god object. Потом меня попросили сделать то же самое для третьего сервера этого пиратского сервиса. И это отлично работало полтора года.

В итоге у меня спустя полтора года:

  • у меня 6 файлов, дублируют себя почти полностью (по 2 файла на сервер)

  • один god object на несколько сотен строк

  • MySQL и Redis на сервере, где разместил код

  • cron задачи, которые запускают файлы

  • ~1400 подписчиков на канале в телеграм

Я откладывал месяцами рефакторинг этого кода, как говориться "работает - не трогай". Но хотелось этот проект привести в порядок, чтобы проще было вносить изменения, легче запускать и переносить на другой сервер, мониторить работоспособность и тд. При этом сделать это за выходные, в свое личное время.

Ожидаемый результат после рефакторинга

  1. Отрефакторить код так, чтобы легче было вносить изменения. Важный момент - отрефакторить без изменения бизнес логики, по сути раскидать god object по файлам, сам код не править, иначе это затянет сроки. Следовать PSR-12.

  2. Докеризировать воркера для удобства переноса на другой сервер и прозрачность запуска и остановки

  3. Запускать воркера через supervisor

  4. Внедрить процесс тестирования кода, настроить Codeception

  5. Докеризировать MySQL и Redis

  6. Настроить Github Actions для запуска тестов и проверки на code style

  7. Поднять Prometheus, Grafana для метрик и мониторинга работоспособности

  8. Сделать докер контейнер, который будет отдавать метрики на страницу /metrics для Prometheus

  9. Сделать докер образ для бота телеграм, который будет отдавать срез по всем статусам 4 боссов в данный момент командами боту в личку

Важное замечание. Все эти шаги выполнялись не совсем в том порядке, как я их описываю в этом туториале. Сделал все требуемое за выходные плюс пара вечеров после работы. Так же в целях не было сделать проект "идеальным", не совершать "революции", а дать возможность проекту плавно эволюционировать. Большая часть пунктов из плана давала возможность развивать проект.

Шаг 1. Рефакторинг приложения

Одним из требований было не потратить на это недели, поэтому основные классы я решил сделать наследниками Singleton

<?phpdeclare(strict_types=1);namespace AsteriosBot\Core\Support;use AsteriosBot\Core\Exception\DeserializeException;use AsteriosBot\Core\Exception\SerializeException;class Singleton{    protected static $instances = [];    /**     * Singleton constructor.     */    protected function __construct()    {        // do nothing    }    /**     * Disable clone object.     */    protected function __clone()    {        // do nothing    }    /**     * Disable serialize object.     *     * @throws SerializeException     */    public function __sleep()    {        throw new SerializeException("Cannot serialize singleton");    }    /**     * Disable deserialize object.     *     * @throws DeserializeException     */    public function __wakeup()    {        throw new DeserializeException("Cannot deserialize singleton");    }    /**     * @return static     */    public static function getInstance(): Singleton    {        $subclass = static::class;        if (!isset(self::$instances[$subclass])) {            self::$instances[$subclass] = new static();        }        return self::$instances[$subclass];    }}

Таким образом вызов любого класса, который от него наследуются, можно делать методом getInstance()

Вот так, например, выглядел класс подключения к базе данных

<?phpdeclare(strict_types=1);namespace AsteriosBot\Core\Connection;use AsteriosBot\Core\App;use AsteriosBot\Core\Support\Config;use AsteriosBot\Core\Support\Singleton;use FaaPz\PDO\Database as DB;class Database extends Singleton{    /**     * @var DB     */    protected DB $connection;    /**     * @var Config     */    protected Config $config;    /**     * Database constructor.     */    protected function __construct()    {        $this->config = App::getInstance()->getConfig();        $dto = $this->config->getDatabaseDTO();        $this->connection = new DB($dto->getDsn(), $dto->getUser(), $dto->getPassword());    }    /**     * @return DB     */    public function getConnection(): DB    {        return $this->connection;    }}

В процессе рефакторинга я не менял саму бизнес логику, оставил все "как было". Цель было именно разнести по файлам для облегчения изменения правок, а так же для возможности потом покрыть тестами.

Шаг 2: Докеризация воркеров

Запуск всех контейнеров я сделал через docker-compose.yml

Конфиг сервиса для воркеров выглядит так:

  worker:    build:      context: .      dockerfile: docker/worker/Dockerfile    container_name: 'asterios-bot-worker'    restart: always    volumes:      - .:/app/    networks:      - tier

А сам docker/worker/Dockerfile выглядит так:

FROM php:7.4.3-alpine3.11# Copy the application codeCOPY . /appRUN apk update && apk add --no-cache \    build-base shadow vim curl supervisor \    php7 \    php7-fpm \    php7-common \    php7-pdo \    php7-pdo_mysql \    php7-mysqli \    php7-mcrypt \    php7-mbstring \    php7-xml \    php7-simplexml \    php7-openssl \    php7-json \    php7-phar \    php7-zip \    php7-gd \    php7-dom \    php7-session \    php7-zlib \    php7-redis \    php7-session# Add and Enable PHP-PDO ExtenstionsRUN docker-php-ext-install pdo pdo_mysqlRUN docker-php-ext-enable pdo_mysql# RedisRUN apk add --no-cache pcre-dev $PHPIZE_DEPS \        && pecl install redis \        && docker-php-ext-enable redis.so# Install PHP ComposerRUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer# Remove CacheRUN rm -rf /var/cache/apk/*# setup supervisorADD docker/supervisor/asterios.conf /etc/supervisor/conf.d/asterios.confADD docker/supervisor/supervisord.conf /etc/supervisord.confVOLUME ["/app"]WORKDIR /appRUN composer installCMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]

Обратите внимание на последнюю строку в Dockerfile, там я запускаю supervisord, который будет мониторить работу воркеров.

Шаг 3: Настройка supervisor

Важный дисклеймер по supervisor. Он предназначен для работы с процессами, которые работают долго, и в случае его "падения" - перезапустить. Мои же php скрипты работали быстро и сразу завершались. supervisor пробовал их перезапустить, и в конце концов переставал пытаться поднять снова. Поэтому я решил сам код воркера запускать на 1 минуту, чтобы это работало с supervisor.

Код файла worker.php

<?phprequire __DIR__ . '/vendor/autoload.php';use AsteriosBot\Channel\Checker;use AsteriosBot\Channel\Parser;use AsteriosBot\Core\App;use AsteriosBot\Core\Connection\Log;$app = App::getInstance();$checker = new Checker();$parser = new Parser();$servers = $app->getConfig()->getEnableServers();$logger = Log::getInstance()->getLogger();$expectedTime = time() + 60; // +1 min in seconds$oneSecond = time();while (true) {    $now = time();    if ($now >= $oneSecond) {        $oneSecond = $now + 1;        try {            foreach ($servers as $server) {                $parser->execute($server);                $checker->execute($server);            }        } catch (\Throwable $e) {            $logger->error($e->getMessage(), $e->getTrace());        }    }    if ($expectedTime < $now) {        die(0);    }}

У RSS есть защита от спама, поэтому пришлось сделать проверку на секунды и посылать не более 1го запроса в секунду. Таким образом мой воркер каждую секунду выполняет 2 действия, сначала проверяет rss, а затем калькулирует время боссов для сообщений о старте или окончании времени респауна боссов. После 1 минуты работы воркер умирает, и его перезапускает supervisor

Сам конфиг supervisor выглядит так:

[program:worker]command = php /app/worker.phpstderr_logfile=/app/logs/supervisor/worker.lognumprocs = 1user = rootstartsecs = 3startretries = 10exitcodes = 0,2stopsignal = SIGINTreloadsignal = SIGHUPstopwaitsecs = 10autostart = trueautorestart = truestdout_logfile = /dev/stdoutstdout_logfile_maxbytes = 0redirect_stderr = true

После старта контейнеров супервизор стартует воркера автоматически. Важный момент - в файле основного конфига /etc/supervisord.confобязательно нужно указать демонизация процесса, а так же подключение своих конфигов

[supervisord]nodaemon=true[include]files = /etc/supervisor/conf.d/*.conf

Набор полезных команд supervisorctl:

supervisorctl status       # статус воркеровsupervisorctl stop all     # остановить все воркераsupervisorctl start all    # запустить все воркераsupervisorctl start worker # запустить один воркера с конфига, блок [program:worker]

Шаг 4: Настройка Codeception

Я планирую в свободное время по чуть-чуть покрывать unit тестами уже существующий код, а со временем сделать еще и интеграционные. Пока что настроил только юнит тестирование и написал пару тестов на особо важную бизнес логику. Настройка была тривиальной, все завелось с коробки, только добавил в конфиг поддержку базы данны

# Codeception Test Suite Configuration## Suite for unit or integration tests.actor: UnitTestermodules:    enabled:        - Asserts        - \Helper\Unit        - Db:              dsn: 'mysql:host=mysql;port=3306;dbname=test_db;'              user: 'root'              password: 'password'              dump: 'tests/_data/dump.sql'              populate: true              cleanup: true              reconnect: true              waitlock: 10              initial_queries:                - 'CREATE DATABASE IF NOT EXISTS test_db;'                - 'USE test_db;'                - 'SET NAMES utf8;'    step_decorators: ~

Шаг 5: Докеризация MySQL и Redis

На сервере, где работало это приложение, у меня было еще пара других ботов. Все они использовали один сервер MySQL и один Redis для кеша. Я решил вынести все, что связано с окружением в отельный docker-compose.yml, а самих ботов залинковать через внешний docker network

Выглядит это так:

version: '3'services:  mysql:    image: mysql:5.7.22    container_name: 'telegram-bots-mysql'    restart: always    ports:      - "3306:3306"    environment:      MYSQL_ROOT_PASSWORD: "${DB_PASSWORD}"      MYSQL_ROOT_HOST: '%'    volumes:      - ./docker/sql/dump.sql:/docker-entrypoint-initdb.d/dump.sql    networks:      - tier  redis:    container_name: 'telegram-bots-redis'    image: redis:3.2    restart: always    ports:      - "127.0.0.1:6379:6379/tcp"    networks:      - tier  pma:    image: phpmyadmin/phpmyadmin    container_name: 'telegram-bots-pma'    environment:      PMA_HOST: mysql      PMA_PORT: 3306      MYSQL_ROOT_PASSWORD: "${DB_PASSWORD}"    ports:      - '8006:80'    networks:      - tiernetworks:  tier:    external:      name: telegram-bots-network

DB_PASSWORD я храню в .env файле, а ./docker/sql/dump.sql у меня лежит бекап для инициализации базы данных. Так же я добавил external network так же, как в этом конфиге - в каждом docker-compose.yml каждого бота на сервере. Таким образом они все находятся в одной сети и могут использовать общие базу данных и редис.

Шаг 6: Настройка Github Actions

В шаге 4 этого туториала я добавил тестовый фреймфорк Codeception, который для тестирования требует базу данных. В самом проекте нет базы, в шаге 5 я ее вынес отдельно и залинковал через external docker network. Для запуска тестов в Github Actions я решил полностью собрать все необходимое на лету так же через docker-compose.

name: Actionson:  pull_request:    branches: [master]  push:    branches: [master]jobs:  build:    runs-on: ubuntu-latest    steps:      - name: Checkout        uses: actions/checkout@v2      - name: Get Composer Cache Directory        id: composer-cache        run: |          echo "::set-output name=dir::$(composer config cache-files-dir)"      - uses: actions/cache@v1        with:          path: ${{ steps.composer-cache.outputs.dir }}          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}          restore-keys: |            ${{ runner.os }}-composer-      - name: Composer validate        run: composer validate      - name: Composer Install        run: composer install --dev --no-interaction --no-ansi --prefer-dist --no-suggest --ignore-platform-reqs      - name: PHPCS check        run: php vendor/bin/phpcs --standard=psr12 app/ -n      - name: Create env file        run: |          cp .env.github.actions .env      - name: Build the docker-compose stack        run: docker-compose -f docker-compose.github.actions.yml -p asterios-tests up -d      - name: Sleep        uses: jakejarvis/wait-action@master        with:          time: '30s'      - name: Run test suite        run: docker-compose -f docker-compose.github.actions.yml -p asterios-tests exec -T php vendor/bin/codecept run unit

Инструкция onуправляет когда билд триггернётся. В моем случае - при создании пулл реквеста или при коммите в мастер.

Инструкция uses: actions/checkout@v2 запускает проверку доступа процесса к репозиторию.

Далее идет проверка кеша композера, и установка пакетов, если в кеше не найдено

Затем в строке run: php vendor/bin/phpcs --standard=psr12 app/ -nя запускаю проверку кода соответствию стандарту PSR-12 в папке ./app

Так как тут у меня специфическое окружение, я подготовил файл .env.github.actionsкоторый копируется в .env Cодержимое .env.github.actions

SERVICE_ROLE=testTG_API=XXXXXTG_ADMIN_ID=123TG_NAME=AsteriosRBbotDB_HOST=mysqlDB_NAME=rootDB_PORT=3306DB_CHARSET=utf8DB_USERNAME=rootDB_PASSWORD=passwordLOG_PATH=./logs/DB_NAME_TEST=test_dbREDIS_HOST=redisREDIS_PORT=6379REDIS_DB=0SILENT_MODE=trueFILLER_MODE=true

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

Затем я собираю проект при помощи docker-compose.github.actions.ymlв котором прописано все необходимое для тестирвания, контейнер с проектом и база данных. Содержимое docker-compose.github.actions.yml:

version: '3'services:  php:    build:      context: .      dockerfile: docker/php/Dockerfile    container_name: 'asterios-tests-php'    volumes:      - .:/app/    networks:      - asterios-tests-network  mysql:    image: mysql:5.7.22    container_name: 'asterios-tests-mysql'    restart: always    ports:      - "3306:3306"    environment:      MYSQL_DATABASE: asterios      MYSQL_ROOT_PASSWORD: password    volumes:      - ./tests/_data/dump.sql:/docker-entrypoint-initdb.d/dump.sql    networks:      - asterios-tests-network##  redis:#    container_name: 'asterios-tests-redis'#    image: redis:3.2#    ports:#      - "127.0.0.1:6379:6379/tcp"#    networks:#      - asterios-tests-networknetworks:  asterios-tests-network:    driver: bridge

Я закомментировал контейнер с Redis, но оставил возможность использовать его в будущем. Сборка с кастомным docker-compose файлом, а затем тесты - запускается так

docker-compose -f docker-compose.github.actions.yml -p asterios-tests up -ddocker-compose -f docker-compose.github.actions.yml -p asterios-tests exec -T php vendor/bin/codecept run unit

Внимательный читатель обратит внимание на пункт между стартом контейнеров и запуском тестов. Это задержка в 30 секунд для того, чтобы база данных успела заполниться тестовыми данными.

Шаг 7: Настройка Prometheus и Grafana

В шаге 5 я вынес MySQL и Redis в отдельный docker-compose.yml. Так как Prometheus и Grafana тоже общие для всех моих телеграм ботов, я их добавил туда же. Сам конфиг этих контейнеров выглядит так:

  prometheus:    image: prom/prometheus:v2.0.0    command:      - '--config.file=/etc/prometheus/prometheus.yml'    restart: always    ports:      - 9090:9090    volumes:      - ./prometheus.yml:/etc/prometheus/prometheus.yml    networks:      - tier  grafana:    container_name: 'telegram-bots-grafana'    image: grafana/grafana:7.1.1    ports:      - 3000:3000    environment:      - GF_RENDERING_SERVER_URL=http://renderer:8081/render      - GF_RENDERING_CALLBACK_URL=http://grafana:3000/      - GF_LOG_FILTERS=rendering:debug    volumes:      - ./grafana.ini:/etc/grafana/grafana.ini      - grafanadata:/var/lib/grafana    networks:      - tier    restart: always  renderer:    image: grafana/grafana-image-renderer:latest    container_name: 'telegram-bots-grafana-renderer'    restart: always    ports:      - 8081    networks:      - tier

Они так же залинкованы одной сетью, которая потом линкуется с external docker network.

Prometheus: я прокидываю свой конфиг prometheus.yml, где я могу указать источники для парсинга метрик

Grafana: я создаю volume, где будут храниться конфиги и установленные плагины. Так же я прокидываю ссылку на сервис рендеринга графиков, который мне понадобиться для отправки alert. С этим плагином alert приходит со скриншотом графика.

Поднимаю проект и устанавливаю плагин, затем перезапускаю Grafana контейнер

docker-compose up -ddocker-compose exec grafana grafana-cli plugins install grafana-image-rendererdocker-compose stop  grafana docker-compose up -d grafana

Шаг 8: Публикация метрик приложения

Для сбора и публикации метрик я использовал endclothing/prometheus_client_php

Так выглядит мой класс для метрик

<?phpdeclare(strict_types=1);namespace AsteriosBot\Core\Connection;use AsteriosBot\Core\App;use AsteriosBot\Core\Support\Singleton;use Prometheus\CollectorRegistry;use Prometheus\Exception\MetricsRegistrationException;use Prometheus\Storage\Redis;class Metrics extends Singleton{    private const METRIC_HEALTH_CHECK_PREFIX = 'healthcheck_';    /**     * @var CollectorRegistry     */    private $registry;    protected function __construct()    {        $dto = App::getInstance()->getConfig()->getRedisDTO();        Redis::setDefaultOptions(            [                'host' => $dto->getHost(),                'port' => $dto->getPort(),                'database' => $dto->getDatabase(),                'password' => null,                'timeout' => 0.1, // in seconds                'read_timeout' => '10', // in seconds                'persistent_connections' => false            ]        );        $this->registry = CollectorRegistry::getDefault();    }    /**     * @return CollectorRegistry     */    public function getRegistry(): CollectorRegistry    {        return $this->registry;    }    /**     * @param string $metricName     *     * @throws MetricsRegistrationException     */    public function increaseMetric(string $metricName): void    {        $counter = $this->registry->getOrRegisterCounter('asterios_bot', $metricName, 'it increases');        $counter->incBy(1, []);    }    /**     * @param string $serverName     *     * @throws MetricsRegistrationException     */    public function increaseHealthCheck(string $serverName): void    {        $prefix = App::getInstance()->getConfig()->isTestServer() ? 'test_' : '';        $this->increaseMetric($prefix . self::METRIC_HEALTH_CHECK_PREFIX . $serverName);    }}

Для проверки работоспособности парсера мне нужно сохранить метрику в Redis после получения данных с RSS. Если данные получены, значит все нормально, и можно сохранить метрику

        if ($counter) {            $this->metrics->increaseHealthCheck($serverName);        }

Где переменная $counter это количество записей в RSS. Там будет 0, если получить данные не удалось, и значит метрика не будет сохранена. Это потом понадобится для alert по работе сервиса.

Затем нужно метрики опубликовать на странице /metric чтобы Prometheus их спарсил. Добавим хост в конфиг prometheus.yml из шага 7.

# my global configglobal:  scrape_interval:     5s # Set the scrape interval to every 15 seconds. Default is every 1 minute.  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.  # scrape_timeout is set to the global default (10s).scrape_configs:  - job_name: 'bots-env'    static_configs:      - targets:          - prometheus:9090          - pushgateway:9091          - grafana:3000          - metrics:80 # тут будут мои метрики по uri /metrics

Код, который вытащит метрики из Redis и создаст страницу в текстовом формате. Эту страничку будет парсить Prometheus

$metrics = Metrics::getInstance();$renderer = new RenderTextFormat();$result = $renderer->render($metrics->getRegistry()->getMetricFamilySamples());header('Content-type: ' . RenderTextFormat::MIME_TYPE);echo $result;

Теперь настроим сам дашборд и alert. В настройках Grafana сначала укажите свой Prometheus как основной источник данных, а так же я добавил основной канал нотификации Телеграм (там добавляете токен своего бота и свой chat_id с этим ботом)

Настройка GrafanaНастройка Grafana
  1. Метрика increase(asterios_bot_healthcheck_x3[1m]) Показывает на сколько метрика asterios_bot_healthcheck_x3 увеличилась за 1 минуту

  2. Название метрики (будет под графиком)

  3. Название для легенды в пункте 4.

  4. Легенда справа из пункта 3.

  1. Правило, по которому проверяется метрика. В моем случае проверяет что за последние 30 секунд проблем не было

  2. Правило, по которому будет срабатывать alert. В моем случае "Когда сумма из метрики А между сейчас и 10 секунд назад"

  3. Если нет данных вообще - слать alert

  4. Сообщение в alert

Выглядит alert в телеграм так (помните мы настраивали рендеринг картинок для alert?)

Alert в ТелеграмAlert в Телеграм
  1. Обратите внимание, alert заметил падение, но все восстановилось. Grafana приготовилась слать alert, но передумала. Это то самое правило 30 секунд

  2. Тут уже все упало больше чем на 30 секунд и alert был отправлен

  3. Сообщение, которое мы указали в настройках alert

  4. Ссылка на dashboard

  5. Источник метрики

Шаг 9: Телеграм бот

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

Итоги

Я жалею, что не сделал этого раньше. Я останавливал бота на период переустановки с новыми конфигами, и пользователи сразу стали просить пару новых фичей, которые добавить стали легче и быстрее. Надеюсь эта публикация вдохновит отрефакторить свой пет-проект и привести его в порядок. Не переписать, а именно отрефакторить.

Ссылки на проекты

Подробнее..

Recovery mode DevOps автоматизация инфраструктуры на примере Terraform, docker, bash, prometheus exporters, Gitlab и WireGuard

16.03.2021 12:09:05 | Автор: admin

Всем привет.

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

Хочу показать на небольшом примере, что автоматизировать инфраструктуру, например в AWS, может быть достаточно просто и приятно, а получившийся результат достаточно прозрачен и сам по себе является документацией, т.к. это инфраструктура как код. Если конечно есть знания Terraform или желание его немного изучить.

К слову, крайне рекомендую для автоматизации много чего, но в особенности облачных провайдеров вроде AWS / GCP / Azure и т.д. использовать именно Terraform, т.к. это достаточно зрелый инструмент, у него большое сообщество и кроме всего прочего он поддерживает автоматизацию далеко не только каких-то облачных провайдеров, но и практически всего у чего есть API. К тому же инструмент open source и при желании можно реализовать что угодно самостоятельно. Для таких облаков, как AWS не рекомендую пытаться реализовывать автоматизации с помощью чистого питона и запросов к AWS API с помощью cli или Cloudformation.

Также у Terraform есть удобная возможность организовывать код в блоки называемые модулями и передавая в них только параметры легко создавать необходимое с другими настройками.

Для тех, кто совсем не знаком с Terraform упомяну, что если в одну папку положить несколько файлов с расширением .tf и запустить Terraform в этой папке, то Terraform прочитает и использует код из всех файлов, а не только одного. Это в том числе позволяет разбивать один большой main.tf файл с кучей ресурсов на какие-то удобные вам логические блоки.

Итак, например, встала задача развернуть vpn серверы WireGuard на базе Ubuntu 20.04 в нескольких регионах + немного мониторинга. Поддержка WireGuard сейчас есть в ядре linux, но дополнительные инструменты, которые можно поставить отдельно облегчают жизнь, поэтому поставим и их.

Весь код модуля выложен здесь.

Также этот модуль опубликован в реестре модулей здесь.

Не буду разжёвывать код построчно, попробую описать архитектуру в целом, а код попробуйте разобрать самостоятельно. Если возникнут неразрешимые проблемы или вопросы попробую ответить.

Итак, создаются необходимые для работы iam политики, роль и т.п.

Используем elastic ip, отдельный для сервера в каждом регионе, которые нужно прописать в dns для того, чтобы пользователь мог использовать единое имя для подключения к vpn серверу. Планировал использовать geo dns route53, чтобы при местоположении пользователя в оперделённом регионе ему бы отдавался ip vpn сервера в его регионе, но т.к. на этом проекте route53 пока не используется, то создание записей в нём пока не автоматизировал.

Создаются security groups с правилами, которые позволяют подключиться к vpn серверу извне по udp (Wireguard работает только по udp) + ssh + несколько портов для prometheus exporter'ов.

Создаётся собственно сервер / ec2 машина, но не просто отдельно стоящая, а входящая в auto scaling group, в данном примере в единственном варианте. Это сделано для того, чтобы если с сервером что-то не так, то Амазон автоматом пересоздаст его. Self healing.

Позже немного допилив конфигурацию и добавив в неё load balancer можно добиться того, для чего auto scaling groups отлично подходят: при повышенной нагрузке на какой-то из ресурсов сервера, например на cpu, можно реализовать автоматическое создание дополнительных vpn серверов, а соответственно при падении нагрузки уменьшать их количество.

Этот модуль можно использовать просто с Terraform, но лучше использовать Terragrunt, который позволяет делать некоторые удобные вещи и местами реализовывать концепцию Keep your Terraform code DRY, например параметризуя некоторые вещи в backend блоке, чего сам Terraform пока не умеет. Terraform хранит состояние инфраструктуры в специальном файле и принято хранить его не локально, а, чаще всего, в S3 бакете. Также, если вы работаете с этим кодом не в одиночку, то принято хранить локи в Dynamodb, чтобы случайно не применить какое-то изменение инфраструктуры несогласованно и не поломать всё.

Именно пример такого использования я привожу в примере здесь.

С помощью файла terragrunt.hcl в корне репозитория (https://github.com/vainkop/terraform-aws-wireguard/blob/master/example/terragrunt.hcl) я могу, например, задать место для хранения state для всех поддиректорий, а потом ссылаться на этот файл в других terragrunt.hcl с помощью функции find_in_parent_folders() https://github.com/vainkop/terraform-aws-wireguard/blob/master/example/us-east-1/terragrunt.hcl#L2

При этом key, т.е. файл, где будет храниться состояние инфраструктуры в конкретном регионе будет храниться отдельно, что достигается с помощью функции path_relative_to_include() https://github.com/vainkop/terraform-aws-wireguard/blob/master/example/terragrunt.hcl#L11

Также я реализовал хранение/чтение переменных в yaml формате, что мне кажется более удобочитаемым с помощью функции yamldecode(file(...)) https://github.com/vainkop/terraform-aws-wireguard/blob/master/example/eu-central-1/terragrunt.hcl#L9

Вот так выглядит пример передаваемых в модуль уникальных параметров (конечно YOUR_... нужно заменить на реальные значения): https://github.com/vainkop/terraform-aws-wireguard/blob/master/example/us-east-1/values.yaml

Иногда удобно реализовать использование имени папки в качестве параметра, например в приведённом примере это мог бы быть параметр region и реализуется это с помощью, например, функций basename(get_terragrunt_dir()) и задавать его в values.yaml не пришлось бы, но по определённым причинам решил этого не делать.

В итоге в вашем приватном репозитории код из которого применяете либо вы, либо какой-то ci cd runner может лежать только содержимое похожее на мою папку example, т.е. только terragrunt.hcl и yaml файлы с параметрами, а модуль можно использовать как публичный и хорошо поддерживаемый, так и написать свой. Это позволяет отдать "пользователям" только задание параметров в yaml и в принципе ничего не знать про Terraform код.

Отдельно советую: по-возможности, не нужно изобретать велосипед и на каждое действие пытаться писать свой Terraform код или тем более модуль. Для большинства вещей модули уже написаны и в их разработке и поддержке участвует множество грамотных людей, поэтому берите и пользуйтесь.

Для того, чтобы изменения в коде open source модулей, как впрочем и в частных, не повлияли на работу вашей автоматизации принято фиксировать версии используемых модулей, например в моём коде это сделано с помощью source = "github.com/vainkop/terraform-aws-wireguard?ref=v1.2.0" здесь https://github.com/vainkop/terraform-aws-wireguard/blob/master/example/eu-central-1/terragrunt.hcl#L6

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

Например я реализовал cloud-init скрипт, который осуществляет предварительную установку и настройку софта на свежеразвёрнутый сервер и делает это каждый раз, когда сервер пересоздаётся в auto scaling group, что очень удобно: https://github.com/vainkop/terraform-aws-wireguard/blob/master/templates/user-data.txt

Ближе к концу скрипта устанавливается 2 prometheus exporter'а, которые позволяют как мониторить метрики самой ec2 машины, так и базовые метрики самого WireGuard, на основании которых можно построить удобные Dashboards и соответственно определённые alerts и т.п.

В частности я реализовал это для того, чтобы видеть к какому из vpn серверов подключён клиент, чтобы, например, была возможность подключиться к нему именно из его региона, т.к. связности между этими vpn серверами нет. Т.к. клиентские публичные ключи зашиты в каждый из серверов и серверные ключи одинаковые, то клиент будет автоматически переключаться между ними путешествуя между регионами на основании geo ip route53.

Также привожу пример кода из .gitlab-ci.yml и Dockerfile где можно увидеть какие команды используются для применения всего этого хозяйства с помощью Gitlab runner'а и какой docker контейнер можно использовать для этого runner'а.

$ cat .gitlab-ci.ymlstages:  - build  - plan  - apply  - destroyvariables:  GIT_DEPTH: 1.aws_configure: &aws_configure  before_script:    - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID    - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY    - aws configure set default.region $AWS_DEFAULT_REGIONbuild-terraform:  image: docker:19.03.15  services:    - docker:19.03.15-dind  stage: build  variables:    DOCKER_TLS_CERTDIR: ""    DOCKER_HOST: tcp://docker:2375    DOCKER_DRIVER: overlay2    TERRAFORM_VERSION: "0.13.6"    TERRAGRUNT_VERSION: "v0.28.9"  before_script:    - printenv    - docker info    - echo $CI_REGISTRY_PASSWORD | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin  script:    - cd docker    - docker build --build-arg TERRAFORM_VERSION=$TERRAFORM_VERSION --build-arg TERRAGRUNT_VERSION=$TERRAGRUNT_VERSION -t $CI_REGISTRY_IMAGE:$TERRAFORM_VERSION .    - docker push $CI_REGISTRY_IMAGE:$TERRAFORM_VERSION  rules:    - changes:        - docker/*plan-us-east-1:  image:    name: registry.gitlab.com/vainkop/terraform:0.13.6    entrypoint: [""]  stage: plan  <<: *aws_configure  script:    - cd wireguard/us-east-1    - terragrunt run-all plan --terragrunt-non-interactive -out $CI_PROJECT_DIR/wireguard/us-east-1/tfplan-$CI_COMMIT_SHA  artifacts:    paths:    - $CI_PROJECT_DIR/wireguard/us-east-1/tfplan-$CI_COMMIT_SHA    expire_in: 1 month  rules:    - changes:        - wireguard/us-east-1/*      allow_failure: trueplan-eu-central-1:  image:    name: registry.gitlab.com/vainkop/terraform:0.13.6    entrypoint: [""]  stage: plan  <<: *aws_configure  script:    - cd wireguard/eu-central-1    - terragrunt run-all plan --terragrunt-non-interactive -out $CI_PROJECT_DIR/wireguard/eu-central-1/tfplan-$CI_COMMIT_SHA  artifacts:    paths:    - $CI_PROJECT_DIR/wireguard/eu-central-1/tfplan-$CI_COMMIT_SHA    expire_in: 1 month  rules:    - changes:        - wireguard/eu-central-1/*      allow_failure: trueapply-us-east-1:  image:    name: registry.gitlab.com/vainkop/terraform:0.13.6    entrypoint: [""]  stage: apply  <<: *aws_configure  script:    - cd wireguard/us-east-1    - terragrunt run-all apply --terragrunt-non-interactive -auto-approve $CI_PROJECT_DIR/wireguard/us-east-1/tfplan-$CI_COMMIT_SHA  rules:    - changes:        - wireguard/us-east-1/*      when: manual      allow_failure: trueapply-eu-central-1:  image:    name: registry.gitlab.com/vainkop/terraform:0.13.6    entrypoint: [""]  stage: apply  <<: *aws_configure  script:    - cd wireguard/eu-central-1    - terragrunt run-all apply --terragrunt-non-interactive -auto-approve $CI_PROJECT_DIR/wireguard/eu-central-1/tfplan-$CI_COMMIT_SHA  rules:    - changes:        - wireguard/eu-central-1/*      when: manual      allow_failure: truedestroy-us-east-1:  image:    name: registry.gitlab.com/vainkop/terraform:0.13.6    entrypoint: [""]  stage: destroy  <<: *aws_configure  script:    - cd wireguard/us-east-1    - terragrunt run-all destroy --terragrunt-non-interactive -auto-approve  rules:    - changes:        - wireguard/us-east-1/*      when: manual      allow_failure: truedestroy-eu-central-1:  image:    name: registry.gitlab.com/vainkop/terraform:0.13.6    entrypoint: [""]  stage: destroy  <<: *aws_configure  script:    - cd wireguard/eu-central-1    - terragrunt run-all destroy --terragrunt-non-interactive -auto-approve  rules:    - changes:        - wireguard/eu-central-1/*      when: manual      allow_failure: true
$ cat docker/DockerfileFROM ubuntu:20.04USER rootARG DEBIAN_FRONTEND=noninteractiveARG TERRAFORM_VERSIONENV TERRAFORM_VERSION=$TERRAFORM_VERSIONARG TERRAGRUNT_VERSIONENV TERRAGRUNT_VERSION=$TERRAGRUNT_VERSIONRUN set -x && \    apt-get update && \    apt-get install -y \    apt-transport-https \    ca-certificates \    build-essential \    software-properties-common \    unzip \    net-tools \    wget \    curl \    python3 \    python3-dev \    python3-pip \    jq \    gettext-base \    git && \    rm -rf /var/lib/apt/lists/*RUN set -x && \    apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CC86BB64 && \    add-apt-repository ppa:rmescandon/yq && \    apt update && \    apt install -y yq && \    rm -rf /var/lib/apt/lists/*RUN set -x && \    pip3 install -U --no-cache-dir setuptools shyamlRUN set -x && \    ln -sf /usr/bin/python3 /usr/bin/python && ln -sf /usr/bin/pip3 /usr/bin/pipRUN set -x && \    curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \    unzip awscliv2.zip && \    rm awscliv2.zip && \    ./aws/installRUN set -x && \    cd /tmp && \    curl -O https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip && \    unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip -d /usr/local/bin && \    chmod +x /usr/local/bin/terraform && \    rm /tmp/terraform_${TERRAFORM_VERSION}_linux_amd64.zipRUN set -x && \    wget "https://github.com/gruntwork-io/terragrunt/releases/download/${TERRAGRUNT_VERSION}/terragrunt_linux_amd64" && \    mv terragrunt_linux_amd64 /usr/local/bin/terragrunt && \    chmod +x /usr/local/bin/terragruntRUN set -x && \    curl --version && \    envsubst --version && \    python --version && \    pip --version && \    shyaml --version && \    jq -V && \    yq -V && \    aws --version && \    terraform --version && \    terragrunt --versionENTRYPOINT ["/bin/bash", "-c"]

За код не ругайте, написал за несколько часов и решил поделиться.

Если есть конкретные замечания/предложения, то готов их выслушать либо в комментариях, либо в личке, например, в телеграм: @vainkop

Прошу также учитывать, что это моя первая публикация на Хабре. Приглашение приму с удовольствием.

Подробнее..

Мониторинг Tarantool логи, метрики и их обработка

28.12.2020 18:17:25 | Автор: admin

Tarantool это платформа in-memory вычислений с гибкой схемой данных. На её основе можно создать распределённое хранилище, веб-сервер, высоконагруженное приложение или, в конце концов, сервис, включающий в себя всё вышеперечисленное. Но какой бы ни была ваша промышленная задача, однажды настанет момент, когда её решение придётся мониторить. В этой статье я хочу дать обзор существующих средств для мониторинга приложения на базе Tarantool и пройтись по основным кейсам работы с ними.


Мониторинг Tarantool


Я работаю в команде, которая занимается разработкой, внедрением и поддержкой готовых решений на основе Tarantool. Для вывода наших приложений в эксплуатацию на контуре заказчика было необходимо не только разобраться в текущих возможностях мониторинга, но и доработать их. Большая часть доработок в результате вошла в те или иные стандартные пакеты. Данный материал является текстовой выжимкой этого опыта, и может пригодиться тем, кто решит пройти по той же тропе.


Настройка логов в Tarantool


Базовое конфигурирование и использование логов


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


Каждое сообщение лога имеет свой уровень детализации. Уровень логирования Tarantool характеризуется значением параметра log_level (целое число от 1 до 7):


  1. SYSERROR.
  2. ERROR сообщения log.error(...).
  3. CRITICAL.
  4. WARNING сообщения log.warn(...).
  5. INFO сообщения log.info(...).
  6. VERBOSE сообщения log.verbose(...).
  7. DEBUG сообщения log.debug(...).

Значение параметра log_level N соответствует логу, в который попадают сообщения уровня детализации N и всех предыдущих уровней детализации < N. По умолчанию log_level имеет значение 5 (INFO). Чтобы настроить этот параметр при использовании Cartridge, можно воспользоваться cartridge.cfg:


cartridge.cfg( { ... }, { log_level = 6, ... } )

Для отдельных процессов настройка производится при помощи вызова box.cfg:


box.cfg{ log_level = 6 }

Менять значение параметра можно непосредственно во время работы программы.


Стандартная стратегия логирования: писать об ошибках в log.error() или log.warn() в зависимости от их критичности, отмечать в log.info() основные этапы работы приложения, а в log.verbose() писать более подробные сообщения о предпринимаемых действиях для отладки. Не стоит использовать log.debug() для отладки приложения, этот уровень диагностики в первую очередь предназначен для отладки самого Tarantool. Не рекомендуется также использовать уровень детализации ниже 5 (INFO), поскольку в случае возникновения ошибок отсутствие информационных сообщений затруднит диагностику. Таким образом, в режиме отладки приложения рекомендуется работать при log_level 6 (VERBOSE), в режиме штатной работы при log_level 5 (INFO).


local log = require('log')log.info('Hello world')log.verbose('Hello from app %s ver %d', app_name, app_ver) -- https://www.lua.org/pil/20.htmllog.verbose(app_metainfo) -- type(app_metainfo) == 'table'

В качестве аргументов функции отправки сообщения в лог (log.error/log.warn/log.info/log.verbose/log.debug) можно передать обычную строку, строку с плейсхолдерами и аргументы для их заполнения (аналогично string.format()) или таблицу (она будет неявно преобразована в строку методом json.encode()). Функции лога также работают с нестроковыми данными (например числами), приводя их к строке c помощью tostring().


Tarantool поддерживает два формата логов: plain и json:


2020-12-15 11:56:14.923 [11479] main/101/interactive C> Tarantool 1.10.8-0-g2f18757b72020-12-15 11:56:14.923 [11479] main/101/interactive C> log level 52020-12-15 11:56:14.924 [11479] main/101/interactive I> mapping 268435456 bytes for memtx tuple arena...

{"time": "2020-12-15T11:56:14.923+0300", "level": "CRIT", "message": "Tarantool 1.10.8-0-g2f18757b7", "pid": 5675 , "cord_name": "main", "fiber_id": 101, "fiber_name": "interactive", "file": "\/tarantool\/src\/main.cc", "line": 514}{"time": "2020-12-15T11:56:14.923+0300", "level": "CRIT", "message": "log level 5", "pid": 5675 , "cord_name": "main", "fiber_id": 101, "fiber_name": "interactive", "file": "\/tarantool\/src\/main.cc", "line": 515}{"time": "2020-12-15T11:56:14.924+0300", "level": "INFO", "message": "mapping 268435456 bytes for memtx tuple arena...", "pid": 5675 , "cord_name": "main", "fiber_id": 101, "fiber_name": "interactive", "file": "\/tarantool\/src\/box\/tuple.c", "line": 261}

Настройка формата происходит через параметр log_format так же, как для параметра log_level. Подробнее о форматах можно прочитать в соответствующем разделе документации.


Tarantool позволяет выводить логи в поток stderr, в файл, в конвейер или в системный журнал syslog. Настройка производится с помощью параметра log. О том, как конфигурировать вывод, можно прочитать в документации.


Обёртка логов


Язык Lua предоставляет широкие возможности для замены на ходу практически любого исполняемого кода. Здесь я хотел бы поделиться способом помещения стандартных методов логирования в функцию-обёртку, который мы использовали при реализации сквозного логирования в своих приложениях. Стоит отметить, что способ этот расположен в "серой" зоне легальности, так что прибегать к нему стоит только при отсутствии возможностей решить проблему более элегантно.


local log = require('log')local context = require('app.context')local function init()    if rawget(_G, "_log_is_patched") then        return    end    rawset(_G, "_log_is_patched", true)    local wrapper = function(level)        local old_func = log[level]        return function(fmt, ...)            local req_id = context.id_from_context()            if select('#', ...) ~= 0 then                local stat                stat, fmt = pcall(string.format, fmt, ...)                if not stat then                    error(fmt, 3)                end            end            local wrapped_message            if type(fmt) == 'string' then                wrapped_message = {                    message = fmt,                    request_id = req_id                }            elseif type(fmt) == 'table' then                wrapped_message = table.copy(fmt)                wrapped_message.request_id = req_id            else                wrapped_message = {                    message = tostring(fmt),                    request_id = req_id                }            end            return old_func(wrapped_message)        end    end    package.loaded['log'].error = wrapper('error')    package.loaded['log'].warn = wrapper('warn')    package.loaded['log'].info = wrapper('info')    package.loaded['log'].verbose = wrapper('verbose')    package.loaded['log'].debug = wrapper('debug')    return trueend

Данный код обогащает информацию, переданную в лог в любом поддерживаемом формате, идентификатором запроса request_id.


Настройка метрик в Tarantool


Подключение метрик


Для работы с метриками в приложениях Tarantool существует пакет metrics. Это модуль для создания коллекторов метрик и взаимодействия с ними в разнообразных сценариях, включая экспорт метрик в различные базы данных (InfluxDB, Prometheus, Graphite). Материал основан на функционале версии 0.6.0.


Чтобы установить metrics в текущую директорию, воспользуйтесь стандартной командой:


tarantoolctl rocks install metrics 0.6.0

Чтобы добавить пакет в список зависимостей вашего приложения, включите его в соответствующий пункт rockspec-файла:


dependencies = {    ...,    'metrics == 0.6.0-1',}

Для приложений, использующих фреймворк Cartridge, пакет metrics предоставляет специальную роль cartridge.roles.metrics. Включение этой роли на всех процессах кластера упрощает работу с метриками и позволяет использовать конфигурацию Cartridge для настройки пакета.


Встроенные метрики


Сбор встроенных метрик уже включён в состав роли cartridge.roles.metrics.


Для включения сбора встроенных метрик в приложениях, не использующих фреймворк Cartridge, необходимо выполнить следующую команду:


local metrics = require('metrics')metrics.enable_default_metrics()

Достаточно выполнить её единожды на старте приложения, например поместив в файл init.lua.


В список метрик по умолчанию входят:


  • информация о потребляемой Lua-кодом RAM;
  • информация о текущем состоянии файберов;
  • информация о количестве сетевых подключений и объёме сетевого трафика, принятого и отправленного процессом;
  • информация об использовании RAM на хранение данных и индексов (в том числе метрики slab-аллокатора);
  • информация об объёме операций на спейсах;
  • характеристики репликации спейсов Tarantool;
  • информация о текущем времени работы процесса и другие метрики.

Подробнее узнать о метриках и их значении можно в соответствующем разделе документации.


Для пользователей Cartridge также существует специальный набор встроенных метрик для мониторинга состояния кластера. На данный момент он включает в себя метрику о количестве проблем в кластере, разбитых по уровням критичности (аналогична Issues в WebUI Cartridge).


Плагины для экспорта метрик


Пакет metrics поддерживает три формата экспорта метрик: prometheus, graphite и json. Последний можно использовать, например, в связке Telegraf + InfluxDB.


Чтобы настроить экспорт метрик в формате json или prometheus для процессов с ролью cartridge.roles.metrics, добавьте соответствующую секцию в конфигурацию кластера:


metrics:  export:    - path: '/metrics/json'      format: json    - path: '/metrics/prometheus'      format: prometheus

Экспорт метрик в формате json или prometheus без использования кластерной конфигурации настраивается средствами модуля http так же, как любой другой маршрут.


local json_metrics = require('metrics.plugins.json')local prometheus = require('metrics.plugins.prometheus')local httpd = require('http.server').new(...)httpd:route(    { path = '/metrics/json' },    function(req)        return req:render({            text = json_metrics.export()        })    end)httpd:route( { path = '/metrics/prometheus' }, prometheus.collect_http)

Для настройки graphite необходимо добавить в код приложения следующую секцию:


local graphite = require('metrics.plugins.graphite')graphite.init{    host = '127.0.0.1',    port = 2003,    send_interval = 60,}

Параметры host и port соответствуют конфигурации вашего сервера Graphite, send_interval периодичность отправки данных в секундах.


При необходимости на основе инструментов пакета metrics можно создать собственный плагин для экспорта. Советы по написанию плагина можно найти в соответствующем разделе документации.


Добавление пользовательских метрик


Ядро пакета metrics составляют коллекторы метрик, созданные на основе примитивов Prometheus:


  • counter предназначен для хранения одного неубывающего значения;
  • gauge предназначен для хранения одного произвольного численного значения;
  • summary хранит сумму значений нескольких наблюдений и их количество, а также позволяет вычислять перцентили по последним наблюдениям;
  • histogram агрегирует несколько наблюдений в гистограмму.

Cоздать экземпляр коллектора можно следующей командой:


local gauge = metrics.gauge('balloons')

В дальнейшем получить доступ к объекту в любой части кода можно этой же командой.


Каждый объект коллектора способен хранить сразу несколько значений метрики с разными лейблами. Например, в результате кода


local gauge = metrics.gauge('balloons')gauge:set(1, { color = 'blue' })gauge:set(2, { color = 'red' })

внутри коллектора возникнет два различных значения метрики. Для того чтобы каким-либо образом изменить значение конкретной метрики в коллекторе, необходимо указать соответствующие ей лейблы:


gauge:inc(11, { color = 'blue' }) -- increase 1 by 11

Лейблы концепт, который также был вдохновлён Prometheus используются для различения измеряемых характеристик в рамках одной метрики. Кроме этого, они могут быть использованы для агрегирования значений на стороне базы данных. Рассмотрим рекомендации по их использованию на примере.


В программе есть модуль server, который принимает запросы и способен сам их отправлять. Вместо того, чтобы использовать две различных метрики server_requests_sent и server_requests_received для хранения данных о количестве отправленных и полученных запросов, следует использовать общую метрику server_requests с лейблом type, который может принимать значения sent и received.


Подробнее о коллекторах и их методах можно прочитать в документации пакета.


Заполнение значений пользовательских метрик


Пакет metrics содержит полезный инструмент для заполнения коллекторов метрик коллбэки. Рассмотрим принцип его работы на простом примере.


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


local metrics = require('metrics')local buffer = require('app.buffer')metrics.register_callback(function()    local gauge = metrics.gauge('buffer_count')    gauge.set(buffer.count())end)

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


Мониторинг HTTP-трафика


Пакет metrics содержит набор инструментов для подсчёта количества входящих HTTP-запросов и измерения времени их обработки. Они предназначены для работы со встроенным пакетом http, и подход будет отличаться в зависимости от того, какую версию вы используете.


Чтобы добавить HTTP-метрики для конкретного маршрута при использовании пакета http 1.x.x, вам необходимо обернуть функцию-обработчик запроса в функцию http_middleware.v1:


local metrics = require('metrics')local http_middleware = metrics.http_middlewarehttp_middleware.build_default_collector('summary', 'http_latency')local route = { path = '/path', method = 'POST' }local handler = function() ... endhttpd:route(route, http_middleware.v1(handler))

Для хранения метрик можно использовать коллекторы histogram и summary.


Чтобы добавить HTTP-метрики для маршрутов роутера при использовании пакета http 2.x.x, необходимо воспользоваться следующим подходом:


local metrics = require('metrics')local http_middleware = metrics.http_middlewarehttp_middleware.build_default_collector('histogram', 'http_latency')router:use(http_middleware.v2(), { name = 'latency_instrumentation' })

Рекомендуется использовать один и тот же коллектор для хранения всей информации об обработке HTTP-запросов (например, выставив в начале коллектор по умолчанию функцией build_default_collector или set_default_collector). Прочитать больше о возможностях http_middleware можно в документации.


Глобальные лейблы


В пункте "Добавление пользовательских метрик" вводится понятие лейблов, предназначенных для различения измеряемых характеристик в рамках одной метрики. Их выставление происходит непосредственно при измерении наблюдения в рамках одного коллектора. Однако это не единственный тип лейблов, которые поддерживает пакет metrics.


Для того чтобы прибавить к каждой метрике какой-то общий для процесса или группы процессов Tarantool (например, имя машины или название приложения) лейбл, необходимо воспользоваться механизмом глобальных лейблов:


local metrics = require('metrics')local global_labels = {}-- постоянное значениеglobal_labels.app = 'MyTarantoolApp'-- переменные конфигурации кластера (http://personeltest.ru/aways/www.tarantool.io/ru/doc/latest/book/cartridge/cartridge_api/modules/cartridge.argparse/)local argparse = require('cartridge.argparse')local params, err = argparse.parse()assert(params, err)global_labels.alias = params.alias-- переменные окружения процессаlocal host = os.getenv('HOST')assert(host)global_labels.host = hostmetrics.set_global_labels(global_labels)

Обратите внимание, что метод set_global_labels полностью перезаписывает таблицу глобальных лейблов при каждом вызове.


Роль cartridge.roles.metrics по умолчанию выставляет alias процесса Tarantool в качестве глобального лейбла.


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


Мониторинг внешних параметров


Tarantool не только позволяет организовывать сбор внутренних метрик о работе приложения, но и способен выступать как агент мониторинга внешних параметров. Подмодуль psutils пакета metrics является примером реализации такого поведения.


С помощью psutils можно настроить сбор метрик об использовании CPU процессами Tarantool. Его информация основывается на данных /proc/stat и /proc/self/task. Подключить сбор метрик можно с помощью следующего кода:


local metrics = require('metrics')metrics.register_callback(function()    local cpu_metrics = require('metrics.psutils.cpu')    cpu_metrics.update()end)

Возможность писать код на Lua делает Tarantool гибким инструментом, позволяющим обходить различные препятствия. Например, psutils возник из необходимости следить за использованием CPU вопреки отказу администраторов со стороны заказчика "подружить" в правах файлы /proc/* процессов Tarantool и плагин inputs.procstat Telegraf, который использовался на местных машинах в качестве основного агента.


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


Визуализация метрик


Пример из tarantool/grafana-dashboard


Хранение метрик в Prometheus


Настройка пути для экспорта метрик Tarantool в формате Prometheus описана в пункте "Плагины для экспорта метрик". Ответ запроса по такому маршруту выглядит следующим образом:


...# HELP tnt_stats_op_total Total amount of operations# TYPE tnt_stats_op_total gaugetnt_stats_op_total{alias="tnt_router",operation="replace"} 1tnt_stats_op_total{alias="tnt_router",operation="select"} 57tnt_stats_op_total{alias="tnt_router",operation="update"} 43tnt_stats_op_total{alias="tnt_router",operation="insert"} 40tnt_stats_op_total{alias="tnt_router",operation="call"} 4...

Чтобы настроить сбор метрик в Prometheus, необходимо добавить элемент в массив scrape_configs. Этот элемент должен содержать поле static_configs с перечисленными в targets URI всех интересующих процессов Tarantool и поле metrics_path, в котором указан путь для экспорта метрик Tarantool в формате Prometheus.


scrape_configs:  - job_name: "tarantool_app"    static_configs:      - targets:         - "tarantool_app:8081"        - "tarantool_app:8082"        - "tarantool_app:8083"        - "tarantool_app:8084"        - "tarantool_app:8085"    metrics_path: "/metrics/prometheus"

В дальнейшем найти метрики в Grafana вы сможете, указав в качестве job соответствующий job_name из конфигурации.


Пример готового docker-кластера Tarantool App + Prometheus + Grafana можно найти в репозитории tarantool/grafana-dashboard.


Хранение метрик в InfluxDB


Чтобы организовать хранение метрик Tarantool в InfluxDB, необходимо воспользоваться стеком Telegraf + InfluxDB и настроить на процессах Tarantool экспорт метрик в формате json (см. пункт "Плагины для экспорта метрик"). Ответ формируется следующим образом:


{    ...    {        "label_pairs": {            "operation": "select",            "alias": "tnt_router"        },        "timestamp": 1606202705935266,        "metric_name": "tnt_stats_op_total",        "value": 57    },    {        "label_pairs": {            "operation": "update",            "alias": "tnt_router"        },        "timestamp": 1606202705935266,        "metric_name": "tnt_stats_op_total",        "value": 43    },    ...}

HTTP-плагин Telegraf требует заранее указать список возможных тегов для метрики. Конфигурация, учитывающая все стандартные лейблы, будет выглядеть следующим образом:


[[inputs.http]]    urls = [        "http://tarantool_app:8081/metrics/json",        "http://tarantool_app:8082/metrics/json",        "http://tarantool_app:8083/metrics/json",        "http://tarantool_app:8084/metrics/json",        "http://tarantool_app:8085/metrics/json"    ]    timeout = "30s"    tag_keys = [        "metric_name",        "label_pairs_alias",        "label_pairs_quantile",        "label_pairs_path",        "label_pairs_method",        "label_pairs_status",        "label_pairs_operation"    ]    insecure_skip_verify = true    interval = "10s"    data_format = "json"    name_prefix = "tarantool_app_"    fieldpass = ["value"]

Список urls должен содержать URL всех интересующих процессов Tarantool, настроенные для экспорта метрик в формате json. Обратите внимание, что лейблы метрик попадают в Telegraf и, соответственно, InfluxDB как теги, название которых состоит из префикса label_pairs_ и названия лейбла. Таким образом, если ваша метрика имеет лейбл с ключом mylbl, то для работы с ним в Telegraf и InfluxDB необходимо указать в пункте tag_keys соответствующего раздела [[inputs.http]] конфигурации Telegraf значение ключа label_pairs_mylbl, и при запросах в InfluxDB ставить условия на значения лейбла, обращаясь к тегу с ключом label_pairs_mylbl.


В дальнейшем найти метрики в Grafana вы сможете, указав measurement в формате <name_prefix>http (например, для указанной выше конфигурации значение measurement tarantool_app_http).


Пример готового docker-кластера Tarantool App + Telegraf + InfluxDB + Grafana можно найти в репозитории tarantool/grafana-dashboard.


Стандартный дашборд Grafana


Для визуализации метрик Tarantool с помощью Grafana на Official & community built dashboards опубликованы стандартные дашборды. Шаблон состоит из панелей для мониторинга HTTP, памяти для хранения данных вместе с индексами и операций над спейсами Tarantool. Версию для использования с Prometheus можно найти здесь, а для InfluxDB здесь. Версия для Prometheus также содержит набор панелей для мониторинга состояния кластера, агрегированной нагрузки и потребления памяти.



Чтобы импортировать шаблон дашборды, достаточно вставить необходимый id или ссылку в меню Import на сервере Grafana. Для завершения процесса импорта необходимо задать переменные, определяющие место хранения метрик Tarantool в соответствующей базе данных.


Генерация дашбордов Grafana с grafonnet


Стандартные дашборды Grafana были созданы с помощью инструмента под названием grafonnet. Что это за заморский зверь и как мы к нему пришли?


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


Одним из первых способов решить большинство возникающих проблем было использование механизма динамических переменных (Variables) в Grafana. Например, он позволяет объединить дашборды с метриками из разных зон в одну с удобным переключателем. К сожалению, мы слишком быстро столкнулись с проблемой: использование механизма оповещений (Alert) не совместимо с запросами, использующими динамические переменные.


Любой дашборд в Grafana по сути представляет собой некоторый json. Более того, платформа позволяет без каких-либо затруднений экспортировать в таком формате существующие дашборды. Работать с ним в ручном режиме несколько затруднительно: размер даже небольшого дашборда составляет несколько тысяч строк. Первым способом решения проблемы был скрипт на Python, который заменял необходимые поля в json, по сути превращая один готовый дашборд в другой. Когда разработка библиотеки скриптов пришла к задаче добавления и удаления конкретных панелей, мы начали осознавать, что пытаемся создать генератор дашбордов. И что эту задачу уже кто-то до нас решал.


В открытом доступе можно найти несколько проектов, посвящённых данной теме. К счастью или несчастью, проблема выбора решилась быстро: на контуре заказчика для хранения метрик мы безальтернативно пользовались InfluxDB, а поддержка запросов к InfluxDB хоть в какой-то форме присутствовала только в grafonnet.


grafonnet opensource-проект под эгидой Grafana, предназначенный для программной генерации дашбордов. Он основан на языке программирования jsonnet языке для генерации json. Сам grafonnet представляет собой набор шаблонов для примитивов Grafana (панели и запросы разных типов, различные переменные) с методами для объединения их в цельный дашборд.


Основным преимуществом grafonnet является используемый в нём язык jsonnet. Он прост и минималистичен, и всегда имеет дело с json-объектами (точнее, с их местной расширенной версией, которая может включать в себя функции и скрытые поля). Благодаря этому на любом этапе работы выходной объект можно допилить под себя, добавив или убрав какое-либо вложенное поле, не внося при этом изменений в исходный код.


Начав с форка проекта и сборки нашей дашборды на основе этого форка, впоследствии мы оформили несколько Pull Request-ов в grafonnet на основе наших изменений. Например, один из них добавил поддержку запросов в InfluxDB на основе визуального редактора.


Визуальный редактор запросов InfluxDB


Код наших стандартных дашбордов расположен в репозитории tarantool/grafana-dashboard. Здесь же находится готовый docker-кластер, состоящий из стеков Tarantool App + Telegraf + InfluxDB + Grafana, Tarantool App + Prometheus + Grafana. Его можно использовать для локальной отладки сбора и обработки метрик в вашем собственном приложении.


При желании из стандартных панелей можно собрать дашборд, расширенный панелями для отображения собственных метрик приложения.


На что смотреть?


В первую очередь, стоит следить за состоянием самих процессов Tarantool. Для этого подойдёт, например, стандартный up Prometheus. Можно соорудить простейший healthcheck самостоятельно:


httpd:route(    { path = '/health' },    function(req)        local body = { app = app, alias = alias, status = 'OK' }        local resp = req:render({ json = body })        resp.status = 200        return resp    end)

Рекомендации по мониторингу внешних параметров ничем принципиально не отличаются от ситуации любого другого приложения. Необходимо следить за потреблением памяти на хранение логов и служебных файлов на диске. Заметьте, что файлы с данными .snap и .xlog возникают даже при использовании движка memtx (в зависимости от настроек). При нормальной работе нагрузка на CPU не должна быть чересчур большой. Исключение составляет момент восстановления данных после рестарта процесса: построение индексов может загрузить все доступные потоки процессора на 100 % на несколько минут.


Потребление RAM удобно разделить на два пункта: Lua-память и память, потребляемая на хранение данных и индексов. Память, доступная для выполнения кода на Lua, имеет ограничение в 2 Gb на уровне Luajit. Обычно приближение метрики к этой границе сигнализирует о наличии какого-то серьёзного изъяна в коде. Более того, зачастую такие изъяны приводят к нелинейному росту используемой памяти, поэтому начинать волноваться стоит уже при переходе границы в 512 Mb на процесс. Например, при высокой нагрузке в наших приложениях показатели редко выходили за предел 200-300 Mb Lua-памяти.


При использовании движка memtx потреблением памяти в рамках заданного лимита memtx_memory (он же метрика quota_size) заведует slab-аллокатор. Процесс происходит двухуровнево: аллокатор выделяет в памяти ячейки, которые после занимают сами данные или индексы спейсов. Зарезервированная под занятые или ещё не занятые ячейки память отображена в quota_used, занятая на хранение данных и индексов arena_used (только данных items_used). Приближение к порогу arena_used_ratio или items_used_ratio свидетельствует об окончании свободных зарезервированных ячеек slab, приближение к порогу quota_used_ratio об окончании доступного места для резервирования ячеек. Таким образом, об окончании свободного места для хранения данных свидетельствует приближение к порогу одновременно метрик quota_used_ratio и arena_used_ratio. В качестве порога обычно используют значение 90 %. В редких случаях в логах могут появляться сообщения о невозможности выделить память под ячейки или данные даже тогда, когда quota_used_ratio, arena_used_ratio или items_used_ratio далеки от порогового значения. Это может сигнализировать о дефрагментации данных в RAM, неудачном выборе схем спейсов или неудачной конфигурации slab-аллокатора. В такой ситуации необходима консультация специалиста.


Рекомендации по мониторингу HTTP-нагрузки и операций на спейсах также являются скорее общими, нежели специфичными для Tarantool. Необходимо следить за количеством ошибок и временем обработки запроса в зависимости от кода ответа, а также за резким возрастанием нагрузки. Неравномерность нагрузки по операциям на спейсы может свидетельствовать о неудачном выборе ключа шардирования.


Заключение


Как этот материал, так и пакет metrics назвать "всеохватными" и "универсальными" на данный момент нельзя. Открытыми или находящимися на данный момент в разработке являются вопросы метрик репликации, мониторинга движка vinyl, метрики event loop и полная документация по уже существующим методам metrics.


Не стоит забывать о том, что metrics и grafana-dashboard являются opensource-разработками. Если при работе над своим проектом вы наткнулись на ситуацию, которая не покрывается текущими возможностями пакетов, не стесняйтесь внести предложение в Issues или поделиться вашим решением в Pull Requests.


Надеюсь, что данный материал помог закрыть некоторые вопросы, которые могли возникнуть при решении задачи мониторинга приложения на базе Tarantool. А если какие-то из них так и не получили ответа, всегда можно обратиться напрямую в наш чат в Telegram.


Полезные ссылки:


Подробнее..

Cortex и не только распределённый Prometheus

04.03.2021 10:11:49 | Автор: admin

В последнее время Prometheus стал де-факто стандартом для сбора и хранения метрик. Он удобен для разработчиков ПО - экспорт метрик можно реализовать в несколько строк кода. Для DevOps/SRE, в свою очередь, есть простой язык PromQL для получения метрик из хранилища и их визуализации в той же Grafana.

Но Prometheus имеет ряд недостатков, способы устранения которых я хочу рассмотреть в этой статье. Также разберём деплой Cortex - распределённого хранилища метрик.

Недостатки

  • Отсутствие отказоустойчивости

    Prometheus работает только в единственном экземпляре, никакого HA.

  • Отсутствие распределения нагрузки

    В принципе, он хорошо скейлится вверх с ростом количества ядер. Так что это проблема только для тех у кого действительно много метрик.

  • Нет поддержки multi-tenancy

    Все метрики летят в один большой котёл и разгребать их потом используя PromQL и метки не всегда удобно. Часто хочется разделить различные приложения и\или команды по своим песочницам чтобы они друг другу не мешали.

    Плюс, многие готовые дашборды для Grafana не готовы к тому что много инстансов одного приложения хранят метрики в одном и том же месте - придётся переделывать все запросы, добавлять фильтры по меткам и так далее.

В принципе, все эти проблемы можно решить настроив несколько HA-пар Prometheus и распределив по ним свои приложения. Если перед каждой парой повесить прокси, то можно получить что-то вроде отказоустойчивости.

Но есть и минусы:

  • После того как один хост из пары упадёт/перезагрузится/whatever - у них случится рассинхронизация. В метриках будут пропуски.

  • Все метрики приложения должны умещаться на один хост

  • Управлять таким зоопарком будет сложнее - какие-то из Prometheus могут быть недогружены, какие-то перегружены. В случае запуска в каком-нибудь Kubernetes это не так важно.

Давайте рассмотрим какими ещё способами можно решить это.

PromQL прокси

Например promxy, который размещается перед 2 или более инстансами Prometheus и делает fan-out входящих запросов на все из них. Затем дедуплицирует полученные метрики и, таким образом, закрывает пропуски в метриках (если, конечно, они не попали на один и тот же временной интервал).

Минусы подобного решения на поверхности:

  • Один запрос нагружает сразу все инстансы за прокси

  • Прокси решает только проблему с пропусками в метриках.

Но для тех, у кого нагрузка укладывается в возможности одного Prometheus (либо ее можно грамотно раскидать по нескольким HA-парам) и кому не нужен multi-tenancy - это очень хороший вариант.

Thanos

Thanos - это уже более продвинутое решение.

Он устанавливает рядом с каждым инстансом Prometheus так называемый Sidecar - отдельный демон, который подглядывает за блоками данных, которые генерирует Prometheus. Как только блок закрывается - Sidecar загружает его в объектное хранилище (S3/GCS/Azure). Длина блоков в Prometheus прибита гвоздями и равна 2 часам.

Также он является прокси между GRPC Thanos StoreAPI и Prometheus для получения метрик, которые еще не были загружены в объектное хранилище.

Отдельный компонент Querier реализует PromQL: в зависимости от временного интервала запроса и настроек глубины хранения данных в Prometheus он может направить его в объектное хранилище, в Sidecar или в разбить на два подзапроса - для свежих данных запрос пойдёт через Sidecar в Prometheus, а для более старых - в объектное хранилище.

Отказоустойчивость свежих данных в Thanos реализуется примерно так же как и в promxy - делается fan-out запросов на все причастные сервера, результаты накладываются друг на друга и дедуплицируются. Задача по защите исторических данных лежит на объектном хранилище.

Multi-tenancy есть в некотором зачаточном состоянии, но в эту сторону проект, судя по всему, не развивается особо.

Cortex

Это наиболее сложный и функциональный проект. Его начали разрабатывать в Grafana Labs для своего SaaS решения по хранению метрик и несколько лет назад выложили в open source, с тех пор разработка идёт на гитхабе.

Как можно видеть на диаграмме выше - в нём очень много компонентов. Но бояться не стоит - большую часть из них можно не использовать, либо запускать в рамках одного процесса - single binary mode.

Так как Cortex изначально разрабатывался как SaaS решение - в нём поддерживается end-to-end multi-tenancy.

Хранение метрик

На данный момент в Cortex есть два движка. Оба они хранят данные в объектном хранилище, среди которых поддерживаются:

  • S3

  • GCS

  • Azure

  • OpenStack Swift (экспериментально)

  • Любая примонтированная ФС (например - NFS или GlusterFS). Хранить блоки на локальной ФС смысла нет т.к. они должны быть доступны всему кластеру.

Далее я буду для краткости называть объектное хранилище просто S3.

Chunks Storage

Изначальный движок в Cortex - он хранит каждую timeseries в отдельном чанке в S3 или в NoSQL (Cassandra/Amazon DynamoDB/Google BigTable), а метаданные (индексы) хранятся в NoSQL.

Chunks Storage, думается, со временем совсем выпилят - насколько я слышал, Grafana Labs свои метрики уже мигрировали в Blocks Storage.

Blocks Storage

Новый, более простой и быстрый движок, основанный на Thanos. Который, в свою очередь, использует формат блоков самого Prometheus. С ним нет нужды в NoSQL и модуле Table Manager (но нужен другой - Store Gateway).

Thanos, в данном, случае является внешней vendored зависимостью в коде Cortex. Есть разговоры о том, чтобы объединить два проекта в один, но когда это будет неизвестно (и будет ли вообще).

Архитектура

Далее я буду рассматривать работу с Blocks Storage.

Упрощённо принцип работы следующий:

  • Prometheus собирает метрики с endpoint-ов и периодически отправляет их в Cortex используя Remote Write протокол. По сути это HTTP POST с телом в виде сериализованных в Protocol Buffers метрик сжатый потом Snappy. В самом Prometheus, при этом, можно поставить минимальный retention period - например 1 день или меньше- читаться из него ничего не будет.

  • Модуль Distributor внутри Cortex принимает, валидирует, проверяет per-tenant и глобальные лимиты и опционально шардит пришедшие метрики. Далее он передает их одному или нескольким Ingester (в зависимости от того применяется ли шардинг).

    Также в рамках этого модуля работает HA Tracker (о нём ниже).

  • Ingester ответственен за запись метрик в долговременное хранилище и выдачу их для выполнения запросов. Изначально метрики записываются в локальную ФС в виде блоков длиной 2 часа. Затем, по истечении некоторого времени, они загружаются в S3 и удаляются с локальной ФС.

    Также поддерживается репликация и zone awareness для дублирования блоков по различным availability domain (стойки, ДЦ, AWS AZ и так далее)

  • Store-Gateway служит для отдачи блоков из S3.

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

  • Querier реализует PromQL.

    При получении запроса анализирует его и, если необходимо, разбивает на два - одна часть пойдёт в Store Gateway (для более старых данных), а другая - в Ingester для свежих.

    По факту параллельных запросов может быть больше если запрашиваемый период большой и настроено разбиение по интервалам (об этом дальше в конфиге)

  • Compactor периодически просыпается и сканирует объектное хранилище на предмет блоков, которые можно склеить в более крупные. Это приводит к более эффективному хранению и быстрым запросам.

    Старые блоки не удаляются сразу, а маркируются и удаляются на следующих итерациях чтобы дать время Store-Gateway обнаружить новые, которые уже сжаты.

Отказоустойчивость

Помимо репликации данных между Ingester-ами нам необходимо обеспечить отказоустойчивость самих Prometheus. В Cortex это реализовано просто и элегантно:

  • Два (или более) Prometheus настраиваются на сбор метрик с одних и тех же endpoint-ов

  • В каждом из них настраиваются специальные внешние метки, которые показывают к какой HA-группе принадлежит данный Prometheus и какой у него идентификатор внутри группы.

    Например так:

  external_labels:    __ha_group__: group_1    __ha_replica__: replica_2
  • При приёме метрик Cortex из каждой группы выбирает один Prometheus и сохраняет метрики только от него

  • Остальным отвечает HTTP 202 Accepted и отправляет в /dev/null всё что они прислали

  • Если же активный инстанс перестал присылать метрики (сработал таймаут) - Cortex переключается на приём от кого-то из оставшихся в живых.

Таким образом дедупликация на этом уровне становится не нужна. Правда остаётся вопрос момента переключения - не теряется ли там что-либо (когда старый не прислал, а от нового ещё не приняли), я этот вопрос не изучал глубоко - нужно тестировать.

Авторизация

Каждый запрос на запись метрик из Prometheus должен содержать HTTP-заголовок X-Scope-OrgId равный идентификатору клиента (далее я буду называть их просто tenant, хорошего перевода не придумал). Метрики каждого tenant-а полностью изолированны друг от друга - они хранятся в разных директориях в S3 бакете и на локальной ФС

Таким же образом происходит и чтение метрик - в PromQL запросах тоже нужно тоже указывать этот заголовок.

При этом никакой реальной авторизации Cortex не проводит - он слепо доверяет этому заголовку. Auth Gateway есть в роадмапе, но когда он будет готов неизвестно. Даже просто добавить этот заголовок напрямую в Prometheus нельзя, только используя промежуточный HTTP прокси.

Для более гибкой интеграции Prometheus & Cortex я набросал простенький Remote Write прокси - cortex-tenant, который может вытаскивать ID клиента из меток Prometheus. Это позволяет использовать один инстанс (или HA-группу) Prometheus для отправки метрик нескольким разным клиентам. Мы используем этот функционал для разграничения данных разных команд, приложений и сред.

Авторизацию можно отключить, тогда Cortex не будет проверять наличие заголовка в запросах и будет подразумевать что он всегда равен fake - то есть multi-tenancy будет отключен, все метрики будут падать в один котёл.

При необходимости данные одного клиента можно полностью удалить из кластера - пока это API экспериментально, но работает.

Настройка Cortex

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

Для всех остальных гораздо проще установить несколько HA-пар Prometheus (например на каждую команду или каждый проект) и поверх них натянуть promxy

Так как документация имеет некоторое количество белых пятен - я хочу рассмотреть настройку простого кластера Cortex в режиме single binary - все модули у нас будут работать в рамках одного и того же процесса.

Danger Zone! Дальше много конфигов!

Зависимости

Нам понадобится ряд внешних сервисов для работы.

  • etcd для согласования кластера и хранения Hash Ring

    Cortex также поддерживает Consul и Gossip-протокол, которому не нужно внешнее KV-хранилище. Но для HA-трекера Gossip не поддерживается из-за больших задержек при сходимости. Так что будем юзать etcd

  • memcached для кеширования всего и вся.

    Cortex поддерживает его в нескольких своих модулях. Можно поднять несколько инстансов и указать их все в Cortex - он будет шардить по всем равномерно. В принципе, он не обязателен, но крайне рекомендован с точки зрения производительности.

    Также есть DNS-based discovery через SRV-записи, если не хочется указывать вручную.

  • minio для реализации распределённого S3 хранилища.

    Несколько странный проект, который часто в процессе разработки ломает обратную совместимость и требует полного перезапуска кластера для апгрейда. Также SemVer это для слабаков, поэтому у них версионирование по датам. Его разрабатывают, в основном, индусы - возможно в этом причина...

    Но других вариантов особо нет, можно поднять Ceph с S3 шлюзом, но это еще более громоздко.

    minio поддерживает Erasure Coding для отказоустойчивости, что есть хорошо.

  • HAProxy для связывания компонентов воедино

  • cortex-tenant для распределения метрик по tenant-ам

  • Prometheus собственно для сбора метрик

Общие вводные

  • Кластер мы будем строить плоский из 4 хостов - все они будут идентичны, с одинаковым набором сервисов. Это хорошо для небольших инсталляций, упрощает структуру.

    3 страйпа не поддерживает minio c Erasure Coding - он нарезает от 4 до 16 дисков в один EC-набор. В реальном проекте лучше использовать 5 или какое-либо большее нечетное число чтобы не было Split Brain.

    Также, если у вас много хостов, то некоторые компоненты, такие как etcd, лучше вынести отдельно. etcd основан на Raft и реплицирует данные на все ноды кластера, смысла в их большом количестве нет - это только увеличит среднюю нагрузку лишними репликами.

  • Все данные будем хранить в /data

  • Конфиги я буду приводить для одного хоста, для остальных обычно достаточно поменять адреса и\или хостнеймы

  • В качестве ОС используем RHEL7, но различия с другими дистрибутивами минимальны

  • У нас всё это дело, конечно, раскатывается через Ansible, но плейбук довольно сильно завязан на нашу инфраструктуру. Я постараюсь потом его подчистить и выложить

  • Некоторые RPM пакеты я собираю вручную (etcd, HAProxy и т.п.) с помощью FPM т.к. в репозиториях древние версии.

/etc/hosts
10.0.0.1 ctx110.0.0.2 ctx210.0.0.3 ctx310.0.0.4 ctx4

etcd

Как и Zookeeper, с настройками по умолчанию etcd - бомба замедленного действия. Он не удаляет ненужные снапшоты и разрастается до бесконечности. Зачем так сделано - мне не понятно.

Поэтому настроим его соответственно:

/etc/etcd/etcd.conf
ETCD_NAME="ctx1"ETCD_LOGGER="zap"ETCD_LOG_LEVEL="warn"ETCD_DATA_DIR="/data/etcd/ctx1.etcd"ETCD_LISTEN_CLIENT_URLS="http://personeltest.ru/away/10.0.0.1:2379,http://127.0.0.1:2379"ETCD_LISTEN_PEER_URLS="http://personeltest.ru/away/10.0.0.1:2380"ETCD_ADVERTISE_CLIENT_URLS="http://personeltest.ru/away/10.0.0.1:2379"ETCD_INITIAL_CLUSTER_TOKEN="cortex"ETCD_INITIAL_ADVERTISE_PEER_URLS="http://personeltest.ru/away/10.0.0.1:2380"ETCD_AUTO_COMPACTION_RETENTION="30m"ETCD_AUTO_COMPACTION_MODE="periodic"ETCD_SNAPSHOT_COUNT="10000"ETCD_MAX_SNAPSHOTS="5"ETCD_INITIAL_CLUSTER="ctx1=http://ctx1:2380,ctx2=http://ctx2:2380,ctx3=http://ctx3:2380,ctx4=http://ctx4:2380"

memcached

Тут всё просто, главное выделить нужное количество памяти. Она зависит от количества метрик, которые мы будем хранить. Чем больше уникальных комбинаций меток - тем больше нужно кеша.

/etc/sysconfig/memcached
PORT="11211"USER="memcached"MAXCONN="512"CACHESIZE="2048"OPTIONS="--lock-memory --threads=8 --max-item-size=64m"

Minio

Тут минимум настроек.

По сути мы просто перечисляем хосты, которые будут использоваться для хранения данных (+ путь до директории где данные хранить - /data/minio) и указываем ключи S3. В моем случае это были ВМ с одним диском, если у вас их несколько - то формат URL несколько меняется.

По умолчанию используется странное распределение дисков под данные и под коды Рида-Соломона: половина сырого объема уходит под redundancy. Так как у нас всего 4 хоста - это не особо важно. Но на большем по размеру кластере лучше использовать Storage Classes для снижения доли Parity-дисков.

/etc/minio/minio.env
MINIO_ACCESS_KEY="foo"MINIO_SECRET_KEY="bar"MINIO_PROMETHEUS_AUTH_TYPE="public"LISTEN="0.0.0.0:9000"ARGS="http://personeltest.ru/away/ctx{1...4}/data/minio"

Также нужно будет создать бакет с помощью minio-client - в нашем случае пусть называется cortex

HAProxy

Он у нас будет служить для равномерного распределения нагрузки по кластеру и отказоустойчивости. Все сервисы обращаются к локальному HAProxy, который в свою очередь проксирует запросы куда нужно.

Таким образом мы имеем что-то вроде Full Mesh топологии и отказ или перезапуск любого из сервисов или хостов целиком не влияет на функциональность кластера.

На больших кластерах (сотни-тысячи хостов) такая схема может быть узким местом, но если вы работаете с такими, то и сами это знаете :)

/etc/haproxy/haproxy.cfg
global    daemon    maxconn 10000    log 127.0.0.1 local2    chroot /var/emptydefaults    mode http    http-reuse safe    hash-type map-based sdbm avalanche    balance roundrobin    retries 3    retry-on all-retryable-errors    timeout connect 2s    timeout client 300s    timeout server 300s    timeout http-request 300s    option splice-auto    option dontlog-normal    option dontlognull    option forwardfor    option http-ignore-probes    option http-keep-alive    option redispatch 1    option srvtcpka    option tcp-smart-accept    option tcp-smart-connect    option allbackupslisten stats    bind 0.0.0.0:6666    http-request use-service prometheus-exporter if { path /metrics }    stats enable    stats refresh 30s    stats show-node    stats uri /frontend fe_cortex    bind 0.0.0.0:8090 tfo    default_backend be_cortexfrontend fe_cortex_tenant    bind 0.0.0.0:8009 tfo    default_backend be_cortex_tenantfrontend fe_minio    bind 0.0.0.0:9001 tfo    default_backend be_miniobackend be_cortex    option httpchk GET /ready    http-check expect rstring ^ready    server ctx1 10.0.0.1:9009 check observe layer7 inter 5s    server ctx2 10.0.0.2:9009 check observe layer7 inter 5s    server ctx3 10.0.0.3:9009 check observe layer7 inter 5s    server ctx4 10.0.0.4:9009 check observe layer7 inter 5sbackend be_cortex_tenant    option httpchk GET /alive    http-check expect status 200    server ctx1 10.0.0.1:8008 check observe layer7 inter 5s    server ctx2 10.0.0.2:8008 check observe layer7 inter 5s backup    server ctx3 10.0.0.3:8008 check observe layer7 inter 5s backup    server ctx4 10.0.0.4:8008 check observe layer7 inter 5s backupbackend be_minio    balance leastconn    option httpchk GET /minio/health/live    http-check expect status 200    server ctx1 10.0.0.1:9000 check observe layer7 inter 5s    server ctx2 10.0.0.2:9000 check observe layer7 inter 5s backup    server ctx3 10.0.0.3:9000 check observe layer7 inter 5s backup    server ctx4 10.0.0.4:9000 check observe layer7 inter 5s backup

cortex-tenant

Это просто прокси между Prometheus и Cortex. Главное - выбрать уникальное имя метки для хранения там tenant ID. В нашем случае это ctx_tenant

/etc/cortex-tenant.yml
listen: 0.0.0.0:8008target: http://127.0.0.1:8090/api/v1/pushlog_level: warntimeout: 10stimeout_shutdown: 10stenant:  label: ctx_tenant  label_remove: true  header: X-Scope-OrgID

Prometheus

В случае 4 хостов Prometheus-ы можно разбить их на две HA-пары, каждую со своим ID группы и раскидать job-ы по ним.

host1 /etc/prometheus/prometheus.yml
global:  scrape_interval: 60s  scrape_timeout: 5s  external_labels:    __ha_group__: group_1    __ha_replica__: replica_1remote_write:  - name: cortex_tenant    url: http://127.0.0.1:8080/pushscrape_configs:  - job_name: job1    scrape_interval: 60s    static_configs:      - targets:          - ctx1:9090        labels:          ctx_tenant: foobar  - job_name: job2    scrape_interval: 60s    static_configs:      - targets:          - ctx2:9090        labels:          ctx_tenant: deadbeef
host2 /etc/prometheus/prometheus.yml
global:  scrape_interval: 60s  scrape_timeout: 5s  external_labels:    __ha_group__: group_1    __ha_replica__: replica_2remote_write:  - name: cortex_tenant    url: http://127.0.0.1:8080/pushscrape_configs:  - job_name: job1    scrape_interval: 60s    static_configs:      - targets:          - ctx1:9090        labels:          ctx_tenant: foobar  - job_name: job2    scrape_interval: 60s    static_configs:      - targets:          - ctx2:9090        labels:          ctx_tenant: deadbeef

По сути конфигурации в пределах одной HA-группы должны отличаться только лейблом реплики.

Cortex

Ну и последнее. Так как мы будем запускать все модули вместе - конфиг получится довольно объемный. Поэтому разделим его на части, чтобы читабельнее было.

Многие модули, такие как Distributor, Ingester, Compactor, Ruler кластеризуются с помощью Hash-Ring в etcd. На весь кластер выделяется некоторое количество токенов, которые распределяются между всеми участниками кольца равномерно.

Упрощенно - при приходе, допустим, новой метрики её метки хешируются, результат делится по модулю на количество токенов и в итоге направляется на хост, который владеет данным диапазоном токенов. Если он не отвечает - то отправляют следующему по кольцу хосту.

Если хост выходит из кластера (помер, перезагружается и т.п.), то его диапазон перераспределяется по остальным.

Все настройки у нас будут лежать в /etc/cortex/cortex.yml

Также т.к. Cortex сделан для работы в контейнерах - всё можно настроить через командную строку чтобы не пропихивать конфиг в контейнер.

Глобальные настройки

# Список модулей для загрузкиtarget: all,compactor,ruler,alertmanager# Требовать ли заголовок X-Scope-OrgIdauth_enabled: true# Портыserver:  http_listen_port: 9009  grpc_listen_port: 9095limits:  # Разрешаем HA-трекинг  accept_ha_samples: true  # Названия меток, которые мы используем в Prometheus для  # маркировки групп и реплик  ha_cluster_label: __ha_group__  ha_replica_label: __ha_replica__  # Максимальный период в прошлое на который мы можем делать  # PromQL запросы (1 год).  # Всё что больше будет обрезано до этого периода.  # Это нужно для реализации retention period.  # Для фактического удаления старых блоков нужно еще настроить lifecycle  # правило в бакете S3 на пару дней глубже  max_query_lookback: 8760h

Так как Cortex создавался для работы в распределённом режиме в облаке, то работа всех модулей в одном бинарнике считается нетипичной, но я никаких проблем не наблюдал.

Другой трудностью изначально было то, что Cortex не поддерживал гибкий список модулей, которые нужно активировать. Была возможность либо указать all, который на самом деле ни разу не all:

# cortex -modulesalertmanagerallcompactorconfigsdistributor *flusheringester *purger *querier *query-frontend *query-schedulerruler *store-gateway *table-manager *Modules marked with * are included in target All.

Либо указать строго один модуль.

Поэтому пришлось сделать пулл-реквест чтобы добавить возможность загружать список любых модулей. В данном случае мы используем all + compactor, ruler и alertmanager

Хранилище

storage:  # Выбираем хранилище Blocks Storage  engine: blocks# Конфигурируем егоblocks_storage:  # Тип бэкенда  backend: s3    # Параметры доступа к S3  s3:    endpoint: 127.0.0.1:9001    bucket_name: cortex    access_key_id: foo    secret_access_key: bar    # TLS у нас нет    insecure: true    tsdb:    # Где хранить локальные блоки до загрузки в S3    dir: /data/cortex/tsdb    # Через какое время их удалять    retention_period: 12h    # Сжимать Write-Ahead Log    wal_compression_enabled: true  bucket_store:    # Где хранить индексы блоков, найденных в S3    # По сути это должно быть в модуле Store-Gateway,    # но по какой-то причине тут    sync_dir: /data/cortex/tsdb-sync    # Как часто сканировать S3 в поиске новых блоков    sync_interval: 1m    # Настраиваем различные кеши на наши memcached    # Каждый кеш имеет свой префикс ключей, так что пересекаться они не будут    index_cache:      backend: memcached      memcached:        addresses: ctx1:11211,ctx2:11211,ctx3:11211,ctx4:11211    chunks_cache:      backend: memcached      memcached:        addresses: ctx1:11211,ctx2:11211,ctx3:11211,ctx4:11211    metadata_cache:      backend: memcached      memcached:        addresses: ctx1:11211,ctx2:11211,ctx3:11211,ctx4:11211

Distributor

distributor:  ha_tracker:    # Включить HA-трекер для Prometheus    enable_ha_tracker: true    # Таймаут после которого срабатывает failover на другую реплику Prometheus.    # Нужно настроить так чтобы метрики приходили не реже этого интервала,    # иначе будут ложные срабатывания.    ha_tracker_failover_timeout: 30s    # Настраиваем etcd для HA-трекера    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379  # Настраиваем etcd для Hash-Ring дистрибьютеров  ring:    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379

Ingester

ingester:  lifecycler:    address: 10.0.0.1    # Название зоны доступности    availability_zone: dc1    # Немного ждём чтобы всё устаканилось перед перераспределением    # токенов на себя    join_after: 10s    # Храним токены чтобы не генерировать их каждый раз при запуске    tokens_file_path: /data/cortex/ingester_tokens    ring:      # На сколько Ingester-ов реплицировать метрики.      # Если указана зона доступности, то реплики будут выбираться из разных зон      replication_factor: 2      # etcd для Hash-Ring Ingester-ов      kvstore:        store: etcd        etcd:          endpoints:           - http://ctx1:2379           - http://ctx2:2379           - http://ctx3:2379           - http://ctx4:2379

Querier

По поводу подбора правильных величин лучше почитать документацию.

Основная идея в том, чтобы никогда не запрашивать те блоки, которые еще не обработал Compactor:

querier:  # Временные файлы  active_query_tracker_dir: /data/cortex/query-tracker  # Запросы с глубиной больше этой будут направляться в S3  query_store_after: 6h  # Запросы с глубиной меньше этой отправляются в Ingester-ы  query_ingesters_within: 6h5mfrontend_worker:  frontend_address: 127.0.0.1:9095query_range:  # Запросы будут разбиваться на куски такой длины и выполняться параллельно  split_queries_by_interval: 24h  # Выравнивать интервал запроса по его шагу  align_queries_with_step: true  # Включить кеширование результатов  cache_results: true    # Кешируем в memcached  results_cache:    # Сжимаем    compression: snappy    cache:      # TTL кеша      default_validity: 60s      memcached:        expiration: 60s      memcached_client:        addresses: ctx1:11211,ctx2:11211,ctx3:11211,ctx4:11211

Store-Gateway

Этот модуль подгружает из S3 бакета заголовки блоков (комбинации меток, временные интервалы и т.п.).

Если включить шардинг и репликацию, то участники кластера распределят все блоки между собой равномерно и каждый блок будет на 2 или более хостах.

store_gateway:  # Включаем шардинг  sharding_enabled: true  sharding_ring:    # Включаем zone awareness    zone_awareness_enabled: true    # Идентификатор зоны    instance_availability_zone: dc1    # Сколько реплик держать    replication_factor: 2    # Hash-ring для Store-Gateway    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379

Compactor

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

compactor:  # Временная директория для блоков.  # Должно быть достаточно много места чтобы можно было загрузить блоки,  # скомпактить их и сохранить результат.  data_dir: /data/cortex/compactor  # Как часто запускать компакцию  compaction_interval: 30m  # Hash-Ring для компакторов  sharding_enabled: true  sharding_ring:    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379

Ruler + AlertManager

Эти модули опциональны и нужны только если хочется генерировать алерты на основе правил.

  • Правила в стандартном Prometheus формате мы будем складывать в /data/cortex/rules/<tenant>/rulesN.yml на каждом хосте. Можно использовать для этого S3 или другие хранилища - см. документацию

  • Cortex периодически сканирует хранилище и перезагружает правила

  • Конфиги AlertManager в стандартном формате складываем в /data/cortex/alert-rules/<tenant>.yml

    Аналогично можно складывать в S3 и т.п.

  • Cortex запускает инстанс AlertManager (внутри своего процесса) отдельно для каждого tenant, если находит конфигурацию в хранилище

ruler:  # Временные файлы  rule_path: /data/cortex/rules-tmp  # Включаем шардинг  enable_sharding: true  # Какому AlertManager-у сообщать об алертах  alertmanager_url: http://ctx1:9009/alertmanager  # Откуда загружать правила  storage:    type: local    local:      directory: /data/cortex/rules    # Hash-ring для Ruler-ов  ring:    kvstore:      store: etcd      etcd:        endpoints:         - http://ctx1:2379         - http://ctx2:2379         - http://ctx3:2379         - http://ctx4:2379alertmanager:  # Где хранить состояние алертов  data_dir: /data/cortex/alert-data  # Внешний URL нашего инстанса (нужен для генерации ссылок и т.п.)  external_url: http://ctx1:9009/alertmanager  # Кластеринг - какой адрес слушать и какой анонсировать пирам  cluster_bind_address: 0.0.0.0:9094  cluster_advertise_address: 10.0.0.1:9094  # Список пиров  peers:    - ctx2:9094    - ctx3:9094    - ctx4:9094  # Откуда загружать настройки  storage:    type: local    local:      path: /data/cortex/alert-rules

Заключение

Вот и всё, можно запускать все сервисы - сначала зависимости, потом Cortex, затем - Prometheus.

Я не претендую на полноту, но этого должно быть достаточно чтобы начать работать.

Нужно учитывать, что Cortex активно развивается и на момент написания статьи часть параметров в master-ветке и документации (которая генерируется из неё) уже объявлено deprecated. Так что, вполне возможно, в новых версиях нужно будет конфиги немного исправлять.

Если есть вопросы и\или замечания - пишите, постараюсь добавить в статью.

Подробнее..

Представляем Quarkus на Red Hat OpenShift

21.01.2021 14:15:10 | Автор: admin

Red Hat очень серьезно относится к будущему Java, остающейся самым популярным языком разработки корпоративных приложений. Почти две трети всех программистов мира относят себя к умеренным или заядлым пользователям Java, которая вот уже 18 лет подряд входит в тройку лучших языков программирования в рейтинге TIOBE. Можно смело сказать, что несмотря на небывалый выбор и доступность новых языков программирования, Java остается де-факто стандартом разработки критически важных бизнес-приложений. Мы в Red Hat считаем своим долгом поддерживать Java-разработчиков и прокладывать для них новые пути для продолжения инноваций.

Среди множества технологий, призванных сохранить роль и место Java в облачных реалиях будущего, одной из наиболее перспективных является Quarkus, фреймворк для разработки Kubernetes-нативных Java-приложений. Quarkus изначально создавался как способ оптимизировать Java для контейнерных сред, и, как результат, способен значительно повысить продуктивность разработчиков и снизить эксплуатационные расходы, превращаясь в ключевой компонент контейнерных рабочих нагрузок. Несмотря на свою молодость и бурный рост, Quarkus уже стал заметным игроком на рынке технологий разработки и задает новый формат работы программистов. Сегодня мы рады сообщить, что Quarkus теперь входит в состав Red Hat OpenShift, и это важный шаг для будущего Java как инструмента разработки современных облачных приложений.

Quarkus на Red Hat OpenShift

Quarkus и до этого уже полностью поддерживался и был доступен в рамках Red Hat Runtimes, но теперь он входит в состав и полностью интегрирован с Red Hat OpenShift, что еще больше упрощает разработку. Разработчики получают знакомые инструменты, возможность удаленной разработки на кластерах с помощью IDE, таких как CodeReady Workspaces, интеграцию с управляемыми конфигурациями, развертывание serverless-нагрузок и управление хранилищами приложений.

В состав Quarkus входят несколько компонентов для разработки и развертывания на OpenShift:

  • Quarkus-расширение генерация кода для новых проектов, управление зависимостями проектов, удаленная разработка и отладка, а также простое, в один шаг, развертывание в OpenShiftPlugins для CodeReady Workspaces, включая предварительно настроенные стеки рабочих пространств разработчика, подсказки по свойствам конфигураций и автозавершение кода, снипеты для стандартных типов классов Quarkus, а также создание и развертывание кода в OpenShift непосредственно из IDE.

  • Автоматическая привязка тестов исправности (health checks), подключение секретов и предоставление метрик для простой интеграции со средствами мониторинга, наподобие Prometheus

  • Автоматическое развертывание контейнеризированных приложений Quarkus в качестве serverless-нагрузок OpenShift.

  • Knative-развертывания в один шаг.

Также обеспечивается интеграция с Kubernetes API, включая generic API client и поддержку динамической конфигурации приложений с использованием Kubernetes ConfigMaps и Secrets.

Migration Toolkit for Applications

Мы также обновили Migration Toolkit for Applications, чтобы помочь с переносом приложения Spring Boot в Quarkus на OpenShift. Этот тулкит для консультантов, архитекторов и разработчиков помогает проанализировать Java-код или двоичные файлы с точки зрения различных вариантов трансформации (задаются в виде наборов правил) для эффективной модернизации и миграции приложений. Свежая версия Migration Toolkit for Applications сочетает правила Containerization, OpenJDK и Linux со специально разработанными и протестированными правилами для переноса кода Spring Boot в Quarkus на OpenShift, чтобы лучше учесть особенности гибридного облака.

Теперь Quarkus доступен еще большему число разработчиков, а чтобы узнать о нем больше и начать использовать, обратитесь к сайту: https://developers.redhat.com/products/quarkus/getting-started

Подробнее..

KubeGraf плагин для мониторинга Kubernetes в Grafana. Как создавался и почему стал востребованным

11.02.2021 16:05:53 | Автор: admin


KubeGraf это плагин для Grafana, который собирает данные с кластера Kubernetes и приложений внутри него, а затем показывает их на красивых и понятных графиках. В феврале этого года вышел релиз 1.5, и стало известно, что предыдущие версии скачали более 250 тысяч раз! Мы расспросили Сергея Спорышева, создателя плагина и директора направления DevOps-продуктов в ITSumma, об истории создания плагина, факапах и причинах популярности.


Сергей Спорышев участвует в создании курса по мониторингу и логированию в Слёрме, набор на который уже начался. В конце интервью мы немного поговорим и про курс тоже.


Как-то, рассказывая о разработке KubeGraf, ты упомянул, что вы хотели сделать универсальный инструмент для мониторинга, который поставил и ничего больше делать не надо. Что скажешь спустя полтора года: получилось ли?


Я думаю, получилось. Здесь логично рассказать, откуда возникла идея плагина. В апреле 2019 года я помогал Евгению Потапову, гендиректору ITSumma, готовить доклад для конференции HighLoad в Питере. Он планировал рассказать про мониторинг Kubernetes: какие есть инструменты, подходы Всего этого и методологий, и инструментов, было очень много, но какого-то более-менее универсального решения, чтобы поставил и ничего делать не нужно, не было.


Углубляясь, мы нашли у самой Grafana плагин для мониторинга Kubernetes. Он вроде был жив, но почему-то не поддерживался. Мы его поставили и увидели, что половина функций не работает. Так возникла идея написать своё и докинуть туда багаж практик, который мы наработали внутри компании (одно из направлений ITSumma как раз техподдержка 24/7 и, соответственно, круглосуточный мониторинг).


Была ещё одна вещь, которую мы хотели реализовать. В России раскатка на bare metal это нормальное явление, а все прогрессивные страны пользуются managed-решениями: Kubernetes от Google, Amazon (у нас теперь ещё Яндекс и Mail.ru). В работе с облаками есть свои нюансы, и мы подумали почему бы не сделать плагин, который будет эти нюансы учитывать?


Такой был план: написать плагин, основываясь на решении, выпущенном самой Grafana, дополнить его своими наработками и сделать так, чтобы он работал везде одинаково. Я думаю, получилось. Лично я его юзаю, и наши клиенты тоже: причём как те, кто на голом железе сидит, так и те, кто сидит в Google и Amazon. Лишних телодвижений делать не приходится никому.


А какие фичи плагина ты используешь каждый день?


Одна из основных фишек это как раз возможность работы с облачными провайдерами. Обычно во всех этих системах используется авторизация по сертификатам. В облаке сертификаты тебе никто не даст, поэтому мы сделали возможность работы через токены. Ты применяешь кубовые манифесты, регистрируешь пользователя, выдаешь ему права, и с этим пользователем плагин работает.


Мы мониторим как приложения, которые крутятся в Kubernetes, так и ноды, на которых Kubernetes работает. В реальном времени можно смотреть нагрузку по ноде, сколько ресурсов на ней зарезервировано и какие выставлены лимиты. Например, мы индицируем, что у ноды есть всего лишь 2.5 Гб памяти, 2 Гб которых уже зарезервировано, и тогда подкрашиваем ноду жёлтым либо красным, таким образом предупреждая: Ребят, эта нода скоро закончится. То же самое с лимитами.



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



Второй режим просмотра это так называемая Карта всех приложений. Виден список всех приложений, как они сгруппированы по неймспейсам, какие сущности за собой тянут, сколько там реплик. Все красивенько отображается.



Ну, и последний ключевой момент в одном месте сведена информация обо всех предупреждениях, ошибках, критических ситуациях. Ты можешь просто открыть страничку Cluster Status и посмотреть, что у тебя с кластером происходит. Куда надо обратить внимание, куда не надо.




Можешь вспомнить ситуации из своей рабочей практики, когда KubeGraf реально тебе помог?
Это случилось, когда мы научились правильно индицировать лимиты, реквесты и всё остальное. На одном из проектов облачный кластер Kubernetes в момент пиковых нагрузок разрастался до 25-26 нод. Мы стали смотреть на данные из плагина и, пользуясь ими, сократили парк почти в два раза. Сейчас этот же кластер с большим количеством приложений спокойно работает на 12-13 нодах.


При этом ресурсы кластера мы никак не разбирали. Мы просто смотрели, что такие-то приложения при таком-то трафике практически не должны ничего жрать, но потребление вырастает до колоссальных размеров. Анализировали приложение, смотрели, что где можно подтюнить, как именно построен scheduling на основе реквестов. Видели, например, что зарезервировано 1.5 Гб памяти, но за два месяца никто столько никогда не использовал и можно это сократить. Потихоньку-помаленьку сократили кластер в два раза.


В новостях о плагине вы писали, что его скачали 250 000 раз


По не совсем официальной информации да. Это такая информация для разработчиков, её в паблике нет, но вот я нечаянно узнал. Круто!


Это много или мало?


Много! У нас четыре с лишним сотни клиентов, у одного клиента может быть максимум кластеров 10. Если мы хотя бы 400 умножим на 10 это 4 000. То есть если бы каждый наш клиент был с Kubernetes, мы бы каждому накатили и получили только 4 000. А тут четверть миллиона! Это много и это круто.


Как думаешь, в чём причина такой популярности?


Один из ключевых факторов: плагин от Grafana, о котором я уже упоминал, разработчики по неизвестным причинам перестали поддерживать. И теперь в issues или в pull request на GitHub, я не помню точно, их спрашивают: ребята, вы собираетесь этот инструмент дальше развивать или нет? А они говорят: нет, вот вам ссылка на нормальное решение. И дают ссылку на нас.


Плюс сарафанное радио: мы раскидываем плагин клиентам, активно взаимодействуем с сообществом, много где его презентуем. Скорее всего, в этом причина.


А если оценивать аудиторию англоязычную, русскоязычную как тебе кажется, где KubeGraf больше используют?


Мне кажется, это неправильным в современном мире разделять аудиторию на русскоговорящую и не русскоговорящую.


Недавно вышла версия 1.5. Что там нового?


Мы починили большое количество ошибок совместимости. В середине 2020 года релизнулась седьмая версия Grafana, там всё еще работало, а с 7.3 начались большие изменения в структуре, поэтому пришлось дорабатывать плагин, чтобы ничего не отваливалось. Плюс добавили поддержку последних версий Kubernetes, доработали, по каким путям мы забираем данные из API Kubernetes.


В версии 1.5 мы добавили сбор метрик по лимитам для приложений. До этого работали только с реквестами и текущим потреблением: приложение сколько-то под себя резервирует и сколько-то использует в реальном времени. Но со временем поняли, что очень важна еще и работа с лимитами.


Переработали UX, чтобы было удобно для восприятия, добавили реалтайм индикации. Раньше была сводная таблица по всем приложениям, и мы просто видели, сколько они резервируют и потребляют. В 1.5 добавили индикацию: в табличке подсвечено, где что-то не указано, где зеленая зона, где желтая, красная и так далее.


Как обычно, были небольшие фиксы: где-то слетела легенда, где-то кнопка не работала поправили.


Последнее ключевое в сводной таблице по состоянию кластера мы наконец-то догадались ввести сортировку по важности уведомления: жёлтое, красное и т. д.


Алертинга у вас нет?


Тут можно холиварить долго, но устоявшееся мнение такое: алерты и Grafana это разные вещи. Алерты в Grafana делать можно, но это неправильно. Для алертинга нужен другой инструмент: Prometheus+Alert Manager, например. Grafana это про визуализацию. Мы только планируем на графике сделать индикацию алертинга.


Ты говорил, что многие фичи появлялись, исходя из ваших потребностей. А были такие, которые вы делали по запросам?


Конечно! Здесь соотношение где-то 60/40. 60 это наша практика. 40 это общение с комьюнити, с клиентами. Сейчас мы работаем над версией 1.6, и одним из ключевых новшеств в ней будет обновлённый дашборд. К нам пришли клиенты, которые активно юзают плагин, и говорят: ребята, мы пользуемся, всё круто, но хотелось бы увидеть ещё такую вот панель. Мы отвечаем: ну, супер.


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


Заспойлери что-нибудь ещё из версии 1.6.


Будет небольшой редизайн, потому что первый макет создавался кустарно. Сначала мы с Женей Потаповым набросали на бумажке, как может выглядеть интерфейс. Потом один из наших разработчиков разобрал тулкит Grafana с визуализационными элементами, и мы просто впихали всё, как было. Позже подключился дизайнер, и где-то в версии 1.3-1.4 мы переработали одну из страниц. Теперь перерабатываем остальные и в 1.6 затащим красоты.


Плюс к странице Cluster Status появится сводный дашборд по общему состоянию кластера. Не по отдельным нодам, а именно по всему кластеру, как если бы он был одним большим сервером. Дашборд даст понимание, как он себя чувствует.


Ну и мы хотим полностью переписать плагин. С выхода первой версии прошло два года, Grafana выпустила много вспомогательных инструментов для разработки. Поэтому мы полностью переписываем сборку, переходим на использование компонентов от Grafana и другой фреймворк: плагин написан на Angular, а где-то год назад пошёл тренд на React-плагины, поэтому я думаю, что после 1.6 будет сразу версия 2.0 на React.


С какой скоростью выпускаете новые версии?


Критичные минорные изменения вносим так быстро, как это только возможно, но вообще стараемся релизиться раз в два месяца. Только с версией 1.5 что-то пошло не так готовили её почти полгода. Сказался и личный загруз (это все-таки наш open source проект), и эпидемия, и задержки со стороны Grafana. С релиза версии может пройти от недели до двух месяцев, когда её примут и опубликуют на сайте Grafana. Поэтому релизный цикл плавающий.


Как устроена работа над плагином: сколько человек в команде, сколько времени каждый из вас уделяет разработке?


Команда это два разработчика: я и мой коллега Дмитрий Воронов. Когда нужно, подключаем дизайнера. Менеджерю этот процесс я сам. Если по работе у нас ничего не горит, то понедельник мы посвящаем плагину. Понедельник у нас Grafana Day, такая разминка перед тяжёлой рабочей неделей.


То есть это не выходные, не ночь, а полноценный рабочий день?


Да. Единственное это предрелизная штука, когда обычно я на себя всё захватываю. Там может выжирать и три дня в неделю. Но стандартно день.


В таком режиме получается отдыхать от плагина? Когда два года делаешь какую-то штуку, она может слегка поднадоесть, нет?


Бывает такое, но можно взять на неделю перерыв. Это же pet project: поднадоело давайте отложим.


Были за эти два года такие факапы, которые прям совсем факапы?


Конечно, они бывают в любой разработке. Как-то раз выкатили версию, которая не работала. Потестили на одной из версий Grafana, а на остальных нет. Где-то всё поехало, где-то метрики перестали приходить.


Заметили довольно быстро, потому что мы обычно, пока ждём официального релиза от Grafana, ходим и через Git и обновляем это всё у наших клиентов. Видим, где ломается.


Второй факап был прямо жёсткий. В одной из версий Kubernetes и Prometheus полностью сменился лейблинг. До этого в основных метриках было два лейбла: container name и pod name, а тут они поменялись на container и pod (я уже сейчас не вспомню точно). В какой-то момент мы заметили, что нейминг разъехался: здесь мы собираем по pod, здесь по pod name; здесь по container, здесь по container name. И у нас половина плагина работала, половина не работала. Никто сначала ничего не мог понять. Пришлось пойти, разобраться, упороться регулярками, которые все ненавидят. Ну и вроде собрали, заработало.


Но, в целом, за счет того, что time-to-market у плагина очень высокий, у нас всегда есть минимум две недели в ожидании релиза, когда можно сходить всё исправить. То есть факапы они локальные, в паблик не могут уйти.


Если бы ты сейчас подходил к разработке плагина, что-нибудь бы сделал иначе?


Думаю, нет. Я даже не знаю, что можно было изменить, потому что оно как шло по желанию, так и шло. Какие-то фишки придумывались, какие-то дорабатывались. Что-то изменить? Не знаю.


Какую побочную, помимо технической, пользу принёс плагин тебе самому и вашей компании?


Brand awareness узнаваемость бренда. Это самое большое, что мы получили. Когда компания выпускает open source продукты, она закрепляет свою экспертизу, повышает узнаваемость. На приток клиентов не рассчитывали. Хотели только, чтобы нас узнавали, чтобы люди понимали, что мы это умеем и в этом шарим.


Мне кажется, у нас получилось, потому что про плагин писали везде. Очень много материалов в интернете, и везде ссылочки на нас. Да, определённый процент клиентов приходили с плагина: понимали, что в теме Kubernetes и мониторинга нам можно довериться, и работали с нами дальше.


В небольшом видео с анонсом версии 1.5 ты сказал, что вы планируете сделать плагин отдельным продуктом. Что это за идеи?


У нас была мысль сделать плагин прямо такой stand-alone версией, потому что мы наработали большое количество практик в работе с Kubernetes, и хотели бы их реализовать в отдельном продукте.


Мы думаем про сервис, который будет делать не только визуализацию, но поможет проводить аудит кластера и тюнить его. Grafana здесь будет только мешать, поэтому думаем оформить его в виде stand-alone решения, которое просто так же поставил через Helm и видишь красивую картиночку, где у тебя что происходит. Но это всё пока только на уровне проработки концепции.


Еще какие-то плагины делать? Я пока не знаю. У нас было много идей, как можно что-то визуализировать. Пока руки не доходят.


Если это будет отдельный продукт, который будет шире, чем просто мониторинг и визуализация, он будет платным?


Open source продукты принято делать бесплатными. Но, например, Grafana тоже open source, но есть Grafana Enterprise. Мне кажется, этот продукт будет в двух вариациях free и с какой-то подпиской.


Такой серьезный продукт придётся не только по понедельникам делать.


Да.


Выходит, ITSumma сейчас смотрит чуть в сторону open sourse, в направлении разработки своих продуктов?


Конечно! Нужны же какие-то артефакты работы компании. Хоть что-то после себя оставить нужно.


На этом мы закончили обсуждать плагин KubеGraf и немного поговорили о видеокурсе по мониторингу Kubernetes, который Сергей Спорышев вместе с другими спикерами разрабатывал в Слёрме. К беседе присоединился СТО Слёрма Марсель Ибраев.


Что будет в новом курсе?


МАРСЕЛЬ: Мы будем говорить о том, как мониторить и собирать логи с приложений, которые запущены в кластере, и с самого кластера Kubernetes. Потому что просто поднять кластер это полдела, и с этим справляется большинство, а вот правильно настроить сбор логов и метрик уже получается не у всех. Цель курса рассказать и показать лучшие практики.


Стоит ли идти на курс по логированию тем, кто уже проходил курсы по Kubernetes в Слёрме?


МАРСЕЛЬ: Стоит, потому что в наших курсах по Kubernetes не раскрывается тема логирования и мониторинга. Мы пробовали её включить в первые базовые курсы, но получалось не очень: материала много, и дать его в рамках двух занятий внутри обзорного курса по Kubernetes невозможно. Уже тогда была идея, что надо делать что-то отдельное. Наконец-то мы к этому пришли.


Чем хорош курс? Почему вы бы сами на него пошли?


МАРСЕЛЬ: Во-первых, курс подготовили практикующие инженеры ребята, которые каждый день работают с Kubernetes, набивают шишки. Они рассказывают именно то, что нужно знать. Это чистый опыт. Да, его можно наработать и самому, но мы конвертируем время в деньги (и наоборот) и приносим опыт на блюдечке.


Мы не обещаем, что, пройдя курс, вы сразу станете крутым специалистом. Чтобы освоить все инструменты, нужно много практиковаться. Но начать это делать можно уже во время обучения. И это вторая причина, по которой я бы сам на него пошёл: Слёрм даёт практические задания и стенды для их выполнения. У студента есть все возможности, чтобы закрепить знания на практике и получить навыки, которые потом можно оттачивать на реальных проектах.


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


СЕРГЕЙ: Я впервые участвую в создании такой штуки, но, посмотрев на внутреннюю кухню, поучаствовав в проработке одной из тем, пообщавшись с другими спикерами, я понял, что все спикеры максимально детально разжевывают материал, но делают это не в формате старых книг вроде PHP для чайников, а на реальных практических примерах, затрагивая современные аспекты технологии.


Сама площадка тоже топчик. Я планировал писать на локальном кластере, но потом понял, что всё есть, всё удобно, и с минимальными усилиями за минимальное количество времени ты можешь попробовать выполнять практические задания, в реальном времени видеть результат.


Всё сделано по-максимуму для студента, слушателя курса. Без воды, без лишнего материала. Всё учтено. Такая тонкая грань между вводом и хардкором.


Программа курса максимально последовательная. Когда меня только привлекли к подготовке, я прочитал план и понял, что он составлен идеально. Студента проводят по тем же этапам, которые он бы прошёл сам, настраивая мониторинг и логирование.


Кому будет полезен этот курс?


СЕРГЕЙ: Есть замечательная шутейка, что в современных реалиях Kubernetes это операционная система, а Helm просто пакетный менеджер для него. Всем приходится иметь дело с Kubernetes, значит всем нужно знать мониторинг и логирование такой промышленный стандарт. То есть курс будет полезен всем.


Какие знания необходимы для учёбы?


МАРСЕЛЬ: Мы рассчитывали курс на тех, кто уже знаком с Kubernetes. Знание систем контейнеризации, Docker в первую очередь, тоже горячо приветствуется. Без них будет тяжело, потому что в рамках курса мы не рассказываем о базовых абстракциях.


Предполагается, что у человека есть кластер, он с ним уже работал, но просто не знает, как правильно настроить мониторинг.


Нужно ли идти на курс, если что-то уже настраивал сам?


МАРСЕЛЬ: Думаю, да. У всех свой взгляд, как это сделать и настроить. Иногда я узнаю, как некоторые делают очень страшные вещи, строят костыли, где не надо, и прочее. Поэтому хочется, чтобы люди просвещались в этом вопросе. В рамках обмена опытом, скажем так.


Что ещё важно сказать про курс?


МАРСЕЛЬ: Первая версия прошла тестирование. Бета-тестеры дали очень много фидбека, после которого мы изменили программу. Например, в программе был Zabbix, а потом мы поспрашивали ребят, экспертную группу, и поняли, что он там не нужен, поэтому убрали его и добавили другие темы. Кроме того, мы значительно усилили команду спикеров, подключили ребят из ITSumma.


Бонусом студенты получают видеокурс о Prometheus. Он будет полезен даже для тех, кто с Prometheus никогда не работал.


Узнать больше о курсе и записаться

Подробнее..

Мониторим основные сервисы в AWS с Prometheus и exporterами для CloudWatch

12.02.2021 14:21:36 | Автор: admin

Если вы используете в облачных провайдерах managed-инсталляции серверных служб вроде RDS или ElastiCache от AWS, то скорее всего уже задавались темой мониторинга инфраструктуры, а главное оповещений по произошедшим инцидентам. При реализации возникают понятные вопросы:

  1. Как можно настроить сбор данных с endpointов в систему мониторинга?

  2. Если использовать Prometheus, то какие экспортеры использовать и где их можно запускать?

  3. Какие есть варианты готовых алертов для покрытия основных причин аварий/частичной недоступности?

Эта статья в большей степени рассчитана на начинающих инженеров: на примере Prometheus и CloudWatch мы рассмотрим одно из самых простых и понятных решений с помощью cloudwatch_exporter и prometheus_aws_cost_exporter в AWS, напишем для них Helm-чарт и задеплоим его в Kubernetes. (K8s будет выступать удобной площадкой для разворачивания экспортеров.) А также посмотрим, как можно мониторить текущие и ежедневные затраты всей вашей инфраструктуры.


CloudWatch сервис для мониторинга инфраструктуры. С его помощью можно настраивать уведомления о произошедших инцидентах по почте, что довольно популярно в проектах, где еще нет централизованной системы мониторинга. Однако мы пойдем по другому пути и будем выводить метрики в формате Prometheus, на основе которых строить графики и настраивать алерты.

У AWS есть отдельные типы инстансов с бюджетами кредитов по CPU и дисков с кредитами по IO. Они позволяют накапливать бюджет кредитов во время сниженной нагрузки и тратить его в том случае, если она резко выросла. Но прогнозировать, когда нагрузка может уйти, проблематично. Поэтому есть риск выработать весь бюджет и перейти на режим ограниченного потребления ресурсов. Данный вариант развития событий сложно диагностировать, так как в мониторинге сервера это напрямую не будет отражено. Поэтому очень полезно мониторить кредиты CPU/IO, чтобы понимать, какое количество кредитов доступно в данный момент, какова динамика их потребления и предвидеть их исчерпание.

Итак, возвращаясь к выбранным инструментам: prometheus_aws_cost_exporter будет использоваться для мониторинга потребления финансов, так как cloudwatch_exporter возвращает информацию только за предыдущий день. Зато cloudwatch_exporter позволяет снимать гораздо больше метрик.

Приступим к непосредственной реализации!

1. Настраиваем IAM

Поскольку рассматривать мы будем два экспортера с немного различным функционалом, потребуются два разных аккаунта в IAM (AWS Identity and Access Management). Ниже представлен список ролей, которые нужны обоим аккаунтам:

  • cloudwatch:ListMetrics

  • cloudwatch:GetMetricStatistics

  • tag:GetResources

Для работы prometheus_aws_cost_exporter требуется больший набор прав: необходимо создать отдельную роль и назначить её пользователю. Для удобства роль можно создать из JSON:

{  "Effect": "Allow",  "Action": [    "cloudwatch:PutMetricData",    "ec2:DescribeVolumes",    "ec2:DescribeTags",    "logs:PutLogEvents",    "logs:DescribeLogStreams",    "logs:DescribeLogGroups",    "logs:CreateLogStream",    "logs:CreateLogGroup",    "ce:GetCostAndUsage"  ],  "Resource": "*"},{  "Effect": "Allow",  "Action": [    "ssm:GetParameter"  ],  "Resource": [    "arn:aws:ssm:*:*:parameter/AmazonCloudWatch-*",    "arn:aws:ce:*:*:/GetCostAndUsage"  ]}

Для учётных записей экспортеров также требуется создать access key ID и secret access key, которые будут передаваться в виде переменных (AWS_ACCESS_KEY_ID и AWS_SECRET_ACCESS_KEY).

2. Создаем IAM-роли через веб-интерфейс

Авторизуемся в консоли управления AWS и перейдем в раздел IAM, где для примера создадим пользователя под названием cloudwatch_users.

Создание пользователя в IAMСоздание пользователя в IAM

В поле Access Type включим опцию Programmatic access, которая позволит сгенерировать упомянутые access key ID и secret access key (они потребуются для работы с API, сохраните их куда-нибудь в надёжное хранилище). Следующая вкладка Attach existing policies directly, где мы создадим новую политику. Для данной IAM-роли требуются права ListMetrics и GetMetricStatistics.

Создание пользовательского policyСоздание пользовательского policy

Если удобнее создавать роль через API, можно воспользоваться JSON-сниппетом:

{  "Version": "2012-10-17",  "Statement": [    {      "Sid": "VisualEditor0",      "Effect": "Allow",      "Action": [        "cloudwatch:GetMetricStatistics",        "cloudwatch:ListMetrics"      ],      "Resource": "*"    }  ]}

После нажатия на Review policy указываем название для Policy и создаем её (Create policy). Дальнейшие пункты не влияют на функции созданной роли. Однако потребуется вернуться на этап создания IAM-роли, чтобы добавить созданный нами Policy. На самом последнем этапе станут доступны для просмотра значения для переменных AWS_ACCESS_KEY_ID и AWS_SECRET_ACCESS_KEY, которые надо записать в values.yaml нашего будущего Helm-чарта.

Если вы используете Terraform, то по данной ссылке есть готовый Terraform receipt для создания IAM-роли и пользователей. Ключи API из terraform.tfstate можно получить с помощью jq:

jq '.resources[].instances[].attributes | {(.id): .secret}'

Примечание: запросы к CloudWatch тарифицируются; актуальные цены можно изучить здесь. Исходя из этой информации можете выбрать, как часто выполнять запросы к API например, раз в час или в сутки в зависимости от задачи.

3. Helm-чарт для экспортеров

Перейдем к деплою cloudwatch-exporter и cost-exporter в Kubernetes. Потребуется написать очень простой Helm-чарт, который будет состоять из нескольких простых объектов.

В самом начале объявим необходимые переменные в values.yaml:

---aws_access_key_id: <AWS_ACCESS_KEY_ID>aws_secret_access_key: <AWS_SECRET_ACCESS_KEY>region: eu-central-1replicas:  1resources:  requests:    cpu: 1m    memory: 512Mienv:  metric_today_daily_costs: "yes"  metric_yesterday_daily_costs: "yes"  query_period: "1800"  metric_today_daily_usage: "yes"  metric_today_daily_usage_norm: "yes"

Подробнее о содержимом этого файла:

  • В переменных aws_access_key_id и aws_secret_access_key объявляются значения, полученные при создании IAM-роли;

  • region регион, в котором требуется выполнять мониторинг ресурсов;

  • query_period периодичность запроса метрик из AWS (в секундах);

  • metric_today_daily_costs, metric_yesterday_daily_costs, metric_today_daily_usage, metric_today_daily_usage_norm включение/выключение запрашивания метрик затрат (costs) и потребления (usage) за вчера и за сегодня (по умолчанию имеет значение no);

  • Параметры из блока env используются cost-exporterом (на работу cloudwatch-exporter не влияют).

Ниже пример листинга с Deployment для cloudwatch-exporter, который носит иллюстративный характер (содержит только основную структуру для удобства чтения). Полная версия доступна здесь.

apiVersion: apps/v1kind: Deploymentmetadata:  name: cloudwatch-exporterspec:  selector:    matchLabels:      app: cloudwatch-exporter  template:    metadata:      labels:        app: cloudwatch-exporter    spec:      containers:      - name: cloudwatch-exporter        image: prom/cloudwatch-exporter:cloudwatch_exporter-0.9.0        env:        - name: AWS_ACCESS_KEY_ID          value: "{{ .Values.aws_access_key_id }}"        - name: AWS_SECRET_ACCESS_KEY          value: "{{ .Values.aws_secret_access_key }}"        volumeMounts:        - name: config          subPath: config.yml          mountPath: /config/config.yml      volumes:      - name: config        configMap:          name: config

Следующий Deployment (точнее, снова его фрагмент) для cost-exporter:

apiVersion: apps/v1kind: Deploymentmetadata:  name: cost-exporterspec:  selector:    matchLabels:      app: cost-exporter  template:    metadata:      labels:        app: cost-exporter    spec:      containers:      - name: cost-exporter        image: nachomillangarcia/prometheus_aws_cost_exporter:latest        args:        - --host        - 0.0.0.0        env:        - name: AWS_ACCESS_KEY_ID          value: "{{ .Values.aws_access_key_id }}"        - name: AWS_SECRET_ACCESS_KEY          value: "{{ .Values.aws_secret_access_key }}"        - name: METRIC_TODAY_DAILY_COSTS          value: "{{ .Values.env.metric_today_daily_costs }}"        - name: METRIC_YESTERDAY_DAILY_COSTS          value: "{{ .Values.env.metric_yesterday_daily_costs }}"        - name: QUERY_PERIOD          value: "{{ .Values.env.query_period }}"        - name: METRIC_TODAY_DAILY_USAGE          value: "{{ .Values.env.metric_today_daily_usage }}"        - name: METRIC_TODAY_DAILY_USAGE_NORM          value: "{{ .Values.env.metric_today_daily_usage_norm }}"

4. Настраиваем метрики для мониторинга

Остается настроить самое главное те метрики, которые будут собираться в Prometheus и на основании значений которых мы хотим делать алерты.

Здесь есть пример конфигурационного файла для cloudwatch_exporter с перечислением необходимых для мониторинга метрик.Посмотреть все доступные для мониторинга метрики например, для EC2 можно с помощью консольной утилиты aws:

aws cloudwatch list-metrics --namespace EC2

Все параметры конфига берутся из вывода консольной утилиты aws. Типичный фрагмент конфига:

  - aws_namespace: AWS/NetworkELB    aws_metric_name: HealthyHostCount    aws_dimensions:    - LoadBalancer    - TargetGroup    aws_statistics:    - Sum    period_seconds: 60

Этот фрагмент указывает экспортеру брать из AWS/NetworkELB метрику HealthyHostCount с периодичностью 60 секунд, агрегировать её по LoadBalancer и TargetGroup, выдавать значение Sum.

Бонус! Несколько примеров алертов

Вот так выглядит алерт на использование CPU в Redis у ElastiCache:

  - alert: RedisCPUUsage    annotations:    description: |      Redis CPU utilization on {{`{{$labels.cache_cluster_id}}`}} in cluster is over than 60%    summary: Redis CPU utilization on {{`{{$labels.cache_cluster_id}}`}} in cluster is over than 60%    expr: |      aws_elasticache_cpuutilization_average >= 60    for: 5m

Алерт на количество target у LoadBalancer:

  - alert: LBTargetGroupIsUnhealthy    annotations:    description: Some hosts are target group {{`{{$labels.target_group}}`}} in cluster is unhealthy!    summary: Some hosts are target group {{`{{$labels.target_group}}`}} in cluster is unhealthy!    expr: |      aws_networkelb_healthy_host_count_sum{load_balancer=~".*someservice.*",target_group=~".*someservice.*"} < 3    for: 1m

Алерт на исчерпание EBS Burst balance:

  - alert: EBSBurst_balance    annotations:         description: EBS Burst balance in cluster is less than 60%                     summary: EBS Burst balance in cluster is less than 60%    expr: |      aws_ebs_burst_balance_average <= 60             for: 5m

В репозитории представлен более обширный список примеров для конфигурации метрик и алертов.

Примеры, как могут выглядеть графики в Prometheus:

AWS EC2 EBS IO balance (average)AWS EC2 EBS IO balance (average)AWS ElastiCache CPU utilization (average)AWS ElastiCache CPU utilization (average)

Заключение

В статье рассмотрен мониторинг сервисов AWS с помощью известных Prometheus exporterов. Настроив его описанным образом, можно получить удобный инструмент для анализа состояния managed-инфраструктуры и ее стоимости, что поможет скорректировать расходы и вовремя получать информацию о потребляемых финансовых ресурсах.

За рамками материала остались некоторые вопросы взаимодействия с Prometheus (как ему сообщать, откуда и куда scrapeить метрики), а также создание панелей в Grafana. (Кстати, для prometheus_aws_cost_exporter есть dashboard от создателя.) Разобравшись и с ними для своего конкретного случая, можно получить более полное, законченное решение.

P.S.

Читайте также в нашем блоге:

Подробнее..

Перевод Разворачиваем кластер Kubernetes с помощью Kubernetes

17.02.2021 20:19:33 | Автор: admin

В рамках курса DevOps практики и инструменты подготовили для вас перевод полезной статьи.

Также приглашаем на открытый вебинар по теме
Prometheus: быстрый старт. На вебинаре участники вместе с экспертом рассмотрят архитектуру Prometheus и как она работает с метриками; разберут, как формировать алерты и события в системе.


Подождите что, что? Да, я уже слышал подобную реакцию на мое предложение использовать Kubernetes для построения кластеров Kubernetes.

Но для автоматизации облачной инфраструктуры мне не приходит на ум ничего лучше, чем сам Kubernetes. Используя один центральный кластер K8s, мы создаем и управляем сотнями других кластеров K8s. В этой статье я покажу вам, как это сделать.

Примечание: SAP Concur использует AWS EKS, но концепции, рассматриваемые здесь, применимы и к Google GKE, Azure AKS и к любому другому облачному провайдеру, предлагающему Kubernetes.

Готовность к промышленной эксплуатации

Развернуть кластер Kubernetes в любом из основных облачных провайдеров очень легко. Создать и запустить кластер в AWS EKS можно одной командой:

$ eksctl create cluster

Однако для создания кластера Kubernetes, готового к промышленной эксплуатации (production ready), требуется нечто большее. Хотя готовность к промышленной эксплуатации каждый понимает по своему, SAP Concur использует следующие четыре этапа создания и поставки кластеров Kubernetes.

Четыре этапа сборки

  • Предварительные тесты. Набор базовых тестов целевого окружения AWS, проверяющих выполнение всех требований перед непосредственным созданием кластера. Например: доступные IP-адреса для каждой подсети, AWS exports, параметры SSM и другие переменные.

  • EKS control plane и nodegroup. Фактическая сборка кластера AWS EKS с подключением рабочих нод.

  • Установка дополнений. Это то, что сделает ваш кластер более милым :-) Установите такие дополнения как Istio, logging integration, autoscaler и т.д. Этот список дополнений не является исчерпывающим и совершенно необязателен.

  • Валидация кластера. На этом этапе мы проверяем кластер (основные компоненты EKS и дополнения) с функциональной точки зрения перед передачей его в эксплуатацию. Чем больше тестов вы напишете, тем крепче будете спать. (Особенно если вы дежурный в техподдержке!)

Склеиваем этапы вместе

На каждом из этих четырех этапов используются разные инструменты и приемы, о которых я расскажу позже. Мы искали единый инструмент для всех этапов, который склеил бы все вместе, поддерживал последовательное и параллельное выполнение, был событийно-ориентированным и, желательно, с визуализацией сборки.

И мы нашли Argo. В частности, Argo Events и Argo Workflows. Оба они запускаются в Kubernetes как CRD и используют декларативный YAML, который используется и в других деплоях Kubernetes.

Мы нашли идеальную комбинацию: императивная оркестрация (Imperative Orchestration), декларативная автоматизация (Declarative Automation).

Готовый к продакшену кластер K8s, построенный с помощью Argo WorkflowsГотовый к продакшену кластер K8s, построенный с помощью Argo Workflows

Argo Workflows

Argo Workflows это container-native workflow engine с открытым исходным кодом для оркестрации параллельных заданий в Kubernetes. Argo Workflows реализован как Kubernetes CRD.

Примечание: Если вы знакомы с K8s YAML, то обещаю, что для вас все будет просто.

Давайте посмотрим, как вышеуказанные этапы сборки могут выглядеть в Argo Workflows.

1. Предварительные тесты

Предварительные тесты выполняются параллельно, с повторными попытками в случае сбояПредварительные тесты выполняются параллельно, с повторными попытками в случае сбоя

Для написания тестов мы используем фреймворк BATS. Написать тест в BATS очень просто:

#!/usr/bin/env bats@test More than 100 available IP addresses in subnet MySubnet {AvailableIpAddressCount=$(aws ec2 describe-subnets --subnet-ids MySubnet | jq -r .Subnets[0].AvailableIpAddressCount) [ ${AvailableIpAddressCount} -gt 100 ]}

Параллельный запуск BATS-тестов (указанного выше теста avail-ip-addresses.bats и еще трех) с использованием Argo Workflow может выглядеть следующим образом:

 name: preflight-tests  templateRef:     name: argo-templates    template: generic-template  arguments:    parameters:     name: command      value: {{item}}  withItems:   bats /tests/preflight/accnt-name-export.bats   bats /tests/preflight/avail-ip-addresses.bats   bats /tests/preflight/dhcp.bats   bats /tests/preflight/subnet-export.bats

2. EKS control plane и nodegroup

EKS control plane и nodegroup с зависимостямиEKS control plane и nodegroup с зависимостями

Для создания кластера EKS вы можете выбрать различные инструменты. Доступны eksctl, CloudFormation или Terraform. Построение EKS в два этапа с зависимостями, используя шаблоны CloudFormation (eks-controlplane.yaml и eks-nodegroup.yaml), в Argo Workflow может выглядеть следующим образом.

 name: eks-controlplane  dependencies: [preflight-tests]  templateRef:     name: argo-templates    template: generic-template arguments:   parameters:    name: command     value: |       aws cloudformation deploy \       --stack-name {{workflow.parameters.CLUSTER_NAME}} \       --template-file /eks-core/eks-controlplane.yaml \       --capabilities CAPABILITY_IAM- name: eks-nodegroup  dependencies: [eks-controlplane]  templateRef:     name: argo-templates    template: generic-template  arguments:    parameters:     name: command      value: |        aws cloudformation deploy \        --stack-name {{workflow.parameters.CLUSTER_NAME}}-nodegroup \        --template-file /eks-core/eks-nodegroup.yaml \        --capabilities CAPABILITY_IAM

3. Установка дополнений

Параллельная установка дополнений с зависимостямиПараллельная установка дополнений с зависимостями

Установить дополнения можно, используя kubectl, helm, kustomize или их комбинацию. Например, установка metrics-server с helm template и kubectl, при условии, что требуется установка metrics-server, в Argo Workflows может выглядеть следующим образом.

 name: metrics-server  dependencies: [eks-nodegroup]  templateRef:     name: argo-templates    template: generic-template  when: {{workflow.parameters.METRICS-SERVER}} != none  arguments:    parameters:     name: command      value: |        helm template /addons/{{workflow.parameters.METRICS-SERVER}}/ \        --name metrics-server \        --namespace kube-system \        --set global.registry={{workflow.parameters.CONTAINER_HUB}} | \        kubectl apply -f -

4. Валидация кластера

Параллельная валидация кластера с повторными попытками при ошибках.Параллельная валидация кластера с повторными попытками при ошибках.

Для проверки функциональности дополнений мы используем великолепную BATS-библиотеку DETIK, которая упрощает написание K8s-тестов.

#!/usr/bin/env batsload lib/utilsload lib/detikDETIK_CLIENT_NAME=kubectlDETIK_CLIENT_NAMESPACE="kube-system"@test verify the deployment metrics-server {  run verify there are 2 pods named metrics-server [ $status -eq 0 ]  run verify there is 1 service named metrics-server [ $status -eq 0 ]  run try at most 5 times every 30s to find 2 pods named metrics-server with status being running [ $status -eq 0 ]  run try at most 5 times every 30s to get pods named metrics-server and verify that status is running [ $status -eq 0 ]}

Выполнение указанного выше тестового файла BATS DETIK (metrics-server.bats), при условии, что metrics-server установлен, в Argo Workflows может выглядеть так:

 name: test-metrics-server  dependencies: [metrics-server]  templateRef:    name: worker-containers    template: addons-tests-template  when: {{workflow.parameters.METRICS-SERVER}} != none  arguments:    parameters:     name: command      value: |        bats /addons/test/metrics-server.bats

Представьте, сколько тестов вы можете сюда подключить. Вы можете запустить Sonobuoy conformance tests, Popeye A Kubernetes Cluster Sanitizer и Fairwinds Polaris. Подключайте их с помощью Argo Workflows!

Если вы дошли до этого момента, то значит, у вас есть полностью рабочий AWS EKS кластер, готовый к промышленной эксплуатации, с установленным, протестированным и готовым metrics-server. Вы молодцы!

Но мы еще не прощаемся, самое интересное я оставил на конец.

WorkflowTemplate

Argo Workflows поддерживает шаблоны (WorkflowTemplate), что позволяет создавать повторно используемые workflow. Каждый из четырех этапов сборки это шаблон. По сути, мы создали строительные блоки, которые можно комбинировать по мере необходимости. Используя один главный workflow, можно выполнять все этапы сборки по порядку (как в примере выше), или независимо друг от друга. Такая гибкость достигается с помощью Argo Events.

Argo Events

Argo Events это управляемый событиями фреймворк автоматизации рабочих процессов для Kubernetes (workflow automation framework), который помогает запускать объекты K8s, рабочие процессы Argo Workflow, бессерверные рабочие нагрузки и др. по событиям из различных источников, таких как webhook, s3, расписания, очереди сообщений, gcp pubsub, sns, sqs и т.д.

Сборка кластера запускается вызовом API (Argo Events) с использованием JSON. Кроме того, каждый из четырех этапов сборки (WorkflowTemplate) имеет собственную конечную точку API. Персонал, сопровождающий Kubernetes, может извлечь из этого большую пользу:

  • Не уверены в состоянии облачной среды? Используйте API предварительных тестов.

  • Хотите построить голый EKS-кластер? Используйте eks-core (control-plane и nodegroup) API.

  • Хотите установить или переустановить дополнения в существующем EKS-кластере? Есть addons API.

  • С кластером происходит что-то странное и вам нужно быстро запустить тесты? Вызовите test API.

Возможности Argo

Как Argo Events, так и Argo Workflows включают в себя большой набор возможностей, которые вам не потребуется реализовывать самостоятельно.

Вот семь из них, которые наиболее важны для нас:

  • Параллелизм

  • Зависимости

  • Повторные попытки обратите внимание на скриншотах выше на красные предварительные и валидационные тесты. Argo автоматически повторил их с последующим успешным завершением.

  • Условия

  • Поддержка S3

  • Шаблоны рабочих процессов (WorkflowTemplate)

  • Параметры Events Sensor

Заключение

Мы получили возможность использовать различные инструменты, которые работают совместно и определяют желаемое состояние инфраструктуры, обеспечивая гибкость и высокую скорость проекта. Мы планируем использовать Argo Events, Argo Workflows и для других задач автоматизации. Возможности безграничны.


Узнать больше о курсе DevOps практики и инструменты.

Смотреть открытый вебинар по теме Prometheus: быстрый старт.

Подробнее..

Перевод Tоп 10 PromQL запросов для мониторинга Kubernetes

12.06.2021 10:05:57 | Автор: admin

В этой статье приведены примеры популярных запросов Prometheus для мониторинга Kubernetes.

Если вы только начинаете работать с Prometheus и у вас возникают сложности при создании запросов PromQL, советуем обратиться к руководству по началу работы с PromQL. Здесь мы пропустим теорию и сразу перейдём к практике.

Рейтинг основан на опыте компании Sysdig, ежедневно оказывающей сотням клиентов помощь в настройке мониторинга их кластеров:

1. Количество pods в каждом namespace

Информация о количестве pod в каждом namespace может быть полезна для обнаружения аномалий в кластере, например, слишком большого количества pods в отдельном namespace:

sum by (namespace) (kube_pod_info)

2. Количество контейнеров без CPU limits в каждом namespace

Важно правильно задавать лимиты для оптимизации производительности приложений и кластера. Этот запрос находит контейнеры без CPU limits:

count by (namespace)(sum by (namespace,pod,container)(kube_pod_container_info{container!=""}) unless sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="cpu"}))

3. Количество перезагрузок pods в каждом namespace

С помощью этого запроса вы получите список pods, которые перезапускались. Это важный показатель, поскольку большое количество перезагрузок pod обычно означает CrashLoopBackOff:

sum by (namespace)(changes(kube_pod_status_ready{condition="true"}[5m]))

4. Pods в статусе Not Ready в каждом namespace

Запрос выводит все pods, при работе которых возникла проблема. Это может быть первым шагом к её локализации и устранению:

sum by (namespace)(kube_pod_status_ready{condition="false"})

5. Превышение ресурсов кластера ЦП

Необходимо избегать ситуации, когда CPU limits превышают ресурсы кластера. В противном случае вы можете столкнуться с проблемой троттлинга процессора. Обнаружить превышение лимитами ресурсов кластера можно с помощью запроса:

sum(kube_pod_container_resource_limits{resource="cpu"}) - sum(kube_node_status_capacity_cpu_cores)

6. Превышение ресурсов кластера Память

Если все Memory limits суммарно превышают ёмкость кластера, то это может привести к PodEviction, если на узле не будет хватать памяти. Для проверки используйте запрос PromQL:

sum(kube_pod_container_resource_limits{resource="memory"}) - sum(kube_node_status_capacity_memory_bytes)

7. Количество исправных узлов кластера

Запрос выведет количество исправных узлов кластера:

sum(kube_node_status_condition{condition="Ready", status="true"}==1)

8. Количество узлов кластера, которые могут работать некорректно

Найти узлы кластера, которые периодически меняют состояние с Ready на Not Ready:

sum(changes(kube_node_status_condition{status="true",condition="Ready"}[15m])) by (node) > 2

9. Обнаружение простаивающих ядер ЦП

Планировании ресурсов кластера Kubernetes не самая простая задача. Этот запрос поможет вам определить, сколько ядер ЦП простаивают:

sum((rate(container_cpu_usage_seconds_total{container!="POD",container!=""}[30m]) - on (namespace,pod,container) group_left avg by (namespace,pod,container)(kube_pod_container_resource_requests{resource="cpu"})) * -1 >0)

10. Обнаружение неиспользуемой памяти

Этот запрос поможет снизить ваши затраты, предоставив информацию о неиспользуемой памяти:

sum((container_memory_usage_bytes{container!="POD",container!=""} - on (namespace,pod,container) avg by (namespace,pod,container)(kube_pod_container_resource_requests{resource="memory"})) * -1 >0 ) / (1024*1024*1024)

Хотите узнать больше?

Рекомендуем изучить нашу шпаргалку по PromQL, чтобы узнать, как писать более сложные запросы PromQL.

Также воспользуйтесь отличной коллекцией Awesome Prometheus alerts collection. Она включает несколько сотен Prometheus alert rules, вы можете изучить их, чтобы узнать больше о PromQL и Prometheus.

Подробнее..

Перевод Как оптимизировать ограничения ресурсов Kubernetes

15.06.2021 10:13:02 | Автор: admin

Поиск оптимальных значений для ограничения ресурсов Kubernetes непростая задача, поскольку вам нужно найти золотую середину между слишком жесткими и недостаточными ограничениями.

В этой статье, которая является продолжением серии статей о рациональном использовании ресурсов в Kubernetes, вы узнаете, как выбрать правильные ограничения ресурсов Kubernetes: от обнаружения контейнеров без каких-либо ограничений до определения оптимальных параметров, которые вы должны установить в своем кластере.

Prometheus одно из самых популярных решений для мониторинга кластеров Kubernetes. Поэтому каждый шаг в этом руководстве содержит примеры запросов PromQL.

Обнаружение контейнеров без ограничения ресурсов

Первым шагом для выбора правильных лимитов является обнаружение контейнеров без каких-либо ограничений.

Отсутствие лимитов в контейнере может оказать негативное воздействие на узлы кластера. В лучшем случае узлы начнут выселять pods с учетом QoS. При этом также возникнут проблемы с производительностью из-за троттлинга процессора. Но в худшем случае узел может перестать работать из-за нехватки ресурсов.

Контейнеры без CPU Limit в каждом namespace

sum by (namespace)(count by (namespace,pod,container)(kube_pod_container_info{container!=""}) unless sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="cpu"}))

Контейнеры без Memory Limit в каждом namespace

sum by (namespace)(count by (namespace,pod,container)(kube_pod_container_info{container!=""}) unless sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="memory"}))

Допустим, вы нашли несколько контейнеров без ограничений ресурсов. Как определить потенциально опасные? Легко! Нужно найти контейнеры, которые используют больше всего ресурсов и при этом не имеют лимитов.

Топ-10 контейнеров без CPU Limits, потребляющих больше всего ресурсов CPU

topk(10,sum by (namespace,pod,container)(rate(container_cpu_usage_seconds_total{container!=""}[5m])) unless sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="cpu"}))

Топ-10 контейнеров без Memory Limits, потребляющих больше памяти

topk(10,sum by (namespace,pod,container)(container_memory_usage_bytes{container!=""}) unless sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="memory"}))

Обнаружение контейнеров со слишком строгими ограничениями

Обнаружение контейнеров со слишком строгими CPU Limits

Если утилизация контейнером ресурсов процессора приближается к установленному лимиту, то его производительность будет снижаться из-за троттлинга ЦП.

Выполните этот запрос, чтобы найти контейнеры, для которых использование ЦП близко к пределу:

(sum by (namespace,pod,container)(rate(container_cpu_usage_seconds_total{container!=""}[5m])) / sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="cpu"})) > 0.8

Обнаружение контейнеров со слишком строгими Memory Limits

Если контейнер превысит лимит по памяти, он будет убит.

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

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

(sum by (namespace,pod,container)(container_memory_usage_bytes{container!=""}) / sum by (namespace,pod,container)(kube_pod_container_resource_limits{resource="memory"})) > 0.8

Как выбрать оптимальные значения для лимитов?

Один из способов выбрать оптимальные значения для лимитов наблюдение за утилизацией ресурсов контейнером в течение некоторого времени. Мы можем использовать две стратегии:

Консервативная

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

Находим оптимальный CPU Limit с помощью консервативной стратегии:

max by (namespace,owner_name,container)((rate(container_cpu_usage_seconds_total{container!="POD",container!=""}[5m])) * on(namespace,pod) group_left(owner_name) avg by (namespace,pod,owner_name)(kube_pod_owner{owner_kind=~"DaemonSet|StatefulSet|Deployment"}))

Находим оптимальный Memory Limit с помощью консервативной стратегии:

max by (namespace,owner_name,container)((container_memory_usage_bytes{container!="POD",container!=""}) * on(namespace,pod) group_left(owner_name) avg by (namespace,pod,owner_name)(kube_pod_owner{owner_kind=~"DaemonSet|StatefulSet|Deployment"}))

Агрессивная

Задаем ограничение ресурсов по 99 квантилю. Это позволит не учитывать 1% наиболее высоких значений. Это хорошая стратегия, если есть редкие аномалии или пики, которые вы хотите игнорировать.

Находим оптимальный CPU Limit с помощью агрессивной стратегии:

quantile by (namespace,owner_name,container)(0.99,(rate(container_cpu_usage_seconds_total{container!="POD",container!=""}[5m])) * on(namespace,pod) group_left(owner_name) avg by (namespace,pod,owner_name)(kube_pod_owner{owner_kind=~"DaemonSet|StatefulSet|Deployment"}))

Находим оптимальный Memory Limit с помощью агрессивной стратегии:

quantile by (namespace,owner_name,container)(0.99,(container_memory_usage_bytes{container!="POD",container!=""}) * on(namespace,pod) group_left(owner_name) avg by (namespace,pod,owner_name)(kube_pod_owner{owner_kind=~"DaemonSet|StatefulSet|Deployment"}))

Достаточно ли ресурсов в вашем кластере?

Узлы кластера гарантируют, что запланированные в них pods будут иметь достаточно ресурсов на основе параметра Requests контейнера каждого pods. К тому же, узлы резервируют за каждым контейнером указанный объем памяти и количество ядер ЦП.

Необходимо также контролировать, что сумма всех ограничений ресурсов Kubernetes не превышает ёмкость ресурсов в вашем кластере.

Когда вы утилизируете большую часть ресурсов кластера, контейнеры могут работать без проблем при обычной нагрузке, но в сценариях с высокой нагрузкой контейнеры могут начать использовать ЦП и память до предела. Это приведет к тому, что узел начнет выселять pods, а в критических ситуациях узел перестанет работать из-за нехватки ресурсов.

Как обнаружить превышение доступных ресурсов кластера?

Процент превышения доступных ресурсов по памяти:

100 * sum(kube_pod_container_resource_limits{container!="",resource="memory"} ) / sum(kube_node_status_capacity_memory_bytes)

Процент превышения доступных ресурсов по ЦП:

100 * sum(kube_pod_container_resource_limits{container!="",resource="cpu"} ) / sum(kube_node_status_capacity_cpu_cores)

Как правило, не все контейнеры полностью утилизируют запрашиваемые ресурсы одновременно. Если заданные лимиты составляют 100% от ресурсов кластера это повлечет за собой дополнительные затраты на инфраструктуру, которая никогда не будет использоваться.

Лучшим решением будет выбрать консервативную стратегию, гарантирующую, что избыточное использование составляет менее 125%, или агрессивную стратегию, которая позволяет лимитам достичь 150% емкости вашего кластера.

Не менее важным будет проверка соответствия лимитов емкости каждого узла. Например, у нас есть контейнер с CPU Requests - 2 и CPU Limit - 8. Этот контейнер можно запланировать на узле с 4 ядрами, но лимиты не дадут нужного эффекта, потому что на узле недостаточно ядер.

Процент превышения доступных ресурсов узла по памяти:

sum by (node)(kube_pod_container_resource_limits{container!=,resource=memory} ) / sum by (node)(kube_node_status_capacity_memory_bytes)

Процент превышения доступных ресурсов узла по ЦП:

sum by (node)(kube_pod_container_resource_limits{container!=,resource=cpu} ) / sum by (node)(kube_node_status_capacity_cpu_cores)

Подведем итоги

В этой статье вы узнали, почему так важно правильно использовать Kubernetes Limits and Requests, как определять наличие неоптимальных параметров в вашем кластере и какие стратегии вы могли бы использовать, чтобы установить правильные ограничения ресурсов Kubernetes.

Если вы хотите узнать больше, рекомендуем прочитать статью рациональное использовании ресурсов в Kubernetes.

Подробнее..

Мониторинг Virtuozzo Hybrid Server с помощь Prometheus

01.06.2021 14:09:01 | Автор: admin

Пользователи продуктов Virtuozzo и OpenVZ обычно работают с множеством развернутых машин под управлением нашего ПО. Поэтому для них логично запустить централизованный мониторинг всего парка таких серверов. И сегодня мы расскажем о том, как использовать для этого встроенные сервисы мониторинга Virtuozzo Hybrid Infrastructure на основе Prometheus.

Схожие сервисы доступны и в VHS, но только если вы используете графические интерфейс для управления кластером Virtuozzo Storage. Если же у вас нет кластера Storage, или вы не используете GUI для его администрирования (либо работаете с OpenVZ, где подключение Virtuozzo Storage является возможным, но редко встречающимся сценарием), то для мониторинга приходится обращаться к сторонним решениям.

Анализ предпочтений пользователей (публично доступный для OpenVZ здесь) показывает следующее: как и для мониторинга серверов с Linux и основанных на нем решениями, для продуктов Virtuozzo популярны Zabbix и Prometheus.

Мы поговорим сегодня про Prometheus более молодой, но активно набирающий популярность инструментарий.

Непосредственно Prometheus занимается сбором данных от подвластных ему экспортеров - в репозиториях VHS доступны node_exporter (для сбора общих характеристик сервера, таких как использование ресурсов и состояние дисков) и libvirt_exporter (для сбора информации о виртуальных машинах, управляемых libvirt). Экспортеры основаны на соответствующих стандартных проектах, но содержат ряд специфичных для Virtuozzo изменений.

Для удобного изучения собираемой информации, в связке с Prometheus обычно устанавливается инструментарий визуализации данных Grafana, а для возможности получения оповещений о требующих внимания администратора событиях Alertmanager.

Готовим плацдарм

Prometheus, Grafana и Alertmanager можно развернуть на отдельной машине, не обязательно физической. Раз уж мы говорим о Virtuozzo Hybrid Server, то логично развернуть их внутри контейнера на одной из машин VHS. Например, внутри контейнера с Virtuozzo Linux 8; для функционирования вполне достаточно двух ядер ЦПУ и пары гигабайт памяти:

# prlctl create promct --vmtype=ct --ostemplate=vzlinux-8-x86_64# prlctl set promct --cpu 2# prlctl set promct --memsize 2G

Не забудьте выставить необходимый IP адрес и доменное имя, по которым будет доступен ваш контейнер.

Внутри контейнера не помешает выставить правильный часовой пояс:

# timedatectl set-timezone 'Europe/Moscow'

А также настроить firewall порт 9090 для веб-интерфейса Prometheus, 9093 для AlertManager и 3000 для Grafana. Обратите внимание, что веб Prometheus и Alertmanager доступны без пароля, так что выставляйте их только в ваши внутренние сети.

# firewall-cmd --zone=public --permanent --add-port=9090/tcp# firewall-cmd --zone=public --permanent --add-port=9093/tcp# firewall-cmd --zone=public --permanent --add-port=3000/tcp# firewall-cmd --reload

Устанавливаем Prometheus & co.

Prometheus входит в репозитории многих дистрибутивов, в том числе и Virtuozzo Linux 8, поэтому можно его установить штатными средствами:

# yum install prometheus

Если вас не устраивает версия из дистрибутива (например, хочется самую свежую, либо наоборот - свежая имеет какие-то раздражающие вас проблемы), то можно просто скачать бинарные файлы с GitHub и положить их в директорию /opt (или в другую на ваш выбор). Например, для версии 2.21.0:

# cd /opt# wget https://github.com/prometheus/prometheus/releases/download/v2.21.0/prometheus-2.21.0.linux-amd64.tar.gz# tar -xzf prometheus*.tar.gz

Единственный нюанс при ручной установке - не забыть создать (либо скорректировать) service-файл для systemd, чтобы там были правильные пути:

# cat /lib/systemd/system/prometheus.service[Unit]Description=PrometheusWants=network-online.targetAfter=network-online.target[Service]Type=simpleWorkingDirectory=/opt/prometheus-2.21.0.linux-amd64Restart=on-failureExecStart=/opt/prometheus-2.21.0.linux-amd64/prometheus[Install]WantedBy=multi-user.target

Аналогично с Alertmanager - можно установить из репозиториев:

# yum install alertmanager

... А можно и с сайта, точно также не забыв service-файл:

# wget https://github.com/prometheus/alertmanager/releases/download/v0.21.0/alertmanager-0.21.0.linux-amd64.tar.gz# cd /opt# tar -xzf alertmanager*.tar.gz# cat /usr/lib/systemd/system/alertmanager.service[Unit]Description=AlertmanagerWants=network-online.targetAfter=network-online.targetAfter=prometheus.service[Service]Type=simpleWorkingDirectory=/root/alertmanager-0.21.0.linux-amd64Restart=on-failureExecStart=/root/alertmanager-0.21.0.linux-amd64/alertmanager --config.file=alertmanager.yml[Install]WantedBy=multi-user.target

Grafana сразу предоставляет собранные rpm-пакеты, так что можно, опять же, поставить либо из дистрибутива:

# yum install grafana

либо с сайта проекта:

# yum install https://dl.grafana.com/oss/release/grafana-7.1.5-1.x86_64.rpm

Подготавливаем машины с Virtuozzo Hybrid Server

На каждом сервере VHS, который предполагается мониторить, необходимо установить пакеты с экспортерами:

# yum install node_exporter libvirt_exporter

Чтобы экспортеры могли отдавать данные в Prometheus, необходимо открыть соответствующие порты - 9177 для libvirt_exporter и 9100 для node_exporter. Доступ к ним желательно ограничить адресом машины с Prometheus, чтобы посторонние люди не делали попыток снять метрики с ваших серверов:

# firewall-cmd --permanent --zone=public --add-rich-rule=' rule family="ipv4" source address="1.2.3.4/32" port protocol="tcp" port="9177" accept'# firewall-cmd --permanent --zone=public --add-rich-rule=' rule family="ipv4" source address="1.2.3.4/32" port protocol="tcp" port="9100" accept'# firewall-cmd --reload

Здесь "1.2.3.4" необходимо поменять на реальный адрес Prometheus.

Наконец, смело включаем и запускаем сервисы экспортеров:

# systemctl enable node_exporter# systemctl enable libvirt-exporter# systemctl start node_exporter# systemctl start libvirt-exporter

Дело за малым - надо сообщить Прометею, откуда ему собирать информацию.

Настройка Prometheus

Конфигурация самого Prometheus содержится в наборе Yaml-файлов. Писать их с нуля, даже с помощью примеров из документации то еще развлечение. Именно этот процесс и решили улучшить в Virtuozzo, положив в репозитории VHS 7 пакет vz-prometheus-cfg с шаблонами файлов конфигурации. Его можно установить на любой машине VHS 7, а если вы развернули Prometheus внутри VzLinux 8 то можно его поставить прямо на сервере из репозиториев этого дистрибутива:

# yum install vz-prometheus-cfg

После чего изучать директорию /usr/share/vz-prometheus-cfg/, начав с файла prometheus-example.yml.

Этот файл необходимо отредактировать под ваши нужды и под именем prometheus.yml сохранить в машину или контейнер, где у вас расположен Prometheus. Главное, что там необходимо изменить это местоположение файлов *rules.yml и *alerts.yml, которые также можно скопировать из директории /usr/share/vz-prometheus-cfg/ на сервер Prometheus. Можно их изучить и даже отредактировать, однако они вполне работоспособны и в исходном варианте.

В первую очередь здесь интересны vz-rules, содержащие правила сбора информации о потреблении ресурсов (диска, процессора, памяти и сети) виртуальными окружениями и предупреждения о проблемах с ресурсами (останов ВМ из-за ошибки диска, исчерпание диска на сервере и тому подобное).

Непосредственно машины, с которых надо собирать информацию, указываются в target-файлах. В первом приближении достаточно одного такого файла со списком машин для каждого экспортера (напомним, node_exporter отдает информацию на порту 9100, а libvirt - 9177):

# cat my-vz-libvirt.yml- labels: group: my-vz-deployment targets: - my.node1:9177 - my.node2:9177# cat my-vz-node.yml- labels: group: my-vz-deployment targets: - my.node1:9100 - my.node2:9100

Пути к этим файлам необходимо указать в соответствующих разделах секции scrape_configs в prometheus.yml:

scrape_configs: ... - job_name: node ... file_sd_configs: - files: - /root/prometheus-2.21.0.linux-amd64/targets/my-vz-node.yml - job_name: libvirt ... file_sd_configs: - files: - /root/prometheus-2.21.0.linux-amd64/targets/my-vz-libvirt.yml

Пример полного файла конфигурации можно посмотреть в документации: https://docs.virtuozzo.com/virtuozzo_hybrid_server_7_users_guide/advanced-tasks/monitoring-via-prometheus.html. Обратите внимание, что важными параметрами являются job_name на них идет отсылка в файлах с правилами и алертами. Так что если задумаете менять эти имена не забудьте пройтись и по другим файлами конфигурации и провести соответствующие замены.

Как только все необходимые файлы отредактированы - пора стартовать сервисы:

# systemctl start prometheus# systemctl start alertmanager# systemctl start grafana-server

После чего продолжать уже с настройкой двух других инструментов

Grafana и Alertmanager

Конфигурация Grafana осуществляется через ее веб-интерфейс по адресу http://<ваш-сервер>:3000. Логин-пароль по умолчанию - "admin" / "admin".

Настройка достаточно проста и стандартна - сначала необходимо указать "Prometheus" как источник данных, пройдя в Configuration -> Data Sources -> "Add data source", выбрав "Prometheus" и указав http://localhost:9090 в качестве его адреса.

Далее можно импортировать готовые json-файлы, опять же поставляемые с пакетов vz-prometheus-cfg - grafana_hn_dashboard.json и grafana_ve_dashboard.json - служащие соответственно для отображения информации о серверах и о виртуальных окружениях. Импорт осуществляется в меню "Dashboards" -> "Manage" -> "Import", в качестве источника данных необходимо добавить настроенный ранее Prometheus.

И, вуаля, к этому моменту Prometheus уже вполне мог получить какие-то данные с ваших машин, так что можно практически сразу изучать графики.

Наконец, настройка оповещений Alertmanager тут все на ваше усмотрение, готовых рецептов а-ля отправить отчет в саппорт Virtuozzo не предусмотрено. Так что можно, например, просто настроить типичные оповещения по email в /etc/alertmanager/alertmanager.yaml:

route:   receiver: 'email'  group_by: ['alertname', 'cluster']   group_wait: 30s   group_interval: 5m   repeat_interval: 3h   receivers:  - name: 'email'   email_configs:   - to: 'admin@myserver.com'   from: 'vz-alert@myserver.com'   smarthost: smtp.myserver.com:587

Всего алертов из коробки предусмотрено почти три десятка полный перечень доступен в документации.

Надеемся, обилие и нетривиальность параметров не отпугнет вас от использования Prometheus. Тем более что настроить его достаточно один раз, а шаблоны от Virtuozzo должны в этом неплохо помочь. В результате вы получаете красивую, удобную и функциональную систему мониторинга, без которой современному системному администратору будет намного сложнее работать.

Подробнее..

Перевод Знакомство с PromQL Cheatsheet

13.06.2021 12:23:01 | Автор: admin

Скачать Cheatsheet по запросам PromQL

Начало работы с PromQL может быть непростым, если вы только начинаете свое путешествие в увлекательный мир Prometheus. Это руководство поможет понять принципы его работы, статья включает интересные и полезные советы, необходимые для начала работы.

Поскольку Prometheus хранит данные в виде временных рядов (time-series data model), запросы PromQL радикально отличаются от привычного SQL. Понимание, как работать с данными в Prometheus, является ключом к тому, чтобы научиться писать эффективные запросы.

Не забудьте скачать Cheatsheet по запросам PromQL!

Как работают time-series databases

Временные ряды это потоки значений, связанных с меткой времени.

Каждый временной ряд можно идентифицировать по названию метрики и меткам, например:

mongodb_up{}

или

kube_node_labels{cluster="aws-01", label_kubernetes_io_role="master"}

В приведенном выше примере присутствует имя метрики (kube_node_labels) и метки (cluster и label_kubernetes_io_role). На самом деле, метрики тоже являются метками. Приведенный выше запрос можно записать так:

{__name__ = "kube_node_labels", cluster="aws-01", label_kubernetes_io_role="master"}

В Prometheus есть четыре типа метрик:

  • Gauges(Измеритель) значения, которые могут меняться. Например, метрика mongodb_up позволяет узнать, есть ли у exporter соединение с экземпляром MongoDB.

  • Counters(Счетчик) показывают суммарные значения и обычно имеют суффикс _total. Например, http_requests_total.

  • Histogram (Гистограмма) это комбинация различных счетчиков, используется для отслеживания размерных показателей и их продолжительности, таких как длительность запросов.

  • Summary (Сводка)работает как гистограмма, но также рассчитывает квантили.

Знакомство с выборкой данных PromQL

Выбрать данные в PromQL так же просто, как указать метрику, из которой вы хотите получить данные. В этом примере мы будем использовать метрику http_requests_total.

Допустим, мы хотим узнать количество запросов по пути / api на хосте 10.2.0.4. Для этого мы будем использовать метки host и path из этой метрики:

http_requests_total{host="10.2.0.4", path="/api"}

Запрос вернет следующие значения:

name

host

path

status_code

value

http_requests_total

10.2.0.4

/api

200

98

http_requests_total

10.2.0.4

/api

503

20

http_requests_total

10.2.0.4

/api

401

1

Каждая строка в этой таблице представляет собой поток с последним доступным значением. Поскольку http_requests_total содержит определенное количество запросов, сделанных с момента последнего перезапуска счетчика, мы видим 98 успешных запросов.

Это называетсяinstant vector, самое раннее значение для каждого потока на указанный в запросе момент времени. Поскольку семплы берутся в случайное время, Prometheus округляет результаты. Если длительность не указана, то возвращается последнее доступное значение.

Кроме того, вы можете получить instant vector из другого отрезка времени (например, день назад).

Для этого вам нужно добавить offset (смещение), например:

http_requests_total{host="10.2.0.4", path="/api", status_code="200"} offset 1d

Чтобы получить значение метрики в пределах указанного отрезка времени, необходимо указать его в скобках:

http_requests_total{host="10.2.0.4", path="/api"}[10m]

Запрос вернет следующие значения:

name

host

path

status_code

value

http_requests_total

10.2.0.4

/api

200

641309@1614690905.515

641314@1614690965.515

641319@1614691025.502

http_requests_total

10.2.0.5

/api

200

641319@1614690936.628

641324@1614690996.628

641329@1614691056.628

http_requests_total

10.2.0.2

/api

401

368736@1614690901.371

368737@1614690961.372

368738@1614691021.372

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

Это называется range vector все значения для каждой серии в пределах указанного временного интервала.

Знакомство с агрегаторами и операторами PromQL

Как видите, селекторы PromQL помогают получить данные метрик. Но что, если вы хотите получить более сложные результаты?

Представим, что у нас есть метрика node_cpu_cores с меткой cluster. Мы могли бы, например, суммировать результаты, объединяя их по определенной метке:

sum by (cluster) (node_cpu_cores)

Запрос вернет следующие значения:

cluster

value

foo

100

bar

50

С помощью этого простого запроса мы видим, что имеется 100 ядер ЦП для кластера cluster_foo и 50 для cluster_bar.

Кроме того, мы можем использовать в запросах PromQL арифметические операторы. Например, используя метрику node_memory_MemFree_bytes, которая возвращает объем свободной памяти в байтах, мы могли бы получить это значение в мегабайтах с помощью оператора деления:

node_memory_MemFree_bytes / (1024 * 1024)

Мы также можем получить процент доступной свободной памяти, сравнив предыдущую метрику с node_memory_MemTotal_bytes, которая возвращает общий объем памяти, доступной на узле:

(node_memory_MemFree_bytes / node_memory_MemTotal_bytes) * 100

Теперь мы можем использовать этот запрос для создания оповещения, когда на узле остается менее 5% свободной памяти:

(node_memory_MemFree_bytes / node_memory_MemTotal_bytes) * 100 < 5

Знакомство с функциями PromQL

PromQL поддерживает большое количество функций, которые мы можем использовать для получения более сложных результатов. Например, в предыдущем примере мы могли бы использовать функцию topk, чтобы определить, какой из двух узлов имеет больший объем свободной памяти (в процентах):

topk(2, (node_memory_MemFree_bytes / node_memory_MemTotal_bytes) * 100)

Prometheus позволяет не только получить информацию о прошедших событиях, но даже строить прогнозы. Функция pred_linear предсказывает, где будет временной ряд через заданный промежуток времени.

Представьте, что вы хотите узнать, сколько свободного места будет доступно на диске в следующие 24 часа. Вы можете применить функцию pred_linear к результатам за прошлую неделю из метрики node_filesystem_free_bytes, которая возвращает доступное свободное место на диске. Это позволяет прогнозировать объем свободного дискового пространства в гигабайтах в ближайшие 24 часа:

predict_linear(node_filesystem_free_bytes[1w], 3600 * 24) / (1024 * 1024 * 1024) < 100

При работе со счетчиками Prometheus удобно использовать функцию rate. Она вычисляет среднюю скорость увеличения временного ряда в векторе диапазона в секунду, сбросы счетчика автоматически корректируются. Кроме того, вычисление экстраполируется к концам временного диапазона.

Что делать, если нам нужно создать оповещение, которое срабатывает, если мы не получали запрос в течение 10 минут. Мы не можем просто использовать метрику http_requests_total, потому что при сбросе счетчика в течение указанного временного диапазона результаты были бы неточными:

http_requests_total[10m]

name

host

path

status_code

value

http_requests_total

10.2.0.4

/api

200

100@1614690905.515

300@1614690965.515

50@1614691025.502

В приведенном выше примере после сброса счетчика мы получаем отрицательные значения от 300 до 50, поэтому нам недостаточно только этой метрики. Мы можем решить проблему с помощью функции rate. Поскольку он считает сбросы счетчика, результаты фиксируются, как если бы они были такими:

name

host

path

status_code

value

http_requests_total

10.2.0.4

/api

200

100@1614690905.515

300@1614690965.515

350@1614691025.502

rate(http_requests_total[10m])

name

host

path

status_code

value

http_requests_total

10.2.0.4

/api

200

0.83

Независимо от сбросов за последние 10 минут в среднем было 0,83 запроса в секунду. Теперь мы можем настроить оповещение:

rate(http_requests_total[10m]) = 0

Что дальше?

В этой статье мы узнали, как Prometheus хранит данные, рассмотрели примеры запросов PromQL для выборки и агрегирования данных.

Вы можете скачать Cheatsheet по PromQL, чтобы узнать больше об операторах и функциях PromQL. Вы также можете проверить все примеры из статьи и Cheatsheet с нашим сервисом Prometheus playground.

Подробнее..

PgSCV экспортер метрик для PostgreSQL

27.05.2021 12:14:32 | Автор: admin

Всем привет. В этом посте я расскажу про pgSCV - новый экспортер метрик для PostgreSQL (и не только), чем он так хорош и какие проблемы решает.

Наверняка все кто используют Prometheus и PostgreSQL сталкивались и с postgres_exporter. Этот экспортер довольно легко запуститьи начать им пользоваться. Также у него есть возможности для расширения, на основе своего запроса можно описать метрики иснимать их. Если есть хорошие знания о том как устроена постгресовая статистика можно собрать довольнобольшое количество метрик. Но как известно кроме метрик самого Postgres, еще желательно собирать метрики системы, а если винфраструктуре есть вспомогательные сервисы, например пулеры соединений (pgbouncer, odyssey и т.п.), то и с них также нужно сниматьметрики. Выходит что нужно поставить еще экспортеров.

В pgSCV я постарался решить обе этих проблемы.

Решение первое. pgSCV сразу умеет снимать очень большое количество метрик с PostgreSQL без необходимости дополнительной настройки. На мой взгляд это сильно облегчает задачу первоначального запуска. Вместо траты времени на написание кастомной конфигурации для съема дополнительных метрик их сразу можно получить из коробки. Возможности конфигурации кастомных метрик также присутствует. Возникает резонный вопрос - а что если метрик слишком много и не все они нужны? В таком случае, можно отключить сбор метрик или ограничить список БД с которых требуется снимать метрики.

Второе решение. На данный момент pgSCV умеет снимать метрики с системы, PostgreSQL и Pgbouncer. Для этого он автоматически ищет эти сервисы и начинает собирать с них метрики. Очевидно что для сбора метрик с сетевых служб нужны реквизиты для подключения. Съем метрик не ограничивается только локальными службами. При желании можно указать сбор метрик и с сервисов размещенных на других узлах.

Типовой сценарий использования заключается в том чтобы просто запустить pgSCV. В примере ниже, предполагается что пользователь уже создан.

curl -O -L https://github.com/weaponry/pgscv/releases/download/v0.5.0/pgscv_0.5.0_linux_amd64.tar.gztar xvzf pgscv_0.5.0_linux_amd64.tar.gzcat << EOF > pgscv.yamldefaults:    postgres_username: "monitoring"    postgres_password: "supersecretpassword"EOF./pgscv --config-file pgscv.yaml

После запуска можно открыть вторую консоль и с помощью curl -s 127.0.0.1:9890/metrics получить список метрик.

Отмечу, что pgSCV создавался для нужд Weaponry (проект по мониторингу PostgreSQL и всего вокруг него), теперь pgSCV на мой взгляд стабилизировался и мне не стыдно его показать.

На этом всё, спасибо за внимание! Если есть идеи, пожелания или нашлись баги, то пишите в discussions или issues. Напоследок немного ссылок:

Подробнее..

Yet another Asterisk monitoring поддержка Prometheus

29.04.2021 06:19:38 | Автор: admin

image


Рассмотрим типичный день новоиспеченного asteriskера: после чтения тонн мануалов, примеров по установке и настройке Asteriska, отправок тысяч сообщений в соответствующие комьюнити-чаты, посылания в Гугл вы наконец-то получили работающий сервер PBX: внутренние пользователи заведены, транки от популярного SIP-провайдера настроены, роутинг есть, и всё вроде бы звонит. Но тут встаёт новый вопрос: а как всё это мониторить? Как узнать, онлайн ли мои пиры и транки? Сколько у меня текущих звонков? Каков uptime моего Asteriska?


Разумеется, на том же Хабре полной статей (статья 1, статья 2, статья N) по мониторингу Asterisk классическими методами: давно излюбленные Zabbix, Nagios, может Voipmonitor.


Но может в 2021 году появился какой-то новый вариант? Может он стильнее/моднее/молодежнее?


Смотрим changelogи Asteriska и видим:


Asterisk 17.0.0
Add native Prometheus support to Asterisk
(Reported by Matt Jordan)

Ура! Вот оно! К тестам!


Собираем Asterisk с поддержкой res_prometheus (выбираем в menuselect resources/res_prometheus).


Настраиваем конфиг /etc/asterisk/prometheus.conf


[general]enabled = yescore_metrics_enabled = yesuri = metrics                   

Не забываем включить http-сервер asterisk.


Добавляем job в настройках прометея (например):


- job_name: 'asterisk_res_prometheus'    metrics_path: /metrics    static_configs:      - targets: ['asterisk_ip:8088']

и смотрим, какие данные прилетают от Asteriska:


image


На самом деле нативных метрик от Asteriska пока не так и много:


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

На данный момент мониторинг с помощью Prometheus из коробки вряд ли может соперничать по функционалу с Zabbix/Nagios (ссылка 1 на такое, ссылка N на такое). Но для общего понимания и ознакомления полезно знать, что Астериск умеет поддержку Прометея.


Также ничего не мешает написать свой экспортер для Астериска и отправлять метрики в Прометей (ссылка1 на подобные экспортеры, другая интересная ссылка на подобное).


автор поста Asterisk'ер компании Southbridge Михаил Комов.

Подробнее..

Перевод Рекомендации по Ansible

03.02.2021 16:18:50 | Автор: admin

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

Переменные можно разделить на две категории:

  • Переменные в отдельных файлах (в таблице ниже "Filesystem").

  • Переменные в коде (в таблице ниже "Code").

Теперь, если вы посмотрите на их приоритет, то все встает на свои места.

Filesystem-переменные имеют более низкий приоритет по сравнению с Code-переменными. Обратили ли вы внимание на упомянутую выше гибкость и чрезмерную свободу? Давайте продолжим далее с учетом этих знаний.

1. Объявляйте переменные в отдельных файлах

Переменные лучше располагать в отдельных файлах (Inventory, group_vars, host_vars, role/defaults/main.yml и role/vars/main.yml). Все "постоянные" переменные должны быть явно определены. Постоянные переменные это переменные, влияющие на роль или поведение плейбука. В отличие от "временных" переменных, используемых в качестве буфера для временного хранения значений, часто с ограниченной областью видимости. Например, переменные, объявленные в vars, существуют только внутри block. Например:

- name: Variables scope  hosts: localhost  connection: local  vars:    MY_VAR: "I am global var"  tasks:    - block:      - name: Print variable inside the block.        debug:          var: MY_VAR        vars:          MY_VAR: "I am local var"- name: Print variable outside the block.  debug:    var: MY_VAR
PLAY [Variables scope] TASK [Gathering Facts] ok: [localhost] TASK [Print variable inside the block.] ok: [localhost] => { "MY_VAR": "I am local var" } TASK [Print variable outside the block.] ok: [localhost] => { "MY_VAR": "I am global var" }

Таким образом, мы должны определять переменные в файлах. И все переменные должны быть определены явно. Для роли есть файл defaults/main.yml. Значения в этом файле имеют самый низкий приоритет, поэтому здесь можно размещать в том числе пустые переменные. Это облегчит жизнь контрибьюторам, особенно тем, кто видит код впервые.

2. Используйте README

Если роль использует много различных переменных, возможно, все они даже нужные и полезные, то описывайте их в файле README. Команда ansible-galaxy init поможет вам в этом, сгенерировав шаблон README. Вероятно, вам самому, в незнакомом репозитории будет приятно увидеть README с информацией о том, что роль ожидает увидеть на входе и что будет на выходе. Плохим примером будет разделение кода и описания. Например, код в git, а описание на wiki-странице. Нет никакой гарантии, что контрибьюторы обновят и код, и wiki-страницу. Обычно после пул реквеста работа заканчивается.

3. Используйте префиксы

У всех "постоянных" переменных (упомянутых в первом совете) должны быть префиксы. В качестве префикса лучше всего использовать имя роли. Это очень полезно, когда переменные для разных ролей нужно разместить в одном месте. Например, что произойдет в плейбуке с несколькими ролями, если все роли используют переменную port? Добавляя префикс, мы гарантируем, что одни переменные не будут перезаписаны другими. Пример: роль consul. переменная url, имя переменной consul_url.

4. Называйте задачи осмысленными именами

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

Например:

# No name/description- copy: dest=/tmp/text.txt, content="bla-bla"- name: Print variable global var. debug:   var: MY_VAR
TASK [copy]changed: [localhost]TASK [Print variable global var.] *ok: [localhost] => {"MY_VAR": "I am global var"}

5. DRY (Don't Repeat Yourself)

Ansible похож на обычный язык программирования. И как и в обычном языке, в Ansible есть различные механизмы, которые помогают следовать принципу DRY (Don't Repeat Yourself). Но это требует предварительного планирования организации вашего кода. При написании кода думайте, чтобы его можно было переиспользовать.

Крупные блоки:

Блоки внутри роли: (include/import)tasks, (include/import)role. Как это может использоваться? Например, вы используете модуль uri для отправки API-запросов. Допустим, это POST-запросы. Вместо того чтобы повторять 10 раз uri со всеми настройками, можно создать что-то вроде метода и использовать его где угодно. Аналогично методам в обычных языках программирования наш метод тоже принимает входные параметры.

Например: send_post.yml

- name: .::::::::::::. [ Sent POST request ] .::::::::::::. uri:   url: "{{ URL }}"   method: POST   status_code: 200   body: "{{ BODY_VAR | to_nice_json }}"   body_format: json   validate_certs: yes   client_cert: tls.crt   client_key: tls.key   register: return_values when: BODY_VAR is defined

Этот код можно использовать повторно.

- name: Bla-bla   include_tasks: send_post.yml   vars:       URL: "{{ main_url }}/{{ item }}"       BODY_VAR: "{{ item }}"

URL и BODY_VAR это параметры метода.

6. Используйте блоки (block)

Используйте block.

Описывайте параметры задач только один раз, группируя их. Также block может использоваться аналогично блоку try / catch в традиционных языках программирования.

- block:   ...  rescue:   ...

block/rescue отличная альтернатива ignore_errors. По сути, расширенная обработка ошибок. Это может быть полезным, например, когда вам нужно выполнить какой-то код в плейбуке, даже в случае сбоя. Например, удалить некоторые файлы.

 - block:   - name: .....   - name: .....   - name: .....   always:     file:       path: /tmp/xxxx       state: absent

7. Не используйте модули command и shell

Старайтесь не использовать модули command и shell, потому что они не идемпотентные. Хотя есть ряд приемов, которые помогают смягчить эту проблему. Используйте:

  • when

  • creates (если файл существует, то этот шаг не выполняется).

  • removes (если файл существует, то этот шаг выполняется).

  • changedwhen.

Тем не менее если это возможно, держитесь подальше от command и shell.

8. Не используйте теги

Не используйте теги. Теги могут стать ночным кошмаром для людей, которые видят ваш код впервые. Комбинации тегов увеличивает количество возможных вариантов выполнения плейбука. Но если у вас нет выбора, то подробно описывайте теги и их комбинации в README. Однако не все теги плохи. Есть исключения. Например, always, never читать подробнее.

skip_ansible_lint пропустить ansible-lint для задачи.

9. Принцип минимальных привилегий

Используйте принцип минимальных привилегий. Параметр become должен быть no, пока вам это действительно не будет необходимо. Всегда используйте become явно. Например:

--- - hosts: wordpress    become: no     ...    role:      - role: wordpresstasks/main.yml---- name: Install mysql-server pkg  apt:    name: mysql-server    state: present  become: yes

10. Используйте YAML-синтаксис для параметров

Используйте YAML вместо встраиваемого синтаксиса. Сравните эти два варианта:

YAML

- name: Install apache httpd  apt:    name: apache2    state: present

Встраиваемый

- name: Install apache httpd  apt: pkg=apache2 state=pesent

11. Используйте gitignore

Добавьте .gitignore к своим ролям, если вы храните код в git-репозитории. Простой .gitignore может выглядеть следующим образом:

*.retry*/__pycache__*.pyc*.log### IntelliJ IDEA ###.idea*.iws*.iml*.ipr

12. Используйте советы из документации Ansible

Используйте рекомендации по организации контента с официальной страницы ansible

13. Используйте отдельный каталог для ролей сообщества

Используйте отдельный каталог для ролей сообщества, наряду с рекомендациями из предыдущего совета.

14. Тестируйте Ansible-код

Используйте фреймворки для тестирования кода Ansible. Например, molecule. Этот фреймворк позволяет тестировать ваш код с разных сторон. Помимо традиционного тестирования, он также может запускать все виды линтеров и проверять код на идемпотентность.

15. Версионирование ролей

Что такое версионирование в мире Ansible? Это подход, используемый git и позволяющий запускать различные версии ролей, просто указав версию. Версией может быть тег, ветка или конкретный коммит. Подробнее об этом можно почитать здесь. Ваша задача настроить процедуру в соответствии с вашими требованиями к управлению версиями ролей.

requirements.yaml:

---- src: git@gitlab.company.com:mygroup/ansible.git scm: git version: "0.1"...

Допустимые атрибуты:

  • src

  • scm

  • version

  • name


Перевод статьи подготовлен в преддверии старта курсаDevOps практики и инструменты.

Приглашаем всех желающих записаться на бесплатный демо-урок курса по теме:Prometheus: быстрый старт. На занятии участники вместе с экспертом рассмотрят архитектуру Prometheus и ее способ работы с метриками, а также разберут, как формировать алерты и события в системе.

Подробнее..

Перевод Мониторинг и профилирование Spring Boot приложения

02.01.2021 00:07:07 | Автор: admin

Мониторинг очень важен для современных приложений, современные приложения по своей природе сильно распределены и имеют разные зависимости, такие как база данных, службы, кеширование и многое другое.Поэтому все более важны отслеживание и мониторинг этих служб, чтобы приложение придерживалось условий SLA(ServiceLevelAgreement).SLA это соглашение между клиентом и поставщиком услуг, в нем учитываются надежность, скорость отклика и другие показатели уровня обслуживания.

Мы всегда стараемся не нарушать никаких SLA, нарушение любой части SLA может иметь множество последствий.Если услуга не соответствует условиям, определенным в SLA, она можкт нанести ущерб репутации бренда и привести к потери дохода.Хуже всего то, что компания может потерять клиента в пользу конкурента из-за своей неспособности удовлетворить требования клиента к уровню обслуживания.

Какие показатели нужно отслеживать?

  • Доступность услуги:время, в течение которого услуга доступна для использования.Это может быть измерено с точки зрения времени отклика, например, процентиль X, сокращенно обозначаемый как pX, например, p95, p99, p99.999.Не для всех сервисов требуется p99,999, системы с гарантированной высокой доступностью, такие как электронная коммерция, поиск, оплата и т. д., должны иметь более высокое SLA.

  • Уровень дефектов:несмотря на все усилия по разработке системы, ни одна система не является идеальной на 100%.Мы должны подсчитывать процент ошибок в основных потоках.В эту категорию могут быть включены производственные сбои, такие как ошибка сервера, ошибка запроса базы данных, ошибка соединения, сетевые ошибки и пропущенные сроки.

  • Безопасность:в наши сверх регулируемые времена нарушения безопасности приложений и сети могут быть дорогостоящими.В эту категорию можно включить измерение контролируемых мер безопасности, таких как доступ к системе, несанкционированный доступ к базе данных, массовая загрузка записей базы данных или перемещение больших объемов данных.

  • Бизнес-результаты: ИТ-клиенты все чаще хотят включать метрики бизнес-процессов в свои SLA, чтобы можно было принимать более правильные бизнес-решения.В этой категории могут быть собраны различные данные, такие как количество пользователей, посетивших страницу, активность входа, эффективность купона и т. д.

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

Настройка мониторинга

Типичная система настройки мониторинга будет состоять из трех компонентов

  1. Хранилища метрик (какправило,база данных временных рядов), такое как InfluxDB, TimescaleDB, Prometheusи т. д.

  2. Панель инструментов (панель будет использоваться для визуализации данных, хранящихся в хранилище метрик).

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

У нас могут быть и другие компоненты, например, оповещение, где каналами оповещения могут быть электронная почта, Slack или любые другие.Компонент оповещения будет отправлять оповещения владельцам приложений или подписчикам событий.Мы собираемся использоватьGrafanaкак панель управления и систему оповещений,Prometheusкак систему хранения метрик.

Нам понадобятся:

  1. Любая IDE

  2. Платформа Java

  3. Gradle

Создайте проект с помощью инициализатора Spring Boot, добавьте столько зависимостей, сколько нам нужно.Мы собираемся использовать библиотеку Micrometer, это инструментальный фасад, который обеспечивает привязки для многих хранилищ метрик, таких как Prometheus, Datadog и New Relic, и это лишь некоторые из них.

Из коробки Micrometer обеспечивает

  1. HTTP-запрос

  2. JVM

  3. База данных

  4. Метрики, относящиеся к системе кэширования и т. д.

Некоторые метрики включены по умолчанию, тогда как другие можно включить, отключить или настроить.Мы будем использовать файл application.properties для включения, отключения и настройки метрик. Нам также нужно использовать Spring boot actuator, так как он откроет доступ к конечной точкеPrometheus.

Добавьте эти зависимости в файл build.gradle:

  1. io.micrometer: micrometer-registry-prometheus

  2. org.springframework.boot: spring-boot-starter-actuator

dependencies {  implementation 'org.springframework.boot:spring-boot-starter-data-jpa'  implementation 'org.springframework.boot:spring-boot-starter-web'  compileOnly 'org.projectlombok:lombok'  annotationProcessor 'org.projectlombok:lombok'  providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'  implementation 'io.micrometer:micrometer-registry-prometheus'  implementation 'org.springframework.boot:spring-boot-starter-actuator'  // https://mvnrepository.com/artifact/com.h2database/h2  compile group: 'com.h2database', name: 'h2', version: '1.4.200'  testImplementation('org.springframework.boot:spring-boot-starter-test') {  exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'  }}

Мы можем включить экспорт Prometheus, добавив следующую строку в файл свойств.

management.metrics.export.prometheus.enabled = true

После добавления этой строки Micrometer начнет накапливать данные о приложении, и эти данные можно будет просмотреть, перейдя на конечную точку actuator/Prometheus.Эта конечная точка будет использоваться в скрипте Prometheus для получения данных с наших серверов приложений.

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

management.endpoints.web.exposure.include=prometheus

ПРИМЕЧАНИЕ. Не включайте все конечные точки actuator, так как это может открыть лазейку в системе безопасности.Мы должны выбирать их выборочно, особенно в производственной системе, даже если мы хотим, не раскрывать конечную точку для всего мира, так как она может раскрыть большой объем данных о приложении, использовать какой-то прокси или какое-то правило, чтобы скрыть данные от внешнего мира.

Различные частиHTTP-запросовнастраиваются, например SLA, настройка признака должна быть вычислена или нет гистограмма процентилей делается спомощьюсвойствmetrics.distribution.

В примере application.properties могут быть такие строки

# Включить экспорт prometheusmanagement.metrics.export.prometheus.enabled = true# Включить конечную точку Prometheusmanagement.endpoints.web.exposure.include = Прометей# включить гистограмму на основе процентилей для http запросовmanagement.metrics.distribution.percentiles-histogram.http.server.requests = true# сегментов гистограммы http SLAmanagement.metrics.distribution.sla.http.server.requests = 100 мс, 150 мс, 250 мс, 500 мс, 1 с# включить метрики JVMmanagement.metrics.enable.jvm = true

Теперь, если мы запустим приложение и перейдем на страницу http://locahost:8080/actator/prometheus, будет отображаться чертовски много данных.

Приведенные выше данные отображают детали HTTP-запроса, exception=None означает, что исключение не произошло, если оно есть, мы можем использовать это для фильтрации количества запросов, которые не удалось выполнить из-за этого исключения,method=GETимя метода HTTP.status=200HTTP статус равен 200,uri=/actator/prometheusотображает путь URL,le=xyzотображает время обработки,N.0отображает количествовызововэтой конечной точки.

Эти данные представляют собой гистограмму, которую можно построить в Grafana, например, чтобы построить график p95 за 5 минут, мы можем использовать следующий запрос.

histogram_quantile(0.95,sum(rate(http_server_requests_seconds_bucke[5m])) by (le))

В Grafana можно построить и другие графики показателей, например круговую диаграмму и т. д.

Пользовательские показатели

Часто нам нужны специальные метрики, некоторые из сценариев использования это количество вошедших в систему пользователей, сведения о запасах доступных, в настоящее время, количество заказов в очереди и т. д. Некоторые из бизнес сценариев использования могут быть решены с использованием пользовательских метрик, микрометра. поддерживает различные типы показателей, такие как таймер, датчик, счетчик, сводки распределения, таймеры для длительных задач и т. д. В основном мы сосредоточимся на датчике и счетчике. Датчик дает нам мгновенные данные, такие как длина очереди, тогда как счетчик похож на монотонно увеличивающееся число, начинающееся с единицы.

Для демонстрации этого мы собираемся создать пример менеджера запасов, который будет хранить детали в памяти и обеспечит две функции:

  1. Добавить товары

  2. Получить товары

Для этого мы создадим один счетчик и одну метрику вметодеinit, при каждомвызове getItems мы увеличиваем счетчик, а также измеряем размер запаса, тогда как при вызове addItems мы только обновляем метрику.

@Componentpublic class StockManager {  @Autowired private MeterRegistry meterRegistry;  private List<String> orders = new Vector<>();  private Counter counter;  private Gauge gauge;@PostConstruct  public void init() {    counter =        Counter.builder("order_created")            .description("number of orders created")            .register(meterRegistry);    gauge =        Gauge.builder("stock.size", this, StockManager::getNumberOfItems)            .description("Number of items in stocks")            .register(meterRegistry);  }public int getNumberOfItems() {    return orders.size();  }public void addItems(List<String> items) {    orders.addAll(items);    // measure gauge    gauge.measure();  }public List<String> getItem(int count) {    List<String> items = new ArrayList<>(count);    while (count < 0) {      try {        items.add(orders.remove(0));      } catch (ArrayIndexOutOfBoundsException e) {        break;      }      count -= 1;    }    // increase counter    counter.increment();    //  measure gauge    gauge.measure();    return items;  }}

В демонстрационных целях мы добавим две конечные точки для добавления товаров и получения товаров.

@RestController@RequestMapping(path = "stocks")@RequiredArgsConstructor(onConstructor = @__(@Autowired))public class StockController {  @NonNull private StockManager stockManager;  @GetMapping  @ResponseBody  public List<String> getItems(@RequestParam int size) {    return stockManager.getItem(size);  }  @PostMapping  @ResponseBody  public int addItems(@RequestParam List<String> items) {    stockManager.addItems(items);    return stockManager.getNumberOfItems();  }}  @PostMapping  @ResponseBody  public int addItems(@RequestParam List<String> items) {    stockManager.addItems(items);    return stockManager.getNumberOfItems();  }}

Давайте сначала добавим десять товаров, используя два вызова API:

  1. Curl -X POST http://localhost:8080/stocks?Items = 1,2,3,4

  2. Curl -X POST http://localhost:8080/stocks?Items = 5,6,7,8,9,10

Теперь, если мы перейдем к конечным точкам Prometheus, то увидим следующие данные, которые показывают, что в настоящее время у нас есть 10 товаров на складе.

# HELP stock_size Количество товаров на складе# TYPE stock_size gaugestock_size 10.0

Теперь мы собираемся разместить заказ на 3 товара:

http://localhost:8080/stocks?size=3

Теперь, если мы посмотрим конечную точку Prometheus, мы получим следующие данные, которые показывают, что размер запаса был изменен до семи.

# HELP stock_size Количество товаров на складе# TYPE stock_size gaugestock_size 7.0

Кроме того, мы видим, что на счетчике добавлено значение 1, это означает, что размещен один ордер.

# HELP order_created_total количество созданных заказов# TYPE order_created_total counterorder_created_total 1.0ordercreated_total 1.0

Профилирование

В программной инженерии профилирование (профилирование программы, профилирование программного обеспечения) - это форма динамического анализа программы, который измеряет, например, объем (память) или временную сложность программы, использование определенных инструкций или частоту и продолжительность вызовов функций.

Чаще всего профилирующая информация помогает оптимизировать программу. Профилирование достигается путем сбора характеристик работы программы, таких как время выполнения отдельных фрагментов (обычно подпрограмм), число верно предсказанных условных переходов, число кэш-промахов и т. д. Инструмент, используемый для анализа работы, называют профилировщиком или профайлером (profiler). Профилировщики могут использовать ряд различных методов, таких как методы, основанные на событиях, статистические, инструментальные методы и методы моделирования. Википедия

Профилирование очень полезно при диагностике системных проблем, например, сколько времени занимает HTTP-вызов, если он занимает N секунд, а затем, где все это время было потрачено, каково распределение N секунд между различными запросами к базе данных, вызовами последующих служб и т. д. Мы можем использовать гистограмму для построения графика распределения на панели инструментов, также мы можем использовать счетчик для измерения количества запросов к БД и т. д. Для профилирования нам необходимо внедрить код во многие функции, которые будут выполняться как часть выполнения метода.

Важно то, что внедряемая часть кода профилирования одинакова для профилировщиков аналогичных типов, что означает, что нам нужно скопировать и вставить аналогичный код в тысячи мест, если нам нужно что-то изменить, нам нужно обновить то же самое. Код профилировщика в каждом файле и, вероятно, в каждой функции, требующей профилирования, повысит сложность и может стать создать полный беспорядок, впрочем мы можем избежать этого беспорядка, используяаспектно-ориентированное программирование (АОП).

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

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

Система зависит от других систем, поэтому мы можем быть заинтересованы в профилировании различных компонентов по-разному, например, вызовов базы данных, HTTP-запросов, последующих вызовов служб или некоторых конкретных методов, которые являются критическими или хотели бы увидеть, что происходит в некоторых конкретных методах.Мы можем использовать ту же библиотеку Micrometer для профилирования, но это может быть не совсем то, что нам нужно, поэтому мы изменим код.

Micrometer поставляется саннотациейTimed, эту аннотацию можно разместить в любомpublic методе, таккак название предполагает, что она может измерять время выполнения, это будет измерять время выполнения соответствующего метода.Вместо того, чтобы напрямую использовать эту аннотацию, мы расширим эту аннотацию для поддержки других функций, таких как ведение журнала, повторная попытка и т. Д.

Аннотация Timed бесполезна без бина TimedAspect, поскольку мы переопределяем аннотациюTimed, поэтому мы также определим класс TimedAspect в соответствии с потребностями, такими как ведение журнала, массовое профилирование (профилируйте все методы в пакете без добавления каких-либо аннотаций к любому методу или классу), повторить попытку и т. д. В этой истории мы рассмотрим три сценария использования:

  1. Массовое профилирование.

  2. Логирование.

  3. Специфический для профиля метод.

Созадим файл java MonitoringTimed.java, в который мы добавим новое поле с именемloggingEnabled,это поле будет использоваться для проверки, включено ли ведение журнала или нет, если оно включено, а затем регистрировать аргументы метода и возвращаемые значения.

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface MonitoringTimed {  /** All fields are same as in {@link io.micrometer.core.annotation.Timed} */  String value() default "";  String[] extraTags() default {};  boolean longTask() default false;  double[] percentiles() default {};  boolean histogram() default false;  String description() default "";  // NEW fields starts here  boolean loggingEnabled() default false;}

Эта аннотация бесполезна без класса аспекта Timed, поэтомубудет определенновый классMonitoringTimedAspectсо всеми необходимыми деталями, этот класс будет иметь метод для профилирования любого метода на основе объединенного объекта обработки иобъектаMonitoringTimed,а другой - для профилирования метода на основе в аннотации MonitoringTimed.

@Around("execution (@com.gitbub.sonus21.monitoring.aop.MonitoringTimed * *.*(..))")public Object timedMethod(ProceedingJoinPoint pjp) throws Throwable {  Method method = ((MethodSignature) pjp.getSignature()).getMethod();  MonitoringTimed timed = method.getAnnotation(MonitoringTimed.class);  if (timed == null) {    method = pjp.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());    timed = method.getAnnotation(MonitoringTimed.class);  }  final String metricName = generateMetricName(pjp, timed);  return timeThisMethod(pjp, timed, metricName);}public Object timeThisMethod(ProceedingJoinPoint pjp, MonitoringTimed timed) throws Throwable {  final String metricName = generateMetricName(pjp, timed);  return timeThisMethod(pjp, timed, metricName);}

МетодTimedMethodсаннотациейAroundиспользуется для фильтрации всех вызовов методов, аннотированных с помощьюMonitoringTimed.

Для массового профилирования мы определим класс профилировщика, который будет работать с фильтрацией на уровне пакета. Например, для профилирования HTTP-запроса мы можем добавить ControllerProfiler, который будет обрабатывать профилирование всех общедоступных методов, доступных в пакете контроллера.

@Aspect@Componentpublic class ControllerProfiler {  private static Map<String, Object> timedAnnotationData = new HashMap<>();  static {    // use percentile data of p90, p95, p99.99    double[] percentiles = {0.90, 0.95, 0.9999};    // set histogram to true    timedAnnotationData.put("histogram", true);    // set percentile    timedAnnotationData.put("percentiles", percentiles);  }  @Autowired private MonitoringTimedAspect timedAspect;  private static final MonitoringTimed timed = Javanna.createAnnotation(MonitoringTimed.class, timedAnnotationData);  private static final Logger logger = LoggerFactory.getLogger(ControllerProfiler.class);  @Pointcut("execution(* com.gitbub.sonus21.monitoring.controller..*.*(..))")  public void controller() {}  @Around("controller()")  public Object profile(ProceedingJoinPoint pjp) throws Throwable {    // here add other logic like error happen then log parameters etc    return timedAspect.timeThisMethod(pjp, timed);  }}

Интересной строкой в приведенном выше коде является @Pointcut(execution(com.gitbub.sonus21.monitoring.controller...*(..))), которая определяет pointcut, выражение pointcut может быть определено с использованием логических операторов вродеnot (!), or (||), and (&&). После того, как метод квалифицирован согласно выражению pointcut, он может вызвать соответствующий метод, определенный с помощью аннотации [at]Around.Поскольку мы определилиметодprofile,который будет вызываться, мы также можем определить другие методы, используя аннотации [at]After, [at]Before и т. д.

После добавления нескольких элементов с помощью метода POST мы можем увидеть следующие данные в конечной точке Prometheus.

method_timed_seconds {class = "com.gitbub.sonus21.monitoring.controller.StockController", exception = "none", method = "addItems", quantile = "0.9",} 0.0method_timed_seconds_bucket {class = "com.gitbub.sonus21.monitoring.controller.StockController", exception = "none", method = "addItems", le = "0.001",} 3.0method_timed_seconds_bucket {class = "com.gitbub.sonus21.monitoring.controller.StockController", exception = "none", method = "addItems", le = "0.002446676",} 3.0

Мы можем напрямую использоватьаннотациюMonitoringTimedтакже для любого метода для измерения времени выполнения, например, давайтеизмерим,сколько времениStockManagerметод addItemsтратит на добавление элементов.

@MonitoringTimedpublic void addItems(List<String> items) {  orders.addAll(items);  // measure gauge  gauge.measure();}

Как только мы запустим приложение и добавим несколько элементов, мы увидим следующее в конечной точке Prometheus.

method_timed_seconds_count{class="com.gitbub.sonus21.monitoring.service.StockManager",exception="none",method="addItems",} 4.0method_timed_seconds_sum{class="com.gitbub.sonus21.monitoring.service.StockManager",exception="none",method="addItems",} 0.005457965method_timed_seconds_max{class="com.gitbub.sonus21.monitoring.service.StockManager",exception="none",method="addItems",} 0.00615316

MonitoringTimed можно дополнительно настроить, например, можно добавить количество повторных попыток для поддержки повторных попыток в случае сбоя, записать аргументы функции в случае сбоя, чтобы позже можно было проанализировать причину сбоя.

Полный код доступен наGitHub.

Дополнительное чтение

Magic With the Spring Boot Actuator

Spring Boot Actuator in Spring Boot 2.0

Spring Boot Admin Client Configuration Using Basic HTTP Authentication

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru