Skip to content

fix(clustercache): include native sidecar containers in pod cost calculations - #4002

Open
somaz94 wants to merge 1 commit into
opencost:developfrom
somaz94:fix/native-sidecar-pod-costs
Open

somaz94 wants to merge 1 commit into
opencost:developfrom
somaz94:fix/native-sidecar-pod-costs

Conversation

@somaz94

@somaz94 somaz94 commented Aug 14, 2026

Copy link
Copy Markdown

Description

TransformPodSpec only copies spec.containers into the cluster cache, so native sidecars (init containers with restartPolicy: Always, such as an injected istio-proxy) never reach the cost model and are costed at zero. InitContainer does not appear anywhere in the codebase today.

This adds init containers with restartPolicy: Always to the cached container list, and deliberately leaves ordinary init containers out.

That split is the one Kubernetes itself makes. In k8s.io/component-helpers/resource.PodRequests, a restartable init container's requests are added to the pod's cumulative requests exactly like a regular container, while an ordinary init container only reaches the total through a max(). See KEP-753. Including every init container would over-charge any pod that runs a plain one for the pod's whole lifetime; including none under-charges every pod with a sidecar.

One change at the cache level covers all three readers of pod.Spec.Containers:

  • costmodel.ComputeCostData, the request figures behind allocation
  • costmodel.NewContainerMetricsFromPod, the per-container allocation keys
  • metrics.KubePodCollector, the kube_pod_container_resource_requests series OpenCost emits itself. That emitter is on by default (EmitKsmV1Metrics defaults to true), so the Prometheus-sourced path picks the sidecars up too.

Sidecars are appended after the regular containers so that index 0 stays a regular container, which ComputeCostData relies on when it assigns PV claims to the first container only.

Not covered here: deployments that switch OpenCost's own emitter off and scrape a real kube-state-metrics instead. There a sidecar arrives as kube_pod_init_container_resource_requests, which carries no restartPolicy label, so it cannot be separated from an ordinary init container at the metric level. That needs its own design, so I left it out rather than guess.

Related Issues

Fixes #3956

User Impact

Pods that run native sidecars will report higher, and now correct, costs. The size of the change depends on the sidecar: for a pod whose sidecar requests are comparable to its app container, the reported pod cost roughly doubles.

Anyone who has been reading OpenCost numbers for a mesh-injected workload has been seeing the app container only, so this will look like a cost increase in dashboards even though nothing about the workload changed. Pods without native sidecars are unaffected, and pods with ordinary init containers are unaffected.

No configuration change, no API change, no new dependency.

Testing

Added core/pkg/clustercache/clustercache_test.go:

  • TestTransformPodSpecIncludesNativeSidecars, table driven over five cases: regular containers only, ordinary init container excluded, native sidecar included and ordered after the regular containers, restartPolicy: OnFailure not treated as a sidecar, and a mix of ordinary init containers and two sidecars.
  • TestTransformPodSpecKeepsSidecarRequests, so a sidecar cannot be carried into the cache and then still costed at zero.

Both were confirmed to fail without the change (removing the restartPolicy branch makes them fail, restoring it makes them pass).

just is not installed here, so I ran the justfile recipes by hand as CONTRIBUTING suggests: go test ./... plus go vet ./... in core, modules/prometheus-source, modules/collector-source and the root module. 88 packages pass, vet is clean everywhere. golangci-lint run ./pkg/clustercache/... reports 0 issues.

One note: format-check currently fails on develop for modules/pricing/public/httpclient/httpclient.go, which is unrelated to this PR and already unformatted upstream (same area as #3942). gofmt -l is clean for both files touched here, and I left that file alone.

@somaz94
somaz94 force-pushed the fix/native-sidecar-pod-costs branch from 7be21b5 to 2f4d18c Compare August 14, 2026 03:56
@somaz94
somaz94 marked this pull request as ready for review August 14, 2026 03:57
@subpathdev

Copy link
Copy Markdown

Not covered here: deployments that switch OpenCost's own emitter off and scrape a real kube-state-metrics instead. There a sidecar arrives as kube_pod_init_container_resource_requests, which carries no restartPolicy label, so it cannot be separated from an ordinary init container at the metric level. That needs its own design, so I left it out rather than guess.

You make a great point. If you look at a standard kube-state-metrics deployment, you are right that the restartPolicy is not included in the labels for kube_pod_init_container_resource_requests. Instead, it is exposed on the kube_pod_init_container_info metric. This is documented in the kube-state-metrics pod workload metrics.

Because of this, we can actually separate sidecars from ordinary init containers at the metric level by joining these two metrics. The PromQL query would look something like this:

sum by (namespace, pod, uid, resource) (
  kube_pod_init_container_resource_requests
  * on (namespace, pod, uid, container) group_left()
  kube_pod_init_container_info{restart_policy="Always"}
)

@somaz94

somaz94 commented Aug 18, 2026

Copy link
Copy Markdown
Author

Thanks @subpathdev, that's a useful correction. I only checked the labels on kube_pod_init_container_resource_requests itself and missed that kube_pod_init_container_info carries restart_policy, so the join does make sidecars separable at the metric level. My scope note was wrong to call that undecidable.

I'd still keep it out of this PR. This one is core/pkg/clustercache only, and the KSM path lives in modules/prometheus-source, which today doesn't query kube_pod_init_container_* at all. So it's a new query plus the plumbing to feed it into the allocation pipeline, rather than an extension of what's here.

Happy to open a follow-up with your query as the starting point if that suits.

…ulations

Signed-off-by: somaz <genius5711@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenCost omits Native Sidecar container resources from Pod cost calculations

2 participants