# Connect Kubernetes

# Connect Kubernetes

Costfluent distributes the cost of a Kubernetes cluster's nodes across namespaces and shows the
remaining idle cost. It does not offer a verified per-pod, workload or container cost view. A small agent, installed with Helm, reads resource usage inside the
cluster and reports it to Costfluent every hour. There is no connection to create first: the
cluster registers itself on its first report and appears under **Settings**, then
**Integrations**, then **Kubernetes**.

## Requirements

- A Kubernetes cluster you can install a Helm chart into, with cluster-wide read access for the
  agent's service account.
- Outbound HTTPS from the cluster to `https://api.costfluent.com`, directly or through a proxy.
- An organization API token with the **Report Kubernetes usage** capability. Workspace tokens are
  refused.

## Install the agent

Open **Settings**, then **API tokens**, and create an organization token with only the **Report
Kubernetes usage** capability. That capability allows the one upload the agent makes and nothing
else. Copy the token; its full value is shown only once.

Pick a cluster ID. It names the cluster in Costfluent and must be unique within your organization:
1 to 63 letters, digits, dots, underscores or hyphens, starting with a letter or digit. Then
install the chart:

```bash
helm repo add costfluent https://costfluent.github.io/helm-charts
helm upgrade -n costfluent cfa costfluent/costfluent-k8s-agent --install --create-namespace \
  --set agent.token=$COSTFLUENT_API_TOKEN,agent.clusterID=$CLUSTER_ID
```

Install one agent per cluster. Within a few minutes the cluster shows on the **Kubernetes** page as
**Importing**. Enable it in the workspaces that should see its cost, as with any other data source.

## Key values

| Value | Default | Purpose |
|---|---|---|
| `agent.token` | none | The organization API token. Set it, or `agent.secret`. |
| `agent.secret.name`, `agent.secret.key` | none | An existing Secret holding the token, instead of `agent.token`. |
| `agent.clusterID` | none, required | The cluster's ID in Costfluent. |
| `agent.apiEndpoint` | `https://api.costfluent.com` | Where reports go. |
| `agent.pollingInterval` | | Seconds between usage samples: 5, 10, 15, 30 or 60. |
| `agent.nodeAddressTypes` | | Which node address the agent uses to reach each kubelet. |
| `agent.disableKubeTLSverify` | `false` | Skip verifying kubelet certificates. |
| `agent.allowedLabels` | all | Pod labels to send. Empty sends every pod label. |
| `agent.allowedAnnotations` | none | Pod annotations to send. Empty sends none. |
| `agent.collectNamespaceLabels` | `false` | Send namespace labels. |
| `agent.reportHTTPProxy` | none | An HTTP proxy for the upload. |
| `persist.size`, `persist.storageClassName` | `1Gi` | The volume that buffers reports while Costfluent cannot be reached. |
| `resources` | requests `50m` CPU and `64Mi`, limit `256Mi` | CPU and memory requests and limits for the agent. |

Keep the token out of your values files in source control: store it in a Secret and point
`agent.secret` at it.

## Sizing

The agent is one pod, whatever the size of the cluster. Its memory grows with the number of
containers it tracks. Measured with 61 pods at a 5 second polling interval, its working set
averaged 11.4 MiB with a 12.8 MiB peak, using under 0.001 CPU cores. The defaults (`64Mi`
requested, `256Mi` limit) leave room for clusters many times that size; raise the memory limit if
the pod is restarted for exceeding it.

## What is read and sent

The agent only reads: nodes, pods, namespaces, and the resource metrics each kubelet serves. It
sends node shape and identity, pod and container identity, resource requests and usage, and the pod
labels you allow. It never sends container images, environment variables, secrets, config maps,
logs or network traffic. The chart README's table of
[what the agent reads and sends](https://github.com/costfluent/helm-charts/tree/main/costfluent-k8s-agent#what-the-agent-reads-and-sends)
is the complete list.

Reports that cannot be delivered are kept on the agent's volume and sent once Costfluent is
reachable again.

## How costs are calculated

Each node's cost for the day is split into CPU, memory and, on GPU nodes, GPU. The calculation uses
container resource requests and measured usage to distribute cost to namespaces. The part of a
node's cost that remains unallocated is idle cost, and appears as the namespace
`__idle__`.

A node's price comes from the first of these that has one:

1. The billing rows of an AWS, Azure or GCP connection in your organization that bill that node.
2. The node's own annotations, described below.
3. The rates set for the cluster in Costfluent.

A node none of them prices is left out and counted as unpriced on the cluster.

Costfluent processes each cluster once a night, at 02:00 UTC, for the previous day. Cost therefore
appears the morning after the agent is installed, while the **Kubernetes** page shows the latest
report within minutes. In Cost Reports, group or filter by **Cluster** and **Namespace**, and use
pod labels as tags named `k8s:label:<key>`.

## On-premises and custom rates

For nodes without cloud billing, such as on-premises or bare-metal clusters, open the cluster on
the **Kubernetes** page and set its hourly rates per vCPU, per GB of memory and per GPU, and their
currency. To price individual nodes differently, annotate them:

```bash
kubectl annotate node <node> \
  costfluent.com/vcpu-hourly-rate=0.021 \
  costfluent.com/ram-gb-hourly-rate=0.003 \
  costfluent.com/gpu-hourly-rate=0.90
```

Annotation rates are read in the cluster's currency and take precedence over the cluster's rates.

## Avoid double counting

A cluster's cost is the cost of its nodes, which the cloud connection that pays for them already
reports. Costfluent does not count Kubernetes cost toward tracked spend, but a Cost Report over a
workspace that holds both sources shows the nodes twice: once as the cloud's instances, once
allocated to namespaces. Filter a report to one side, by provider, when you want a total.

## Cluster statuses

| Status | Meaning |
|---|---|
| **Importing** | The cluster has reported, and its first day has not been processed yet. |
| **Stable** | Reports arrive and each day is processed. |
| **Stale** | No report for two hours. Check that the agent pod is running and can reach Costfluent. |
| **Warning** | Some nodes have no price, or processing a day failed. The cluster page says which. |

## Troubleshooting

The agent logs every refused upload with the reason. The common ones:

- **401 or 403.** The token is wrong, revoked, a workspace token, or lacks the **Report
  Kubernetes usage** capability. Reports stay buffered, so fixing the token recovers them.
- **409.** Another agent already reports this cluster ID, or the organization has reached its
  limit of 100 clusters. Give each cluster its own ID.
- **410.** The cluster was deleted in Costfluent. Uninstall the agent, or install it again under a
  new cluster ID.
- **413.** A report was too large. The agent backs off and retries; limit `agent.allowedLabels` if
  it persists.
- **No usage on AKS.** Kubelets on AKS often serve certificates the agent cannot verify, or are
  not reachable at their default address. Set `agent.disableKubeTLSverify=true` and
  `agent.nodeAddressTypes=InternalIP`.

## Uninstall

```bash
helm uninstall -n costfluent cfa
```

The cluster turns **Stale** two hours later and its past cost stays in Cost Reports. Delete it on
the **Kubernetes** page to remove it; an agent still running for a deleted cluster is refused. Then
revoke its API token.
