Allocation API

Throughout our API documentation, we use localhost:9090 as the default Kubecost URL, but your Kubecost instance may be exposed by a service or ingress. To reach Kubecost at port 9090, run: kubectl port-forward deployment/kubecost-cost-analyzer -n kubecost 9090. When querying the cost-model container directly (ex. localhost:9003), the /model part of the URI should be removed.

Allocation API

GET http://<your-kubecost-address>/model/allocation

The Allocation API is the preferred way to query for costs and resources allocated to Kubernetes workloads and optionally aggregated by Kubernetes concepts like namespace, controller, and label. Data is served from one of Kubecost's ETL pipelines.

Path Parameters

{
    "code": 200,
    "data": [
        {
            "aws-dev-1-niko/ip-192-168-12-152.us-east-2.compute.internal/kubecost/kubecost-cost-analyzer-5b84f94b7f-9lxx5/cost-model": {
                "name": "aws-dev-1-niko/ip-192-168-12-152.us-east-2.compute.internal/kubecost/kubecost-cost-analyzer-5b84f94b7f-9lxx5/cost-model",
                "properties": {
                    "cluster": "aws-dev-1-niko",
                    "node": "ip-192-168-12-152.us-east-2.compute.internal",
                    "container": "cost-model",
                    "controller": "kubecost-cost-analyzer",
                    "controllerKind": "deployment",
                    "namespace": "kubecost",
                    "pod": "kubecost-cost-analyzer-5b84f94b7f-9lxx5",
                    "services": [
                        "kubecost-frontend",
                        "kubecost-cost-analyzer"
                    ],
                    "providerID": "i-0f227c52893c7957f",
                    "labels": {
                        "app": "cost-analyzer",
                        "app_kubernetes_io_instance": "kubecost",
                        "app_kubernetes_io_name": "cost-analyzer",
                        "kubernetes_io_metadata_name": "kubecost",
                        "node_kubernetes_io_instance_type": "m5.large",
                        "pod_template_hash": "5b84f94b7f"
                    }
                },
                "window": {
                    "start": "2023-01-27T23:00:00Z",
                    "end": "2023-01-28T00:00:00Z"
                },
                "start": "2023-01-27T23:00:00Z",
                "end": "2023-01-27T23:16:00Z",
                "minutes": 16.000000,
                "cpuCores": 0.050000,
                "cpuCoreRequestAverage": 0.050000,
                "cpuCoreUsageAverage": 0.000964,
                "cpuCoreHours": 0.013333,
                "cpuCost": 0.000426,
                "cpuCostAdjustment": 0.000000,
                "cpuEfficiency": 0.019287,
                "gpuCount": 0.000000,
                "gpuHours": 0.000000,
                "gpuCost": 0.000000,
                "gpuCostAdjustment": 0.000000,
                "networkTransferBytes": 797941.832389,
                "networkReceiveBytes": 1243829.277373,
                "networkCost": 0.000000,
                "networkCrossZoneCost": 0.000000,
                "networkCrossRegionCost": 0.000000,
                "networkInternetCost": 0.000000,
                "networkCostAdjustment": 0.000000,
                "loadBalancerCost": 0.003333,
                "loadBalancerCostAdjustment": 0.000000,
                "pvBytes": 16169288643.764706,
                "pvByteHours": 4311810305.003922,
                "pvCost": 0.000550,
                "pvs": {
                    "cluster=aws-dev-1-niko:name=pvc-16465f04-6464-483e-89af-9e68b4a59e72": {
                        "byteHours": 4311810305.0039215,
                        "cost": 0.0005500940102068225
                    }
                },
                "pvCostAdjustment": 0.000000,
                "ramBytes": 330301440.000000,
                "ramByteRequestAverage": 330301440.000000,
                "ramByteUsageAverage": 202833175.272727,
                "ramByteHours": 88080384.000000,
                "ramCost": 0.000351,
                "ramCostAdjustment": 0.000000,
                "ramEfficiency": 0.614085,
                "sharedCost": 0.000000,
                "externalCost": 0.000000,
                "totalCost": 0.004661,
                "totalEfficiency": 0.288087
            },
            "...etc": {}
        },
        {
            "...etc": {}
        }
    ]
}

Allocation API (Aggregator only)

GET http://<your-kubecost-address>/model/allocation

The Allocation API is the preferred way to query for costs and resources allocated to Kubernetes workloads and optionally aggregated by Kubernetes concepts like namespace, controller, and label. This Allocation API should only be consulted for users who have configured Kubecost Aggregator.

Path Parameters

{
    "code": 200,
    "data": [
        {
            "aws-dev-1-niko/ip-192-168-12-152.us-east-2.compute.internal/kubecost/kubecost-cost-analyzer-5b84f94b7f-9lxx5/cost-model": {
                "name": "aws-dev-1-niko/ip-192-168-12-152.us-east-2.compute.internal/kubecost/kubecost-cost-analyzer-5b84f94b7f-9lxx5/cost-model",
                "properties": {
                    "cluster": "aws-dev-1-niko",
                    "node": "ip-192-168-12-152.us-east-2.compute.internal",
                    "container": "cost-model",
                    "controller": "kubecost-cost-analyzer",
                    "controllerKind": "deployment",
                    "namespace": "kubecost",
                    "pod": "kubecost-cost-analyzer-5b84f94b7f-9lxx5",
                    "services": [
                        "kubecost-frontend",
                        "kubecost-cost-analyzer"
                    ],
                    "providerID": "i-0f227c52893c7957f",
                    "labels": {
                        "app": "cost-analyzer",
                        "app_kubernetes_io_instance": "kubecost",
                        "app_kubernetes_io_name": "cost-analyzer",
                        "kubernetes_io_metadata_name": "kubecost",
                        "node_kubernetes_io_instance_type": "m5.large",
                        "pod_template_hash": "5b84f94b7f"
                    }
                },
                "window": {
                    "start": "2023-01-27T23:00:00Z",
                    "end": "2023-01-28T00:00:00Z"
                },
                "start": "2023-01-27T23:00:00Z",
                "end": "2023-01-27T23:16:00Z",
                "minutes": 16.000000,
                "cpuCores": 0.050000,
                "cpuCoreRequestAverage": 0.050000,
                "cpuCoreUsageAverage": 0.000964,
                "cpuCoreHours": 0.013333,
                "cpuCost": 0.000426,
                "cpuCostAdjustment": 0.000000,
                "cpuEfficiency": 0.019287,
                "gpuCount": 0.000000,
                "gpuHours": 0.000000,
                "gpuCost": 0.000000,
                "gpuCostAdjustment": 0.000000,
                "networkTransferBytes": 797941.832389,
                "networkReceiveBytes": 1243829.277373,
                "networkCost": 0.000000,
                "networkCrossZoneCost": 0.000000,
                "networkCrossRegionCost": 0.000000,
                "networkInternetCost": 0.000000,
                "networkCostAdjustment": 0.000000,
                "loadBalancerCost": 0.003333,
                "loadBalancerCostAdjustment": 0.000000,
                "pvBytes": 16169288643.764706,
                "pvByteHours": 4311810305.003922,
                "pvCost": 0.000550,
                "pvs": {
                    "cluster=aws-dev-1-niko:name=pvc-16465f04-6464-483e-89af-9e68b4a59e72": {
                        "byteHours": 4311810305.0039215,
                        "cost": 0.0005500940102068225
                    }
                },
                "pvCostAdjustment": 0.000000,
                "ramBytes": 330301440.000000,
                "ramByteRequestAverage": 330301440.000000,
                "ramByteUsageAverage": 202833175.272727,
                "ramByteHours": 88080384.000000,
                "ramCost": 0.000351,
                "ramCostAdjustment": 0.000000,
                "ramEfficiency": 0.614085,
                "sharedCost": 0.000000,
                "externalCost": 0.000000,
                "totalCost": 0.004661,
                "totalEfficiency": 0.288087
            },
            "...etc": {}
        },
        {
            "...etc": {}
        }
    ]
}

Allocation schema

Quick start

Request allocation data for each 24-hour period in the last three days, aggregated by namespace:

$ curl http://localhost:9090/model/allocation \
  -d window=3d \
  -d aggregate=namespace \
  -d accumulate=false \
  -d shareIdle=false \
  -G

Querying for window=3d should return a range of four sets because the queried range will overlap with four precomputed 24-hour sets, each aligned to the configured time zone. For example, querying window=3d on 2021/01/04T12:00:00 will return:

  • 2021/01/04 00:00:00 until 2021/01/04T12:00:00 (now)

  • 2021/01/03 00:00:00 until 2021/01/04 00:00:00

  • 2021/01/02 00:00:00 until 2021/01/03 00:00:00

  • 2021/01/01 00:00:00 until 2021/01/02 00:00:00

See Querying for the full list of arguments and Examples for more example queries.

Special types of allocation

  • __idle__ refers to resources on a cluster that were not dedicated to a Kubernetes object (e.g. unused CPU core-hours on a node). An idle resource can be shared (proportionally or evenly) with the other allocations from the same cluster. (See the argument shareIdle.)

  • __unallocated__ refers to aggregated allocations without the selected aggregate field; e.g. aggregating by label:app might produce an __unallocated__ allocation composed of allocations without the app label.

  • __unmounted__ (or "Unmounted PVs") refers to the resources used by PersistentVolumes that aren't mounted to a pod using a PVC, and thus cannot be allocated to a pod.

Query examples

Allocation data for today unaggregated:

$ curl http://localhost:9090/model/allocation \
-d window=today \
-G

Allocation data for last week, per day, aggregated by cluster:

$ curl http://localhost:9090/model/allocation \
  -d window=lastweek \
  -d aggregate=cluster \
  -G

Allocation data for the last 30 days, aggregated by the "app" label, sharing idle allocation, sharing allocations from two namespaces, sharing $100/mo in overhead, and accumulated into one allocation for the entire window:

$ curl http://localhost:9090/model/allocation \
  -d window=30d \
  -d aggregate=label:app \
  -d accumulate=true \
  -d shareIdle=weighted \
  -d shareNamespaces=kube-system,kubecost \
  -d shareCost=100 \
  -G

Allocation data for 2021-03-10T00:00:00 to 2021-03-11T00:00:00 (i.e. 24h), multi-aggregated by namespace and the "app" label, filtering by properties.cluster == "cluster-one", and accumulated into one allocation for the entire window.

$ curl http://localhost:9090/model/allocation \
  -d window=2021-03-10T00:00:00Z,2021-03-11T00:00:00Z \
  -d aggregate=namespace,label:app \
  -d accumulate=true \
  -d filterClusters=cluster-one \
  -G

Allocation data for today, aggregated by annotation. See Enabling Annotation Emission to enable annotations.

$ curl http://localhost:9090/model/allocation \
  -d window=today \
  -d aggregate=annotation:my_annotation \
  -G

Querying with /summary endpoint to view condensed payload per line item

/summary is an optional API endpoint which can be added to your Allocation query via .../model/allocation/summary?window=... to provide a condensed list of your cost metrics per line item. Instead of returning the full list of schema values listed above, your query will return something like:

"allocation-line-item": {
                        "name": "allocation-line-tem",
                        "start": "",
                        "end": "",
                        "cpuCoreRequestAverage": ,
                        "cpuCoreUsageAverage": ,
                        "cpuCost": ,
                        "gpuCost": ,
                        "networkCost": ,
                        "loadBalancerCost": ,
                        "pvCost": ,
                        "ramByteRequestAverage": ,
                        "ramByteUsageAverage": ,
                        "ramCost": ,
                        "sharedCost": ,
                        "externalCost": ,
                        "totalEfficiency": ,
                        "totalCost":
                    },

Querying with /topline endpoint to view cost totals across query (Aggregator only)

/topline is an optional API endpoint which can be added to your /summary Allocation query via .../model/allocation/summary/topline?window=... to provide a condensed overview of your total cost metrics including all line items sampled. You will receive a single list which sums the values per all items queried, formatted similar to a regular /summary query, where numResults displays the total number of items sampled. Idle costs still need to be configured separately.

{
    "code": 200,
    "data": {
        "numResults": ,
        "combined": {
            "allocations": {
                "total": {
                    "name": "total",
                    "start": "",
                    "end": "",
                    "cpuCoreRequestAverage": ,
                    "cpuCoreUsageAverage": ,
                    "cpuCost": ,
                    "gpuCost": ,
                    "networkCost": ,
                    "loadBalancerCost": ,
                    "pvCost": ,
                    "ramByteRequestAverage": ,
                    "ramByteUsageAverage": ,
                    "ramCost": ,
                    "sharedCost": ,
                    "externalCost": 
                }
            },
            "window": {
                "start": "",
                "end": ""
            }
        }
    }
}

Allocation of asset costs

Both the reconcile and shareTenancyCosts flags start processes that distribute the costs of Assets to Allocations related to them. For the reconcile flag, these connections can be straightforward like the connection between a node Asset and an Allocation where the CPU, GPU, and RAM usage can be used to distribute a proportion of the node's cost to the Allocations that run on it. For Assets and Allocations where the connection is less well-defined, such as network Assets we have opted for a method of distributing the cost that we call Distribution by Usage Hours.

Distribution by Usage Hours takes the usage of the windows (start time and end time) of an Asset and all the Allocations connected to it and finds the number of hours that both the Allocation and Asset were running. The number of hours for each Allocation related to an Asset is called Alloc_Usage_Hours. The sum of all Alloc_Usage_Hours for a single Assets is Total_Usage_Hours. With these values, an Assets cost is distributed to each connected Allocation using the formula Asset_Cost * Alloc_Usage_Hours/Total_Usage_Hours. Depending on the Asset type an Allocation can receive proportions of multiple Asset Costs.

Asset types that use this distribution method include:

  • Network (reconcile): When the network pod is not enabled cost is distributed by usage hours. If the network pod is enabled cost is distributed to Allocations proportionally to usage.

  • Load Balancer (reconcile)

  • Cluster Management (shareTenancyCosts)

  • Attached disks (shareTenancyCosts): Does not include PVs, which are handled by reconcile

Querying on-demand (experimental)

Querying on-demand with high resolution for long windows can cause serious Prometheus performance issues, including OOM errors. Start with short windows (1d or less) and proceed with caution.

Computing allocation data on-demand allows for greater flexibility with respect to step size and accuracy-versus-performance. (See resolution and error bounds for details.) Unlike the standard endpoint, which can only serve results from precomputed sets with predefined step sizes (e.g. 24h aligned to the UTC time zone), asking for a "7d" query will almost certainly result in 8 sets, including "today" and the final set, which might span 6.5d-7.5d ago. With this endpoint, however, you will be computing everything on-demand, so "7d" will return exactly seven days of data, starting at the moment the query is received. (You can still use window keywords like "today" and "lastweek", of course, which should align perfectly with the same queries of the standard ETL-driven endpoint.)

Additionally, unlike the standard endpoint, querying on-demand will not use reconciled asset costs. Therefore, the results returned will show all adjustments (e.g. CPU, GPU, RAM) to be 0.

Allocation On-Demand API

GET http://<kubecost>/model/allocation/compute

Path Parameters

{
                "name": "cluster-one//integration/integration-unmounted-pvcs/__unmounted__",
                "properties": {
                    "cluster": "cluster-one",
                    "container": "__unmounted__",
                    "namespace": "integration",
                    "pod": "integration-unmounted-pvcs"
                },
                "window": {
                    "start": "2023-01-17T15:00:00Z",
                    "end": "2023-01-17T16:00:00Z"
                },
                "start": "2023-01-17T15:00:00Z",
                "end": "2023-01-17T16:00:00Z",
                "minutes": 60.000000,
                "cpuCores": 0.000000,
                "cpuCoreRequestAverage": 0.000000,
                "cpuCoreUsageAverage": 0.000000,
                "cpuCoreHours": 0.000000,
                "cpuCost": 0.000000,
                "cpuCostAdjustment": 0.000000,
                "cpuEfficiency": 0.000000,
                "gpuCount": 0.000000,
                "gpuHours": 0.000000,
                "gpuCost": 0.000000,
                "gpuCostAdjustment": 0.000000,
                "networkTransferBytes": 0.000000,
                "networkReceiveBytes": 0.000000,
                "networkCost": 0.000000,
                "networkCrossZoneCost": 0.000000,
                "networkCrossRegionCost": 0.000000,
                "networkInternetCost": 0.000000,
                "networkCostAdjustment": 0.000000,
                "loadBalancerCost": 0.000000,
                "loadBalancerCostAdjustment": 0.000000,
                "pvBytes": 0.000000,
                "pvByteHours": 0.000000,
                "pvCost": 0.000003,
                "pvs": {
                    "cluster=cluster-one:name=pvc-demo": {
                        "byteHours": 0.000000,
                        "cost": 0.000000
                },
                "pvCostAdjustment": 0.000000,
                "ramBytes": 0.000000,
                "ramByteRequestAverage": 0.000000,
                "ramByteUsageAverage": 0.000000,
                "ramByteHours": 0.000000,
                "ramCost": 0.000000,
                "ramCostAdjustment": 0.000000,
                "ramEfficiency": 0.000000,
                "sharedCost": 0.000000,
                "externalCost": 0.000000,
                "totalCost": 0.000000,
                "totalEfficiency": 0.000000,
                "rawAllocationOnly": null
}

On-demand query examples

Allocation data for the last 60m, in steps of 10m, with resolution 1m, aggregated by cluster.

$ curl http://localhost:9090/model/allocation/compute \
  -d window=60m \
  -d step=10m \
  -d resolution=1m \
  -d aggregate=cluster \
  -d accumulate=false \
  -G

Allocation data for the last 9d, in steps of 3d, with a 10m resolution, aggregated by namespace.

$ curl http://localhost:9090/model/allocation/compute \
  -d window=9d \
  -d step=3d \
  -d resolution=10m
  -d aggregate=namespace \
  -d accumulate=false \
  -G

Theoretical error bounds

Tuning the resolution parameter allows the querier to make tradeoffs between accuracy and performance. For long-running pods (>1d) resolution can be tuned aggressively low (>10m) with relatively little effect on accuracy. However, even modestly low resolutions (5m) can result in significant accuracy degradation for short-running pods (<1h).

Here, we provide theoretical error bounds for different resolution values given pods of differing running durations. The tuple represents lower- and upper-bounds for accuracy as a percentage of the actual value. For example:

  • 1.00, 1.00 means that results should always be accurate to less than 0.5% error

  • 0.83, 1.00 means that results should never be high by more than 0.5% error, but could be low by as much as 17% error

  • -1.00, 10.00 means that the result could be as high as 1000% error (e.g. 30s pod being counted for 5m) or the pod could be missed altogether, i.e. -100% error.

Troubleshooting

Incomplete cost data for short window queries when using Thanos

While using Thanos, data can delayed from 1 to 3 hours, which may result in allocation queries retrieving inaccurate or incomplete data when using short window intervals. Avoid using values for window smaller than 5h as a best practice.

Last updated