В последнее время 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.
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. Так что,
вполне возможно, в новых версиях нужно будет конфиги немного
исправлять.
Если есть вопросы и\или замечания - пишите, постараюсь добавить
в статью.