Observability: dashboards, alerts, and a way to scrape at all - #12
Merged
Conversation
Closes #7. The twelve gpucell_* series existed and were documented; nothing could consume them. There was no Service in front of the metrics port and no container port declared, so the endpoint was unreachable — the pack starts by fixing that. Then, behind monitoring.enabled (off by default, because it needs the Prometheus Operator CRDs and a default install should not): a ServiceMonitor, a four-row Grafana dashboard delivered as a sidecar-discovered ConfigMap, and eight warning-biased alerts. All three are individually disableable, and the value plumbing is nil-safe so an explicit false still wins under helm upgrade --reuse-values. The alerts keep the two layers apart, which is the whole reason these metrics are shaped the way they are: GPUCellPoolNoFreePhysicalGPU means the infrastructure cluster has no whole device left AND this pool wants one, while GPUCellPoolSharedGPUExhausted means the cells are healthy and their GPU is entirely handed out. Different problems, different remedies; an alert that merged them would be useless. The physical one is deliberately conjoined with 'the pool actually wants a cell', because a saturated cluster whose pools are all satisfied is not a fault and alerting on it trains people to ignore the rule. Verified rather than eyeballed: the chart renders in all three modes (off, on, explicitly-partial), and every metric name, label selector and by() grouping across all 25 alert and panel expressions was cross-checked against what internal/metrics/metrics.go actually declares — the design doc had already drifted to {pool} where the code emits {pool, namespace}, so this is a real trap. Dashboards live in config/grafana/ with the chart copy synced by make manifests, because Helm can only package files inside the chart directory; drift fails verify. Also corrects a security claim: values.yaml said the metrics endpoint served 'HTTPS with authn/authz'. It does not — no FilterProvider is set, so it is HTTPS with a self-signed certificate and no authorization at all. Enabling the filter pulls k8s.io/apiserver into a dependency tree this project keeps deliberately small, so the claim is corrected here and the trade filed as #11 rather than made silently inside a dashboards change. Signed-off-by: William Rizzo <william.rizzo@gmail.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #7.
The twelve
gpucell_*series existed and were documented — and nothing could consume them. There was noServicein front of the metrics port and no container port declared, so the endpoint was unreachable. The pack starts by fixing that.Then, behind
monitoring.enabled(off by default, because it needs the Prometheus Operator CRDs and a default install should not depend on them):All three are individually disableable, and the value plumbing is nil-safe so an explicit
enabled: falsestill wins underhelm upgrade --reuse-values(where the subtree is not merged and sprig'sdefaultwould swallow the false).The alerts keep the two layers apart
This is the whole reason the metrics are shaped the way they are:
GPUCellPoolNoFreePhysicalGPUGPUCellPoolSharedGPUExhaustedDifferent problems, different remedies; an alert that merged them would be useless. The physical one is deliberately conjoined with "the pool actually wants a cell" — a saturated cluster whose pools are all satisfied is not a fault, and alerting on it trains people to ignore the rule.
GPUCellPoolCapacityStaleearns its place for a subtler reason: a failed capacity read retains the previous values rather than reporting zero, because "0 GPUs, plenty free" from a failed scrape would be the worst possible lie. When it fires, every capacity number on the dashboard is untrustworthy.Verified, not eyeballed
monitoring.coreos.comobjects), on, and explicitly-partial.by()grouping across all 25 alert and panel expressions was cross-checked against whatinternal/metrics/metrics.goactually declares. This is a real trap, not a theoretical one: the design doc had already drifted to{pool}where the code emits{pool, namespace}.Dashboards live in
config/grafana/with the chart copy synced bymake manifests(Helm can only package files inside the chart directory); drift between them failsmake verify.One security claim corrected
values.yamlsaid the metrics endpoint serves "HTTPS with authn/authz". It does not — noFilterProvideris set, so it is HTTPS with a self-signed certificate and no authorization at all. The claim is corrected and the real posture documented (plus how to restrict it).I did not just enable the filter:
controller-runtime/pkg/metrics/filtersimportsk8s.io/apiserver, which pulls a large dependency tree into an operator whose first design principle is avoiding exactly that. That is a trade to make deliberately, so it is filed as #11 rather than slipped into a dashboards PR.make test,make lintandmake verifypass.🤖 Generated with Claude Code