Monitoring AKS: Metrics, Alarms, and Dashboards
Monitoring Azure Kubernetes Service (AKS) is the continuous process of collecting, analyzing, and acting on performance data, logs, and events from your cluster, its nodes, and the workloads running inside it. Effective monitoring gives you visibility into everything from cluster health and resource saturation down to individual container metrics and application-level signals. Without it, you are flying blind — unable to detect regressions, predict capacity needs, or troubleshoot failures efficiently.
What Makes AKS Monitoring Different
AKS sits at the intersection of infrastructure and platform. You are responsible for the nodes (if not using virtual nodes), the Kubernetes control plane is managed by Azure, and your workloads bring their own observability requirements. A robust monitoring strategy must therefore cover three distinct layers:
- Cluster infrastructure — node CPU, memory, disk, network, and control plane API server activity.
- Kubernetes objects — pod counts, deployment states, replica health, persistent volume usage, and service endpoint availability.
- Application workloads — request latency, error rates, custom business metrics, and distributed traces.
Why Monitoring Matters for AKS
Beyond the obvious "know when something breaks," monitoring on AKS drives several critical operational functions:
- Proactive incident prevention — spot trends like creeping memory leaks before pods get OOMKilled.
- Cost optimization — identify over-provisioned nodes or namespaces burning compute with no real traffic.
- Autoscaling validation — confirm that the Horizontal Pod Autoscaler and Cluster Autoscaler are actually working as expected.
- Compliance and auditing — retain control plane logs for security investigations and regulatory requirements.
- SLO/SLA tracking — measure availability and latency percentiles against the promises you make to users.
Core Monitoring Components in Azure
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Azure Monitor and Container Insights
The primary managed offering for AKS observability is Container Insights, built on top of Azure Monitor and Log Analytics. It deploys a DaemonSet called the OMS agent (or the newer Azure Monitor Agent for Linux) on every node, plus a replica pod that collects Kubernetes-specific data via the Kubelet API and Kubernetes API server. Together they ship the following into a Log Analytics workspace:
- Performance counters (Perf table) — node-level cpu/network/memory every 60 seconds.
- Container logs (ContainerLog table) — stdout/stderr from every container.
- Kube events (KubeEvents table) — Kubernetes Event objects like failed scheduling, pulled images, unhealthy probes.
- Kube node/pod/container inventory (KubeNodeInventory, KubePodInventory, KubeContainerInventory) — snapshot of what exists, updated every few minutes.
- Kube services (KubeServices) — service and endpoint mappings.
- InsightsMetrics — curated Prometheus-compatible metrics collected from the kube-state-metrics exporter bundled with the agent.
Enabling Container Insights on an Existing AKS Cluster
You can enable monitoring at cluster creation via the Azure portal, Azure CLI, or ARM/Bicep. For an existing cluster, the CLI approach is the most repeatable:
# Enable Container Insights on an existing AKS cluster
az aks enable-addons \
--resource-group MyResourceGroup \
--name MyAKSCluster \
--addons monitoring \
--workspace-resource-id "/subscriptions/.../resourceGroups/LogAnalyticsRG/providers/Microsoft.OperationalInsights/workspaces/MyWorkspace"
If you don't already have a Log Analytics workspace, create one first:
az monitor log-analytics workspace create \
--resource-group LogAnalyticsRG \
--workspace-name MyWorkspace \
--location eastus
After a few minutes, the DaemonSet pods appear in the kube-system namespace. Verify they are running:
kubectl get pods -n kube-system -l component=oms-agent
kubectl get pods -n kube-system -l app=omsagent-rs
Querying Metrics and Logs
Kusto Query Language (KQL) Fundamentals
All data collected by Container Insights lands in Log Analytics tables and is queried with KQL. The query language is powerful and worth learning deeply. Here are essential patterns:
Get current CPU usage across nodes, aggregated per node:
Perf
| where ObjectName == "K8s Node"
| where CounterName == "cpuUsageNanoCores"
| summarize AvgCpuNanoCores = avg(CounterValue) by Computer, bin(TimeGenerated, 5m)
| render timechart
Find pods with high memory usage, ranked:
Perf
| where ObjectName == "K8s Container"
| where CounterName == "memoryWorkingSetBytes"
| summarize MaxMemoryBytes = max(CounterValue) by InstanceName, ContainerName, bin(TimeGenerated, 1h)
| order by MaxMemoryBytes desc
| project TimeGenerated, InstanceName, ContainerName, MaxMemoryMB = MaxMemoryBytes / 1048576
Tail container logs for a specific deployment:
ContainerLog
| where ContainerName contains "my-app"
| where TimeGenerated > ago(30m)
| project TimeGenerated, LogEntry, ContainerName
| order by TimeGenerated desc
Check Kubernetes events for warning-level issues:
KubeEvents
| where TimeGenerated > ago(1h)
| where Type == "Warning"
| project TimeGenerated, Namespace, Name, Reason, Message
| order by TimeGenerated desc
InsightsMetrics: Prometheus-Style Metrics Without Scraping
The bundled agent exposes a set of pre-aggregated metrics in the InsightsMetrics table. These come from kube-state-metrics and node-exporter-like collectors and are ideal for dashboards:
InsightsMetrics
| where Namespace == "container"
| where Name == "cpuUsagePercentage"
| summarize AvgCpuPct = avg(Val) by bin(TimeGenerated, 5m), Computer
| render timechart
Commonly used InsightsMetrics names include:
cpuUsagePercentage,memoryWorkingSetPercentage— container-level percent of request/limit.podReadyPercentage— ratio of ready pods to desired pods in a deployment.restartCount— cumulative restarts per container.kube_pod_status_phase— count of pods per phase (Running, Pending, Failed).
Alarms: Creating and Managing Alerts
Azure Monitor Alert Rules
Alerts in Azure Monitor are created as alert rules that combine a signal (metric or log query), a condition, and an action group. For AKS, you will use two types of signals:
- Metric alerts — based on Azure Monitor platform metrics like node CPU percentage, disk bytes used, or the number of pods in a Ready state. These are near-real-time (evaluated every minute) and cost less to run than log-based alerts.
- Log search alerts — based on a KQL query that runs on a schedule (e.g., every 5 minutes). Use these when you need complex logic across multiple tables or when the signal doesn't exist as a platform metric.
Creating a Metric Alert for Node CPU
This alert fires when the average CPU percentage across all nodes exceeds 85% for 10 minutes:
az monitor metrics alert create \
--resource-group MyResourceGroup \
--name "NodeCPUHigh" \
--scopes "/subscriptions/.../resourceGroups/MC_MyRG_MyAKSCluster_eastus/providers/Microsoft.ContainerService/managedClusters/MyAKSCluster" \
--condition "avg Percentage CPU > 85" \
--window-size 10m \
--evaluation-frequency 1m \
--description "Alert when average node CPU exceeds 85 percent for 10 minutes" \
--action-group "/subscriptions/.../resourceGroups/MyResourceGroup/providers/Microsoft.Insights/actionGroups/MyActionGroup"
The scope must point to the actual managed cluster resource (the one in the MC_ resource group), not the AKS resource itself. You can find it with:
az aks show --resource-group MyResourceGroup --name MyAKSCluster --query "id" -o tsv
Creating a Log Search Alert for Pod Restart Spikes
Log alerts let you detect anomalies like a sudden increase in container restarts over a short window:
# First, create the action group if you don't have one
az monitor action-group create \
--resource-group MyResourceGroup \
--name MyActionGroup \
--short-name MyAG \
--action email myemail@example.com
# Then create the log search alert
az monitor scheduled-query create \
--resource-group MyResourceGroup \
--name "PodRestartSpike" \
--scopes "/subscriptions/.../resourceGroups/LogAnalyticsRG/providers/Microsoft.OperationalInsights/workspaces/MyWorkspace" \
--query 'InsightsMetrics
| where Namespace == "container"
| where Name == "restartCount"
| summarize TotalRestarts = sum(Val) by bin(TimeGenerated, 5m)
| where TotalRestarts > 10' \
--window-size 15m \
--evaluation-frequency 5m \
--severity 2 \
--auto-mitigate false \
--action-groups "/subscriptions/.../resourceGroups/MyResourceGroup/providers/Microsoft.Insights/actionGroups/MyActionGroup"
Action Groups: Where Alerts Go
An action group defines the notification channels and automated responses. You can include:
- Email, SMS, or push notifications to specific recipients.
- Voice calls to on-call numbers.
- Webhooks that trigger PagerDuty, Opsgenie, or a custom HTTP endpoint.
- Azure Functions or Logic Apps for self-healing automation (e.g., scaling out a deployment, restarting a failing node).
- IT Service Management (ITSM) integration for ServiceNow, etc.
Alert Processing Rules (Suppression and Filtering)
Once alerts fire, you can apply alert processing rules (formerly action rules) to suppress noise during maintenance windows or route alerts differently based on severity or resource group:
# Suppress all alerts on weekends for a specific cluster
az monitor alert-processing-rule create \
--resource-group MyResourceGroup \
--name "WeekendSuppression" \
--rule-type RemoveActionGroups \
--scopes "/subscriptions/.../resourceGroups/MC_MyRG_MyAKSCluster_eastus/providers/Microsoft.ContainerService/managedClusters/MyAKSCluster" \
--schedule-recurrence-type Weekly \
--schedule-recurrence-days Saturday Sunday \
--schedule-recurrence-start-time "00:00:00" \
--schedule-recurrence-end-time "23:59:59" \
--description "Suppress alerts during weekend maintenance windows"
Dashboards: Visualizing Cluster Health
Azure Workbooks and the Built-In Container Insights Views
Container Insights ships with a set of curated workbooks accessible from the Azure portal under your AKS cluster's Insights blade. These include:
- Cluster view — node inventory, CPU/memory heatmaps, pod counts, and a trend line of cluster-level utilization.
- Nodes view — per-node breakdown of running pods, resource pressure, and node conditions.
- Controllers view — deployments, statefulsets, daemonsets with pod readiness and restart counts.
- Containers view — individual container metrics and log tails.
You can customize these or build entirely new workbooks from scratch. Workbooks support KQL queries, rich visualizations (time charts, bar charts, scatter plots, grids), and parameter-driven interactivity.
Building a Custom Workbook for Application SLO Tracking
Below is a KQL snippet you might embed in a workbook to track HTTP 5xx rate for a service, using Application Insights data from instrumented pods:
let threshold = 0.01; // 1% error rate threshold
AppRequests
| where Name startswith "POST /api/"
| summarize TotalRequests = count(), FailedRequests = countif(ResponseCode >= 500) by bin(TimeGenerated, 5m)
| extend ErrorRate = (FailedRequests * 1.0) / TotalRequests
| extend Breached = ErrorRate > threshold
| project TimeGenerated, ErrorRate, TotalRequests, FailedRequests, Breached
| render timechart with (kind=stacked)
Workbooks let you parameterize the time range, namespace, and threshold so operators can interact without editing queries.
Azure Managed Grafana for AKS Dashboards
For teams that prefer Grafana's dashboarding model, Azure offers Azure Managed Grafana (an Azure-native Grafana 10 instance with managed authentication). You connect it to your Azure Monitor data sources — including Log Analytics and Azure Monitor Metrics — and build dashboards with the full Grafana panel library.
To connect Managed Grafana to your Log Analytics workspace:
# Create a Managed Grafana instance
az grafana create \
--resource-group MyResourceGroup \
--name MyGrafanaInstance \
--location eastus
# Grant the Grafana managed identity access to the Log Analytics workspace
az role assignment create \
--assignee "$(az grafana show --resource-group MyResourceGroup --name MyGrafanaInstance --query "identity.principalId" -o tsv)" \
--role "Log Analytics Reader" \
--scope "/subscriptions/.../resourceGroups/LogAnalyticsRG/providers/Microsoft.OperationalInsights/workspaces/MyWorkspace"
Inside Grafana, add an Azure Monitor data source, authenticate with the managed identity, and start creating dashboards that query the same KQL tables you use in the portal. This unifies infrastructure monitoring with application dashboards your team already maintains in Grafana.
Native Kubernetes Dashboards with Metrics Server and kube-prometheus-stack
Many teams also run Prometheus and Grafana directly inside the cluster for sub-minute granularity and custom service monitors. The kube-prometheus-stack Helm chart bundles Prometheus, Alertmanager, and Grafana with a rich set of Kubernetes-focused dashboards:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--set prometheus.prometheusSpec.podMonitorSelectorNilUsesHelmValues=false \
--set prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false
This gives you the classic Grafana dashboards for node-exporter, kube-state-metrics, and the Kubernetes control plane — all running on your cluster. Combine this with Azure Monitor for long-term retention and cross-cluster aggregation.
Best Practices for AKS Monitoring
-
Layer your alert severity.
Define Sev 0 (critical, wakes people up), Sev 1 (needs attention within business hours), and Sev 2 (informational, logged for trend analysis). Not every CPU spike is a Sev 0 — reserve that for user-facing error rate breaches or cluster-wide outages.
-
Separate platform alerts from workload alerts.
Node failures, disk pressure, and API server latency belong to the platform team. Application error rates, queue depth, and business metric anomalies belong to the service teams. Use distinct action groups and alert processing rules to route them correctly.
-
Use metric alerts whenever possible.
Metric alerts are cheaper, faster, and more reliable than log alerts. The AKS platform emits dozens of useful platform metrics —
cpuUsagePercentage,memoryRssPercentage,diskUsedPercentage,podReadyPercentage— that should be your first line of defense. Reserve log alerts for multi-signal correlations or conditions that require KQL logic. -
Set up alert for cluster capacity early.
The most common AKS incident is resource exhaustion. Create an alert on node CPU and memory exceeding 80% across the cluster, and another on pending pods (pods stuck in Pending state for more than 10 minutes). These give you warning before the cluster autoscaler fails or deployments stall.
-
Monitor the control plane.
Enable AKS audit logs and send them to Log Analytics or a storage account. Query the
AzureDiagnosticstable for API server activity. Watch for excessive 403/409 responses, which indicate RBAC misconfigurations or etcd contention. -
Retain logs strategically.
Log Analytics charges per GB ingested and retained. Use the default 31-day retention for operational queries, but export long-lived data to an Azure Storage Account or Event Hub for archival. Configure per-table retention in the workspace to save cost on noisy tables like
ContainerLog. -
Template your monitoring configuration.
Define alert rules, workbooks, and action groups in Terraform, Bicep, or ARM templates. Store them in the same repo as your cluster definition. This ensures every new cluster inherits the same baseline monitoring without manual clicking.
-
Validate alert rules regularly.
An alert rule that hasn't fired in six months is probably broken or too loose. Run periodic "fire drills" — simulate a condition (e.g., deploy a memory-hungry pod, scale to zero) and verify the alert arrives through the expected channel within the expected window.
-
Instrument your application code.
Container Insights gives you infrastructure and Kubernetes visibility, but it cannot tell you about your application's internal state. Use OpenTelemetry with the Azure Monitor Exporter to ship distributed traces and custom metrics from your code. This bridges the gap between cluster health and user experience.
Example End-to-End Monitoring Deployment
The following Bicep snippet bundles a baseline: Log Analytics workspace, Container Insights enabled on the AKS cluster, a CPU alert, and an action group. Use it as a starting template for production deployments:
// monitoring.bicep - deploy alongside your AKS cluster
param location string = resourceGroup().location
param aksClusterName string
param logAnalyticsWorkspaceName string = 'aks-monitoring-${aksClusterName}'
param alertEmail string
// Log Analytics workspace
resource law 'Microsoft.OperationalInsights/workspaces@2023-09-01' = {
name: logAnalyticsWorkspaceName
location: location
properties: {
sku: { name: 'PerGB2018' }
retentionInDays: 31
}
}
// Enable Container Insights on AKS
resource aksCluster 'Microsoft.ContainerService/managedClusters@2023-08-01' existing = {
name: aksClusterName
}
resource aksMonitoring 'Microsoft.ContainerService/managedClusters/addonProfiles@2023-08-01' = {
parent: aksCluster
name: 'omsagent'
properties: {
config: {
logAnalyticsWorkspaceResourceID: law.id
}
enabled: true
}
}
// Action group for email notifications
resource actionGroup 'Microsoft.Insights/actionGroups@2023-01-01' = {
name: 'aks-critical-ag'
location: 'global'
properties: {
enabled: true
groupShortName: 'AKSCrit'
emailReceivers: [
{
name: 'oncall'
emailAddress: alertEmail
useCommonAlertSchema: true
}
]
}
}
// Metric alert on average node CPU > 85% for 10 minutes
resource cpuAlert 'Microsoft.Insights/metricAlerts@2023-01-01' = {
name: 'node-cpu-high'
location: 'global'
properties: {
description: 'Alert when average node CPU exceeds 85 percent for 10 minutes'
severity: 2
enabled: true
scopes: [aksCluster.id]
evaluationFrequency: 'PT1M'
windowSize: 'PT10M'
criteria: {
'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria'
allOf: [
{
name: 'Metric1'
criterionType: 'StaticThresholdCriterion'
metricName: 'cpuUsagePercentage'
metricNamespace: 'Microsoft.ContainerService/managedClusters'
operator: 'GreaterThan'
threshold: 85
timeAggregation: 'Average'
dimensions: []
}
]
}
actions: [
{
actionGroupId: actionGroup.id
}
]
}
}
Conclusion
Monitoring AKS effectively means weaving together Azure's native observability stack — Container Insights, Log Analytics, metric alerts, and workbooks — with Kubernetes-native tools like Prometheus and Grafana where sub-minute granularity or custom service monitors are required. Start with the built-in views and a small set of high-signal alerts (node capacity, pod restarts, deployment readiness), then iterate as you learn what normal looks like for your workloads. Treat alert rules as code, test them regularly, and route them to the right people with action groups and processing rules. A well-monitored cluster doesn't just help you sleep better — it directly reduces mean time to detect and recover, keeps your autoscaling honest, and gives you the data you need to make confident capacity and architecture decisions.