Argo CD показывал Synced, а API-сервер молча выкинул мой фикс
Месяцы без метрик, фикс уже в git и зелёный Synced. Поля не было в схеме CRD, API-сервер его отрезал, и никто не пожаловался.
У пет-проекта в моей домашней лаборатории месяцами не было ни одной метрики приложения. Postgres там на CloudNativePG, оператор создал PodMonitor, а в GitOps-репе уже лежал фикс для пропавшего скрейпа. Argo CD показывал Synced / Healthy ровно на том коммите, где был фикс. Тем временем отказ реплики остался незамеченным 23 часа — за этим просто никто не следил.
Сначала измерить слепое пятно
Прежде чем что-то трогать, я посчитал. У Prometheus было 46 активных таргетов и ни одного в этом namespace. Не «меньше, чем ожидалось», а ноль. Эта цифра превратила смутное ощущение в конкретный вопрос: почему PodMonitor, который явно существует, никто не скрейпит?
Два дефекта, каждый по отдельности выглядит безобидно
Несовпадение меток. kube-prometheus-stack с дефолтами чарта выбирает только мониторы со своей меткой release. У PodMonitor, который создаёт оператор CNPG, есть app.kubernetes.io/* и cnpg.io/cluster — и больше ничего. Объект существует, при осмотре выглядит как покрытие, а ни один Prometheus его не читает. Тот же дефолт молча отбросил и ServiceMonitor от cert-manager.
Поле, которого нет. Фикс в git пытался решить это со стороны CNPG — добавить метки через spec.monitoring.podMonitorMetadata.labels. Этого поля нет в схеме CRD моей версии CNPG. При структурных схемах API-сервер при apply отрезает неизвестные поля. Ни ошибки, ни предупреждения в Argo CD. Argo сравнил обрезанный живой объект с обрезанным желаемым состоянием, нашёл их равными и сказал Synced.
Ключевой шаг — спросить саму CRD, есть ли такое поле:
kubectl get crd clusters.postgresql.cnpg.io -o json \
| jq -r '.spec.versions[0].schema.openAPIV3Schema.properties.spec.properties.monitoring.properties | keys[]'
podMonitorMetadata в списке не было. Дело закрыто.
Чинить потребителя, а не каждого производителя
Вместо того чтобы гоняться за метками на каждом мониторе от операторов, я поменял Prometheus: выставил в чарте опции *SelectorNilUsesHelmValues в false, селекторы стали пустыми, и Prometheus подхватывает все ServiceMonitor и PodMonitor в кластере. Одна правка починила CNPG, cert-manager и каждый монитор, который создаст будущий чарт. Сразу после синка появился cnpg_collector_up для каждого пода Postgres.
ruleSelector я сознательно оставил строгим. Лишняя собранная метрика безвредна, лишний сработавший алерт — шум. Цена: каждый PrometheusRule обязан нести метку release, иначе он не загрузится никуда. Та же ловушка, слоем выше, поэтому я проверяю это при каждом добавлении правил.
Откат тривиален: убрать две строки, и Prometheus вернётся к строгому селектору без потери данных.
Альтернативой был spec.inheritedMetadata.labels у CNPG, но он вешает метки на все объекты кластера, включая поды Postgres, а смена меток подов — это rolling restart базы. Правка на стороне потребителя этого избежала.
Выводы
- Зелёный
Syncedзначит «то, что API-сервер сохранил, совпадает с git — после того, как сохранил». О полях, отрезанных по дороге, он ничего не говорит. - Если GitOps-изменение ни на что не повлияло, сначала спроси схему CRD, существует ли поле, и только потом отлаживай контроллер.
- Существующий объект — ещё не покрытие. Считай активные таргеты, а не разглядывай мониторы.
- Лучше один раз поправить селектор у потребителя, чем вечно перевешивать метки у каждого производителя.