Skip to main content

Cluster Autoscaler

Cluster Autoscaler is the Kubernetes project that resizes a cloud provider's node groups. In the Web App Blueprint it owns one thing: the EKS managed node group that carries Kubernetes and the system-critical workloads. It grows the group when a system pod has nowhere to go and shrinks it when the nodes are under-used.

What it does

The controller watches unschedulable pods and simulates, per node group, whether adding a node would let the pod run; if so it raises that Auto Scaling group's desired capacity. In the other direction it removes nodes whose pods could all run elsewhere, subject to PodDisruptionBudgets. It never goes outside the group's own minimum and maximum.

How BuiltForProd uses it

The cluster-autoscaler unit installs the cluster-autoscaler chart (version 9.59.0, controller 1.35.0) into kube-system as a single replica; the controller leader-elects, so a second pod would only sit idle. Its identity is an EKS Pod Identity role whose policy is describe-only except for SetDesiredCapacity and TerminateInstanceInAutoScalingGroup, and those two are conditioned on the Auto Scaling group carrying this cluster's tag, so the controller cannot resize any other cluster's group or anything the landing zone owns.

It finds the node group by tag rather than by name, because EKS names the Auto Scaling group itself. The EKS module writes k8s.io/cluster-autoscaler/enabled and k8s.io/cluster-autoscaler/<cluster name> onto the managed node group, EKS propagates them to the Auto Scaling group, and the controller discovers the group from there.

The bounds are the node group's own, set per stage in the stage stack file:

StageMinimumMaximum
dev23
staging34
prod34

The staging and production minimum of three is there because each system component runs two replicas and ArgoCD's redis-ha needs three nodes for its anti-affinity. There is nothing to size in the Cluster Autoscaler unit itself.

Three behavioral settings are @optional: values. The expander is least-waste, which decides between node groups once a second one exists, and balance-similar-node-groups keeps same-shaped groups evenly sized. skip-nodes-with-system-pods is false, the opposite of upstream's default: the node group exists precisely to run system pods, so leaving it true would mean a node could essentially never be removed. PodDisruptionBudgets still gate every eviction.

Why it never fights Karpenter

The two autoscalers own disjoint sets of nodes. Cluster Autoscaler touches only the Auto Scaling group carrying this cluster's tag, which is the managed node group; Karpenter touches only the nodes it created itself. The controller also tolerates the baseline's CriticalAddonsOnly taint and carries a node affinity that refuses any node Karpenter owns, so it always runs on the group it manages rather than on capacity that could be taken away underneath it.

Terms you will see

TermMeaning
Auto Scaling groupThe EC2 group behind a managed node group; what the controller resizes.
Desired capacityThe number of instances the group runs, between minimum and maximum.
Auto-discoveryFinding node groups by tag rather than by name.
ExpanderThe rule for choosing between node groups that could both take a pending pod.
Scale-inRemoving a node whose pods can run elsewhere, subject to disruption budgets.

Where to read more

  • Karpenter for the application capacity above it
  • EKS for the node group and its tags
  • Kubernetes for what runs on each tier