Cluster Autoscaler Decisions¶
Use these queries when pods stay unschedulable, node pools stop growing, or scale-down behavior looks too slow or too aggressive.
Query Purpose¶
These queries focus on AKS cluster-autoscaler control-plane logs. They help explain what the autoscaler decided, why it refused to act, and how long node-pool growth took after pressure started.
Required Tables¶
AzureDiagnostics- Legacy Azure diagnostics mode for AKS resource logs.AKSControlPlane- Resource-specific mode for AKS control-plane logs.
Query¶
1) Scale-up and scale-down decision timeline¶
let lookback = 2h;
union isfuzzy=true
(
AzureDiagnostics
| where TimeGenerated > ago(lookback)
| where ResourceType == "MANAGEDCLUSTERS"
| where Category == "cluster-autoscaler"
| extend RawLog = tostring(pack_all())
),
(
AKSControlPlane
| where TimeGenerated > ago(lookback)
| where Category == "cluster-autoscaler"
| extend RawLog = tostring(pack_all())
)
| where RawLog has_any ("scale up", "scale-down", "scaled up", "scaled down", "increase size", "decrease size")
| extend Direction = case(
RawLog has_any ("scale down", "scaled down", "decrease size"), "ScaleDown",
RawLog has_any ("scale up", "scaled up", "increase size"), "ScaleUp",
"Other"
)
| extend NodePool = coalesce(
extract(@'node group\s+([^,\]]+)', 1, RawLog),
extract(@'agentpool[^\s,:=]*[=: ]+([^",\s]+)', 1, RawLog),
"unknown"
)
| summarize Events = count(), Sample = any(RawLog) by bin(TimeGenerated, 5m), Direction, NodePool
| order by TimeGenerated desc
2) "No scale up" / "no candidates" reason extraction¶
let lookback = 2h;
union isfuzzy=true
(
AzureDiagnostics
| where TimeGenerated > ago(lookback)
| where ResourceType == "MANAGEDCLUSTERS"
| where Category == "cluster-autoscaler"
| extend RawLog = tostring(pack_all())
),
(
AKSControlPlane
| where TimeGenerated > ago(lookback)
| where Category == "cluster-autoscaler"
| extend RawLog = tostring(pack_all())
)
| where RawLog has_any ("No scale up", "no scale-up", "No candidates", "No expansion options", "max node group size reached", "upcoming 0 nodes")
| extend Reason = case(
RawLog has "max node group size reached", "PoolMaxReached",
RawLog has_any ("No candidates", "No expansion options"), "NoCandidatePool",
RawLog has_any ("No scale up", "no scale-up"), "NoScaleUpDecision",
RawLog has "upcoming 0 nodes", "ProvisioningLagOrNoAction",
"Other"
)
| summarize Hits = count(), Sample = any(RawLog) by Reason
| order by Hits desc
3) Node-pool growth lag after first unschedulable signal¶
let lookback = 2h;
let autoscaler = union isfuzzy=true
(
AzureDiagnostics
| where TimeGenerated > ago(lookback)
| where ResourceType == "MANAGEDCLUSTERS"
| where Category == "cluster-autoscaler"
| extend RawLog = tostring(pack_all())
),
(
AKSControlPlane
| where TimeGenerated > ago(lookback)
| where Category == "cluster-autoscaler"
| extend RawLog = tostring(pack_all())
);
let FirstPressure = toscalar(autoscaler
| where RawLog has_any ("unschedulable", "pod didn't trigger scale-up", "cannot schedule")
| summarize min(TimeGenerated));
let FirstScaleUp = toscalar(autoscaler
| where RawLog has_any ("scale up", "scaled up", "increase size")
| summarize min(TimeGenerated));
print FirstPressure = FirstPressure, FirstScaleUp = FirstScaleUp
| where isnotnull(FirstPressure) and isnotnull(FirstScaleUp)
| extend GrowthLagMinutes = datetime_diff("minute", FirstScaleUp, FirstPressure)
| project FirstPressure, FirstScaleUp, GrowthLagMinutes
Expected Interpretation¶
- Decision timeline: repeated scale-up events without recovery suggest quota, subnet, or node-boot bottlenecks outside the autoscaler itself.
- Reason extraction:
PoolMaxReachedorNoCandidatePoolusually means the autoscaler is behaving correctly but boundaries or pool design are wrong. - Growth lag: long delays between first unschedulable evidence and first scale-up action point to log gaps, autoscaler blockage, or infrastructure bottlenecks.
Assumptions and Limits¶
- These queries require
cluster-autoscalerin the AKS diagnostic setting. - Message text varies between autoscaler versions, so reason extraction is pattern-based instead of schema-based.
- Growth-lag output is most useful when an incident has a single dominant burst of unschedulable pods.