Documentation navigation
Cluster Autoscaler Unhealthy
Runbook for a cluster-autoscaler that has stopped being healthy, covering the ClusterAutoscalerUnhealthy alert (this runbook) and the ClusterAutoscalerUnregisteredNodes alert (both route here — they share the autoscaler deployment and the diagnosis).
Meaning
The cluster-autoscaler runs in kube-system and scales node groups up/down based on Pending pods. A cluster-autoscaler-status-exporter (deployed in the shared namespace) scrapes the autoscaler's status and exposes cluster_autoscaler_status_* metrics, remote-written to Mimir.
ClusterAutoscalerUnhealthy—cluster_autoscaler_status_health_status{status="Healthy"} != 1for 5m. Thestatus="Healthy"series is1when healthy,0when unhealthy.min by (k8s_cluster_name) ... != 1fires when any autoscaler instance on the cluster reports unhealthy.ClusterAutoscalerUnregisteredNodes—cluster_autoscaler_status_long_unregistered_total >= 1for 5m. Nodes the autoscaler tried to add but never registered with the Kubernetes API — almost always a cloud-provider authentication issue.
Both are scoped to k8s_cluster_name=~"prd-.*|stg-.*|dev-.*" — the par (Scaleway/kommodity) and AKS GPU clusters that run cluster-autoscaler, across production, staging, and development. The AKS general-service clusters (prod-weu, prod-us) do not run cluster-autoscaler and have no series. The metrics are already scraped into Mimir — no chart PR is needed.
Impact
- The cluster stops scaling in response to Pending pods — workloads that need more nodes sit Pending indefinitely. HPAs and KEDA ScaledObjects scale pods, but the pods cannot schedule if the autoscaler is not adding nodes.
- For
ClusterAutoscalerUnregisteredNodesspecifically: the autoscaler is requesting nodes from the cloud provider but they never join the cluster — capacity is requested but never delivered, and the autoscaler may be stuck retrying. - Scale-down is also affected — the autoscaler may not remove underutilized nodes, leaving capacity (and cost) stranded.
Diagnosis
The alert label carries k8s_cluster_name (and environment). From the alert, note both.
1. Confirm the autoscaler is unhealthy / has unregistered nodes
export KUBECONFIG=~/.kube/<cluster>.yaml # e.g. prd-par01.yaml
Confirm via Mimir (port-forward synapse-server-mimir-gateway in monitoring-system, header X-Scope-OrgID: corti):
# health (should be 1; 0 = unhealthy)
QUERY='min by (k8s_cluster_name) (cluster_autoscaler_status_health_status{status="Healthy", k8s_cluster_name="<cluster>"})'
# unregistered nodes (should be 0)
QUERY2='max by (k8s_cluster_name) (cluster_autoscaler_status_long_unregistered_total{k8s_cluster_name="<cluster>"})'
curl -s -G -H "X-Scope-OrgID: corti" "http://localhost:9090/prometheus/api/v1/query" --data-urlencode "query=$QUERY" | jq -r '.data.result[0].value[1]'
2. Check the autoscaler logs
kubectl -n kube-system logs -l app.kubernetes.io/name=cluster-autoscaler --tail=100
Common failure patterns:
failed to register node/node failed to register— nodes the autoscaler added are not joining the cluster. ForClusterAutoscalerUnregisteredNodesthis is the signature. Causes: cloud-provider auth (the node's kubelet cannot authenticate to the API server), a broken node bootstrap script, a misconfigured cloud-provider config, or the node never reachingReady.cloud provider error/Failed to ensure nodes— the autoscaler cannot talk to the cloud provider (Scaleway / Azure) to request nodes. Check the autoscaler's cloud-provider credentials (secret/Kubeconfig) and egress to the cloud API.Cluster autoscaler is not healthy(self-reported) — the autoscaler's internal health check failed; the logs before this line name the failing component.- Scale-up stuck —
PodUnschedulablePending pods but noScaleUpactivity in the logs: the autoscaler sees the pods but is not acting. Check for a--scale-down-delay, a paused autoscaler, or a max-node cap reached (see08-capacity/05-nodegroup-utilization-high.md).
3. Check the autoscaler deployment
kubectl -n kube-system get deploy -l app.kubernetes.io/name=cluster-autoscaler -o wide
kubectl -n kube-system get pods -l app.kubernetes.io/name=cluster-autoscaler -o jsonpath='{range .items[*]}{.metadata.name} restarts={.status.containerStatuses[0].restartCount} phase={.status.phase}{"\n"}{end}'
A crashlooping autoscaler pod reports unhealthy. See 02-pod-crashloop.md for crashloop diagnosis.
4. For unregistered nodes — check the node and the MachineDeployment
kubectl get nodes -o wide | grep -iE 'NotReady|unknown'
# For kommodity/Cluster-API clusters, the nodegroup is a MachineDeployment:
kubectl get machinedeployment -A | grep <cluster>
kubectl get machines -A | grep -iE 'failed|error'
An unregistered node shows as NotReady or never appears in kubectl get nodes. The MachineDeployment/Machine status names the registration failure.
Mitigation
Step 1 — Activate PIM
Activate the required Azure Privileged Identity Management roles before touching production (MultiTenantEuOnlyAccessPimRoles / DevOpsPimRoles / Platform Team (privileged)), as in 03-kafka/01-consumer-lag.md.
Step 2 — Fix the autoscaler
The fix depends on the failure mode from Diagnosis:
- Cloud-provider auth / credentials — the autoscaler's cloud-provider config is a Secret/Kubeconfig mounted into the pod. If expired or rotated, update it via a GitOps change to the autoscaler chart values. The Scaleway clusters use a Scaleway access key; the AKS GPU clusters use the AKS cloud-provider config.
- Crashlooping autoscaler — see
02-pod-crashloop.md. A restart (kubectl -n kube-system rollout restart deploy/cluster-autoscaler) may recover a transient crash, but identify the crash reason first. - Unregistered nodes (kommodity) — the node bootstrap (talos/kommodity) failed to join the node to the API server. Check the Machine status and the node's bootstrap logs. This is usually a Scaleway auth or network issue at node boot, not an autoscaler bug — the autoscaler requested the node correctly; the node failed to register. Fix the bootstrap / cloud config, then the autoscaler's next scale-up succeeds.
- Unregistered nodes (AKS) — the VMSS failed to provision or the node never reached
Ready. Check the Azure VMSS activity log for provisioning failures.
Kommodity-specific fix (Scaleway sovereign clusters)
For kommodity-managed clusters (prd-par01, prd-par02, prd-ric02, stg-par01, stg-par02, dev-par01, dev-par02), the autoscaler wedge is often caused by an expired or stale signing key / kubeconfig secret. The recovery procedure:
- Delete the
*-kubeconfigsecret in the downstream cluster (the cluster the autoscaler runs on):kubectl -n kube-system delete secret <cluster>-kubeconfig - Delete the signing key secret in kommodity (the management cluster):
kubectl delete secret <cluster>-signing-key - Wait for automatic recreate — kommodity's reconciler regenerates the signing key and the kubeconfig secret automatically.
- Validate the
*-kubeconfigsecret is recreated in the downstream cluster:kubectl -n kube-system get secret <cluster>-kubeconfig - Restart the autoscaler:
kubectl -n kube-system rollout restart deploy/cluster-autoscaler
Step 3 — Verify
The alert clears once cluster_autoscaler_status_health_status{status="Healthy"} returns to 1 (and long_unregistered_total drops to 0 for the nodes alert) sustained for the for: 5m window. Confirm the autoscaler is scaling: Pending pods get nodes, and the logs show ScaleUp activity.
Verify
ClusterAutoscalerUnhealthyclears oncemin by (k8s_cluster_name) (...health_status{status="Healthy"})is 1 sustained for 5m.ClusterAutoscalerUnregisteredNodesclears oncemax by (k8s_cluster_name) (...long_unregistered_total)is 0 sustained for 5m.kubectl get nodesshows the previously-unregistered nodes asReady(or removed if they were garbage-collected).- The autoscaler logs show
ScaleUp/ScaleDownactivity resuming.
Notes
- Scope —
k8s_cluster_name=~"prd-.*|stg-.*|dev-.*": covers all par clusters (Scaleway/kommodity) and AKS GPU clusters that run cluster-autoscaler, across production, staging, and development. The AKS general-service clusters do not run it. - Two alerts, one runbook —
ClusterAutoscalerUnhealthy(autoscaler self-reported health) andClusterAutoscalerUnregisteredNodes(nodes the autoscaler added never registered) both point at the autoscaler deployment and share the diagnosis. - All environments — production, staging, and development clusters are all covered. An unhealthy autoscaler in dev blocks dev work; in staging it blocks pre-prod validation. No
environment=filter — thek8s_cluster_name=~"prd-.*|stg-.*|dev-.*"filter is the scope. - Related —
08-capacity/05-nodegroup-utilization-high.mdfor nodegroup capacity ceiling;08-capacity/02-hpa-at-max-replicas.mdfor the HPA-at-max signal that often pairs with autoscaler scaling.