Error on ingesting samples that are too old or are too far into the future

It appears that Prometheus does not accept my metrics. The metrics I am trying to scrape do not include timestamps. This seems to be allowed in the documentation: # Minimalistic line: metric_withou...

It appears that Prometheus does not accept my metrics. The metrics I am trying to scrape do not include timestamps. This seems to be allowed in the documentation:

# Minimalistic line:
metric_without_timestamp_and_labels 12.47

My metrics that are failing:

# HELP cpanel_resellers Number of cPanel users with Reseller privileges
# TYPE cpanel_resellers gauge
cpanel_resellers 0
# HELP cpanel_domains_remote Number of Remote domains
# TYPE cpanel_domains_remote gauge
cpanel_domains_remote 1
# HELP cpanel_domains_local Number of Local domains
# TYPE cpanel_domains_local gauge
cpanel_domains_local 40
# HELP cpanel_domains_total Total number of domains
# TYPE cpanel_domains_total gauge
cpanel_domains_total 41
# HELP cpanel_users Number of cPanel users
# TYPE cpanel_users gauge
cpanel_users 11
# HELP cpanel_mailq_queue_frozen Number of frozen messages in the mail queue
# TYPE cpanel_mailq_queue_frozen gauge
cpanel_mailq_queue_frozen 0
# HELP cpanel_mailq_queue_total Number of messages in the mail queue
# TYPE cpanel_mailq_queue_total gauge
cpanel_mailq_queue_total 0

These metrics validate within promtool (with warnings regarding _total suffix of course, but that is to be fixed later — for now I’d appreciate the metrics importing)

I have another box running version 2.7.2 and it has no problem importing these metrics.

Bug Report

What did you do?
Setup metrics scraping
What did you expect to see?
Metrics being scraped
What did you see instead? Under which circumstances?

Error on ingesting samples that are too old or are too far into the future

Environment

  • System information:

    Linux 3.10.0-693.el7.x86_64 x86_64

  • Prometheus version:

    prometheus, version 2.15.1 (branch: HEAD, revision: 8744510)
    build user: root@4b1e33c71b9d
    build date: 20191225-01:04:15
    go version: go1.13.5

  • Prometheus configuration file:

# my global config
global:
  scrape_interval: 1m
  evaluation_interval: 1m
  scrape_timeout: 30s

# Alertmanager configuration
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      - 127.0.0.1:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  - "monitoring.yml"

# A scrape configuration containing exactly one endpoint to scrape:
scrape_configs:
  - job_name: 'cpanel'
    static_configs:
      - targets:
        - 'target_host:2089'
  • Logs:
Jan  4 20:32:46 box prometheus: level=warn ts=2020-01-04T19:32:46.888Z caller=scrape.go:1170 component="scrape manager" scrape_pool=cpanel target=http://target_host:2089/metrics msg="Error on ingesting samples that are too old or are too far into the future" num_dropped=7
Jan  4 20:32:46 box prometheus: level=warn ts=2020-01-04T19:32:46.889Z caller=scrape.go:945 component="scrape manager" scrape_pool=cpanel target=http://target_host:2089/metrics msg="appending scrape report failed" err="out of bounds"
Источник изображения — СоюзМультфильм ©
Источник изображения — СоюзМультфильм ©

Привет! Меня зовут Григорий, я техлид в Cloud Infrastructure Team в Сравни. Моя команда отвечает за observability системы и облачную инфраструктуру. Не так давно мы полностью обновили наш стек мониторинга. Хочу рассказать, как у нас организовано хранение long-term метрик без использования Object Storage.

Мы в Сравни долгое время использовали связку Prometheus + Thanos для мониторинга и хранения данных. Для Thanos мы использовали схему с sidecar’ом. Эта схема работала довольно неплохо, но с ростом проекта — росло и потребление ресурсов. Со временем задачи по scrape samples уже потребляли значительные ресурсы. Когда только на Prometheus стало уходить больше 30 ядер vCPU и 100 гигабайт RAM, мы начали искать способы оптимизации потребления ресурсов. 

Первым делом определили требования, которые необходимы для системы мониторинга:

  • должно поддерживаться развертывание в Kubernetes;

  • система должна быть способна переезжать из одного Kubernetes-кластера в другой без потери данных;

  • нужна поддержка downsampling;

  • возможность построить high availability систему;

  • в идеале, чтобы система требовала очень мало внимания на обслуживание ;)

Мы поизучали варианты, и сперва показалось, что будет хорошей идеей взять стек VMAgent + Thanos receiver. Как несложно угадать из названия статьи, этого у нас не получилось. Недавно я увидел в одном профессиональном чате, что коллеги захотели использовать такой же стек и по тем же причинам, что и мы. Поэтому решил поделиться нашим опытом и рассказать, к чему мы в итоге пришли.

Причины выбора VMAgent + Thanos receiver

Начнем с агента, осуществляющего сбор samples. В Prometheus для каждого remote write создается in-memory очередь, в которую записываются samples из журнала (WAL). Данные в очереди хранятся в течение двух часов, и это поведение не изменить. Но есть issue на изменение такого поведения. 

Забегая вперед, при выборе данного стека, каким-то образом мы упустили один, казалось бы, очевидный момент в схеме с Thanos Receiver. То есть по факту мы бы просто перешли бы из pull на push модель (о плюсах и минусах можно посмотреть здесь), получили возможность поддержки мультитенантности с global view, а также исключили ситуацию, когда sidecar может перестать заливать time series в object storage. И вряд ли бы получили бы экономию в ресурсах (как записывали изначально в своих целях). 

Когда поняли это, мы приняли решение всё же продолжить тестирование данной схемы, но уже параллельно продумывая другое решение.

VMAgent умеет кешировать samples на диск в случае проблем в сети или с удаленным стораджем, и потом отправлять их, как только связь с удаленным стораджем восстановится — это очень удобно. Размер буфера ограничивается размером диска. Также есть опция -memory.allowedPercent – позволяет регулировать допустимый процент системной памяти, которую могут занимать кэши метрик (по умолчанию 60% от доступной памяти). И да, VictoriaMetrics умеет в автоматическое определение лимитов памяти и процессора под управлением cgroupv2.

Протестировав Prometheus и VMAgent на одинаковых количествах samples, мы увидели, что VMAgent потребляет меньше ресурсов, чем Prometheus (к сожалению, результаты тестов не дожили до написания данной статьи).

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

Thanos же выбрали по следующим причинам:

  • Community edition версия поддерживает downsampling (VMStorage поддерживает только в Enterprise-версии);

  • есть поддержка работы с Object Storage (изначально нам казалось, что это единственный способ не терять данные без downtime при переезде стораджа между кластерами).

Начало тестирования

Начали мы, конечно же, с поиска issue и чтения документации. Вооружившись этими сакральными знаниями и с помощью helm chart’ов, все необходимые компоненты развернули в Kubernetes, и схема довольно быстро заработала (подозрительно!).

Схема работы c Thanos Receiver мало чем отличается от Thanos с sidecar. Если вам интересно более подробно разобраться в компонентах Thanos и VictoriaMetrics, рекомендую прочитать две статьи Александра Валялкина — на хабре, и на medium. Я кратко пройдусь по компонентам, останавливаясь на тех частях, которых нет в статьях Александра.

VMAgent собирает samples из таргетов по pull-модели. После этого VMAgent отправляет samples в Thanos Receiver (на самом деле, отправляет на L7 балансировщик нагрузки, а тот – уже по round robin на Thanos Receiver). Thanos Receiver может состоять из одной или нескольких реплик (далее буду называть их эндпоинтами). Для балансировки нагрузки, репликации данных и service discovery используется файл hashring configuration. Выглядит он так – внутри перечислены эндпоинты, tenant ID и порты. 

Thanos Receiver бывает soft tenancy и hard tenancy. 

  • Soft tenancy – запросы, tenant ID которых явно не совпадает ни с одним другим эндпоинтом в конфигурационном файле, автоматически попадают в это хэш-кольцо soft tenancy; то есть по факту это тенант по умолчанию, если не найдется нужный эндпоинт. К слову, id данного тенанта задается через переменную receive.default-tenant-id. А еще данный тенант может выступать как балансировщик (в документации значится как Load distribution) к hard tenancy. 

  • Hard tenancy – если просто, это те эндпоинты, у которых установлен tenant ID в каждом HTTP-запросе. Данный механизм нужен для мультитенантной архитектуры Thanos. В нашем случае не было таких задач, и мы везде использовали Soft tenancy Thanos Receiver.

На тестовым стенде Thanos Receiver состоял из четырех реплик (по две реплики в каждой из зон доступности) и хранил данные в течении трёх дней (-tsdbRetention=3d). Почему решили хранить данные на Thanos Receiver три дня? Наш анализ показал, что чаще всего используются метрики в течении трех дней (скажем так, горячие метрики) и чтобы ускорить их выдачу пользователям, было принято решение оставить метрики до трех дней на SSD-дисках. 

Thanos Receiver, получив samples от VMAgent, отправляет данные time series на другие эндпоинты, которые указаны в hashring configuration файле. Если какой-либо time series в запросе на запись на эндпоинт Thanos Receiver не был успешно записан по крайней мере на (ReplicationFactor / 2) + 1, то Thanos Receiver отвечает ошибкой к VMAgent. Например, при replication-factor=3 Thanos Receiver убедится, что каждый time series успешно записан по крайней мере на двух Thanos receivers в эндпоинтах hashring файла ( 3/2 + 1 = 2,5 = 2).

Еще одно отличие от схемы с sidecar – в том, что Thanos Query делает запросы вместо sidecar в receiver (помимо Store Gateway), и агенту (Prometheus или VMAgent) необходимо отправить данные в Thanos Receiver (в схеме с sidecar, сам Thanos Sidecar сохранял данные в Object Storage).

Но есть нюанс…

В целом, вышеописанная схема сработала для нас. Но дьявол кроется в мелочах, и для нас они стали критичными.

Нюансы номер раз и два

Проблемы начались при удалении пода одного из эндпоинтов Thanos Receiver — тестировали случаи, когда под с Thanos Receiver находился на ноде, в которой произошел сбой. Например, инженер ошибся и удалил ноду из кластера, сработал дешедуллер, и нода удалилась, или по каким-то причинам нода с подом Thanos Receiver упала. 

Мы стали периодически получать 409 ошибки в VMAgent, а в логах Thanos Receiver увидели: hashring has changed; server is not ready to receive web requests.

Затем мы решили проверить случай отказа кластера или большей его части – отключили три ноды, на которых были поды с Thanos Receiver и логи перестали писаться. Тут все логично. 

Вернули все три ноды в работу, но логи не появились в VMAgent, и были видны 409 ошибки. В логах: Error on ingesting samples that are too old or are too far into the future. В документации есть описание данной проблемы и ее причины, но нет решения.

Обе проблемы помог решить thanos-receive-controller, который отслеживает количество running подов с лейблом controller.receive.thanos.io=thanos-receive-controller (можно поставить и свой, разумеется) и обновляет configMap c hashring configuration файлом, и с задержкой (по нашим тестам, примерно через 120 секунд) обновляется в контейнере. После этого Thanos Receiver перечитывает конфиг. Важно — не монтировать configMap как subPath, иначе не обновится файл в контейнере; подробнее о нюансах configMap можно почитать в блоге Фланта

Учтите, что у данного подхода с контроллером есть и минусы.

Нюанс номер три

Отключили все ноды с подами Thanos Receiver больше, чем на 2 часа; помним, что Thanos Receiver – это реализация над Prometheus Remote Write API, со всеми вытекающими. Получили проблему с тем, что Thanos Receiver отбрасывает логи. 

У этой проблемы пока что нет решения; это нужно учитывать при эксплуатации.

Нюанс номер четыре

Решили добавить реплик VMAgent (мы же все за высокую доступность всех компонентов, да ведь?)

Но для дедупликации Thanos Querier использует query.replica-label. И есть требование, чтобы для каждого агента должен отличаться extrnal_labels. Значит, на каждой реплике VMAgent он должен отличаться. 

Решение нашлось довольно быстро: в конфиге VMAgent проставляем переменную окружения POD_NAME:

external_labels:
  replica: %{POD_NAME}

И в statefulSet VMagent пробросить имя пода в переменную окружения:

- name: POD_NAME
  valueFrom:
    fieldRef:
      fieldPath: metadata.name

Все заработало.

Нюанс номер пять

Для двух реплик VMAgent и сбоев в сети была ещё одна проблема – уезжали метрики. Иногда на час, иногда на два часа — с дельтой во времени. 

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

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

Суммируем нюансы

На основании результатов тестирования мы приняли решение не тащить связку VMAgent + Thanos receiver в прод. 

Вышеописанные нюансы в Thanos проявляются из-за того, что данный вид стораджа не поддерживает backfilling – вставку данных, соответствующих временной метке в прошлом. Тут есть некоторые вещи, которые можно сделать, начиная с версии Prometheus v2.24.0. Если интересны объяснения на примерах, как это работает, можно посмотреть тут и тут

А что с VictoriaMetrics stack?

Как я говорил выше, параллельно с тестированием Thanos Receiver, мы начали изучать другие варианты и решили сконцентрироваться на VictoriaMetrics.

Одной из фич VictoriaMetrics является поддержка Backfilling из коробки. Поэтому проблемы с out-of-order samples при использовании стека VictoriaMetrics нам не страшны. Это же позволяет использовать параллельные очереди для каждого remote write стораджа, если одной очереди недостаточно для отправки большого объема собранных данных в удаленное хранилище. При этом в Thanos мы всегда должны использовать только одну очередь, иначе получим out-of-order samples

За время тестирования мы выяснили, что VictoriaMetrics больше подходит под наши задачи. Поэтому нам осталось только решить, как перенести данные из одного кластера k8s в другой. 

Первый и очевидный вариант — использовать vmbackup. Но это подразумевает временный простой, когда будут переноситься данные из одного кластера в другой. А нам нужна была возможность переноса VMStorage между кластерами по очереди и с последующим переключением stateless-сущностей (vmagent, vmselect, vminsert).

Мы используем managed kubernetes в облаках, поэтому нам нужно было, чтобы решение поддерживалось обоими нашими облачными провайдерами (у нас это Яндекс.Облако и Azure). Изучив документацию наших облачных провайдеров, обнаружили, что оба вендора поддерживают возможность добавления static PersistentVolume (подробнее – для Azure тут, для ЯО тут). В итоге мы пришли к следующей архитектуре:

  • через terraform создаем диски в облаке;

  • создаем объект PersistentVolume в Kubernetes под каждый созданный диск. 

Так как StatefulSet при создании пода имеет уникальный упорядоченный идентификатор, то при использовании volumeClaimTemplates мы можем описать, какое имя нашего тома будет использоваться с указанием claimName (оно тоже упорядоченное, и будет использоваться тот же идентификатор что в поде), и таким образом, не будет запускаться провижинер для создания новых томов PersistentVolume. 

В этой схеме правильное наименование claimRef очень важно. Ниже — немного упрощенный конфиг, для понимания. В примере созданы две PersistentVolume с отличающимися claimRef и volumeHandle (индентификатор нашего диска) и один kind StatefulSet c volumeClaimTemplates.

---
apiVersion: v1
kind: PersistentVolume
metadata:
 name: prod-yc-vm-storage-0
spec:
 capacity:
   storage: 100Gi
 accessModes:
   - ReadWriteOnce
 persistentVolumeReclaimPolicy: Retain
 storageClassName: yc-network-ssd
 csi:
   driver: disk-csi-driver.mks.ycloud.io
   fsType: ext4
   # See it in terraform.
   volumeHandle: qwerty0
 claimRef:
   namespace: monitoring-system
   name: pvc-vm-vmstorage-0
---
apiVersion: v1
kind: PersistentVolume
metadata:
 name: prod-yc-vm-storage-1
spec:
 capacity:
   storage: 100Gi
 accessModes:
   - ReadWriteOnce
 persistentVolumeReclaimPolicy: Retain
 storageClassName: yc-network-ssd
 csi:
   driver: disk-csi-driver.mks.ycloud.io
   fsType: ext4
   # See it in terraform.
   volumeHandle: qwerty1
 claimRef:
   namespace: monitoring-system
   name: pvc-vm-vmstorage-1
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: vmstorage
 namespace: monitoring-system
spec:
 selector:
   matchLabels:
     app: vmstorage
 serviceName: "vmstorage"
 replicas: 2
 podManagementPolicy: OrderedReady
 template:
   metadata:
     labels:
       app: vmstorage
       app.kubernetes.io/name: vmstorage
       app.kubernetes.io/instance: vmstorage-b
       app.kubernetes.io/version: "v1.74.0"
   spec:
     containers:
     - name: vmstorage
       image: "victoriametrics/vmstorage:v1.74.0-cluster"
       volumeMounts:
       - name: pvc-vm
         mountPath: /storage
 volumeClaimTemplates:
 - metadata:
     name: pvc-vm
   spec:
     accessModes: [ "ReadWriteOnce" ]
     resources:
       requests:
         storage: 100Gi
     storageClassName: yc-network-ssd

Теперь мы можем переносить VMStorage из одного кластера Kubernetes в другой, поочередно уменьшая реплики StatefulSet в одном кластере с удалением PersistentVolume и добавляя PersistentVolume с увеличением реплик в другом, сопровождая это изменением конфигурации в VMSelect и VMInsert. 

В этой схеме есть два нюанса: 

  • Автоматизацию этого переключения мы пока не сделали, отложив эту задачу в бэклог — до времени, когда такая ситуация понадобится. Данное решение — скорее задел на будущее.

  • Чтобы схема заработала, необходимо между кластерами обеспечить pod-to-pod связанность без NAT (в наших облаках такая возможность есть), либо при переезде на каждый под прокинуть по сетевому балансировщику, создавая таким образом сетевую доступность между VMSelect/VMInsert и VMStorage в разных кластерах.

Взяв за основу статью Resizing StatefulSet Persistent Volumes with zero downtime, мы отработали схему с задачей по увеличению размера диска для VMStorage. 

Ниже – упрощенный процесс без использования terraform в Яндекс Облаке:

  • удаляем kind StatefulSet VMStorage оставляя “сиротские” поды без управления контроллера StatefulSet.

k delete sts --cascade=orphan vmstorage

  • удаляем pod с самым высоким id (в нашем пример два пода, у одного имя vmstorage-0, у второго vmstorage-1). Удалять под приходится из-за того, что наше облако не дает возможность увеличить размер диска, если он примаунчен.

k delete pod vmstorage-1

  • увеличиваем размер диска в облаке до 110G.

yc compute disk update --size 110G --id qwerty1

  • патчим PVC, увеличивая сторадж до 110G.

k patch pvc pvc-vm-vmstorage-1 --patch '{"spec": {"resources": {"requests": {"storage": "110Gi"}}}}'

  • деплоим StatefulSet не изменяя размер storage в storageClassName.

cat <<EOF | kubectl apply -f -
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: vmstorage
 namespace: monitoring-system
spec:
 selector:
   matchLabels:
     app: vmstorage
 serviceName: "vmstorage"
 replicas: 2
 podManagementPolicy: OrderedReady
 template:
   metadata:
     labels:
       app: vmstorage
       app.kubernetes.io/name: vmstorage
       app.kubernetes.io/instance: vmstorage-b
       app.kubernetes.io/version: "v1.74.0"
   spec:
     containers:
     - name: vmstorage
       image: "victoriametrics/vmstorage:v1.74.0-cluster"
       volumeMounts:
       - name: pvc-vm
         mountPath: /storage
 volumeClaimTemplates:
 - metadata:
     name: pvc-vm
   spec:
     accessModes: [ "ReadWriteOnce" ]
     resources:
       requests:
         storage: 100Gi
     storageClassName: yc-network-ssd
EOF
  • повторяем пункты 1-4 д1я vmstorage-1.

  • После чего деплоим манифест с увеличенным storage в storageClassName.

cat <<EOF | kubectl apply -f -
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: vmstorage
 namespace: monitoring-system
spec:
 selector:
   matchLabels:
     app: vmstorage
 serviceName: "vmstorage"
 replicas: 2
 podManagementPolicy: OrderedReady
 template:
   metadata:
     labels:
       app: vmstorage
       app.kubernetes.io/name: vmstorage
       app.kubernetes.io/instance: vmstorage-b
       app.kubernetes.io/version: "v1.74.0"
   spec:
     containers:
     - name: vmstorage
       image: "victoriametrics/vmstorage:v1.74.0-cluster"
       volumeMounts:
       - name: pvc-vm
         mountPath: /storage
 volumeClaimTemplates:
 - metadata:
     name: pvc-vm
   spec:
     accessModes: [ "ReadWriteOnce" ]
     resources:
       requests:
         storage: 110Gi
     storageClassName: yc-network-ssd
EOF

Downsampling? 

Как было сказано выше, downsampling в VictoriaMetrics используется в enterprise-версии.

Хотя разработчики оставили хак для этой фичи, мы придумали очередной костыль пошли своим путем: добавили отдельные группы VMAgent со scrape Interval в 5 минут (для графиков до 3х месяцев) и сохраняем в отдельную группу VMStorage c соответствующим dedup.minScrapeInterval (о причинах наличия отдельных VMStorage ниже). 

Таким образом, мы делаем некое подобие downsampling. А с учетом того, что VictoriaMetrics довольно хорошо сжимает time series — это позволяет экономить на утилизации места на диске. 

Вообще, по поводу downsampling и как оно работает — есть хорошее выступление у команды Thanos.

Нюансы VictoriaMetrics

  • Если в Thanos для дедупликации важно, чтобы у агентов был разный external label, то для VictoriaMetrics он должен быть идентичным.

  • При использовании двух VMAgent, которые скрейпят одни и те же данные, и пишут тот же набор VMStorage (точнее в VMInsert’ы, а те уже в VMStorage), стали замечать, что графики при обновлении странички начинают “скакать”. Это связано это с тем, что два VMAgent’а скрейпят одни и те же samples с дельтой по времени (т.е. не одновременно). Одно из решений — шардирование. Деплоя VMAgent как statefulset и указывания уникальное значение promscrape.cluster.memberNum=%{POD_NAME}. Второе решение — при использовании дедупликации в VMStorage и VMSelect dedup.minScrapeInterval желательно выставлять большее значение чем ScrapeInterval в VMAgent. Так же есть issue при работе VMAgent в разных AZ делающих scrape samples идентичных эндпоинтов (это кстати, наш случай). Данная issue решается с выставлением уникального значения promscrape.cluster.name с версии v1.78.0.

Более подробно про дедупликацию описано в документации.

  • Репликация:

    • В отличии от Thanos Receiver, VMStorage не находятся в одном кластере, и вообще ничего не знаю друг о друге. Репликация time series происходит на стороне VMInsert, который и сохраняет эти данные в VMStorage. Вычисляется репликация по формуле 2N-1, где N – это replicationFactor. То есть репликация на несколько хостов будет происходить, если replicationFactor будет больше единицы. Например: replicationFactor=1 (2*1-1=1) – сохраняем данные на один VMStorage, replicationFactor=2 (2*2-1=3) –  сохраняем данные на три VMStorage. 

    • VMInsert реплицирует данные синхронно на VMStorage (ожидает, пока данные запишутся во все VMStorage). Так что если не наберется необходимое количества нод на запись, запись не будет совершена.

    • VMSelect отправляет запросы на все VMStorage, которые указаны в параметре storageNode и ждет ответа от <количество VMStorage> — replicationFactor + 1. Такая настройка позволяет ускорить отдачу ответа, не дожидаясь ответа от VMStorage. Но replicationFactor в VMSelect работает хорошо, когда все хорошо на VMStorage нодах хранятся равное количество копий всех данных выставленных при replicationFactor. Но если, например, вы вывели на обслуживание ноды с VMStorage или добавили новые – вы можете получать пропуски данных на графиках. На это есть issue. Поэтому в таких случаях лучше убрать параметр replicationFactor из VMSelect, чтобы VMSelect дожидался ответа от всех VMStorage нод перед отправкой ответа на запрос.

    • Чем больше replicationFactor, тем больше потребуется ресурсов CPU, RAM и места на диске

  • VMInsert и VMStorage сжимают данные перед отправкой их, что естественно, сказывается на утилизации CPU. Если вы сильно ограничены в ресурсах CPU, отключить это поведение можно через аргумент -rpc.disableCompression.

  • Для деплоя в Kubernetes в компании используются как кастомные написанные helm-чарты, так и вендорные, в которых используется kind ServiceMonitor для service discovery в prometheus. VMAgent не умеет работать с данным типом CRD. Мы не стали переписывать наши кастомные чарты (и тем более вендорные), а просто задеплоили VMOpertor который умеет конвертировать CR ServiceMonitor в сущности VictoriaMetrics. Это не отменяет того факта, что CRD ServiceMonitor необходимо задеплоить в Kubernetes, так как данное CRD не идет в поставке с VictoriaMetrics). Данная функциональность облегчает переезд с Prometheus на VictoriaMetrics.

Профит от миграции 

Изначально для переезда мы рассматривали стек VMAgent + Thanos Receiver, но он не подошел нам по ряду причин – тех самых нюансов, которые в нашем случае были важны. Критичным оказалось наличие механизма backfilling, который есть из коробки в VictoriaMetrics, куда мы в итоге и мигрировали. VictoriaMetrics подкупила еще более простой архитектурой.

В результате миграции мы снизили затраты на ресурсы по RAM примерно в два раза; по CPU — примерно в полтора раза; на сторадж — примерно в три раза. Это произошло по той причине, что в Object Storage взимается плата не только за хранение данных, но и за количество запросов. В нашем случае, переход на диски привел к экономии ресурсов.  

Также бонусом мы получили отдачу метрик быстрее — примерно на секунды 2-3 (проверяли на графиках нашей инфраструктурной команды), в сравнении со схемой с Thanos Sidecar.

На данный момент экономия составила порядка 60 000 рублей в месяц. По времени исследование заняло около двух с половиной недель. Так что примерно через 3-4 месяца данное решение должно окупиться.

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

imra…@gmail.com

unread,

Nov 17, 2017, 4:39:58 AM11/17/17

to Prometheus Users

I am trying to expose metrics from a file (using a simple http server on my dev box). I have a local instance of prometheus running, fresh without any data. When I launch it with target pointing to my http server, I get the following warning and data is not ingested.

level=warn ts=2017-11-17T01:31:04.68807107Z caller=scrape.go:908 component=»target manager» scrape_pool=zodiac target=http://localhost:9999/data/metrics.txt msg=»Error on ingesting samples that are too old or are too far into the future» num_dropped=1

Any idea how to get rid of this message? I tried with multiple samples, different timestamps, but data doesn’t get ingested. The timestamp is GMT: Thursday, November 16, 2017 8:09:44 PM

——————

# HELP zodiac_metric_hv_count Count of hvs.

# TYPE zodiac_metric_hv_count gauge

zodiac_metric_hv_count{dim=»total»,domain=»xxx2″,region=»test»,state=»ok»,instance=»prod»,job=»zodiac»} 2 1510862984

Config:

# my global config

global:

  scrape_interval:     15s # 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).

  # Attach these labels to any time series or alerts when communicating with

  # external systems (federation, remote storage, Alertmanager).

  external_labels:

      monitor: ‘codelab-monitor’

# A scrape configuration containing exactly one endpoint to scrape:

# Here it’s Prometheus itself.

scrape_configs:

  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.

  — job_name: ‘zodiac’

    honor_labels: true

    scrape_interval: 5s

    metrics_path: ‘/data/metrics.txt’

    static_configs:

      — targets:

         — ‘localhost:9999’

        labels:

Ben Kochie

unread,

Nov 17, 2017, 9:56:07 AM11/17/17

to imra…@gmail.com, Prometheus Users

It’s recommended to omit the timestamp.  The timestamp in the spec is there to allow for federation or importing of data from external TDSBs.  For normal application metrics, you want to assign the timestamp of the scrape.

Ben Kochie

unread,

Nov 17, 2017, 9:58:29 AM11/17/17

to imra…@gmail.com, Prometheus Users

Also, you should omit the labels like instance, job, and probably domain and region.  These look like target identifying labels that are added at scrape time by the server.  Metrics exposed by applications should only expose labels that relate to the metric, not the target host.

Imran

unread,

Nov 17, 2017, 11:04:08 AM11/17/17

to Ben Kochie, Prometheus Users

Thank you for the reply, Ben. 

Couple of questions:

1. How old of a timestamp can prometheus accept? I tried ingesting values that are an hour old, and it worked. But when tried ingesting a day old values, it throws me the same error.

2. I am trying for ways to feed in output from a prometheus server API to another prometheus instance.  If I use federation, would it retain timestamps? I want to run a prometheus server running on my laptop to federate to one behind a gateway. Whenever I start the server on my laptop, I expect it to catch up. Is that possible?

Ben Kochie

unread,

Nov 17, 2017, 11:26:16 AM11/17/17

to Imran, Prometheus Users

Sorry, but this is not how Prometheus is designed to be used.  It’s a monitoring system, not a generic time-series database.  Simply remove the timestamps.

Federation is not for mirroring / replication. It will not work this way.

I would look into using a remote write destination.

Here are two SaaS options:

Imran

unread,

Nov 17, 2017, 6:24:48 PM11/17/17

to Ben Kochie, Prometheus Users

Hi Ben,

Thank you for the SaaS references and guidance. 

-Imran

uvlig…@gmail.com

unread,

Nov 30, 2018, 1:40:49 AM11/30/18

to Prometheus Users

I have a similar issue. I am reading data from a nagios system which contains valid timestamps.
Replacing them with random values depending on the duration of a https query feels terribly wrong!

So I think the question regarding the actual limits that are causing  this error is totally  valid and I think refering to the deprecation is not very constructive.
This is our current use case nevertheless. 

Also the threat with the deprecation can always be resolved by not updating prometheus :)

So which are those limits please?

Hi!

Faced strange situation with timestamps.

Briefly:

  1. If timestamp is taken from datetime.datetime.now() — everything is ok.
  2. If timestamp is parsed from the date which looks same — I got warning in Prometheus server log about bad datetime and metric is not shown.

Here is code example with comments about which metrics works and which don’t:

import datetime
import sys
import time
import pytz

from prometheus_client import (
    start_http_server,
)
from prometheus_client.core import (
    GaugeMetricFamily,
    REGISTRY,
)


class BugDemoMetricsCollector:

    def collect(self):
        dt_format = '%Y-%m-%d_%H-%M-%S.%f %z'
        dt_now = datetime.datetime.now(tz=pytz.timezone('UTC'))
        print(dt_now)
        # Works
        gobj = GaugeMetricFamily('FooMetricGood', '')
        gobj.add_metric([], 123, timestamp=dt_now.timestamp())
        yield gobj
        # Works too
        dt_now_str = dt_now.strftime(dt_format)
        dt_parsed = datetime.datetime.strptime(dt_now_str, dt_format)
        gobj = GaugeMetricFamily('FooMetricGoodToo', '')
        gobj.add_metric([], 456, timestamp=dt_parsed.timestamp())
        yield gobj
        # Does not work, but same date
        dt_custom_str = '2021-11-11_18-12-59.000000 +0000'
        dt_parsed_from_custom = datetime.datetime.strptime(dt_custom_str, dt_format)
        gobj = GaugeMetricFamily('FooMetricNotWorking', '')
        gobj.add_metric([], 789987, timestamp=dt_parsed_from_custom.timestamp())
        yield gobj


def main():
    start_http_server(8080)
    REGISTRY.register(BugDemoMetricsCollector())
    while True:
        time.sleep(1)


if __name__ == '__main__':
    sys.exit(main())

Message from Prometheus log about trouble metric:

prometheus-prometheus-1  | ts=2021-11-11T13:41:01.895Z caller=scrape.go:1563 level=warn component="scrape manager" scrape_pool=services target=http://192.168.64.1:8080/metrics msg="Error on ingesting samples that are too old or are too far into the future" num_dropped=1

But date is correct, here is Python code for used date and format:

>>> datetime.datetime.strptime('2021-11-11_18-12-59.000000 +0000', '%Y-%m-%d_%H-%M-%S.%f %z')
datetime.datetime(2021, 11, 11, 18, 12, 59, tzinfo=datetime.timezone.utc)

I’ve spent more than one day trying to google, read docs and resolve it — no results.

Any help will be very appreciated.

Table of Contents

Troubleshooting; Common cases #

Overlaps #

Block overlap: Set of blocks with exactly the same external labels in meta.json and for the same time or overlapping time period.

Thanos is designed to never end up with overlapped blocks. This means that (uncontrolled) block overlap should never happen in a healthy and well configured Thanos system. That’s why there is no automatic repair for this. Since it’s an unexpected incident:

  • All reader components like Store Gateway will handle this gracefully (overlapped samples will be deduplicated).
  • Thanos compactor will stop all activities and HALT or crash (with metric and will error log). This is because it cannot perform compactions and downsampling. In the overlap situation, we know something unexpected happened (e.g manual block upload, some malformed data etc), so it’s safer to stop or crash loop (it’s configurable).

Let’s take an example:

  • msg="critical error detected; halting" err="compaction failed: compaction: pre compaction overlap check: overlaps found while gathering blocks. [mint: 1555128000000, maxt: 1555135200000, range: 2h0m0s, blocks: 2]: <ulid: 01D94ZRM050JQK6NDYNVBNR6WQ, mint: 1555128000000, maxt: 1555135200000, range: 2h0m0s>, <ulid: 01D8AQXTF2X914S419TYTD4P5B, mint: 1555128000000, maxt: 1555135200000, range: 2h0m0s>

In this halted example, we can read that compactor detected 2 overlapped blocks. What’s interesting is that those two blocks look like they are “similar”. They are exactly for the same period of time. This might mean that potential reasons are:

  • Duplicated upload with different ULID (non-persistent storage for Prometheus can cause this)
  • 2 Prometheus instances are misconfigured and they are uploading the data with exactly the same external labels. This is wrong, they should be unique.

Checking producers log for such ULID, and checking meta.json (e.g if sample stats are the same or not) helps. Checksum the index and chunks files as well to reveal if data is exactly the same, thus ok to be removed manually. You may find scripts/thanos-block.jq script useful when inspecting meta.json files, as it translates timestamps to human-readable form.

Reasons #

  • You are running Thanos (sidecar, ruler or receive) older than 0.13.0. During transient upload errors there is a possibility to have overlaps caused by the compactor not being aware of all blocks See: this
  • Misconfiguraiton of sidecar/ruler: Same external labels or no external labels across many block producers.
  • Running multiple compactors for single block “stream”, even for short duration.
  • Manually uploading blocks to the bucket.
  • Eventually consistent block storage until we fully implement RW for bucket

Solutions #

  • Upgrade sidecar, ruler and receive to 0.13.0+
  • Compactor can be blocked for some time, but if it is urgent. Mitigate by removing overlap or better: Backing up somewhere else (you can rename block ULID to non-ulid).
  • Who uploaded the block? Search for logs with this ULID across all sidecars/rulers. Check access logs to object storage. Check debug/metas or meta.json of problematic block to see how blocks looks like and what is the source.
  • Determine what you misconfigured.
  • If all looks sane and you double-checked everything: Then post an issue on Github, Bugs can happen but we heavily test against such problems.

Sidecar #

Connection Refused #

Description #

level=warn ts=2020-04-18T03:07:00.512902927Z caller=intrumentation.go:54 msg="changing probe status" status=not-ready reason="request flags against http://localhost:9090/api/v1/status/config: Get "http://localhost:9090/api/v1/status/config": dial tcp 127.0.0.1:9090: connect: connection refused"
  • This issue might happen when thanos is not configured properly.

Possible Solution #

  • Make sure that prometheus is running while thanos is started. The connection_refused states that there is no server running in the localhost:9090, which is the address for prometheus in this case.

Thanos not identifying Prometheus #

Description #

level=info ts=2020-04-18T03:16:32.158536285Z caller=grpc.go:137 service=gRPC/server component=sidecar msg="internal server shutdown" err="no external labels configured on Prometheus server, uniquely identifying external labels must be configured"
  • This issue happens when thanos doesn’t recognise prometheus

Possible Solution #

  • Thanos requires unique external_labels for further processing. So make sure that the external_labels are not empty and globally unique in the prometheus config file. A possible example —
global:
  external_labels:
    cluster: eu1
    replica: 0

Receiver #

Out-of-bound Error #

Description #

Thanos Receiver Log #

level=warn ts=2021-05-01T04:57:12.249429787Z caller=writer.go:100 component=receive component=receive-writer msg="Error on ingesting samples that are too old or are too far into the future" num_droppped=47

Root Cause #

  • “Out-of-bound” error occurs when the timestamp of the to-be-written sample is lower than the minimum acceptable timestamp of the TSDB head.

Possible Cause #

  1. Thanos Receiver was stopped previously and is just resumed, remote Prometheus starts to write from the oldest sample, which is too old to be digested and hence rejected.
  2. Thanos Receiver does not have enough compute resources to ingest the remote write data (is too slow). The latest ingested sample is gradually falling behind the latest scraped samples.

Diagnostic and Possible Solution #

  • Check the pod history of Thanos Receiver to see if it is case #1.
  • For case #2, if you installed Prometheus using the kube-prometheus-stack helm chart from the Prometheus Community, you can check the “Prometheus / Remote Write dashboard”. If the Rate[5m] is above 0 for a long period, it is case #2 and you should consider adding replica count or resources to Thanos Receiver.

Example Grafana dashboard showing the falling-behind remote write case

Out-of-order Samples Error #

Description #

Thanos Receiver Log #

level=warn ts=2021-05-01T05:02:23.596022921Z caller=writer.go:92 component=receive component=receive-writer msg="Error on ingesting out-of-order samples" num_dropped=14

Root Cause #

  • TSDB expects to write samples in chronological order for each series.
  • A sample with timestamp t1 is sent to the Thanos Receiver and accepted, any sample with timestamp t < t1 and identical label set being sent to the receiver after this will be determined as out-of-order sample.

Possible Cause #

  • Remote Prometheus is running in high availability mode (more than 1 replica are running). But the replica_external_label_name is not correctly configured (e.g. empty).

Example topology diagram of out-of-order error case caused by empty replica_external_label_name

  • Remote Prometheus is running in a federation
    • the remote-writing Prometheus is running in HA
    • federation has both honor_label = true and honor_timestamp = true
    • all layers of Prometheus is using the same replica_external_label_name (e.g. the default “prometheus_replica”)

Example topology diagram of out-of-order error case caused by misconfigured Prometheus federation

  • There are multiple deployments of remote Prometheus, their external_labels are identical (e.g. all being empty), and they have metrics with no unique label (e.g. aggregated cluster CPU usage).

Example topology diagram of out-of-order error case caused by missing external_labels

Diagnostic #

  • Enable debug log on Thanos Receiver (you may need to update cli parameter or helm chart values, depending on how you deployed Thanos Receiver). You can inspect the label set of the out-of-order sample in the debug log of Thanos Receiver, it may provide you some insight.
  • Inspect the topology and configuration of your Prometheus deployment, see if they match the above possible causes.

Possible Solution #

  • Configure distinct sets of external_labels for each remote Prometheus deployments.
  • Use different replica_external_label_name for each layer of Prometheus federation (e.g. layer 1: lesser_prometheus_replica, layer 2: main_prometheus_replica).
  • Use static endpoint based federation in Prometheus if the lesser Prometheus is in HA (service monitor based federation will pull metrics from all lesser Prometheus instances).

Found a typo, inconsistency or missing information in our docs? Help us to improve Thanos documentation by proposing a fix on GitHub here ❤️

Понравилась статья? Поделить с друзьями:
  • Error on gpu1 an illegal memory access was encountered
  • Error on gpu0 unspecified launch failure
  • Error on gpu0 out of memory gminer ошибка
  • Error on gpu out of memory
  • Error on google recaptcha contact your site administrator