Миллионы разработчиков пишут запросы PromQL и создают пользовательские дашборды Grafana для Kubernetes. И все используют одинаковые метрики из node-exporter, kubelet и kube-state-metrics. К сожалению, не все знают, как при работе с метриками обойти некоторые подводные камни.
Команда VK Cloud перевела статью, в которой автор разбирает ошибки в одном простом с виду запросе Prometheus для Kubernetes. Он должен возвращать сведения об использовании памяти пода:
container_memory_working_set_bytes{pod="agency-dashboard-api-89b7f557c-xd4l7"}
К сожалению, это неверный запрос. Давайте разбираться, что с ним не так и как избежать аналогичных ошибок в других запросах.
Ошибка №1: повторяющиеся временные ряды
Чтобы исправить запрос PromQL, нужно убедиться, что у вас в ожидаемых результатах указано точное число временных рядов. Поскольку в фильтре я указываю один под, то ожидаю, что и результат будет один.
Вместо этого я получаю два результата (я отредактировал несколько меток с конфиденциальными данными или поменял их значения):
container_memory_working_set_bytes{__replica__="replica-0", cluster="dev-cluster", container="agency-dashboard-api", endpoint="https-metrics", id="/kubepods/burstable/pod66a2c312-6066-42e9-99f8-6e045b863e9d/d24d2a7b79f4fac2f51ab2d9126b9be9a58bd122b18c6928faac463882df7ef0", instance="10.128.0.79:10250", job="cadvisor", metrics_path="/metrics/cadvisor", name="d24d2a7b79f4fac2f51ab2d9126b9be9a58bd122b18c6928faac463882df7ef0", namespace="default", node="gke-dev-cluster-lg-workloads-addf1-44", pod="agency-dashboard-api-89b7f557c-xd4l7", project="dev-k8s", service="kubelet"}
container_memory_working_set_bytes{__replica__="replica-0", cluster="dev-cluster", endpoint="https-metrics", id="/kubepods/burstable/pod66a2c312-6066-42e9-99f8-6e045b863e9d/d24d2a7b79f4fac2f51ab2d9126b9be9a58bd122b18c6928faac463882df7ef0", instance="10.128.0.79:10250", job="cadvisor", metrics_path="/metrics/cadvisor", name="d24d2a7b79f4fac2f51ab2d9126b9be9a58bd122b18c6928faac463882df7ef0", namespace="default", node="gke-dev-cluster-lg-workloads-addf1-44", pod="agency-dashboard-api-89b7f557c-xd4l7", project="dev-k8s", service="kubelet"}
Изначально я подозревал, что проблема в настройках мониторинга: может быть, несколько заданий извлекают одни и те же данные. Чтобы правильно построить графики, в Prometheus нужно использовать верные метки в запросе. (Prometheus добавляет в запрос несколько меток, в частности job и instance.) Я решил выяснить: может быть, одни и те же данные извлекают несколько заданий или выдают разные инстансы. Ведь если одинаковые данные поступают из разных мест, запрос выдает непонятные или неверные результаты. (Подробнее об этом в видео на YouTube об ошибках мониторинга в Prometheus.)
Я добавил метку задания:
container_memory_working_set_bytes{pod="agency-dashboard-api-89b7f557c-xd4l7", job="kubelet"}
Но запрос все равно выдавал два временных ряда для одного пода. Это значит, что только одно задание извлекало данные, так что у меня правильные настройки. Но запрос все равно был неверный.
Проблема №2: Ошибки группирования/суммирования
Поскольку запрос возвращал два почти одинаковых временных ряда, мой внутренний гений решил суммировать метрики и разделить результат на два.
sum(container_memory_working_set_bytes{pod=”my-pod-123”, job=”kubelet”}) by (pod) / 2
Но когда я перепроверил результаты этого запроса по данным у себя в консоли GKE, выяснилось, что запрос выдавал результаты, разнящиеся по времени. Это отчасти объяснялось тем, как метрики рассчитываются в GKE: GKE учитывает только Non-evictable memory.
Какой урок из этого можно извлечь? Не надо делать дедупликацию метрик, выводя их среднеарифметическое — по крайней мере, пока вы не разберетесь в подробностях, как они рассчитываются и почему вообще появились дубликаты.
Проблема №3: неожиданное количество элементов множества
Давайте вернемся к нашим дублированным временным рядам и рассмотрим их подробнее. Чем один временной ряд отличается от другого?
Ответ нам подскажет метка container. Эта метка есть у одного временного ряда и отсутствует у другого.
Чтобы понять почему, нужно пойти в обход и выяснить, что такое контейнер pause. Из документации Kubernetes:
«В поде Kubernetes для размещения контейнера сначала создается так называемый инфраструктурный, или Pause-контейнер. В Linux для поддержания существования cgroups и пространств имен, входящих в под, нужен процесс — он называется Pause-процесс. У контейнеров, относящихся к одному поду, включая инфраструктурные и рабочие контейнеры, общая конечная точка сети (один и тот же IPv4 и/или Ipv6-адрес, одни и те же пространства сетевого порта). В Kubernetes Pause-контейнеры нужны, чтобы при сбое и перезапуске рабочих контейнеров сохранялись их сетевые настройки».
Когда создается под в Kubernetes, число контейнеров, созданных на ноде, всегда больше, чем число подов, указанных в манифесте пода. Чтобы это проверить, выполните SSH на ноде и запустите docker ps. У Pause-контейнера имеются все сетевые настройки пода. При удалении Pause-контейнера удаляется также и контейнер приложения, а под перезапускается.
Теперь в дублирующихся временных рядах появляется смысл: у нас по одному временному ряду на контейнер, включая пустой Pause-контейнер.
После этого я подправил запрос и наконец получил верные метрики.
container_memory_working_set_bytes{pod=”agency-dashboard-api-89b7f557c-xd4l7”, job=”kubelet”, container!=”}
Какой урок из этого можно извлечь? Всегда проверяйте метки. Обязательно разберитесь, какие метки можно менять, какие жестко заданы, а какие факультативные. В большинстве случаев из-за ошибок с метками или количеством элементов множества вы получите неверные метрики.
Заключение
Надеюсь, что этот пример простого, но неправильного запроса PromQL поможет вам избежать аналогичных ошибок с собственными метриками. Простота PromQL обманчива. Нужно понимать, что именно вы измеряете в каждом запросе. Лишние данные могут пробраться незаметно и исказить агрегаты данных.
Присоединяйтесь к телеграм-каналу «Вокруг Kubernetes», чтобы быть в курсе новостей из мира K8s!
Регулярные дайджесты, полезные статьи, а также анонсы конференций и вебинаров.