prole/docs/plans/junie/13-podmonitor-manual-management.md
chrisfu f83ec8169c docs(plans): file Junie briefs for queue items #2, #6, #7, #13, #15
Five self-contained work briefs in a new docs/plans/junie/ subdirectory,
each tagged against a numbered item in docs/TODO.md so Junie can take
them independently in any order.

  02-k3s-prole-rename.md            -> queue #2  (drift R5)
  06-patch-garage-script-fixes.md   -> queue #6  (drift R9)
  07-init-cnpg-gke-sa-wiring.md     -> queue #7  (drift R8)
  13-podmonitor-manual-management.md-> queue #13
  15-remove-dead-dashboard-consumer.md-> queue #15

Each brief follows the same shape: Why -> What changes (concrete file
paths + line numbers + before/after) -> Verification -> Out of scope ->
Commit shape -> Definition of done. The intent is that Junie reads cold
(no shared chat history) and lands the change without escalating
questions.

Also adds:
  - docs/plans/junie/README.md describing the convention.
  - Row in docs/plans/README.md so newcomers find the subdirectory.
  - Brief reference + "Assigned to Junie" tag on each of the five queue
    items in docs/TODO.md.

Existing in-progress assignment to Junie (Phase 2 pg_oauth) is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 16:13:20 -07:00

278 lines
11 KiB
Markdown

# Junie brief — Queue #13: migrate off CNPG-deprecated `enablePodMonitor` + `podMonitorRelabelings`
> **Self-contained brief.** Manage the PodMonitor as a sibling resource
> instead of through CNPG operator fields. Closes a deprecation warning
> we get on every `kubectl patch` of the cluster spec.
---
## 1. Why
CNPG operator (running v1.29.0 on `knoe-dev-cnpg-0`) emits these warnings
on every patch of `Cluster.spec.monitoring`:
```
Warning: spec.monitoring.enablePodMonitor is deprecated and will be removed
in a future release. Please migrate to manually managing your PodMonitor
resources. Set this field to false and create a PodMonitor resource for
your cluster as described in the documentation.
Warning: spec.monitoring.podMonitorRelabelings is deprecated and will be
removed in a future release. Please migrate to manually managing your
PodMonitor resources with custom relabeling configurations.
```
Migration is straightforward: write the PodMonitor as a sibling YAML doc,
apply it alongside the Cluster, set `enablePodMonitor: false` (or remove
the whole `monitoring` block once that's also been deprecated). No
functional change — just lifts the relabel config out of the CNPG-managed
field and into a standard prometheus-operator resource.
The cnpg-grafana dashboard relies on the `cluster` label; **breaking the
relabel breaks the dashboard**, so verify carefully.
## 2. Live state to match
```bash
DB=gke_plenary-truck-485623-p7_us-west3_knoe-dev-cnpg-0
kubectl --context=$DB -n knoe-db-0 get podmonitor knoe-db -o yaml
```
The CNPG operator currently auto-creates this PodMonitor. Relevant fields:
```yaml
spec:
namespaceSelector: {}
podMetricsEndpoints:
- port: metrics
relabelings:
- action: replace
sourceLabels: [__meta_kubernetes_pod_label_cnpg_io_cluster]
targetLabel: cluster
selector:
matchLabels:
cnpg.io/cluster: knoe-db
cnpg.io/podRole: instance
```
You'll write this manifest into the repo, lose the CNPG-managed metadata
(labels/annotations/ownerReferences) since you own it now, and apply it
yourself.
## 3. What changes
### 3.1 — New file: `deploy/gcp/gke/knoe-db-podmonitor.yaml`
```yaml
---
# Manually-managed PodMonitor for the CNPG `knoe-db` cluster.
#
# Migrated 2026-05-XX from the (now-deprecated) CNPG fields
# `Cluster.spec.monitoring.enablePodMonitor` +
# `Cluster.spec.monitoring.podMonitorRelabelings`. Same scrape spec, same
# relabel config — now living as a first-class resource we control.
#
# The selector matches every CNPG instance pod via the operator-applied
# labels `cnpg.io/cluster=knoe-db` + `cnpg.io/podRole=instance`. The
# `metrics` port is the postgres-exporter (sidecar) on the CNPG pod.
#
# The relabel injects a `cluster` label on every scraped sample, sourced
# from the `cnpg.io/cluster` pod label. The cnpg-grafana dashboards filter
# every panel by `cluster=$cluster`; without this relabel only the
# operator-collector metrics carry the cluster label and 83 of 85 panels
# render empty.
#
# Apply alongside `knoe-db.yaml` from etc/init_cnpg_gke.sh.
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
name: knoe-db
namespace: knoe-db-0
labels:
# Match what kube-prometheus-stack discovers via Helm-managed Prometheus
# serviceMonitorSelector / podMonitorSelector. kps's defaults match
# release=kps; verify in the live cluster:
# kubectl -n monitoring get prometheus -o yaml | grep -A2 podMonitorSelector
# If the selector requires a specific label, set it here.
release: kps
spec:
namespaceSelector: {}
selector:
matchLabels:
cnpg.io/cluster: knoe-db
cnpg.io/podRole: instance
podMetricsEndpoints:
- port: metrics
relabelings:
- action: replace
sourceLabels:
- __meta_kubernetes_pod_label_cnpg_io_cluster
targetLabel: cluster
```
**Verify the `release: kps` label trick.** Inspect the live Prometheus's
`podMonitorSelector` and ensure your manifest matches it. The current
auto-created PodMonitor doesn't carry `release: kps` (it uses
`app.kubernetes.io/managed-by: cloudnative-pg`), but it still gets
discovered — meaning the kps Prometheus must be configured with
`podMonitorSelector: {}` (any) or `podMonitorSelectorNilUsesHelmValues:
false` (which is what `monitoring/kps-cnpg-values.yaml` sets). Confirm
this and document in the manifest comment whether the `release: kps`
label is actually required.
### 3.2 — `deploy/gcp/gke/knoe-db.yaml`
Replace the entire `monitoring:` block:
```yaml
monitoring:
# NOTE: enablePodMonitor + podMonitorRelabelings are CNPG-deprecated and
# will be removed in a future release. Migration path: manually manage
# the PodMonitor resource. Tracked in docs/TODO.md as a follow-up.
enablePodMonitor: true
podMonitorRelabelings:
- sourceLabels: ["__meta_kubernetes_pod_label_cnpg_io_cluster"]
targetLabel: cluster
action: replace
```
with:
```yaml
monitoring:
# PodMonitor is managed manually as a sibling resource:
# deploy/gcp/gke/knoe-db-podmonitor.yaml. The CNPG-managed alternative
# (enablePodMonitor + podMonitorRelabelings) is deprecated.
enablePodMonitor: false
```
### 3.3 — `etc/init_cnpg_gke.sh`
Find the section that applies `knoe-db.yaml` (search for
`render_cnpg_manifest "$storage_class" "$tmp_manifest"`). After the
cluster apply succeeds (and after the cluster reaches a working storage
class), apply the PodMonitor:
```bash
# ── 11. Apply the manually-managed PodMonitor ────────────────────────────────
log "Applying PodMonitor for $CNPG_CLUSTER_NAME ..."
kubectl apply -f "$GKE_MANIFEST_DIR/knoe-db-podmonitor.yaml"
```
Place this AFTER the cluster apply completes (since the PodMonitor doesn't
depend on the cluster being healthy — it just needs the namespace) but
BEFORE any verification waits.
Also: when CNPG-managed PodMonitor was set, the operator auto-deleted it
when `enablePodMonitor` flipped to false. Verify what happens during the
transition on a live cluster: when you change `enablePodMonitor: true →
false` AND apply your manual PodMonitor with the same name in the same
namespace, does CNPG try to delete your manual one? Based on
`ownerReferences` on the auto-created PodMonitor, the answer should be
"the auto-created one gets deleted by garbage collection when the
controller flag flips, and your manually-applied one (no ownerReference)
survives". Test this on a non-prod cluster before claiming victory in the
commit message.
### 3.4 — Cross-mode parity (k3s + k8s/min)
Both `deploy/opentofu/k3s/manifests/knoe/knoe-db.yaml` and
`k8s/knoe/knoe-db.yaml` have `monitoring.enablePodMonitor: false` already
— they don't run the dashboard, so no PodMonitor at all today. **Leave
those modes alone for now.** They'll need their own PodMonitor manifest if
we ever wire monitoring there, but that's separate work. Add a comment to
each pointing at the GKE manifest as the reference if/when needed.
## 4. Verification
End-to-end — must pass before merging:
1. **Apply the new manifest doesn't 404:**
```bash
kubectl --dry-run=server -f deploy/gcp/gke/knoe-db-podmonitor.yaml apply
```
2. **Cluster spec change doesn't break operator:**
```bash
yq '.spec.monitoring' deploy/gcp/gke/knoe-db.yaml
# Expected: only enablePodMonitor: false (no podMonitorRelabelings)
```
3. **Live transition test (the scary one).** On the live cluster, do the
transition AND verify dashboard still works:
```bash
DB=gke_plenary-truck-485623-p7_us-west3_knoe-dev-cnpg-0
APP=gke_plenary-truck-485623-p7_us-west3_knoe-dev-0
# 1. Apply the new manual PodMonitor
kubectl --context=$DB apply -f deploy/gcp/gke/knoe-db-podmonitor.yaml
# 2. Verify it's there
kubectl --context=$DB -n knoe-db-0 get podmonitor
# 3. Apply the cluster spec change (flips enablePodMonitor false)
kubectl --context=$DB apply -f deploy/gcp/gke/knoe-db.yaml
# (or however the script renders + applies it)
# 4. Confirm only ONE PodMonitor remains (the manual one) — the
# CNPG-owned one should garbage-collect once the field flips
kubectl --context=$DB -n knoe-db-0 get podmonitor
# 5. Wait 30s and confirm DB-cluster Prometheus still has scrape targets:
kubectl --context=$APP -n monitoring exec sts/kps-grafana -c grafana -- \
wget -qO- "http://admin:$(kubectl --context=$APP -n monitoring get secret kps-grafana -o jsonpath='{.data.admin-password}' | base64 -d)@localhost:3000/api/datasources/proxy/uid/P5531627C358300FE/api/v1/targets?state=active" \
| python3 -c 'import sys,json; t=json.load(sys.stdin)["data"]["activeTargets"]; print(f"{len(t)} active targets, postgres exporters: {sum(1 for x in t if \"9187\" in x[\"scrapeUrl\"])}")'
# Expected: ≥3 postgres exporters active (our 3 CNPG pods)
# 6. Refresh the cnpg-grafana dashboard, confirm panels still populate
```
4. **Rollback path documented.** If step 3.5 shows dropped targets, the
rollback is to flip `enablePodMonitor: true` and delete the manual
PodMonitor. Note this in the commit message.
## 5. Out of scope
- Any work on `monitoring/kps-cnpg-values.yaml` itself.
`podMonitorSelectorNilUsesHelmValues: false` stays as is.
- Cross-mode PodMonitor (k3s/k8s/min) — defer.
- The `cluster=knoe-db` label vs. dynamic templating — currently hardcoded
to `knoe-db`. If a second CNPG cluster ever lands, the manual PodMonitor
needs to be parameterized. Leave a TODO comment but don't solve.
- Don't touch the cnpg-grafana dashboard transforms
(`monitoring/cnpg-dashboard-transforms.yaml`) — those are unrelated.
## 6. Commit shape
```
feat(monitoring): manage knoe-db PodMonitor manually (drop CNPG-deprecated fields)
CNPG operator emits deprecation warnings on every cluster.spec.monitoring
patch:
- enablePodMonitor will be removed in a future release
- podMonitorRelabelings will be removed in a future release
Migrate to a manually-managed PodMonitor:
+ deploy/gcp/gke/knoe-db-podmonitor.yaml (NEW — same spec as auto-created)
~ deploy/gcp/gke/knoe-db.yaml (enablePodMonitor: true → false,
podMonitorRelabelings dropped)
~ etc/init_cnpg_gke.sh (apply PodMonitor after cluster)
Verified live: cnpg-grafana dashboard panels render unchanged after the
flip; 3 postgres-exporter targets active in cnpg-prometheus.
Closes queue item #13 in docs/TODO.md.
```
## 7. Definition of done
- [ ] `deploy/gcp/gke/knoe-db-podmonitor.yaml` created, content verified
against live PodMonitor spec.
- [ ] `deploy/gcp/gke/knoe-db.yaml` no longer contains
`podMonitorRelabelings`; `enablePodMonitor: false` with comment.
- [ ] `etc/init_cnpg_gke.sh` applies the new manifest after cluster apply.
- [ ] Live cnpg-grafana dashboard verified after the transition: panels
populate, 3 postgres-exporter targets in Prometheus.
- [ ] No new deprecation warnings on `kubectl apply` of the cluster manifest.
- [ ] `docs/TODO.md` queue item #13 archived to **Done** with verification
notes.