How to Enable Cost Allocation for Azure Kubernetes Service (AKS)

Track AKS infrastructure costs by namespace and workload using OpenCost

18 min readUpdated January 2025

Understanding where costs are being generated within your Azure Kubernetes Service (AKS) cluster is critical for optimizing cloud spending. This guide shows you how to implement granular cost allocation for AKS workloads using OpenCost, Azure Cost Analysis, and native AKS cost features to track expenses by namespace, deployment, and pod.

Overview

Azure Kubernetes Service clusters often run multiple applications, teams, and environments within a single infrastructure. Without proper cost allocation, it's impossible to know which workloads are consuming the most resources or to perform accurate showback/chargeback to different cost centers.

This guide covers three complementary approaches:

  1. AKS Cost Analysis (Azure Portal) - Built-in cost breakdown by namespace and cluster resources
  2. OpenCost - Open-source tool for detailed Kubernetes cost allocation with custom metrics
  3. Azure Monitor Container Insights - Infrastructure metrics correlated with cost data

By the end of this guide, you'll be able to track AKS costs at the namespace, deployment, pod, and label level.

Prerequisites

Before you begin, ensure you have:

  • An active Azure subscription with an existing AKS cluster
  • Owner or Contributor role on the AKS cluster resource
  • Cost Management Reader role at the subscription or resource group level
  • Azure CLI installed and authenticated (az login)
  • kubectl installed and configured to connect to your AKS cluster
  • Helm 3.x installed for deploying OpenCost
  • Basic understanding of Kubernetes namespaces, deployments, and labels
  • Container Insights enabled on your AKS cluster (recommended)

Verify Your Access

# Check your current role assignments
az role assignment list --assignee $(az account show --query user.name -o tsv) \
  --scope /subscriptions/$(az account show --query id -o tsv) \
  --query "[].{Role:roleDefinitionName, Scope:scope}" -o table

# Verify kubectl access to your cluster
kubectl auth can-i get pods --all-namespaces

Method 1: Enable AKS Cost Analysis in Azure Portal

Azure provides native cost breakdown capabilities for AKS clusters directly in the Azure Portal.

Step 1: Navigate to Cost Analysis

  1. Sign in to the Azure Portal
  2. Navigate to Cost Management + Billing
  3. Select Cost analysis from the left navigation
  4. Change the scope to your AKS cluster's resource group or subscription

Step 2: Create AKS-Specific Cost Views

  1. In Cost analysis, click + Add filter
  2. Select Resource type and choose Microsoft.ContainerService/managedClusters
  3. Click Group by and select Resource
  4. This shows total cost per AKS cluster

Step 3: View Costs by Namespace (Azure Portal)

Important: Namespace-level cost breakdown requires Container Insights to be enabled.

  1. Navigate to your AKS cluster in the Azure Portal
  2. Select Insights from the left navigation
  3. Click the Containers tab
  4. Select the Metrics view
  5. Use the namespace filter to see resource utilization by namespace
  6. Cross-reference utilization with Cost Analysis data

Step 4: Enable Split Billing by Namespace Tags

To get automated namespace cost attribution:

  1. Navigate to your AKS cluster
  2. Select Configuration > Tags
  3. Add namespace-specific tags to cluster resources
  4. Use Azure Policy to automatically tag resources based on Kubernetes labels

Example Azure Policy for Auto-Tagging:

{
  "mode": "Indexed",
  "policyRule": {
    "if": {
      "allOf": [
        {
          "field": "type",
          "equals": "Microsoft.ContainerService/managedClusters"
        }
      ]
    },
    "then": {
      "effect": "append",
      "details": [
        {
          "field": "tags['Environment']",
          "value": "[resourceGroup().tags['Environment']]"
        }
      ]
    }
  }
}

Method 2: Deploy OpenCost for Granular Cost Allocation

OpenCost is a CNCF sandbox project that provides real-time cost allocation for Kubernetes workloads.

Step 1: Install OpenCost Using Helm

# Add the OpenCost Helm repository
helm repo add opencost https://opencost.github.io/opencost-helm-chart
helm repo update

# Create a namespace for OpenCost
kubectl create namespace opencost

# Install OpenCost with Azure provider
helm install opencost opencost/opencost \
  --namespace opencost \
  --set opencost.exporter.cloudProviderApiKey="AIzaSyD29bGCUHHbGQXaW5j9XXXXXXXXX" \
  --set opencost.prometheus.internal.enabled=true \
  --set opencost.ui.enabled=true

Note: For Azure, the cloudProviderApiKey is optional but recommended for accurate pricing.

Step 2: Configure Azure Pricing Integration

To get accurate Azure pricing data, configure OpenCost with Azure credentials:

# Create Azure Service Principal for OpenCost
az ad sp create-for-rbac --name opencost-reader \
  --role "Cost Management Reader" \
  --scopes /subscriptions/$(az account show --query id -o tsv)

# Save the output (appId, password, tenant)

Create a Kubernetes secret with the credentials:

kubectl create secret generic azure-opencost-config \
  --namespace opencost \
  --from-literal=azure-subscription-id="YOUR_SUBSCRIPTION_ID" \
  --from-literal=azure-client-id="YOUR_APP_ID" \
  --from-literal=azure-client-secret="YOUR_PASSWORD" \
  --from-literal=azure-tenant-id="YOUR_TENANT_ID"

Update the OpenCost Helm values:

# opencost-values.yaml
opencost:
  exporter:
    cloudProviderApiKey: ""
    azureSubscriptionID: "YOUR_SUBSCRIPTION_ID"
    azureClientID: "YOUR_APP_ID"
    azureClientSecret: "YOUR_PASSWORD"
    azureTenantID: "YOUR_TENANT_ID"
  ui:
    enabled: true
  prometheus:
    internal:
      enabled: true
      servicemonitor:
        enabled: true

Upgrade the Helm release:

helm upgrade opencost opencost/opencost \
  --namespace opencost \
  --values opencost-values.yaml

Step 3: Access the OpenCost Dashboard

# Port-forward to access the OpenCost UI
kubectl port-forward -n opencost service/opencost 9090:9090

# Open in browser
open http://localhost:9090

The OpenCost UI provides:

  • Cost breakdown by namespace, deployment, and pod
  • Historical cost trends
  • Cost efficiency metrics
  • Resource allocation vs usage

Step 4: Query OpenCost API for Cost Data

OpenCost exposes a REST API for programmatic access:

# Get cost allocation for all namespaces (last 7 days)
curl "http://localhost:9090/allocation/compute?window=7d&aggregate=namespace"

# Get cost by deployment in production namespace
curl "http://localhost:9090/allocation/compute?window=30d&aggregate=deployment&filterNamespaces=production"

# Get cost by label
curl "http://localhost:9090/allocation/compute?window=7d&aggregate=label:team"

Example Response:

{
  "code": 200,
  "data": {
    "production": {
      "name": "production",
      "properties": {
        "namespace": "production"
      },
      "window": {
        "start": "2025-01-10T00:00:00Z",
        "end": "2025-01-17T00:00:00Z"
      },
      "cpuCost": 45.23,
      "gpuCost": 0.00,
      "ramCost": 23.18,
      "pvCost": 12.50,
      "networkCost": 8.92,
      "totalCost": 89.83
    }
  }
}

Method 3: Use Azure CLI to Export Cost Data with AKS Tags

You can programmatically retrieve cost data filtered by AKS resources using Azure CLI.

Step 1: Create Cost Export with Azure CLI

# Define variables
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
RESOURCE_GROUP="your-rg-name"
AKS_CLUSTER="your-aks-cluster"
STORAGE_ACCOUNT="costexportstorage"
CONTAINER_NAME="aks-costs"

# Create storage account for cost exports
az storage account create \
  --name $STORAGE_ACCOUNT \
  --resource-group $RESOURCE_GROUP \
  --location eastus \
  --sku Standard_LRS

# Create container
az storage container create \
  --name $CONTAINER_NAME \
  --account-name $STORAGE_ACCOUNT

# Create cost export for AKS resources
az costmanagement export create \
  --name "aks-monthly-export" \
  --type "ActualCost" \
  --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP" \
  --storage-account-id "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Storage/storageAccounts/$STORAGE_ACCOUNT" \
  --storage-container "$CONTAINER_NAME" \
  --timeframe "MonthToDate" \
  --recurrence "Daily" \
  --recurrence-period-from "2025-01-01T00:00:00Z" \
  --recurrence-period-to "2025-12-31T00:00:00Z" \
  --schedule-status "Active"

Step 2: Filter Cost Data by AKS Tags

Once exports are running, query the cost data:

# Download the latest export
az storage blob download \
  --account-name $STORAGE_ACCOUNT \
  --container-name $CONTAINER_NAME \
  --name "aks-monthly-export/$(date +%Y%m%d)/cost-data.csv" \
  --file "aks-costs.csv"

# Parse and filter using tools like jq, csvkit, or pandas

Method 4: Integrate with Azure Monitor and Log Analytics

Combine resource metrics with cost data for comprehensive analysis.

Step 1: Enable Container Insights

# Enable Container Insights on existing cluster
az aks enable-addons \
  --resource-group $RESOURCE_GROUP \
  --name $AKS_CLUSTER \
  --addons monitoring

Step 2: Create Custom Log Analytics Queries

// Query: CPU and Memory usage by namespace
let startTime = ago(7d);
let endTime = now();
Perf
| where TimeGenerated between (startTime .. endTime)
| where ObjectName == "K8SContainer"
| where CounterName == "cpuUsageNanoCores" or CounterName == "memoryRssBytes"
| extend Namespace = tostring(split(InstanceName, "/")[0])
| summarize
    AvgCPU = avg(CounterValue),
    MaxCPU = max(CounterValue),
    AvgMemory = avg(CounterValue),
    MaxMemory = max(CounterValue)
  by Namespace, CounterName
| order by Namespace asc

Step 3: Join Cost Data with Usage Metrics

// Query: Correlate namespace usage with estimated costs
let costPerCPUCore = 0.05; // Example: $0.05 per CPU hour
let costPerGB = 0.01; // Example: $0.01 per GB hour
let startTime = ago(30d);
let endTime = now();
Perf
| where TimeGenerated between (startTime .. endTime)
| where ObjectName == "K8SContainer"
| extend Namespace = tostring(split(InstanceName, "/")[0])
| summarize
    TotalCPUHours = sum(CounterValue) / 1000000000 / 3600, // Convert nanocores to hours
    TotalGBHours = sum(CounterValue) / 1073741824 / 3600 // Convert bytes to GB hours
  by Namespace, CounterName
| extend EstimatedCost = iff(CounterName == "cpuUsageNanoCores", TotalCPUHours * costPerCPUCore, TotalGBHours * costPerGB)
| summarize TotalEstimatedCost = sum(EstimatedCost) by Namespace
| order by TotalEstimatedCost desc

Best Practices

1. Implement Consistent Tagging Strategy

Tag all Kubernetes resources with cost allocation labels:

apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    environment: production
    team: platform
    cost-center: "1234"
    business-unit: engineering

Apply labels to deployments:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  namespace: production
  labels:
    app: web-app
    team: platform
    cost-center: "1234"
spec:
  template:
    metadata:
      labels:
        app: web-app
        team: platform
        cost-center: "1234"

2. Set Resource Requests and Limits

Accurate cost allocation requires defined resource requests:

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    resources:
      requests:
        memory: "512Mi"
        cpu: "500m"
      limits:
        memory: "1Gi"
        cpu: "1000m"

3. Use Namespace Resource Quotas

Prevent cost overruns with resource quotas:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: production
spec:
  hard:
    requests.cpu: "100"
    requests.memory: "200Gi"
    limits.cpu: "200"
    limits.memory: "400Gi"
    persistentvolumeclaims: "50"

4. Implement Cost Allocation Labels Policy

Use Azure Policy to enforce tagging:

# Example: Require cost-center tag on all resources
az policy assignment create \
  --name "require-cost-center-tag" \
  --policy "require-tag" \
  --params '{"tagName":{"value":"cost-center"}}' \
  --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP"

5. Schedule Regular Cost Reviews

Automate cost reporting with scheduled queries:

# Create Log Analytics scheduled query
az monitor log-analytics query \
  --workspace "your-workspace-id" \
  --analytics-query "Perf | where ObjectName == 'K8SContainer' | summarize avg(CounterValue) by Namespace" \
  --timespan "P7D"

6. Right-Size Node Pools

Use cost data to optimize node pool sizes:

# Review node pool utilization
kubectl top nodes

# Scale node pool based on cost analysis
az aks nodepool scale \
  --resource-group $RESOURCE_GROUP \
  --cluster-name $AKS_CLUSTER \
  --name nodepool1 \
  --node-count 5

7. Enable Cluster Autoscaler

Automatically adjust cluster size based on workload demands:

az aks update \
  --resource-group $RESOURCE_GROUP \
  --name $AKS_CLUSTER \
  --enable-cluster-autoscaler \
  --min-count 3 \
  --max-count 10

Troubleshooting

Issue: OpenCost Shows Incorrect Pricing

Symptoms: OpenCost displays costs that don't match Azure invoices

Solution:

  1. Verify Azure credentials are correctly configured
  2. Check that the Service Principal has "Cost Management Reader" role
  3. Update to latest OpenCost version: helm upgrade opencost opencost/opencost -n opencost
  4. Verify region pricing matches your cluster location
# Test Azure credentials
kubectl logs -n opencost deployment/opencost -c opencost | grep -i "azure"

# Expected output should show successful Azure API connection

Issue: Cost Data Not Available for Namespaces

Symptoms: Namespace-level costs show as zero or unavailable

Solution:

  1. Ensure Container Insights is enabled
  2. Verify resources have defined requests and limits
  3. Check that OpenCost has sufficient RBAC permissions
# Verify Container Insights
az aks show \
  --resource-group $RESOURCE_GROUP \
  --name $AKS_CLUSTER \
  --query "addonProfiles.omsagent.enabled" -o tsv

# Should return "True"

# Check OpenCost RBAC
kubectl auth can-i get pods --namespace opencost --as system:serviceaccount:opencost:opencost

Issue: Historical Cost Data Missing

Symptoms: Only current day costs are visible

Solution:

  1. OpenCost requires Prometheus to store historical metrics
  2. Verify Prometheus retention settings
  3. Increase PVC size for Prometheus if needed
# Check Prometheus storage
kubectl get pvc -n opencost

# Increase Prometheus retention
helm upgrade opencost opencost/opencost \
  --namespace opencost \
  --set opencost.prometheus.internal.retention=30d \
  --set opencost.prometheus.internal.storageSize=50Gi

Issue: Cost Export Failing to Storage Account

Symptoms: Automated exports not appearing in storage

Solution:

  1. Verify storage account permissions
  2. Check export configuration status
  3. Review activity logs for errors
# Check export status
az costmanagement export show \
  --name "aks-monthly-export" \
  --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP"

# View recent export runs
az monitor activity-log list \
  --resource-group $RESOURCE_GROUP \
  --max-events 50 \
  --query "[?contains(operationName.value, 'Microsoft.CostManagement')]"

Issue: High Memory Usage by OpenCost

Symptoms: OpenCost pods consuming excessive memory

Solution:

  1. Reduce Prometheus scrape frequency
  2. Limit metric retention period
  3. Increase resource limits
# Update resource limits
helm upgrade opencost opencost/opencost \
  --namespace opencost \
  --set opencost.exporter.resources.limits.memory=2Gi \
  --set opencost.exporter.resources.requests.memory=1Gi

Next Steps

After implementing AKS cost allocation, consider these advanced configurations:

  1. Set Up Cost Budgets and Alerts: Create proactive notifications when namespace costs exceed thresholds

  2. Implement Chargeback Process: Distribute costs to business units

  3. Optimize AKS Costs: Use cost data to identify optimization opportunities

    • Review pod resource utilization vs requests
    • Identify idle resources
    • Right-size node pools
    • Leverage spot instances for non-critical workloads
  4. Automate Cost Reporting: Build dashboards and automated reports

    • Connect OpenCost to Grafana
    • Export cost data to PowerBI
    • Create Slack/Teams notifications for cost anomalies
  5. Integrate with FinOps Workflows: Establish cloud financial operations practices

    • Regular cost review meetings
    • Cost optimization sprints
    • Showback/chargeback processes
    • Budget planning and forecasting

Related Resources

Frequently Asked Questions

Find answers to common questions

To verify if Container Insights is enabled for your AKS cluster, use the Azure CLI. Run the command: `az aks show --resource-group <your-resource-group> --name <your-aks-cluster> --query 'addonProfiles.omsagent.enabled' -o tsv`. If the output is 'True,' Container Insights is enabled. If it returns 'False,' you'll need to enable it using the command: `az aks enable-addons --resource-group <your-resource-group> --name <your-aks-cluster> --addons monitoring`.

Need Professional Help?

Our team of experts can help you implement and configure these solutions for your organization.