Repository navigation
feat(monitoring): scrape KubeVirt VMI metrics from virt-handler - #4219
Andrei Kvapil (kvaps) wants to merge 1 commit into
Conversation
KubeVirt exports the resource usage of every VirtualMachineInstance (kubevirt_vmi_*) from virt-handler, not from the VMI's own pod, so no per-application scrape object collects it. virt-operator would create a ServiceMonitor for it, but only when the monitorAccount ServiceAccount exists in the monitorNamespace, and Cozystack never creates that account, so no Monitoring application received VM metrics at all. Every Monitoring application now scrapes virt-handler through a VMPodScrape. A tenant Monitoring keeps only the series of its own namespace and of the sub-tenants below it, which is what the namespace.cozystack.io/monitoring label already promises for a sub-tenant without a Monitoring of its own; the root Monitoring keeps everything. The tenant chart already opens the network path for this (the per-tenant egress-virt-handler policy). Assisted-by: LLM Signed-off-by: Andrei Kvapil <[email protected]>
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Timofei Larkin (lllamnyp)
left a comment
There was a problem hiding this comment.
Thanks for chasing this down, Andrei Kvapil (@kvaps). The diagnosis is right: no Monitoring receives kubevirt_vmi_* today. The tenant-subtree semantics are carefully worked out too. Since this is still a draft, I'd like to raise a placement concern before more goes into it.
Core monitoring shouldn't know about pluggable components
system/monitoring is the chart every Monitoring application renders. With this PR it hard-codes KubeVirt's namespace, pod label, port and TLS quirks. The test I'd apply: if the FoundationDB app, or an app in an external repository, wanted its metrics collected, would its author have to patch system/monitoring? Today the answer is no. Each package ships its own scrape objects and rules, and the platform picks them up by convention:
- apps:
kafka(templates/podscrape.yaml),redisandvalkey(templates/servicescrape.yaml),clickhouse(keeper scrape intemplates/chkeeper.yaml),postgres(CNPGenablePodMonitor: true, converted by vm-operator); - tenant modules:
extra/etcd(scrape plus rules),extra/ingress(nginx-scrape.yaml); - system components:
etcd-operator,linstor,kubeovn-plunger,opencost, and rules inkafka-operator,postgres-operator,keda,seaweedfs,piraeus-operator; system/monitoringitself scrapes only its own VictoriaMetrics components (vmcluster-scrape.yaml,vmalert-scrape.yaml).
The selection side is generic. A scrape object in a tenant namespace is picked up by that tenant's vmagent through the namespace.cozystack.io/monitoring namespace label, and the cluster-wide vmagent (selectAllByDefault: true) picks up everything. Neither needs to know what it is scraping. KubeVirt should stay on that contract rather than become the first component whose scrape lives inside the monitoring chart.
KubeVirt used to ship its own scrape
system/kubevirt-operator shipped a ServiceMonitor on KubeVirt's kubevirt-prometheus-metrics Service from #573 onwards. The v1.8.2 refresh (6af0bf7) dropped it, because the update recipe at the time ran rm -rf templates. The alert rule came back a few days later as templates/prometheusrule.yaml (7106f07), but the scrape did not. So what this PR fixes is a regression, and the scrape's natural home is next to the rule it feeds.
That rule is worth looking at in the same change:
- It matches
exported_namespace, which only exists when the scrape runs withouthonorLabels. With this PR'shonorLabels: trueit still never fires. kubevirt_vm_infocomes from virt-controller, which this PR doesn't scrape.- The
kubevirt/kubevirt-control-planedashboard indashboards-infra.listalso reads virt-controller and virt-api series, such as the workqueue metrics. A virt-handler-only scrape leaves it empty.
Two correctness issues with the current shape
- Duplicates in root storage. The cluster-wide vmagent has
selectAllByDefault: trueand no namespace selector, so it also selects every tenant's copy ofkubevirt-virt-handler. With T tenant Monitorings it scrapes each virt-handler T+1 times. Root storage gets each tenant's VMI series once from thetenant-rootcopy and again from the tenant's own, and once more per nesting level. vm-operator's defaultjobis per scrape object (<namespace>/<name>, as in prometheus-operator), so the copies shouldn't dedup and root-side sums would double-count; I haven't confirmed that on a live cluster. cozystack/community#94 reads VM series fromtenant-rootand assumes they are scraped once. - The
cozy-monitoringrelease. The monitoring source also installs this chart asmonitoringincozy-monitoring, whichvmagent.yamlanddashboards.yamltreat as root-equivalent. There this template renderskeepwithcozy-monitoring(-.*)?, which drops every series.
A smaller point: tenant-a(-.*)? also keeps the series of a sub-tenant that has its own Monitoring. A tenant vmagent otherwise skips such a namespace, so the filter doesn't match the label semantics described in the template comment.
Proposal
-
Move the scrape to
system/kubevirt-operator, next toprometheusrule.yaml. Make it aVMServiceScrape, or aServiceMonitorsince the converter is on, selectingprometheus.kubevirt.io: "true"incozy-kubevirt: portmetrics,https,insecureSkipVerifyas upstream's own ServiceMonitor does, andhonorLabels: true. That keepsnamespaceandnameon the VMI, which is whatdashboards/vm/virtual-machines.jsonalready queries. It covers virt-handler, virt-controller and virt-api in one object. -
Fix the alerts in the same move. Switch the
PrometheusRulefromexported_namespacetonamespace, and pin the scrape intests/vendored_manifest_test.yamlthe wayprometheusrule.yamlalready is, so a future refresh can't drop it again. -
Drop the per-tenant fan-out from this PR. The cluster-wide vmagent then scrapes each virt-handler exactly once into
tenant-root. Scoping a shared platform exporter's series to a tenant is a read-path problem, and cozystack/community#94 designs that for exactly these series, with a forcednamespacematcher in front oftenant-root's vmselect. -
If tenant stores must hold these series, for example for tenant vmalert rules on VM metrics, make that a generic monitoring capability rather than a KubeVirt one. For instance, a tenant Monitoring could also select scrape objects that carry an opt-in label from platform namespaces, and drop any series whose
namespaceis a tenant namespace outside its subtree. KubeVirt would opt in by labelling its own scrape. Two caveats:- vm-operator ANDs a VMAgent's selector and namespace selector per kind, so the opt-in channel needs its own kind (for example
VMScrapeConfigthroughscrapeConfigSelector) or a second agent. - Every tenant vmagent would pull each virt-handler's full payload. virt-handler gathers domain stats from the virt-launchers at scrape time, so the cost grows with tenants × nodes.
That seems worth a short community proposal rather than this PR. The tenant chart's
egress-virt-handlerpolicy (#2199) couples core to KubeVirt in the same way. If the fan-out does happen, the allow rule could move into the KubeVirt package as an ingress rule on virt-handler. - vm-operator ANDs a VMAgent's selector and namespace selector per kind, so the opt-in channel needs its own kind (for example
Writing the contract down
As far as I can find, the "ship your own scrape objects and rules" convention isn't documented in this repository. Writing it down would make the FoundationDB question above easy to answer for the next contributor. It would cover:
- where scrape objects and rules live;
- that one in a tenant namespace reaches both the tenant's Monitoring and root, and one in a system namespace reaches root only;
- that series should keep
namespacenaming the workload's namespace.
Dashboards are the one place a package still has to edit core: dashboards.list and dashboards-infra.list, plus the JSON under dashboards/ baked into the grafana-dashboards image. That's a separate follow-up, but it belongs on the same list.
Happy to help with the relocation if useful.
What this PR does
Makes every Monitoring application collect the resource usage of the VirtualMachineInstances it is responsible for.
KubeVirt exports
kubevirt_vmi_*(CPU, memory, storage and network usage of every VMI) from virt-handler incozy-kubevirt, not from the VMI's own pod, so no per-application scrape object can collect it. virt-operator would create a ServiceMonitor for virt-handler, but only when themonitorAccountServiceAccount (prometheus-k8sby default) exists in themonitorNamespace(tenant-root), and Cozystack never creates that account. As a result no Monitoring application received VM metrics at all: the cluster-wide vmagent incozy-monitoringhas no virt-handler target, and a tenant Monitoring has nothing to select.The
monitoringchart now renders aVMPodScrapefor virt-handler in every Monitoring release:namespaceis its own namespace or a sub-tenant below it (tenant-akeepstenant-aandtenant-a-*), which is what thenamespace.cozystack.io/monitoringlabel already promises for a sub-tenant without a Monitoring of its own;The tenant chart already opens the network path for this: the
<tenant>-egress-virt-handlerpolicy (#2199) lets the tenant vmagent reach virt-handler on 8443 whenever monitoring is enabled, but nothing used it until now.Verified on a v1.6.3 cluster in the root mode: with the rendered VMPodScrape applied in
tenant-root, the cluster-wide vmagent reports the three virt-handler endpoints asup, andkubevirt_vmi_*series of VMs from two tenants appear invmselect-shorttermwith the VMI'snamespace,nameandnodelabels intact. The tenant mode was run on the same cluster with the chart's rendered VMAgent and this VMPodScrape applied in a child tenant (with one-replica VMClusters in place of the application's own, which does not fit that cluster): the tenant vmagent reaches all three virt-handlers through theegress-virt-handlerpolicy, its vmselect holds only the VMI series of that tenant, and nothing without anamespacelabel gets past thekeepfilter. The chart unit test covers both render modes.Screenshots
Not applicable.
Downstream repositories
Walked the trigger map against the diff: the change is a new template plus its unit test in
packages/system/monitoring, with no values key, schema, package list or version pin touched, so nothing on the website, provider or installer side is affected.Release note