Prometheus 2.18 в production: снижение памяти и перестройка remote_write под Thanos
Обновляем Prometheus до 2.18 на клиентском кластере: потребление памяти упало на треть, перестраиваем remote_write для долгосрочного хранения в Thanos.
Prometheus 2.18 (май 2020): улучшения производительности TSDB, снижение потребления памяти head chunk-ами, продвижение remote_write для интеграции с Thanos и Cortex
В рамках managed-сопровождения мы несколько недель назад обновили Prometheus на одном из клиентских кластеров с 2.15 до 2.18 и заодно разобрались с накопившимися вопросами по remote_write. Выход получился насыщенный: потребление памяти заметно просело без каких-либо изменений в конфигурации, а работа с Thanos перестала быть "ну как-то работает".
Почему вообще трогали
Клиент - средний e-commerce, Kubernetes-кластер с полутора сотнями подов, мониторинг через Prometheus. Проблема была конкретная: Prometheus периодически упирался в лимит памяти и получал OOMKill. Не каждый день, но раз в неделю - стабильно. Мы подняли лимит до неприличного значения, поставили алерт и поместили в беклог. Выход 2.18 с заявленными улучшениями TSDB дал повод разобраться наконец.
Параллельно давно висела задача настроить долгосрочное хранение метрик - клиент хотел смотреть тренды за несколько месяцев, а локальное хранилище Prometheus держит примерно две недели при текущих настройках retention. Thanos стоял ещё с прошлого года в минимальной конфигурации: sidecar, query, store - но remote_write в него не был толком настроен, всё работало через sidecar-режим без дополнительной надёжности.
Что изменилось в 2.18 по части памяти
Главное изменение в 2.18 касается head chunk-ов в TSDB. В прошлых версиях head chunks (то есть данные за последние два часа, которые ещё не сброшены на диск) хранились в памяти в несжатом виде. В 2.18 они перешли на сжатие - тот же формат, что и на диске.
На практике это значит, что оперативную память теперь занимают сжатые данные вместо сырых float64. Для типичного production-кластера с сотнями тысяч временных рядов разница ощутимая. В нашем случае потребление памяти Prometheus упало примерно на 30% сразу после обновления - при тех же scrape targets, том же интервале сборки, том же наборе правил.
Проверили через process_resident_memory_bytes в самом же Prometheus: до обновления стабильные 4.2-4.5 Гб, после - 2.9-3.1 Гб. OOMKill с тех пор не было.
Есть и обратная сторона: небольшое увеличение нагрузки на CPU при ingestion - распаковывать/упаковывать head chunks при каждой записи. На нашем кластере это в районе шума, заметно не стало. Но на очень высокочастотных scrape-ах с тысячами targets это стоит проверить.
Обновление: как делали
Обновление само по себе прошло без сюрпризов. Prometheus совместим по формату TSDB между версиями 2.x, данные на диске после обновления никуда не деваются и читаются без конвертации.
Несколько наблюдений по процессу:
Резервная копия TSDB перед обновлением. Не потому что ожидали поломки, а потому что production. Сделали снапшот через API Prometheus (/api/v1/admin/tsdb/snapshot), убедились что файлы скопировались.
Перезапуск с новым бинарником. В нашем случае Prometheus развёрнут как Deployment в Kubernetes с образом prom/prometheus:v2.18.1. Обновили тег, откатили деплой, подождали. Несколько минут на старт - TSDB инициализируется, head block собирается из WAL.
Первые 15 минут - аномальное потребление. При старте Prometheus загружает WAL, и память поначалу растёт до значений, близких к старым. Это нормально - он восстанавливает состояние head chunk-ов из журнала. После полного старта цифры падают до нового нормального уровня.
Перестройка remote_write под Thanos
Вот тут пришлось подумать чуть больше.
Sidecar-режим Thanos, который у нас стоял, работает следующим образом: sidecar читает блоки TSDB напрямую из файловой системы Prometheus и загружает их в object storage. Это надёжно для исторических данных, но есть задержка: блок появляется в object storage через два часа, когда Prometheus его закрывает. Для долгосрочного хранения это приемлемо. Для near-realtime запросов через Thanos Query - нет.
Thanos Receiver позволяет получать данные через remote_write напрямую, без задержки на компакцию. Мы развернули Receiver, настроили Prometheus на отправку туда.
Базовая конфигурация remote_write в prometheus.yml:
remote_write:
- url: http://thanos-receive:19291/api/v1/receive
queue_config:
max_samples_per_send: 2000
max_shards: 10
capacity: 5000
Несколько параметров queue_config, на которые стоило обратить внимание:
max_shards - количество параллельных горутин, отправляющих данные. По умолчанию 5, мы подняли до 10 - у клиента высокий cardinality и нам нужно было избежать накопления очереди при пиковой нагрузке.
capacity - буфер в памяти перед отправкой. Если Thanos Receiver временно недоступен, Prometheus продолжает собирать метрики и буферизует их. При 5000 сэмплах и нашем scrape interval 15 секунд это примерно минута автономной работы.
min_backoff / max_backoff - таймауты при ошибках отправки. Оставили дефолт (100ms / 5s), пока устраивает.
Метрики самого remote_write смотрим через prometheus_remote_storage_* - там видно queue length, pending samples, failed samples. Первую неделю держали дашборд открытым, убеждались что очереди не копятся.
Sidecar оставили
Убирать sidecar мы не стали. Теперь работают оба механизма: sidecar загружает закрытые блоки в object storage для архива, receiver получает свежие данные с минимальной задержкой. Thanos Query смотрит и в Store Gateway (для архивных данных), и в Receiver (для свежих).
Дублирования данных формально нет: sidecar и receiver пишут в разные tenant-ы или разные bucket-ы - зависит от конфигурации. У нас разные bucket-ы: metrics-archive для sidecar, metrics-realtime для receiver. Store Gateway указывает на оба.
Где сейчас
Кластер работает на Prometheus 2.18 третью неделю. OOMKill нет, потребление памяти стабильно на треть ниже. Remote_write в Thanos Receiver работает, задержка появления данных в Thanos Query - секунды, не часы.
Открытый вопрос: компакция в Thanos Receiver. Receiver накапливает данные в своём собственном TSDB и периодически сбрасывает их в object storage - но алгоритм компакции там немного отличается от основного Thanos Compactor. Пока данных за несколько недель недостаточно чтобы убедиться что всё ок на длинном горизонте, следим.