PROM 12: Long Term Storage

Описание:

Всем привет!

Продолжаем разбираться с Prometheus и на очереди у нас достаточно любопытная тема - long term storage. Строго говоря в реальной жизни вы навряд ли станете работать с данной технологией, ведь стандартного хранилища prometheus хватает и для анализа инцидентов и для просмотра текущей ситуации в инфраструктуре. Но иногда случаются моменты, когда вам нужно хранить метрики за достаточно долгий период времени. Именно в этой ситуации и приходит на помощь long term storage. Итак, сегодня мы с вами посмотрим как prometheus хранит данные внутри tsdb, разберемся с тем как вы сможете увеличить интервал хранения своих метрик, а так же настроим сохранение старых метрик во внешнее хранилище.

В реальном примере обычно приходит руководство и говорит, что оно хочет хранить метрики о состоянии системы за последний год. На ваш резонный вопрос зачем так долго вам отвечают - "Надо!". На самом деле я придерживаюсь мнения, что детальной информации за последний месяц о работе системы вполне достаточно для решения 99% задач. Оставшийся процент мы оставляем на требования регулирующих органов и какие-то специфичные штуки. Навряд ли вам действительно будет интересно узнать какой процент cpu использовал веб сервер 8 месяцев назад. Но раз руководство говорит надо, мы отвечаем - есть!

Итак, перейдем к рассказу про то, как prometheus хранит данные внутри себя.

Как работает TSDB

Все метрики, которые prometheus получает с экспортеров, группируются в двухчасовые блоки и сохраняются на диск. Один блок - это отдельная директория, которая содержит внутри себя непосредственно сами данные (chunks), metadata файл, а так же index файл, внутри которого находится информация по всем метрикам, доступным за данный временной промежуток, а так же все лейблы этих метрик. Сами метрики внутри chunks директории группируются в файлы размером до 512 Мб:

root@prom-test:/opt/prometheus/data# ls -la
total 24
drwxr-xr-x 5 prometheus prometheus 4096 Jun 16 07:52 .
drwxr-xr-x 5 prometheus prometheus 4096 Apr 30 08:03 ..
drwxr-xr-x 3 prometheus prometheus 4096 Jun 16 07:52 01F89WWE7DY9X2X3Q2E47VAA8C
drwxr-xr-x 2 prometheus prometheus 4096 Jun 16 07:52 chunks_head
-rw-r--r-- 1 prometheus prometheus 0 Apr 30 08:03 lock
-rw-r--r-- 1 prometheus prometheus 20001 Jun 16 07:51 queries.active
drwxr-xr-x 2 prometheus prometheus 4096 Jun 16 07:52 wal
root@prom-test:/opt/prometheus/data# ls -la 01F89WWE7DY9X2X3Q2E47VAA8C/
total 68
drwxr-xr-x 3 prometheus prometheus 4096 Jun 16 07:52 .
drwxr-xr-x 5 prometheus prometheus 4096 Jun 16 07:52 ..
drwxr-xr-x 2 prometheus prometheus 4096 Jun 16 07:52 chunks
-rw-r--r-- 1 prometheus prometheus 45866 Jun 16 07:52 index
-rw-r--r-- 1 prometheus prometheus 272 Jun 16 07:52 meta.json
-rw-r--r-- 1 prometheus prometheus 9 Jun 16 07:52 tombstones
root@prom-test:/opt/prometheus/data#

Те метрики, которые prometheus в данный момент получает с экспортеров не сразу попадают на диск - они какое-то время хранятся в памяти. Чтобы защититься от неожиданного завершения, используется write ahead log, благодаря которому prometheus при старте может прогнать заново историю получения метрик и дальше уже сохранить их на диск в нужной директории. WAL логи хранятся в директории wal и занимают максимально 128 Мб места каждый:

root@prom-test:/opt/prometheus/data# ls -la wal/
total 100
drwxr-xr-x 2 prometheus prometheus 4096 Jun 16 07:52 .
-rw-r--r-- 1 prometheus prometheus 32768 Apr 30 08:03 00000000
-rw-r--r-- 1 prometheus prometheus 32768 Jun 16 07:52 00000001
-rw-r--r-- 1 prometheus prometheus 25704 Jun 16 07:55 00000002

После записи в wal, prometheus в фоне производит операцию compaction и сохраняет полученные метрики в нужные блоки данных. Помимо этого, изначальные двухчасовые блоки данных могут группироваться в большие блоки - обычно до 10% от retention time. То есть если вы задали 30 дней на хранение метрик, то блоки могут содержать данные за 3 дня.

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

Основные настройки tsdb

В первом задании мы рассказывали про основные параметры запуска prometheus. Давайте немного освежим память и посмотрим какие опции вы можете использовать для тонкой настройки tsdb:

--storage.tsdb.path - путь где будут находиться данные tsdb. Если вы запускаете prometheus в docker'е, то данную директорию есть смысл смонтировать на хост систему.

--storage.tsdb.retention.time - время хранения собранных метрик. По умолчанию метрики хранятся 15 дней.

--storage.tsdb.retention.size - эксперементальная функция по очистке метрик на основе размера, которое они занимают.

--storage.tsdb.wal-compression - включение сжатия wal архивов. По умолчанию включена с версии 2.20.

--storage.tsdb.no-lockfile - не создавать lockfile в data директории. Позволит запустить второй prometheus на этих же данных. Но консистентность данных гарантировать уже будет нельзя.

Собственно все опции говорят сами за себя. Еще одной крутой фишкой prometheus является то, что он из коробки отдает свое состояние через свои же метрики. И внутри находится достаточно много параметров, относящихся к tsdb:

root@prom-test:/opt/prometheus/data# curl -s localhost:9090/metrics | grep tsdb | grep -v ^#
prometheus_tsdb_blocks_loaded 1
prometheus_tsdb_checkpoint_creations_failed_total 0
prometheus_tsdb_checkpoint_creations_total 0
prometheus_tsdb_checkpoint_deletions_failed_total 0
prometheus_tsdb_checkpoint_deletions_total 0
prometheus_tsdb_compaction_chunk_range_seconds_bucket{le="100"} 32
prometheus_tsdb_compaction_chunk_range_seconds_bucket{le="400"} 32
prometheus_tsdb_compaction_chunk_range_seconds_bucket{le="1600"} 32
prometheus_tsdb_compaction_chunk_range_seconds_bucket{le="6400"} 32

... skipped ...

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

Настройка long term storage.

Итак, переходим к главному вопросу на сегодня - что делать если вы хотите хранить много метрик, а ваш локальный prometheus уже не справляется? Вы можете использовать remote write / read api от prometheus для того чтобы выгружать ваши метрики во внешнее хранилище. При запросе метрик через promql, promethues так же запросит нужные метрики из удаленного стораджа и выдаст вам результат.

Работает эта магия благодаря стандартизированному API, который предлагает prometheus. По факту схема работает следующим образом - prometheus обращается к внешнему приложению по своему api, то в свою очередь преобразует запрос в нужный формат и отправляет запрос в сторадж - например в postgresql или clickhouse. Дальше получает ответ, преобразует его обратно в формат prometheus и отправляет обратно. Таким образом вся схема очень похожа на схему с экспортерами. Prometheus обращается к адаптеру, а адаптер уже взаимодействует с хранилищем. Пример такого адаптера можно посмотреть здесь.

В официальной документации есть набор remote адаптеров, которые вы сможете взять и использовать. Мне кажется идеальной схема, когда все prometheus закидывают свои метрики в kafk'у, а дальше из кафки вы можете записывать их либо в clickhouse, либо в hadoop. Такая схема очень хорошо масштабируется и позволит вам хранить метрики за действительно огромный период времени.

Поэтому давайте сегодня соберем первую часть схемы с кафкой, а в последствии вы сможете подключить clickhouse для чтения данных из кафки. Итак, для начала установим zookeeper, так как его использует kafka:

root@prom-test:/opt# apt-get install zookeeper zookeeper-bin zookeeperd
... skipped ...

Теперь можем сконфигурировать его и добавить айдишник нашего первого зукипера:

root@prom-test:/opt# echo 1 > /etc/zookeeper/conf/myid
root@prom-test:/opt# systemctl start zookeeper
root@prom-test:/opt# systemctl status zookeeper
● zookeeper.service - LSB: centralized coordination service
Loaded: loaded (/etc/init.d/zookeeper; generated)
Active: active (running) since Wed 2021-06-16 08:24:11 UTC; 18s ago
Docs: man:systemd-sysv-generator(8)
Tasks: 17 (limit: 2345)
Memory: 37.2M
CGroup: /system.slice/zookeeper.service
└─11859 /usr/bin/java -cp /etc/zookeeper/conf:/usr/share/java/jline.jar:/usr/share/java/log4j-1.2.jar:/usr/share/java/xercesImpl.jar:/usr/share/java/xmlParserAPIs.j>

Как видно зукипер успешно запустился, поэтому мы можем переходить к настройке кафки:

root@prom-test:~# cd /opt/
root@prom-test:/opt# wget https://mirror.linux-ia64.org/apache/kafka/2.8.0/kafka_2.13-2.8.0.tgz
--2021-06-16 08:25:19-- https://mirror.linux-ia64.org/apache/kafka/2.8.0/kafka_2.13-2.8.0.tgz
Resolving mirror.linux-ia64.org (mirror.linux-ia64.org)... 37.193.156.169
Connecting to mirror.linux-ia64.org (mirror.linux-ia64.org)|37.193.156.169|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 71403603 (68M) [application/x-gzip]
Saving to: ‘kafka_2.13-2.8.0.tgz’

kafka_2.13-2.8.0.tgz 100%[============================================================================================>] 68.10M 9.52MB/s in 6.3s

2021-06-16 08:25:26 (10.8 MB/s) - ‘kafka_2.13-2.8.0.tgz’ saved [71403603/71403603]

root@prom-test:/opt# tar zxf kafka_2.13-2.8.0.tgz
root@prom-test:/opt# mv kafka_2.13-2.8.0 kafka

Конфигурировать кафку не требуется - она по умолчанию будет пытаться подключиться к локальному зукиперу, поэтому просто запустим ее:

root@prom-test:/opt# cd kafka/
root@prom-test:/opt/kafka# ./bin/kafka-server-start.sh -daemon config/server.properties

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

root@prom-test:/opt/kafka# ./bin/kafka-topics.sh --zookeeper localhost:2181 --topic "test" --create --partitions 5 --replication-factor 1
Created topic test.
root@prom-test:/opt/kafka# ./bin/kafka-topics.sh --list --zookeeper localhost:2181
test
root@prom-test:/opt/kafka#

К сожалению здесь нам не хватит времени чтобы подробно рассказать про кафку и ее преимущества, но в двух словах - это message broker, который делает следующее - producers подключаются к кафке и пишут в нужный топик (топик - это по сути очередь), с другой стороны к кафке подключаются consumers, которые читают сообщения из нужного топика. Благодаря своей архитектуре, кафка масштабируется до гигантских масштабов. По моему опыту - 3 машинки на хецнере за 40 евро позволяли делать 200 000 инсертов в топик в секунду.

Итак, кафку установили - теперь осталось заставить prometheus писать в нее.

Настройка prometheus для записи в remote storage

Для подключения prometheus нам потребуется запустить адаптер, который будет принимать сообщения от prometheus и отправлять их в кафку. Для этого воспользуемся официально рекомендуемым адаптером - prometheus-kafka-adapter. Он будет принимать сообщения от прометеуса в его формате, преобразовывать его в json и сохранять в кафку. На официальной странице доступны различные опции конфигурации, такие как настройка аутентификации, настройка сжатия, формата и так далее. Мы в своей задаче заменим только один из них - адрес кафка сервера.

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

root@prom-test:/opt/prom-kafka-adapter# wget -qO - https://packages.confluent.io/deb/6.2/archive.key | sudo apt-key add -

OK
root@prom-test:/opt#
root@prom-test:/opt# sudo add-apt-repository "deb [arch=amd64] https://packages.confluent.io/deb/6.2 stable main"
Get:1 http://mirrors.digitalocean.com/ubuntu focal InRelease [265 kB]
Hit:2 https://repos-droplet.digitalocean.com/apt/droplet-agent main InRelease
Get:3 https://packages.confluent.io/deb/6.2 stable InRelease [5871 B]
Get:4 http://security.ubuntu.com/ubuntu focal-security InRelease [114 kB]
Hit:5 http://mirrors.digitalocean.com/ubuntu focal-updates InRelease
Hit:6 http://mirrors.digitalocean.com/ubuntu focal-backports InRelease
Get:7 https://packages.confluent.io/deb/6.2 stable/main amd64 Packages [9435 B]
Fetched 394 kB in 1s (376 kB/s)
Reading package lists... Done

root@prom-test:/opt/prom-kafka-adapter# echo "deb http://security.ubuntu.com/ubuntu bionic-security main" | sudo tee -a /etc/apt/sources.list.d/bionic.list

... skipped ...

root@prom-test:/opt/prom-kafka-adapter# apt-get install librdkafka-dev librdkafka1 zlib1g zlib1g-dev libcrypto++-dev libcrypto++6 libssl-dev

... skipped ...

Отлично, теперь можно устанавливать prom-kafka-adapter:

root@prom-test:/opt/prom-kafka-adapter# cd /opt/
root@prom-test:/opt# wget https://golang.org/dl/go1.16.5.linux-amd64.tar.gz
... skipped ...
root@prom-test:/opt# export GOROOT=/opt/go
root@prom-test:/opt# export PATH=/opt/go/bin:$PATH
root@prom-test:/opt# go version
go version go1.16.5 linux/amd64

root@prom-test:/opt# mkdir prom-kafka-adapter
root@prom-test:/opt# cd prom-kafka-adapter/
root@prom-test:/opt/prom-kafka-adapter# export GOPATH=`pwd`
root@prom-test:/opt/prom-kafka-adapter# go get github.com/Telefonica/[email-masked]

... skipped ...

Итак, вроде все собралось - проверяем что все работает:

root@prom-test:/opt/prom-kafka-adapter# KAFKA_BROKER_LIST=localhost:9092 ./bin/prometheus-kafka-adapter
{"level":"warning","msg":"invalid serialization format, using json","serialization-format-value":"","time":"2021-06-16T09:00:15Z"}
{"level":"info","msg":"creating kafka producer","time":"2021-06-16T09:00:15Z"}
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET /metrics --> github.com/gin-gonic/gin.WrapH.func1 (3 handlers)
[GIN-debug] POST /receive --> main.receiveHandler.func1 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080

Отлично - вроде все запустилось. Осталось только настроить prometheus. Для этого добавляем в prometheus.yml:

global:
scrape_interval: 15s
evaluation_interval: 15s

scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']

remote_write:
- url: "http://localhost:8080/receive"

И перезапускаем prometheus:

root@prom-test:/opt/prometheus# systemctl restart prometheus

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

root@prom-test:/opt/kafka# ./bin/kafka-topics.sh --list --zookeeper localhost:2181
metrics
test
root@prom-test:/opt/kafka# ./bin/kafka-console-consumer.sh --from-beginning --bootstrap-server localhost:9092 --topic metrics

... skipped ...
{"labels":{"__name__":"prometheus_wal_watcher_records_read_total","consumer":"4586a5","instance":"localhost:9090","job":"prometheus","type":"series"},"name":"prometheus_wal_watcher_records_read_total","timestamp":"2021-06-16T09:03:29Z","value":"7"}
{"labels":{"__name__":"prometheus_wal_watcher_samples_sent_pre_tailing_total","consumer":"4586a5","instance":"localhost:9090","job":"prometheus"},"name":"prometheus_wal_watcher_samples_sent_pre_tailing_total","timestamp":"2021-06-16T09:03:29Z","value":"0"}
{"labels":{"__name__":"prometheus_web_federation_errors_total","instance":"localhost:9090","job":"prometheus"},"name":"prometheus_web_federation_errors_total","timestamp":"2021-06-16T09:03:29Z","value":"0"}

... skipped ...

Итак, все заработало как и планировалось! Prometheus закидывает все наши метрики в prom-kafka-adapter, а тот в свою очередь в кафку, в json формате. Теперь можно настроить clickhouse и начать импортировать метрики в нужную таблицу и дальше хранить их сколько угодно долго.

Итоги

Сегодня мы с вами обсудили формат хранения prometheus tsdb и узнали что он не поддается кластеризации и репликации. Для хранения исторических метрик в других системах вы можете использовать remote write/read API, который позволяет передавать метрики prometheus в адаптер, а тот в свою очередь будет сохранять их в нужное хранилище. В следующем задании мы поговорим про основную безопасность вашей инсталляции prometheus.

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

Remote storage

Prometheus Storage

TSDB Format

Задание:

В этом задании вам потребуется настроить Prometheus для хранения метрик в postgresql. На подготовленной виртуальной машине уже установлен Prometheus.

Критерии оценки выполнения задания:

Установите postgresql из официальных репозиториев ubuntu (пакет postgresql-12)

Подключитесь к локальной базе данных postgresql с помощью утилиты psql и выполните следующие запросы:

2.1. Создайте пользователя prom: create user prom with password 'password';

2.2. Сделайте пользователя prom суперпользователем: alter user prom with superuser;

2.3. Создайте базу данных timescale: create database timescale owner prom;

Установите расширение timescaledb для postgresql:

$ sudo sh -c "echo 'deb [signed-by=/usr/share/keyrings/timescale.keyring] https://packagecloud.io/timescale/timescaledb/ubuntu/ $(lsb_release -c -s) main' > /etc/apt/sources.list.d/timescaledb.list"

$ sudo wget --quiet -O - https://packagecloud.io/timescale/timescaledb/gpgkey | sudo apt-key add -

$ sudo add-apt-repository ppa:timescale/timescaledb-ppa

$ sudo apt-get update

$ apt install timescaledb-2-postgresql-12 -y

3.1. Добавьте расширение timescaledb в конфигурацию postgresql (/etc/postgresql/12/main/postgresql.conf):

shared_preload_libraries = 'timescaledb' # (change requires restart)

3.2. Перезапустите postgresql.

Установите promscale адаптер. Он уже имеет собранные бинарники для Linux, поэтому компилировать ничего не придется.

4.1. promscale должен подключаться к postgresql под пользователем prom к базе timescale (./promscale_0.4.1_Linux_x86_64 -db-uri postgres://prom:password@localhost:5432/timescale?sslmode=disable)

4.2. promscale должен запускаться на порту 9201.

Настройте Prometheus:

5.1. Prometheus должен отправлять метрики в promscale через remote_write (пример конфигурации доступен на github в описании promscale).