Before you begin
- An API key with the
operator:readscope. Create one in Settings > API keys. - At least one connected Kubernetes cluster.
Connect a client
Point your client at the base URL and send the key as a header.http://opencost:9003 needs that value replaced.
CostGraph serves these routes:
/allocation and /allocation/compute run the same query, as they do in
OpenCost. The summary routes return the same data in the lighter summary shape.
Serve the API inside the cluster
Clients that take a URL need only the address and the key. Others reach a Kubernetes Service instead.kubectl cost is one: it resolves a Service and
port rather than calling a host, so it needs one to exist.
The operator can stand that Service up. Enable it when you install or upgrade:
api.costgraph.ai. On a self-hosted
install, point it at your own backend with
--set openCostApi.config.apiHost=<your-backend-host>.
The Service answers on the name and port OpenCost uses, and carries the
operator’s key. It filters to the cluster it runs in, so a tool run here reports
this cluster rather than every cluster on your account.
kubectl cost --opencost looks for a Service named opencost on port 9003 in a
namespace called opencost. The chart creates the Service with that name and
port, but in the namespace you install the release into. Install the operator
into opencost and the plugin finds it with no flags. Anywhere else, name the
namespace:
--historical shows what the window cost. Without it the plugin projects a
monthly rate instead. If you also change openCostApi.service.name or .port,
pass --service-name and --service-port to match, and --allocation-path /allocation/compute, which is the path --opencost sets for you.
Replace Kubecost
Kubecost clients look for a different name, port and path: a Service calledkubecost-cost-analyzer on port 9090, serving the API under /model. Turn on
the compatibility Service and CostGraph answers to those too:
kubecost namespace, or pass --kubecost-namespace.
kubectl cost then works with no flags at all:
namespace, controller, deployment, pod and node views all read
CostGraph. kubectl cost predict and kubectl cost tui do not: prediction is a
Kubecost endpoint CostGraph does not serve.
Query allocation
window is required. Everything else is optional.
aggregate accepts cluster, node, namespace, controller, pod, and
container. CostGraph composes names the way OpenCost does, joining the
properties you asked for with /. A controller carries its kind and name, so
aggregate=controller returns deployment:api. A property with no value
returns __unallocated__.
Filter the result
filter narrows the result using OpenCost’s filter syntax.
cluster, node, namespace, controllerName,
controllerKind, pod, and container, and combine terms with and, or, and
not.
Any other field returns 400 rather than being ignored. That includes label and
annotation selectors such as label[app]:"web", and the services, provider,
and account fields.
Idle follows the filter. Filtering to one cluster reports that cluster’s idle,
not every cluster’s.
Response
The response is adata array holding one allocation set per step, each keyed
by allocation name.
Query assets
GET /assets returns the nodes and disks billed over the window, keyed the way
OpenCost keys them. It takes window and step.
A node carries its instance type, the cores and bytes it offered over the
window, and its cost split across CPU and memory. The two parts add back to the
node’s bill. A disk carries its provisioned bytes and storage class. Load
balancers aren’t reported.
Query cloud cost
GET /cloudCost returns what your vendors billed you, rather than how CostGraph
allocated that spend to your workloads. It takes window, aggregate, step,
and accumulate.
aggregate accepts invoiceEntityID, accountID, regionID,
availabilityZone, provider, providerID, category, and service. It
defaults to provider,service.
Every vendor you connect appears here, software services and cloud providers
alike, so aggregating by provider returns Datadog and GitHub next to AWS.
kubernetesPercent reports 0, because CostGraph doesn’t yet attribute a
billed resource back to the Kubernetes node that used it.
Cost model
CostGraph bills CPU and memory on the greater of a container’s request and its usage, which is what the OpenCost specification defines as workload cost. A container that reserves more than it uses is billed for the reservation. Storage, network, and load balancers are billed on what the vendor charged. Idle is the part of a node’s bill that no container accounts for. CostGraph measures it per node, so an over-committed node can’t offset an empty one. PassincludeIdle=true and workload cost plus idle equals the cluster bill.
Storage
A volume’s cost reaches the container that mounts it. Each allocation lists the volumes behind it inpvs, and pvCost is their total. A volume mounted by
several containers is divided between them, so the shares add back to the
volume’s bill.
Network
CostGraph prices traffic by locality, so crossing a zone or leaving for the internet costs where staying inside a zone doesn’t. An edge charged in both directions gives half to each end. An edge charged one way gives the whole cost to that end. A workload’s traffic is then divided between the pods running it, so a controller sees the whole and a pod sees its share.Load balancers
A load balancer’s cost reaches the pods its Service selects, divided between them. Credits and tax name no resource on the vendor’s bill, so they belong to no pod and are left out.What one allocation looks like
An entry fromaggregate=controller over a two-day window, carrying every cost
type an allocation can report:
lbAllocations accounts for the whole
loadBalancerCost. Two GPUs over the window dominate everything
else. That is why the types are reported separately: a workload whose compute
costs a cent can still carry 1 of traffic.
What CostGraph doesn’t serve
CostGraph serves the allocation, asset, and cloud cost queries. The following parts of the OpenCost API return no data.Next steps
Already running OpenCost? See Migrate from OpenCost for the order to do it in and what changes.Limits
Two limits return400. A request produces more than 10,000 sets, such as a
30-day window at a one-second step. A request returns more than 200,000
allocations. Widen the step or narrow the window.