✻ Урок 8.9 · Тема 8: Наблюдаемость
Мониторинг в Kubernetes: kube-prometheus-stack
Содержание урока
Зачем это нужно
В Docker Compose ты писал prometheus.yml руками и перечислял цели: «ходи по адресу notes:8080 и забирай метрики». В Kubernetes так не выйдет. Поды (pod, минимальная единица запуска, урок 5.2) приходят и уходят, у каждого нового пода новый IP-адрес, и статичный список адресов устаревает за час.
Кроме того, сам кластер требует наблюдения: под падает и перезапускается по кругу, на узле кончается память, Deployment застрял и не может поднять нужное число копий. На работе это первый пункт после «поставили кластер»: ставят готовый набор kube-prometheus-stack, и через полчаса есть дашборды узлов и подов, а команды подключают свои сервисы одним небольшим манифестом (манифест это YAML-файл с описанием объекта Kubernetes). kube-prometheus-stack это «мониторинг в коробке»: один Helm-пакет (урок 5.9), внутри которого Prometheus, Grafana, Alertmanager и датчики для кластера уже подружены между собой. Как купить готовую сигнализацию с монтажом вместо того, чтобы собирать её из отдельных датчиков. Без него ты неделю соединял бы всё это сам.
Шаг проекта: в кластер kind-notes ставится стек мониторинга (namespace monitoring), чарт helm/notes получает ServiceMonitor и PrometheusRule (chart 0.3.0, appVersion 0.7.0), и Prometheus сам находит «Заметки». ServiceMonitor это небольшой файл-заявка «собирай метрики вот с этого сервиса», как запись в график обхода. PrometheusRule это файл с правилами алертов, например «если ошибок больше 5%, подними тревогу». Оба разберём подробно ниже. Namespace monitoring это отдельная «комната» внутри кластера (урок 5.2): мониторинг живёт в своей, чтобы не смешиваться с приложениями.
Что нужно знать
- Урок 5.3: Service и DNS кластера: Service (постоянный адрес для набора подов) и labels (ярлыки на объектах). ServiceMonitor находит поды через Service и его labels.
- Урок 5.9: Helm: Helm ставит программы в кластер «пакетами» (чартами). Им мы поставим стек и расширим чарт
helm/notes. - Урок 5.11: HPA и metrics-server: metrics-server это маленькая служба кластера, которая помнит только текущую загрузку подов (для
kubectl topи автомасштабирования), как табло «сейчас на весах столько-то» без журнала. Чем она отличается от полноценного мониторинга, разберём в теории. - Урок 8.2: Prometheus: scrape (периодический сбор метрик), target (цель сбора), страница
/metrics. - Урок 8.3: PromQL: язык запросов,
rate, агрегации. - Урок 8.5: Alertmanager: правила алертов и runbook.
- Урок 8.6: Grafana: дашборды и datasource.
Картина целиком
Представь большой офис, где сотрудники всё время переезжают с места на место. Раньше у тебя был бумажный список «Иванов, кабинет 5, Петров, кабинет 7», и им пользовался обходчик (это Prometheus: он обходит цели и записывает показания). В офисе, где люди переезжают каждый час, такой список бесполезен. Нужен администратор, который сам следит за переездами и обновляет маршрут обходчика. Ему достаточно повесить на дверь табличку: «Меня нужно обходить».
В Kubernetes администратор называется Prometheus Operator, а табличка на двери это объект ServiceMonitor. Оператор (operator) это программа внутри кластера, которая берёт на себя рутину по одной программе: как опытный администратор, который знает, как настроить именно Prometheus, и делает это по твоим описаниям. Без оператора ты правил бы конфиг Prometheus руками при каждом новом сервисе. Про операторов вообще расскажет урок 9.2. Остальное приезжает в комплекте: датчики для узлов и объектов кластера, дашборды, набор готовых правил.
flowchart LR
subgraph CH["Твой чарт helm/notes"]
DEP["Deployment (поды)"]
SVC["Service (порт http)"]
SM["ServiceMonitor"]
PR["PrometheusRule"]
end
subgraph KPS["Чарт kube-prometheus-stack, ns monitoring"]
OP["Prometheus Operator<br>следит за объектами"]
PROM["Prometheus"]
AM["Alertmanager"]
GR["Grafana: дашборды"]
EX["node-exporter, kube-state-metrics, cAdvisor<br>метрики узлов и объектов кластера"]
end
SM -->|"обходи"| OP
PR -->|"правила"| OP
OP -->|"пишет конфиг"| PROM
PROM -->|"находит поды через Service"| SVC
SVC --- DEP
PROM --> AM --> H["Люди"]
PROM --> GR
EX --> PROM
Слева то, что лежит в чарте твоего приложения, справа стек. Оператор читает описания слева и превращает их в настройки Prometheus.
За урок ты разберёшь, что входит в стек и зачем, как устроены ServiceMonitor и PrometheusRule, как Prometheus находит цели без ручного списка, что такое CRD (расширение Kubernetes новыми типами объектов) и почему одна пропущенная строка-ярлык (label) делает мониторинг молча пустым. Ярлык (label) это пара ключ: значение на объекте Kubernetes, как цветная наклейка на папке: по наклейке объекты находят друг друга (урок 5.3).
Теория
Зачем нужен весь стек, а не один Prometheus
Одного Prometheus мало: он умеет хранить и считать метрики, но кто-то должен их отдавать. Про узел (сервер, на котором крутятся поды) нужны данные о процессоре, дисках и сети. Про объекты Kubernetes нужны данные вроде «сколько копий приложения должно быть и сколько живо». Про потребление контейнеров нужны свои цифры. Собрать всё это по отдельности можно, но на это уйдёт неделя. kube-prometheus-stack (стек) это один Helm-чарт (урок 5.9), который ставит связанный набор компонентов за одну команду.
Стек как готовый набор «умный дом» вместо россыпи датчиков: в коробке хаб, датчики в каждой комнате, приложение на телефон и готовые сценарии («протечка: закрыть воду»). Оговорка: коробка рассчитана на типичный дом. Свои особенности (наши сервисы) ты добавляешь сам.
В стек входят:
- Prometheus Operator (оператор, operator): программа-контроллер. Контроллер в Kubernetes это цикл «сравни, что описано в объектах, с тем, что есть на деле, и подтяни второе к первому». Этот следит за объектами мониторинга и сам пишет конфиг Prometheus.
- Prometheus и Alertmanager (рассылка алертов, урок 8.5): создаются оператором из объектов
PrometheusиAlertmanager. - Grafana (урок 8.6) с готовыми дашбордами кластера.
- node-exporter: программа на каждом узле, отдаёт метрики самого сервера (процессор, память, диски). Запускается как DaemonSet: объект, который держит ровно по одному поду на каждом узле (урок 5.8).
- kube-state-metrics: превращает состояние объектов Kubernetes в метрики, например
kube_pod_status_phase(в какой фазе под) иkube_deployment_status_replicas_available(сколько копий приложения готовы). - Набор готовых правил алертов и recording rules (правила, которые заранее считают сложное выражение и сохраняют результат как новую метрику).
Как различать источники цифр. Это самое частое место путаницы, поэтому разберём на вопросе «что случилось с приложением?»:
вопрос кто отвечает пример метрики
------------------------------------------------------------------------------------------------
сколько CPU и памяти съел контейнер? cAdvisor (в kubelet) container_memory_working_set_bytes
насколько загружен сам сервер? node-exporter node_cpu_seconds_total
сколько копий должно быть и сколько готово? kube-state-metrics kube_deployment_status_replicas_available
последние значения для kubectl top и HPA? metrics-server (истории нет, метрик Prometheus не даёт)
cAdvisor встроен в kubelet (агент Kubernetes на каждом узле, он запускает контейнеры). metrics-server из урока 5.11 хранит только последнее значение и нужен для kubectl top и автомасштабирования. Истории в нём нет, поэтому графиков за неделю он не построит.
Разберём на примере. Deployment notes просит 3 копии, живы две. kube-state-metrics публикует два числа: kube_deployment_spec_replicas{deployment="notes"} 3 (желаемое) и kube_deployment_status_replicas_available{deployment="notes"} 2 (доступное). Готовое правило стека KubeDeploymentReplicasMismatch сравнивает их: 3 не равно 2 дольше 15 минут, значит алерт. Обрати внимание: ни cAdvisor, ни node-exporter этого не знают, они измеряют потребление, а не «что должно быть».
Прикинь сам: какой компонент подскажет, что у Deployment 3 желаемых реплики, а доступна одна: cAdvisor, node-exporter или kube-state-metrics?
kube-state-metrics: метрики kube_deployment_spec_replicas и kube_deployment_status_replicas_available. cAdvisor и node-exporter измеряют потребление ресурсов, а не состояние объектов.
Осторожно, тут часто путают. metrics-server и kube-state-metrics. Первый отдаёт цифры «сколько потрачено сейчас», второй «в каком состоянии объекты». Названия похожи, задачи разные.
Главное: Prometheus хранит и считает, а отдают метрики разные компоненты: cAdvisor про контейнеры, node-exporter про узлы, kube-state-metrics про объекты.
Откуда Kubernetes знает слово ServiceMonitor?
CRD: как научить Kubernetes новым словам
Kubernetes из коробки знает Pod, Service, Deployment и десяток других типов объектов. Слова «мониторить» в нём нет. Чтобы его добавить, можно было бы дописать код самого Kubernetes, но это невозможно для каждого проекта. Поэтому придумали расширение: CRD (Custom Resource Definition, определение пользовательского ресурса). Это «словарь-дополнение»: ты регистрируешь новый тип объекта, и Kubernetes начинает хранить его так же, как любой свой.
Библиотечная картотека: есть карточки «книга» и «журнал». CRD это добавление в картотеку нового вида карточки, «диск». Сама картотека (Kubernetes) просто хранит карточки. А библиотекарь, который что-то с ними делает (оператор), отдельный человек. Оговорка: карточка без библиотекаря ничего не делает. Без установленного оператора объект ServiceMonitor можно создать, но толку не будет.
- Ты ставишь стек. Вместе с ним в кластер регистрируются CRD (проверишь командой
kubectl get crd). - Теперь в кластере есть новые типы. Главные три:
ServiceMonitor(«собирай метрики с подов за этим Service»),PodMonitor(то же, но напрямую по подам, без Service) иPrometheusRule(правила алертов и recording rules в виде объекта). - Ты создаёшь объект нового типа, например ServiceMonitor, в чарте своего приложения.
- Оператор видит объект, генерирует из него кусок конфига
scrape_configs(файл Prometheus, где перечислены цели) и просит Prometheus перечитать конфиг.
Итог: вместо файла prometheus.yml, который правит один человек, у каждой команды своё описание рядом с её приложением. Команда, которая владеет сервисом, владеет и его мониторингом.
Разберём на примере. Минимальный ServiceMonitor:
apiVersion: monitoring.coreos.com/v1 # группа и версия CRD: «новое слово» из словаря оператора
kind: ServiceMonitor # тип объекта
metadata:
name: notes
labels:
release: kps # ярлык для Prometheus, см. следующий раздел
spec:
selector: # какие Service мониторить: по их ярлыкам
matchLabels:
app.kubernetes.io/name: notes
endpoints:
- port: http # ИМЯ порта в Service (не номер)
path: /metrics
Читается как приказ: «найди Service с ярлыком app.kubernetes.io/name=notes, возьми у него порт по имени http и раз за интервалом забирай с адресов подов страницу /metrics». apiVersion: monitoring.coreos.com/v1 это адрес нового типа в словаре Kubernetes, так CRD оператора отличается от встроенных типов.
Осторожно, тут часто путают. Что создание ServiceMonitor само что-то мониторит. Он только описание. Работает связка «CRD + оператор + Prometheus».
Главное: CRD добавляет новые типы объектов, а работает связка «CRD, оператор и Prometheus»: сам ServiceMonitor лишь описание.
Проверь понимание: зачем стеку нужны CRD, если Prometheus можно настроить обычным файлом?
Ответ
CRD дают каждой команде описывать мониторинг своего приложения отдельным объектом рядом с приложением. Оператор сам собирает из объектов конфиг Prometheus. Без этого пришлось бы править центральный файл вручную при каждом новом сервисе, а IP подов в нём устаревали бы.
Как Prometheus находит цель по ярлыкам?
Как Prometheus находит цель: три ярлыка и одно имя
Связь «ServiceMonitor к подам» держится на ярлыках (labels): пары ключ: значение на объектах Kubernetes (урок 5.3). Ярлыки нужны, чтобы один объект мог «выбрать» другие без жёстких имён и IP.
Рассылка по тегам. У Prometheus в настройках написано: «читай только письма с тегом release=kps». Твой ServiceMonitor это письмо. Нет тега, письмо не прочитано, хотя лежит в ящике. Оговорка: письмо не «теряется с ошибкой», оно просто молча игнорируется. Поэтому такие поломки трудно заметить.
Поиск идёт в три «прыжка», и на каждом свой ярлык или имя:
flowchart TD
P["Prometheus<br>serviceMonitorSelector: release=kps"] -->|"1. беру только ServiceMonitor с release=kps"| SM["ServiceMonitor<br>selector.matchLabels: app.kubernetes.io/name=notes"]
SM -->|"2. беру Service с этим ярлыком<br>внутри namespace из namespaceSelector"| SV["Service notes<br>порт с name: http"]
SV -->|"3. endpoints.port: http<br>совпадение по ИМЕНИ порта"| T["Поды за Service<br>10.244.0.12:8080/metrics<br>target в /targets"]
Три шага, и на каждом совпадение: по ярлыку release, по ярлыку Service и по имени порта. Если любое звено не совпало, в /targets цели не будет.
Три условия должны сойтись одновременно:
- У ServiceMonitor есть ярлык, который ждёт Prometheus (
serviceMonitorSelector). В нашем стеке по умолчанию этоrelease: <имя релиза стека>, то естьrelease: kps. - Ярлыки Service подходят под
selector.matchLabels, а Service лежит в namespace изnamespaceSelector. - В
endpoints[].portнаписано имя порта из Service, а не число.
Если хоть одно не сошлось, цели нет, а ошибки в логах нет. Prometheus не «падает», он просто не видит объект.
Разберём на примере. Service объявлен так: ports: [{port: 8080}] без name. ServiceMonitor пишет port: http. Оператор ищет порт с именем http, не находит, и target не появляется. Исправление: дать порту в Service name: http.
Прикинь сам: в Service порт объявлен как
port: 8080безname. Можно ли в ServiceMonitor написатьport: 8080?
Нет. В endpoints[].port указывается имя порта. Поле targetPort в ServiceMonitor тоже есть, но оно устарело, поэтому дай порту имя (name: http) в Service и ссылайся на него. Без имени оператор не сможет сопоставить endpoint, и target не появится.
Осторожно, тут часто путают. «kubectl get servicemonitor показывает объект, значит всё работает». Нет: это только факт, что объект есть. Работает ли он, показывает страница /targets Prometheus.
Главное: цель находится по трём совпадениям: ярлык
release, ярлык Service и имя порта.
Как из метрики получается алерт?
Цепочка от метрики до алерта и PrometheusRule
Алерт (сигнал «что-то не так») в кластере тоже описывается объектом, PrometheusRule. Это те же правила, которые ты писал в файле в уроке 8.5, только в виде YAML-объекта в чарте. Плюс: правила версионируются и выкатываются вместе с приложением.
Инструкция для дежурного лежит не в общем шкафу, а на рабочем месте у того, кто отвечает за сервис. Оговорка: общая инструкция для инфраструктуры (упал узел, кончился диск) остаётся в стеке, её не дублируют.
Полный путь данных:
flowchart TD
M["/metrics приложения"] -->|"ServiceMonitor описывает, как собирать"| O["Оператор обновляет конфиг Prometheus"]
O --> C["Prometheus считает метрики"]
C -->|"PrometheusRule превращается в файл правил"| R{"Правило истинно<br>дольше for?"}
R -->|да| A["Алерт"] --> AM["Alertmanager"] --> RT["Маршрут"] --> H["Человек"]
R -->|нет| W["Ждём следующей проверки"]
Алерт не срабатывает сразу: условие должно продержаться дольше for, и только потом оно идёт по маршруту к человеку.
Любое звено может сломаться молча: объект создан, ошибок нет, а данных нет. Поэтому диагностика идёт по цепочке снизу вверх: target в интерфейсе Prometheus, затем ServiceMonitor, затем ярлык и имя порта.
Разберём на примере. Правило из задания 3:
record: notes:http_errors:ratio5m(recording rule): выражениеsum(rate(status=~"5..")) / sum(rate(всё))считается раз в интервал и сохраняется как метрика. Считаем так: за 5 минут пришло 100 запросов в секунду в сумме, из них 7 закончились кодом 5xx; 7 / 100 = 0,07, то есть 7 процентов.alert: NotesHighErrorRatioсрабатывает, когдаratio5m > 0.05держится дольшеfor: 5m. В нашем примере 0,07 больше 0,05, значит через 5 минут алерт уйдёт.
Стек приносит собственные правила (KubePodCrashLooping, KubeDeploymentReplicasMismatch, NodeFilesystemSpaceFillingUp) для инфраструктуры. Правила про боль пользователя (доля 5xx, задержка) пишешь ты, и лежат они рядом с приложением.
Осторожно, тут часто путают. «Правило есть, но не срабатывает». Причины по частоте: нет ярлыка release, выражение не даёт данных, условие не продержалось for.
Главное: правило алерта это объект
PrometheusRule, а алерт идёт по цепочке до человека только после выдержкиfor.
Проверь понимание: алерт
KubePodCrashLoopingсработал, но метрики самого приложения пропали. Какой из двух источников данных для этого алерта жив, а какой нет?
Ответ
Алерт строится на метриках kube-state-metrics (kube_pod_container_status_restarts_total), они живут независимо от /metrics приложения. Приложение в CrashLoop не отвечает, поэтому его собственные метрики пропали, а состояние пода kube-state-metrics видит по-прежнему.
Почему стек такой тяжёлый?
Ресурсы и хранение: почему стек «тяжёлый»
Мониторинг это тоже программы, и им нужны процессор, память и диск. Prometheus, Grafana, Alertmanager, оператор и kube-state-metrics вместе просят порядка 1-2 ГБ памяти. На ноутбуке с kind это заметно.
В values (файл настроек чарта, урок 5.9) мы делаем три вещи:
- Отключаем то, чего в kind нет: etcd, scheduler, controller-manager, kube-proxy недоступны для сбора метрик из узла-контейнера. Если оставить включёнными, эти цели будут DOWN, и «красное» на странице
/targetsначнёт мозолить глаза. - Задаём небольшие
requests(сколько ресурсов резервируется поду). - Кладём данные Prometheus на PVC (запрос на диск, урок 5.5), а не в
emptyDir, иначе перезапуск пода сотрёт историю.retention(срок хранения) ограничиваем: 3 дня, иначе диск заполнится.
Прикинь сам: почему Prometheus в стеке запускается как StatefulSet с PVC, а не как обычный Deployment без диска?
Prometheus хранит историю на диске и должен получать при перезапуске тот же диск и то же имя. StatefulSet даёт устойчивое имя и свой PVC для каждой копии. В Deployment без диска история пропадала бы вместе с подом.
Осторожно, тут часто путают. Что retention: 3d защищает от переполнения всегда. Он ограничивает по времени, а не по размеру. Если серий (уникальных рядов метрик) стало в десять раз больше, диск заполнится раньше срока.
Главное: стек просит 1-2 ГБ, поэтому в
valuesзадают лимиты, срок хранения и том для данных.
Как узнать, что цель не отвечает?
Метрика up: как Prometheus сообщает, что цель не отвечает
Если приложение упало, оно перестаёт отдавать метрики, и на графиках просто пустота. Пустоту легко принять за «всё спокойно». Нужен сигнал «я не смог собрать данные», и он не зависит от приложения.
Обходчик с журналом. Если в двери никто не открыл, он пишет в журнал «не открыли». Запись «не открыли» делает сам обходчик, а не жилец, так что она есть, даже когда жилец исчез. Оговорка: обходчик не знает причину (жилец спит или съехал), он знает только факт.
При каждом сборе Prometheus сам добавляет в базу метрику up для каждой цели: 1, если страница /metrics получена, и 0, если нет (нет соединения, таймаут, ответ 404 или 500). Ярлыки у неё те же, что у цели: job (имя задания, для ServiceMonitor это имя Service), instance (адрес и порт), namespace, pod.
Разберём на примере. Запрос up{job="notes"} вернёт по строке на каждый под, например up{instance="10.244.0.12:8080", job="notes"} 1. Под упал: значение станет 0. Правило NotesTargetDown из задания 3 (up{job="notes"} == 0 дольше for: 2m) сработает через две минуты. Именно поэтому оно смотрит на up, а не на метрики приложения: те при падении просто исчезают, и алерт на «долю 5xx» промолчит (деление пустоты на пустоту даёт no data).
Осторожно, тут часто путают. «Нет цели в /targets» и «цель DOWN» это разные поломки. Нет цели: Prometheus не знает про Service (ярлык, имя порта, namespace). Цель DOWN: знает, но достучаться не может (неверный path, порт, сетевая политика, приложение упало).
Главное:
upпишет сам Prometheus, и0означает «сбор не удался», а не «пусто, значит спокойно».
Проверь понимание: метрика
upравна 1, но метрики приложенияnotes_*пустые. Что это значит?
Ответ
Страница /metrics отвечает (иначе up был бы 0), но в ней нет метрик с префиксом notes_. Скорее всего, цель ведёт не на то приложение или приложение работает без метрик приложения (например, отдаёт только служебные). Смотри содержимое /metrics через kubectl port-forward.
Какой монитор выбрать для пода без Service?
ServiceMonitor или PodMonitor: через что искать поды
Не у каждого пода есть Service. Если под запускается Job-ом (одноразовая задача, урок 5.8) или это вспомогательный контейнер (сайдкар), которому Service не положен, ServiceMonitor его не найдёт.
ServiceMonitor ищет жильца через вывеску подъезда («Подъезд 3, квартиры 10-20»), а PodMonitor стучит в дверь конкретной квартиры. Оговорка: вывеска (Service) удобнее, она сама следит, кто в подъезде живёт и готов ли.
ServiceMonitor берёт список адресов из Endpoints Service, а в Endpoints попадают только готовые поды (прошедшие readiness-пробу, урок 5.7). PodMonitor выбирает поды по ярлыкам напрямую, порт указывается в podMetricsEndpoints. Для обычных приложений с Service выбирай ServiceMonitor, для остального PodMonitor.
Разберём на примере. Под notes не прошёл readiness: Service его не включает. ServiceMonitor его тоже не видит, и цель пропадает. PodMonitor нашёл бы его и показал up 1, если /metrics доступен. Так что при диагностике «цель пропала» смотри и kubectl get endpoints notes.
Прикинь сам: у тебя CronJob, который каждую ночь запускает под на 30 секунд и Service к нему нет. Что выберешь для метрик?
Скорее всего, ни то и ни другое: Prometheus собирает раз в 15-60 секунд и короткий Job может не успеть. Для коротких задач используют Pushgateway (Job сам отправляет итог) или считают результат в метриках, которые пишет долгоживущий сервис. PodMonitor подойдёт для долгоживущих подов без Service.
Осторожно, тут часто путают. Что PodMonitor «лучше». У него нет защиты «не собирать с неготовых подов», а вывески нет, значит и привязки к сервису.
Главное: ServiceMonitor ищет через Service, PodMonitor прямо по подам, и нужный выбирают по наличию Service.
Как Prometheus узнаёт про новые поды?
Как Prometheus узнаёт о подах: service discovery
В Compose ты вписал адрес notes:8080 в файл и забыл. В кластере под живёт минуты или дни, и при каждом перезапуске у него другой IP-адрес. Список адресов, написанный руками, врёт уже через час. Нужен способ, при котором Prometheus сам узнаёт, кто сейчас живёт в кластере. Этот способ называется обнаружением сервисов (service discovery).
Обходчик не носит с собой бумажный список жильцов, а перед каждым обходом звонит в диспетчерскую и спрашивает: «Кто сейчас живёт в подъезде 3?». Диспетчерская всегда знает актуальный список. Оговорка: диспетчер называет только тех, кто зарегистрирован. Если ты не повесил табличку (ServiceMonitor), обходчик про жильца не узнает.
У Kubernetes есть центральный справочник, API-сервер (API server): к нему обращаются все команды kubectl, и он знает обо всех объектах кластера. Prometheus подключается к нему и «подписывается» на изменения: появился под, исчез под, изменился Service. Как только что-то меняется, Prometheus пересобирает список целей. Порядок такой:
- Оператор читает ServiceMonitor и пишет в конфиг Prometheus правило: «ищи Service с такими ярлыками».
- Prometheus спрашивает у API-сервера, какие Service подходят, и берёт адреса их подов.
- К каждому адресу он ходит за
/metricsс заданным интервалом (по умолчанию в стеке 30 секунд). - Под пересоздали: API-сервер сообщил новый адрес, Prometheus перестроил список. Тебе ничего делать не нужно.
Разберём на примере. Deployment notes масштабируют с 2 до 3 копий. Появляется третий под с адресом 10.244.0.15:8080. Через несколько секунд в /targets видна новая строка. Старые две строки не пропали. Если потом один под удалят, его цель исчезнет из списка, а метрики, которые он успел отдать, остаются в базе: история не стирается.
Осторожно, тут часто путают. Что «Prometheus сам находит всё в кластере». Он находит только то, что ему описали: у него нет права «угадывать» сервисы. Если описания нет (нет ServiceMonitor или PodMonitor), цели не будет.
Главное: адреса подов меняются, поэтому Prometheus спрашивает у API Kubernetes актуальный список, а не хранит его.
Проверь понимание: ты пересоздал под, у него новый IP. Нужно ли править ServiceMonitor?
Ответ
Нет. ServiceMonitor описывает ярлыки Service, а не адреса. Новый под с теми же ярлыками попадёт в Endpoints Service, Prometheus узнает о нём через API-сервер и начнёт собирать метрики сам.
Откуда берутся слова kps и monitoring?
Helm-релиз, namespace и ярлык release: откуда берётся kps
Ты увидишь в уроке слова kps и monitoring почти в каждой команде. Если не понять, откуда они, то скопированная команда сработает, а при смене имени всё молча сломается.
Установка чарта как прописка жильца. Релиз (release) это имя, под которым Helm запомнил конкретную установку: «квартира kps». Чарт это типовой проект дома, а релиз конкретный построенный дом. Один чарт можно поставить дважды под разными именами. Namespace это номер подъезда, в котором дом стоит. Оговорка: имя релиза уникально только внутри одного namespace.
Команда helm install kps <чарт> -n monitoring говорит: «установи чарт, назови установку kps, положи в namespace monitoring». Шаблоны чарта kube-prometheus-stack подставляют имя релиза в ярлык release: kps, и этот же ярлык они прописывают в serviceMonitorSelector Prometheus: он ищет ServiceMonitor именно с ним (мы разбирали выше). Сам Helm чужие объекты этим ярлыком не помечает: на ServiceMonitor из других чартов его ставят руками. Если назвать релиз иначе, скажем mon, ярлык будет release: mon, и ServiceMonitor с release: kps Prometheus уже не примет.
Разберём на примере. Коллега скопировал ServiceMonitor из урока, а свой стек поставил как helm install monitoring .... Цель не появилась. Причина: Prometheus ждёт release: monitoring, а в манифесте release: kps. Проверить, что ждёт Prometheus: kubectl get prometheus -n monitoring -o yaml, поле serviceMonitorSelector. Исправление: поставить правильное значение в манифест.
Прикинь сам: в кластере два стека,
kpsв namespacemonitoringиteam-aв namespaceteam-a. Чей Prometheus подберёт ServiceMonitor сrelease: team-a?
Только тот, чей serviceMonitorSelector ищет release: team-a, то есть Prometheus из релиза team-a. Prometheus релиза kps такой объект проигнорирует: ярлык не совпадает.
Осторожно, тут часто путают. Имя релиза и имя namespace. Это два разных названия, они совпадают только если ты сам так назвал. У нас kps (релиз) и monitoring (namespace) разные.
Главное:
kpsэто имя релиза Helm, оно попадает в ярлыкrelease, и при смене имени цели молча пропадают.
Что занимает память Prometheus?
Серия и кардинальность: что именно занимает память
В разделе про ресурсы ты встретишь «серий стало в десять раз больше». Чтобы понимать, почему Prometheus может съесть всю память, нужно знать, из чего состоит его база.
Серия это один отдельный график на бумаге: своя линия, своя подпись. Метрика http_requests_total с разными ярлыками (путь, код ответа, под) это не один график, а много: по одному на каждое сочетание. Оговорка: на бумаге график стоит копейки, а каждая серия в Prometheus занимает место в памяти, пока она «жива».
Серия (time series) это метрика с конкретным набором ярлыков: http_requests_total{path="/notes", code="200"} и http_requests_total{path="/notes", code="500"} это две разные серии. Кардинальность (cardinality) это общее число серий. Оно растёт перемножением: 3 пути, 4 кода ответа, 10 подов дают 3 × 4 × 10 = 120 серий от одной метрики.
Разберём на примере. Кто-то добавил в метрику ярлык user_id. Пользователей 10 000, значит серий стало 120 × 10 000 = 1 200 000. Память Prometheus пухнет, под падает с OOMKilled (урок 5.7). Правило: в ярлык кладут значения из короткого, заранее известного списка (путь, код ответа), а не уникальные идентификаторы (пользователь, номер заказа, IP клиента).
Столбик справа в десять тысяч раз выше. Нагрузку создаёт число серий, а не число запросов.
Осторожно, тут часто путают. Что нагрузку на Prometheus создаёт число запросов к приложению. Нагрузку создаёт число серий: 1 000 000 запросов в секунду с одним набором ярлыков дают ту же одну серию.
Главное: серий столько, сколько сочетаний ярлыков, и в ярлыки кладут короткие заранее известные списки.
Проверь понимание: в метрику добавили ярлык
pod. В сервисе 5 подов, 4 пути, 3 кода. Сколько серий у метрики?
Ответ
5 × 4 × 3 = 60 серий. Каждый новый под при перезапуске даёт новое значение ярлыка pod, поэтому со временем серий накапливается больше, пока старые не «состарятся» и не уйдут из памяти.
Почему данные приходят с задержкой?
Интервал сбора: почему данные «опаздывают»
Новичок удивляется: сломал приложение, а на графике ещё красиво, и алерт молчит. Это не баг, а следствие того, как устроен сбор.
Обходчик проходит по подъезду раз в 30 секунд. Если дверь выломали сразу после обхода, он узнает об этом только на следующем круге. Оговорка: обходчик может ходить чаще, но тогда записей в журнале станет больше, а значит вырастут диск и память.
Параметр scrapeInterval задаёт паузу между сборами (в стеке 30 секунд). К ней добавляются ещё задержки: время for в правиле (алерт должен подтвердиться), интервал проверки правил (тоже обычно 30 секунд) и group_wait в Alertmanager (по умолчанию 30 секунд). Итог складывается.
Разберём на примере. Под упал в 12:00:05. Последний сбор был в 12:00:00, следующий в 12:00:30: там up станет 0. Правила проверяются раз в 30 секунд, так что up = 0 увидят на ближайшей проверке после 12:00:30, допустим в 12:00:45. Правило NotesTargetDown держит for: 2m, значит алерт перейдёт в «горит» около 12:02:45, а Alertmanager выдержит ещё group_wait (30 секунд). Всего около 3 минут от поломки до сообщения, и это нормально.
Сдвинь момент поломки сразу после обхода и посмотри, сколько ждать следующего сбора.
Прикинь сам: сбор раз в 30 секунд,
for: 1m. Какое самое позднее время до срабатывания после поломки, если учитывать только эти два числа?
Примерно 30 + 60 = 90 секунд: до 30 секунд ждём следующего сбора, затем минуту держится условие for. Проверка правил добавит ещё до 30 секунд, а group_wait Alertmanager в этот подсчёт не входит.
Осторожно, тут часто путают. Что алерт обязан прийти через секунду после поломки. Мониторинг по метрикам не мгновенный: от события до сообщения проходит минуты. Для сверхбыстрой реакции нужны другие средства, например проверки самого балансировщика.
Главное: от поломки до сообщения проходят минуты: интервал сбора плюс
forплюс проверка правил.
В каком порядке ставить?
Порядок установки и почему метрики выключены по умолчанию
ServiceMonitor и PrometheusRule это новые типы объектов. Если в кластере нет CRD, Helm не сможет создать объект и остановит весь helm upgrade. Значит, чарт с ServiceMonitor нельзя ставить в кластер без стека.
В values.yaml чарта helm/notes метрики выключены (metrics.enabled: false), а в values-dev.yaml для нашего кластера со стеком включены. Шаблон обёрнут в условие if, и при false Helm вообще не рисует объект. Тогда чарт ставится и в кластер без стека. Порядок: сначала стек (появляются CRD), потом чарт с включённым флагом.
Разберём на примере. Коллега ставит твой чарт в чистый кластер с флагом metrics.enabled=true. Ошибка: no matches for kind "ServiceMonitor" in version "monitoring.coreos.com/v1". Это значит: «такого слова в кластере нет», то есть стек не поставлен.
Осторожно, тут часто путают. Что Helm обновляет CRD сам. Helm ставит CRD из каталога crds/ чарта только при первой установке и не трогает при upgrade. Поэтому при переходе на новую версию стека CRD применяют отдельно (kubectl apply --server-side).
Главное: сначала стек с CRD, потом чарт с ServiceMonitor, поэтому метрики по умолчанию выключены.
Проверь понимание: почему в чарте приложения метрики включены флагом, а не всегда?
Ответ
Чарт должен ставиться и там, где нет стека мониторинга. Без CRD объект ServiceMonitor не создать, и вся установка упадёт. Флаг с условием if позволяет включать метрики только там, где стек есть.
Практика
Перед началом убедись, что кластер жив и приложение развёрнуто релизом notes (урок 5.9).
Две команды-проверки. kubectl config current-context печатает, к какому кластеру сейчас подключён kubectl (нужно kind-notes, иначе поставишь стек не туда). helm list -n notes показывает Helm-релизы в namespace notes (-n это namespace).
kubectl config current-context
helm list -n notes
kind-notes
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
notes notes 4 2026-09-29 09:12:44.51 +0300 MSK deployed notes-0.2.0 0.7.0
Как читать вывод: первая строка это контекст. В таблице REVISION (номер выкатки: растёт при каждом helm upgrade), STATUS deployed (релиз установлен), CHART (версия чарта) и APP VERSION (версия приложения внутри). У тебя ревизия и время будут другими, нужны deployed и 0.7.0.
Если APP VERSION другой, образ 0.7.0 нужен из урока 8.8: собери его и загрузи в kind командой kind load docker-image notes:0.7.0 --name notes (урок 5.2).
Если у тебя 8 ГБ
Стек и приложение вместе в 8 ГБ тесны. Останови Compose-стек из уроков 8.2-8.8 (docker compose -f monitoring/compose.yml down), не поднимай Loki и Tempo, оставь в кластере одну реплику notes и в values ниже уменьши retention до 2d. Alertmanager можно выключить (alertmanager.enabled: false), но тогда пропустишь часть задания 3.
Задание 1. Ставим kube-prometheus-stack
Цель. Развернуть стек в namespace monitoring с минимальными ресурсами и открыть Prometheus и Grafana.
Предскажи: сколько подов будет в namespace monitoring после установки и какой из них DaemonSet?
Ответ
Обычно 6-7: оператор, Prometheus (StatefulSet), Alertmanager (StatefulSet), Grafana, kube-state-metrics и node-exporter (по одному на узел, это DaemonSet). При одном узле kind получится 6 подов.
Шаги.
- Создай файл
monitoring/k8s/values-kps.yaml:
# Значения для kube-prometheus-stack 91.8.2 под учебный кластер kind
fullnameOverride: kps
# Компоненты control-plane в kind недоступны для scrape, отключаем
kubeEtcd:
enabled: false
kubeScheduler:
enabled: false
kubeControllerManager:
enabled: false
kubeProxy:
enabled: false
prometheus:
prometheusSpec:
retention: 3d
resources:
requests:
cpu: 100m
memory: 400Mi
limits:
memory: 800Mi
storageSpec:
volumeClaimTemplate:
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 5Gi
alertmanager:
alertmanagerSpec:
resources:
requests:
cpu: 10m
memory: 50Mi
grafana:
adminPassword: CHANGE_ME
resources:
requests:
cpu: 50m
memory: 128Mi
kube-state-metrics:
resources:
requests:
cpu: 10m
memory: 32Mi
prometheusOperator:
resources:
requests:
cpu: 50m
memory: 64Mi
- Добавь репозиторий и поставь чарт с закреплённой версией:
Разбор команд. helm repo add <имя> <адрес> подключает каталог чартов, как «добавить магазин приложений». helm repo update скачивает свежий список. В helm install: kps это имя релиза (оно же попадёт в ярлык release: kps), prometheus-community/kube-prometheus-stack это «каталог/чарт», --version 91.8.2 закрепляет версию (без неё завтра поставится другая), --namespace monitoring --create-namespace ставит в отдельный namespace и создаёт его, -f читает наши значения, --wait --timeout 8m ждёт до 8 минут, пока поды станут готовы.
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install kps prometheus-community/kube-prometheus-stack \
--version 91.8.2 \
--namespace monitoring --create-namespace \
-f monitoring/k8s/values-kps.yaml \
--wait --timeout 8m
- Проверь поды и CRD:
kubectl get crd печатает все зарегистрированные CRD, а | grep monitoring.coreos.com оставляет только те, что принёс оператор.
kubectl get pods -n monitoring
kubectl get crd | grep monitoring.coreos.com
- Открой интерфейсы (в двух терминалах).
kubectl port-forward svc/<Service> <локальный порт>:<порт Service>пробрасывает порт Service на твой компьютер, пока команда работает. Терминал занят, остановка Ctrl+C:
kubectl port-forward -n monitoring svc/kps-prometheus 9090:9090
kubectl port-forward -n monitoring svc/kps-grafana 3000:80
Что должно получиться.
NAME READY STATUS RESTARTS AGE
alertmanager-kps-alertmanager-0 2/2 Running 0 2m
kps-grafana-6f7c9d8b5-x2k4p 3/3 Running 0 2m
kps-kube-state-metrics-7d9c6b5f4-9tqzd 1/1 Running 0 2m
kps-operator-5b8f7c6d9-lm8vw 1/1 Running 0 2m
kps-prometheus-node-exporter-4hkxs 1/1 Running 0 2m
prometheus-kps-prometheus-0 2/2 Running 0 2m
alertmanagerconfigs.monitoring.coreos.com 2026-09-29T07:20:11Z
alertmanagers.monitoring.coreos.com 2026-09-29T07:20:11Z
podmonitors.monitoring.coreos.com 2026-09-29T07:20:11Z
prometheuses.monitoring.coreos.com 2026-09-29T07:20:11Z
prometheusrules.monitoring.coreos.com 2026-09-29T07:20:11Z
servicemonitors.monitoring.coreos.com 2026-09-29T07:20:11Z
Как читать вывод: в колонке READY 2/2 значит «два контейнера из двух готовы» (в поде Prometheus рядом работает контейнер, перечитывающий конфиг). STATUS Running и RESTARTS 0 это норма. Имена подов с хвостом (x2k4p) случайные, у тебя будут другие; у StatefulSet имена с номером (-0). В списке CRD дата это момент регистрации.
На http://localhost:9090/targets десяток целей в состоянии UP, в Grafana на http://localhost:3000 (логин admin, пароль из values) есть папка дашбордов Kubernetes.
Объясни себе.
- Почему Prometheus - StatefulSet, а не Deployment (урок 5.5)?
- Зачем отключены
kubeEtcdиkubeSchedulerи что случится, если оставить включёнными?
Типичные ошибки.
Error: INSTALLATION FAILED: context deadline exceeded: не хватило времени или памяти, поды Pending. Смотриkubectl get pods -n monitoringиkubectl describe pod, увеличь Docker-ресурсы или включи режим 8 ГБ.Error: INSTALLATION FAILED: cannot re-use a name that is still in use: релизkpsуже есть. Используйhelm upgrade --installилиhelm uninstall kps -n monitoring.- Цели etcd и scheduler в состоянии DOWN с
connection refused: не отключены в values, поправь и сделайhelm upgrade.
Задание 2. Подключаем «Заметки» через ServiceMonitor
Цель. Добавить в чарт helm/notes шаблон ServiceMonitor, включаемый флагом, и увидеть цель notes в Prometheus.
Предскажи: если создать ServiceMonitor без label release: kps, появится ли цель в /targets? Что покажет kubectl get servicemonitor?
Ответ
Цели не будет. kubectl get servicemonitor -n notes покажет объект как ни в чём не бывало, ошибок нет: Prometheus просто не выбирает его своим serviceMonitorSelector (по умолчанию требуется release: kps).
Шаги.
- Убедись, что порт в Service назван. В
helm/notes/templates/service.yamlпорт должен быть таким (изменена только строкаname):
ports:
- name: http # имя порта нужно ServiceMonitor
port: 8080
targetPort: 8080
- Добавь в
helm/notes/values.yamlблок:
# Метрики (урок 8.9)
metrics:
enabled: false
serviceMonitor:
enabled: false
interval: 15s
# label, по которому Prometheus выбирает ServiceMonitor (имя релиза стека)
release: kps
- Создай
helm/notes/templates/servicemonitor.yaml:
{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: {{ include "notes.fullname" . }}
labels:
{{- include "notes.labels" . | nindent 4 }}
release: {{ .Values.metrics.serviceMonitor.release }}
spec:
selector:
matchLabels:
{{- include "notes.selectorLabels" . | nindent 6 }}
namespaceSelector:
matchNames:
- {{ .Release.Namespace }}
endpoints:
- port: http
path: /metrics
interval: {{ .Values.metrics.serviceMonitor.interval }}
{{- end }}
- Подними версию чарта: в
Chart.yamlversion: 0.3.0,appVersion: "0.7.0". Проверь рендер и обнови релиз:
helm lint проверяет чарт на ошибки до выкатки. helm upgrade <релиз> <папка чарта> обновляет релиз, --set ключ=значение переопределяет значение из values.yaml на один запуск. kubectl get servicemonitor показывает объекты нового типа так же, как поды.
helm lint helm/notes
helm upgrade notes helm/notes -n notes \
--set metrics.enabled=true --set metrics.serviceMonitor.enabled=true
kubectl get servicemonitor -n notes
- В
http://localhost:9090/targetsнайди цельserviceMonitor/notes/notes/0. Выполни запрос:
sum by (path, status) (rate(notes_http_requests_total[5m]))
Что должно получиться.
Release "notes" has been upgraded. Happy Helming!
NAME AGE
notes 6s
Как читать вывод: Happy Helming! означает, что обновление прошло. AGE показывает, сколько живёт объект. Главное здесь не вывод, а страница /targets.
В /targets цель в состоянии UP (1/1 up) с адресом вида 10.244.0.12:8080/metrics. PromQL возвращает серии с path="/notes" после нескольких запросов к приложению.
Объясни себе.
- Откуда Prometheus узнал IP подов, если ты нигде их не писал?
- Почему в
matchLabelsберём selector-labels, а не все labels чарта?
Типичные ошибки.
Error: UPGRADE FAILED: unable to build kubernetes objects from current manifest: resource mapping not found for name: "notes" namespace: "notes" from "": no matches for kind "ServiceMonitor" in version "monitoring.coreos.com/v1": CRD стека не установлены (задание 1 не выполнено). Поставь стек.- Цель есть, но
DOWNиError: 404: неверныйpath. Приложение отдаёт метрики на/metrics. - Цели нет, объект создан: нет label
release: kpsили порт в Service без имени (разбор в блоке «Сломай и почини»).
Задание 3. PrometheusRule: алерт на долю 5xx
Цель. Добавить правила как объект Kubernetes и увидеть их в Prometheus.
Предскажи: ты добавишь правило и в Prometheus UI на вкладке Alerts оно появится через сколько: мгновенно, несколько секунд или после рестарта пода? Почему?
Ответ
Через несколько секунд, рестарт не нужен. Оператор следит за объектами PrometheusRule, сам обновляет ConfigMap с правилами, а sidecar config-reloader в поде Prometheus перечитывает их.
Шаги.
- Добавь в
values.yamlподmetrics:ключ для правил (правила включаются тем жеmetrics.enabled, отдельного ключа не заводим) и создайhelm/notes/templates/prometheusrule.yaml. Шаблон Prometheus с$labelsэкранируем, чтобы Helm его не разворачивал. Адресrunbook_urlэто пример: так выглядит адрес runbook в твоём репозитории, подставь свой:
{{- if .Values.metrics.enabled }}
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: {{ include "notes.fullname" . }}
labels:
{{- include "notes.labels" . | nindent 4 }}
release: {{ .Values.metrics.serviceMonitor.release }}
spec:
groups:
- name: notes.rules
rules:
- record: notes:http_errors:ratio5m
expr: |
sum(rate(notes_http_requests_total{status=~"5.."}[5m]))
/
sum(rate(notes_http_requests_total[5m]))
- alert: NotesHighErrorRatio
expr: notes:http_errors:ratio5m > 0.05
for: 5m
labels:
severity: page
annotations:
summary: "Доля 5xx у Заметок выше 5% уже 5 минут"
runbook_url: "https://github.com/distinguished-sre/learning/tree/main/devops/project/notes/docs/runbooks/NotesHighErrorRatio.md"
- alert: NotesTargetDown
expr: up{job="{{ include "notes.fullname" . }}"} == 0
for: 2m
labels:
severity: page
annotations:
summary: "Prometheus не может собрать метрики с {{ "{{ $labels.instance }}" }}"
{{- end }}
- Обнови релиз и проверь:
helm upgrade notes helm/notes -n notes \
--set metrics.enabled=true --set metrics.serviceMonitor.enabled=true
kubectl get prometheusrule -n notes
- На
http://localhost:9090/alertsнайди группуnotes.rulesи оба алерта в состоянии Inactive. Затем выполниnotes:http_errors:ratio5mв Graph.
Что должно получиться.
NAME AGE
notes 5s
В интерфейсе алерты NotesHighErrorRatio и NotesTargetDown в состоянии Inactive (зелёные). Запрос notes:http_errors:ratio5m возвращает 0 либо no data, если 5xx-ответов не было (no data при делении на ноль запросов это нормально).
Объясни себе.
- Почему правило
NotesTargetDownсмотрит на метрикуup, а не на метрики приложения? - Чем этот алерт отличается от
KubePodCrashLoopingиз стека?
Типичные ошибки.
Error: UPGRADE FAILED: YAML parse error ... did not find expected key: не экранирован шаблон$labels.instance, Helm попытался развернуть его сам. Оборачивай Prometheus-шаблоны в строку-литерал Helm (см. пример выше).- Правило создано, но в
/alertsего нет: нет labelrelease: kpsнаPrometheusRule(те же причины, что у ServiceMonitor). Error from server (BadRequest): admission webhook "prometheusrulemutate.monitoring.coreos.com" denied the request: синтаксическая ошибка вexpr, проверь PromQL в Graph.
Задание 4. Шаг проекта: chart 0.3.0 и выкат метрик
Цель. Закрепить состояние проекта: чарт helm/notes версии 0.3.0 с метриками включаемыми через values, приложение 0.7.0 в кластере.
Предскажи: какие три ресурса появятся в namespace notes после helm upgrade с включёнными метриками, которых там не было раньше?
Ответ
ServiceMonitor/notes, PrometheusRule/notes и (косвенно) новая ревизия релиза Helm. Deployment и Service не изменятся, кроме имени порта в Service.
Шаги.
- Включи метрики по умолчанию для кластера в
helm/notes/values-dev.yaml(вvalues.yamlони остаютсяfalse, чтобы чарт ставился и без стека):
metrics:
enabled: true
serviceMonitor:
enabled: true
- Убедись, что в
Chart.yamlверсия 0.3.0 и appVersion 0.7.0, и выкати:
helm lint helm/notes -f helm/notes/values-dev.yaml
helm upgrade --install notes helm/notes -n notes -f helm/notes/values-dev.yaml
helm list -n notes
kubectl get servicemonitor,prometheusrule -n notes
- Сгенерируй немного трафика и посмотри на дашборд. В Grafana (
Dashboards -> Kubernetes / Compute Resources / Namespace (Pods)) выбери namespacenotes.
for i in $(seq 1 30); do curl -s -o /dev/null -H 'Host: notes.lab' http://127.0.0.1/notes; done
- Зафиксируй в git:
git add monitoring/k8s helm/notes
git commit -m "8.9: kube-prometheus-stack, ServiceMonitor и PrometheusRule (chart 0.3.0)"
Что должно получиться.
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
notes notes 6 2026-09-29 10:05:31.20 +0300 MSK deployed notes-0.3.0 0.7.0
NAME AGE
servicemonitor.monitoring.coreos.com/notes 20s
NAME AGE
prometheusrule.monitoring.coreos.com/notes 20s
Сверяй чарт и values-kps.yaml с кодом из этого урока и с выводом helm template и kubectl get prometheusrule выше.
Объясни себе.
- Почему метрики выключены в
values.yamlи включены вvalues-dev.yaml? - Что сломается у человека без стека, если бы флаг был включён по умолчанию (подсказка: ошибка из задания 2)?
Типичные ошибки.
Error: rendered manifests contain a resource that already exists: ServiceMonitor создан руками черезkubectl apply. Удали его (kubectl delete servicemonitor notes -n notes) и повториhelm upgrade.- В Grafana дашборд пуст: не выбран namespace
notesв переменной или ещё не прошло 1-2 минуты после первого scrape.
Сломай и почини
Поломка делается скриптом, он меняет объекты в кластере kind-notes. Скачай и запусти (сценарий 1, 2 или 3; fix возвращает всё как было). Скрипт не читай, диагностируй по симптомам.
curl -fsSL -o /tmp/break-8.9.sh https://raw.githubusercontent.com/distinguished-sre/learning/main/devops/project/notes/break/8.9/break.sh
bash /tmp/break-8.9.sh 1
Разбери причину, исправь вручную, а если запутался, выполни bash /tmp/break-8.9.sh fix. Потом можно взять другой номер.
Симптом
Ты выкатил изменения, helm upgrade прошёл без ошибок, а Prometheus чего-то не видит: нет цели notes, либо цель есть, но пустая, либо алерты не появились в /alerts. Дашборд и алерты по «Заметкам» пусты, при этом приложение отвечает 200.
Гипотезы
- Оператор не выбрал объект: label не совпадает с
serviceMonitorSelectorPrometheus. - ServiceMonitor не находит порт: у порта Service нет имени или оно другое.
PrometheusRuleне загружен: нет нужного label, либо ошибка в выражении.- Приложение действительно не отдаёт
/metricsили Service не выбирает поды.
Проверки
# 1. Что именно выбирает Prometheus по label
kubectl get prometheus -n monitoring kps-prometheus -o jsonpath='{.spec.serviceMonitorSelector}{"\n"}{.spec.ruleSelector}{"\n"}'
# 2. Какие label у наших объектов
kubectl get servicemonitor,prometheusrule -n notes --show-labels
# 3. Есть ли у Service именованный порт и endpoints
kubectl get svc notes -n notes -o jsonpath='{.spec.ports}{"\n"}'
kubectl get endpoints notes -n notes
# 4. Что говорит сам Prometheus (в /targets и /config), логи оператора
kubectl logs -n monitoring deploy/kps-operator --tail=30
Исправление
Разбор сценариев
Сценарий 1: ServiceMonitor не находится. Селектор Prometheus {"matchLabels":{"release":"kps"}}, а у объекта label release нет или в нём другое значение. Исправление: вернуть release: kps (значение metrics.serviceMonitor.release) и сделать helm upgrade. Альтернатива на стороне стека: serviceMonitorSelectorNilUsesHelmValues: false, тогда Prometheus выбирает все ServiceMonitor, но это ослабляет изоляцию.
Сценарий 2: порт без имени. endpoints[].port: http, а в Service у порта нет name (или он назван web). Объект принимается, target не появляется. Исправление: дать порту в Service имя http и выкатить.
Сценарий 3: PrometheusRule не загружен. Нет label release: kps (селектор ruleSelector) либо объект отклонён валидацией. Исправление: вернуть label, проверить PromQL в Graph, затем kubectl get prometheusrule -n notes и вкладка Alerts.
Общий алгоритм: цепочка снизу вверх, target в /targets -> label на объекте -> имя порта -> endpoints у Service -> /metrics через kubectl port-forward.
ИИ в помощь
Общие правила работы с ИИ-помощником собраны на странице «ИИ-помощник», здесь только сценарии этой темы.
Задача: найти, почему ServiceMonitor не создаёт цель.
ServiceMonitor notes создан, но в Prometheus Targets цели нет. У ServiceMonitor ярлык release=monitoring, стек поставлен как release kps, у Service порт назван web, а в endpoints указан port: http. Назови все причины по порядку проверки.
Проверь ответ: должны быть названы ярлык release (нужен kps) и имя порта (http не совпадает с web). Типичная ошибка: советовать перезапустить Prometheus или проверить номер порта вместо имени.
Задача: посчитать кардинальность.
У метрики 4 пути, 5 кодов ответа и 8 подов. Сколько серий? Что будет, если добавить ярлык request_id с миллионом значений? Что стоит делать вместо этого?
Проверь ответ: 4 × 5 × 8 = 160 серий, с request_id получатся миллионы, и идентификаторы в ярлык не кладут. Проверь арифметику сам.
Задача: оценить задержку алерта.
Сбор раз в 30 секунд, правило проверяется раз в 30 секунд, for: 2m. Через сколько после поломки человек получит сообщение в худшем случае? Покажи слагаемые.
Проверь ответ: около 30 + 30 + 120 секунд плюс доставка. Типичная ошибка: назвать только for и забыть остальные слагаемые.
Словарик урока
| Термин | Простыми словами |
|---|---|
| kube-prometheus-stack | Один Helm-чарт, который ставит Prometheus, Alertmanager, Grafana и датчики кластера |
| Prometheus Operator | Программа, которая сама пишет конфиг Prometheus по объектам ServiceMonitor и PrometheusRule |
| CRD (Custom Resource Definition) | Регистрация нового типа объектов в Kubernetes |
| ServiceMonitor | Объект «собирай метрики с подов за этим Service» |
| PodMonitor | То же, но выбирает поды напрямую, без Service |
| PrometheusRule | Объект с правилами алертов и recording rules |
| recording rule | Правило, которое заранее считает выражение и хранит результат как новую метрику |
| node-exporter | Датчик метрик сервера (узла), по одному на узел |
| kube-state-metrics | Публикует состояние объектов Kubernetes как метрики |
| cAdvisor | Часть kubelet, считает потребление ресурсов контейнерами |
| metrics-server | Хранит только последние значения для kubectl top и HPA |
| label (ярлык) | Пара ключ: значение на объекте, по ней объекты выбирают друг друга |
| retention | Срок хранения данных в Prometheus |
| оператор (operator) | Программа в кластере, которая по описаниям-объектам сама настраивает другую программу (здесь Prometheus) |
| манифест | YAML-файл с описанием объекта Kubernetes |
| service discovery | Автоматический поиск целей: Prometheus спрашивает у API-сервера, какие поды сейчас есть |
| API-сервер | Центральный справочник Kubernetes: знает все объекты, к нему обращается kubectl |
| релиз (Helm release) | Имя конкретной установки чарта, например kps |
| серия (time series) | Метрика с конкретным набором ярлыков, один «график» в базе |
| scrape interval | Пауза между сборами метрик с одной цели (в стеке 30 секунд) |
| кардинальность | Общее число серий; растёт перемножением значений ярлыков |
| PVC | Запрос на диск, чтобы данные пережили перезапуск пода |
Вопросы с собеседований
Раздел для повторения: ответь вслух, потом открой ответ. Вопросы с пометкой «часто» задают почти на каждом собеседовании по теме урока: начни с них.
1. [junior] [часто] Ты создал ServiceMonitor, а цели в Prometheus нет. Твои действия?
Ответ
Иду по цепочке. Смотрю label на ServiceMonitor и сравниваю с serviceMonitorSelector у Prometheus (обычно release: <релиз стека>). Проверяю, что в Service у порта есть имя, на которое ссылается endpoint, что у Service есть endpoints и что namespaceSelector указывает на нужный namespace. Логи оператора подтвердят догадку.
Что хотят услышать: label release, именованный порт, selector против labels Service, namespaceSelector, логи оператора, /targets и /config.
Красный флаг: «пересоздам Prometheus» или «перезапущу под».
2. [junior] [часто] Чем kube-state-metrics отличается от node-exporter и cAdvisor?
Ответ
kube-state-metrics публикует состояние объектов Kubernetes (реплики, фазы подов, Jobs). node-exporter отдаёт метрики железа и ОС узла. cAdvisor, встроенный в kubelet, даёт потребление ресурсов контейнерами.
Что хотят услышать: по одному примеру метрики на компонент, что «что должно быть» и «сколько потребляется» разные вопросы.
Красный флаг: путает kube-state-metrics с metrics-server.
3. [junior] Тебя просят «поставить мониторинг в кластер». С чего начнёшь?
Ответ
Поставлю kube-prometheus-stack Helm-чартом с закреплённой версией и своим values: ресурсы, retention, PVC. Он даёт Prometheus, Alertmanager, Grafana, node-exporter, kube-state-metrics и базовые правила. Потом подключу приложения через ServiceMonitor.
Что хотят услышать: Prometheus Operator, закреплённая версия чарта, отдельный namespace, PVC и retention, дефолтные дашборды и правила, затем свои сервисы.
Красный флаг: «установлю Prometheus и напишу scrape_configs с IP подов».
4. [middle] Чем ServiceMonitor отличается от PodMonitor и когда нужен каждый?
Ответ
ServiceMonitor работает через Service и его endpoints, подходит для обычных приложений. PodMonitor выбирает поды напрямую, нужен, когда Service нет (Job, DaemonSet, сайдкары) или нужно собирать метрики с портов, которых нет в Service.
Что хотят услышать: источник обнаружения (Service endpoints против pods), примеры без Service, podMetricsEndpoints.
Красный флаг: считает, что это одно и то же с разными названиями.
5. [junior] [на скорость] Prometheus в кластере перезапустился, и графики за неделю пропали. Что не так?
Ответ
Скорее всего хранилище без PVC: данные лежали в emptyDir и потерялись с подом. Нужно задать storageSpec с PersistentVolumeClaim и разумный retention.
Что хотят услышать: PVC в prometheusSpec.storageSpec, retention и размер, долгосрочное хранение (Thanos, Mimir, remote write) как следующий шаг.
Красный флаг: «Prometheus всегда хранит данные сам, значит сломался диск».
6. [middle] Prometheus в кластере падает по OOMKilled. Что проверишь?
Ответ
Число серий (prometheus_tsdb_head_series), кардинальность лейблов: кто добавил user_id или сырой URL. Топ метрик по числу серий, интервал scrape, retention. Затем лимиты памяти и, если серии обоснованы, шардирование или вынос части в другой Prometheus.
Что хотят услышать: кардинальность, prometheus_tsdb_head_series, topk по __name__, metric_relabel_configs и sample_limit, а не просто «увеличу лимит».
Красный флаг: «подниму limits в три раза и забуду».
7. [middle] Поды нового релиза в статусе Running, но алерт KubeDeploymentReplicasMismatch горит. Как разбираться?
Ответ
Алерт строится на kube-state-metrics: желаемых реплик больше, чем доступных. Running не значит Ready. Смотрю kubectl get deploy, describe, состояние readiness-проб, события. Часто readiness падает из-за зависимости (БД) или нехватки ресурсов, и часть подов Pending.
Что хотят услышать: разница Running и Ready, kube_deployment_status_replicas_available, readiness-проба, events, requests и Pending.
Красный флаг: «алерт ложный, заглушу».
8. [middle] Алерт на CPU пода срабатывает постоянно, хотя жалоб нет. Что сделаешь?
Ответ
Пойму, что измеряет алерт. Алерт на «CPU выше 80% от limit» бывает шумным из-за троттлинга без реальной боли. Заменю его или дополню симптомным алертом (латентность, доля 5xx), а причинный оставлю как информационный. Проверю container_cpu_cfs_throttled_periods_total, подправлю requests и limits.
Что хотят услышать: симптом против причины, троттлинг, привязка к SLO, отказ от алертов, на которые нечего делать.
Красный флаг: «подниму порог до 99%» без разбирательства.
9. [middle] Нужно, чтобы команды сами подключали мониторинг и алерты, не трогая центральный конфиг. Как устроишь?
Ответ
Каждая команда кладёт ServiceMonitor и PrometheusRule в чарт своего сервиса. Prometheus выбирает их по label (или по всем namespace через NilUsesHelmValues: false). Общие правила инфраструктуры остаются в стеке, а маршрутизацию алертов по командам делают через AlertmanagerConfig или label team.
Что хотят услышать: мониторинг как код рядом с приложением, селекторы, границы ответственности, label team и маршрутизация.
Красный флаг: все правила в одном файле у SRE, команды просят его править в тикетах.
10. [junior] Метрики в Grafana есть, но алерт по ним ни разу не сработал, хотя ошибки были. Куда смотришь?
Ответ
Проверяю, загружено ли правило (вкладка Rules, /alerts), верно ли выражение на реальных данных, длится ли условие дольше for, попал ли алерт в Alertmanager и ушёл ли по маршруту. Цепочка: правило -> Prometheus -> Alertmanager -> получатель.
Что хотят услышать: for, отсутствие данных (absent), загрузка PrometheusRule, маршрутизация, silence и inhibit.
Красный флаг: «алерты, наверное, не работают вообще».
11. [middle] Helm-релиз стека нужно обновить с 91.8.2 на новую версию. Что сделаешь?
Ответ
Прочитаю release notes чарта, особенно про CRD: Helm не обновляет CRD из каталога crds/ при upgrade, их применяют отдельно (kubectl apply --server-side). Сначала пробую на тестовом кластере, сравниваю helm diff или helm template, затем обновляю и проверяю targets и правила.
Что хотят услышать: CRD и Helm, закреплённая версия, тест на стенде, откат helm rollback, проверка после обновления.
Красный флаг: helm upgrade на проде без чтения changelog.
12. [middle] [на скорость] В кластере есть Prometheus, но нужен единый обзор трёх кластеров. Варианты?
Ответ
Federation (простой, но грубый), remote write в центральное хранилище (Mimir, Thanos Receive, VictoriaMetrics) или Thanos Sidecar с общим запросом. Выбор зависит от объёма и хранения. Обязательно добавляю лейбл cluster через externalLabels.
Что хотят услышать: externalLabels, remote write против federation, долгосрочное хранение, компромисс стоимости.
Красный флаг: «пусть открывают три Grafana».
13. [junior] Чем metrics-server отличается от Prometheus?
Ответ
Metrics-server собирает текущее потребление CPU и памяти с узлов и подов и отдаёт через API Kubernetes. Из него работают kubectl top и HorizontalPodAutoscaler. Он хранит только последние значения в памяти, истории и алертов нет. Prometheus хранит временные ряды, по ним строят графики, правила и оповещения. Поэтому для автоскейлинга по CPU мне нужен metrics-server, а для мониторинга и разбора инцидентов нужен Prometheus.
Что хотят услышать: metrics-server даёт текущие значения для top и HPA, истории нет, Prometheus хранит историю и алерты.
Красный флаг: «metrics-server заменяет Prometheus».
14. [middle] Сервис тормозит, хотя CPU пода не упирается в лимит на графике. Что такое CPU throttling и как его увидеть?
Ответ
Лимит CPU в Kubernetes реализован квотами: в каждом коротком периоде контейнер получает ограниченное время процессора и, исчерпав его, ждёт до следующего периода. Средний график CPU может быть ниже лимита, а задержки уже растут. В Prometheus это видно по метрикам cAdvisor container_cpu_cfs_throttled_periods_total и container_cpu_cfs_periods_total: доля их rate показывает, как часто контейнер упирался в квоту. Если доля высокая, я увеличиваю лимит или пересматриваю его (и requests) по фактической нагрузке.
Что хотят услышать: лимит как квота времени, средний CPU скрывает троттлинг, метрики cfs_throttled, решение через лимиты.
Красный флаг: «CPU ниже лимита, значит проблема не в CPU».
15. [middle] Какие базовые алерты ты заведёшь для кластера Kubernetes?
Ответ
Я начинаю с отказов, которые бьют по пользователям: под перезапускается (kube_pod_container_status_restarts_total растёт), под долго в Pending или CrashLoopBackOff, узел NotReady (kube_node_status_condition), у Deployment не хватает доступных реплик. Добавляю ресурсы: заканчивается место на PersistentVolume (kubelet_volume_stats_available_bytes), узел близок к пределу памяти или диска. Плюс алерт на сам мониторинг: Prometheus недоступен или цели пропали. Обычно я беру готовые правила из kube-prometheus-stack и отключаю те, что шумят без пользы.
Что хотят услышать: перезапуски, Pending, NotReady, недостаток реплик, место на томах, алерт на сам мониторинг, готовые правила.
Красный флаг: алертить только на CPU и память узлов.
Проверено на версиях
- Не прогонялось: кластер и стек не запускались, выводы
kubectlиhelmпоказаны по документации; шаблоны чарта проверены чтением (в проекте нет готового каталогаhelm/notes,helm lintне запускался) - kube-prometheus-stack: chart 91.8.2
- Helm: v4 (см. урок 5.9)
- Kubernetes: kind, кластер
notes, версия узла не закреплена, проверь актуальную версию на странице проекта - Приложение «Заметки»: 0.7.0 (app v7), chart
helm/notes0.3.0 break.sh:shellcheckбез замечаний, не запускался
Итог урока: ты умеешь
- умею поставить kube-prometheus-stack с закреплённой версией и своим values
- умею объяснить, что делают Prometheus Operator, kube-state-metrics и node-exporter
- умею подключить приложение через ServiceMonitor и проверить цель в
/targets - умею описать алерт и recording rule объектом PrometheusRule в чарте
- умею находить причину, по которой ServiceMonitor не находится (label, имя порта, namespace)
- умею открывать Prometheus и Grafana кластера через
kubectl port-forward - умею включать метрики в чарте флагом и не ломать установку без стека
Дальше: Урок 8.10: Service mesh: Istio ambient
Проверь себя
Короткий тест по уроку: 5 вопросов из банка в 30. Засчитывается только полностью правильный ответ, порог 60%. Каждая новая попытка даёт другие вопросы, пока банк не закончится. Ответы видны после проверки.
Тест работает с включённым JavaScript.