Horizontal Autoscaling of Shoot Nodes
Every Shoot cluster that has at least one worker pool
with minimum < maximum nodes configuration will get a cluster-autoscaler deployment.
Gardener is leveraging the upstream community Kubernetes
cluster-autoscaler component.
Which means that logging of Kubernetes cluster-autoscaler is the same.
Scaling of Shoot nodes are defined in their yaml configuration in part.
In our stack the cluster-autoscaler in the Shoot namespace on the Seed cluster
is responsible for scaling up and down pod deployment.
Scaling Up
Scale-up creates a watch on the API server looking for all pods.
It checks for any unschedulable pods every 10 seconds (configurable by --scan-interval flag).
A pod is unschedulable when the Kubernetes scheduler is unable to find a node
that can accommodate the pod. For example, a pod can request more CPU
that is available on any of the cluster nodes. Unschedulable pods
are recognized by their PodCondition. Whenever a Kubernetes scheduler fails to find
a place to run a pod, it sets "schedulable" PodCondition to false
and reason to "unschedulable".
If there are any items in the unschedulable pods list,
Cluster Autoscaler tries to find a new place to run them.
It is assumed that the underlying cluster is run on top of some kind of node groups. Inside a node group, all machines have identical capacity and have the same set of assigned labels. Thus, increasing a size of a node group will create a new machine that will be similar to these already in the cluster – they will just not have any user-created pods running (but will have all pods run from the node manifest and daemon sets).
It may take some time before the created nodes appear in Kubernetes.
It almost entirely depends on the cloud provider and the speed of node provisioning,
including the TLS bootstrapping process. Cluster Autoscaler expects requested nodes
to appear within 15 minutes (configured by --max-node-provision-time flag.)
After this time, if they are still unregistered, it stops considering them in simulations
and may attempt to scale up a different group if the pods are still pending.
It will also attempt to remove any nodes left unregistered after this time.
Example
Consider a Shoot cluster with a single node that has 2 CPUs and 4 GB of RAM. We will deploy a pod whose resource requests exceed the capacity of the node.
Some load is currently running on the Shoot and could not provide to pod resources mentioned in resource part and therefore autoscaling will be activated.

Below you can find logs from cluster-autoscaler-xxxxxx-xxx pod
when its activating scale-up
When you check the status of the pod by kubectl get pod/cluster-autoscaler-xxxxxx-xxx
in section status you should see False status with reason reason: Unschedulable
After some time you can see that the cluster has 2 nodes

And than the pod could be safely scheduled.

Scaling Down
Every 10 seconds (configurable by --scan-interval flag), if no scale-up is needed,
the Cluster Autoscaler checks which nodes are unneeded.
A node is considered for removal when all below conditions hold:
-
The sum of CPU and memory requests of all pods running on this node (DaemonSet pods and Mirror pods are included by default but this is configurable with
--ignore-daemonsets-utilizationand--ignore-mirror-pods-utilizationflags) is smaller than 50% of the node's allocatable. (Before 1.1.0, node capacity was used instead of allocatable.) Utilization threshold can be configured using--scale-down-utilization-thresholdflag. -
All pods running on the node (except those that run on all nodes by default, like manifest-run pods or pods created by daemonsets) can be moved to other nodes. See What types of pods can prevent CA from removing a node? description for more details on what pods don't fulfill this condition, even if there is space for them elsewhere. While checking this condition, the new locations of all movable pods are memorized. With that, Cluster Autoscaler knows where each pod can be moved, and which nodes depend on which other nodes in terms of pod migration. Of course, it may happen that eventually the scheduler will place the pods somewhere else.
-
It doesn't have scale-down disabled annotation (see How can I prevent Cluster Autoscaler from scaling down a particular node?)
-
If a node is unneeded for more than 10 minutes, it will be terminated. (This time frame can be configured by flags – please see the I have a couple of nodes with low utilization, but they are not scaled down. Why? section for a more detailed explanation.) Cluster Autoscaler terminates one non-empty node at a time to reduce the risk of creating new unschedulable pods. The next node may possibly be terminated just after the first one, if it was also unneeded for more than 10 min and didn't rely on the same nodes in simulation (see below example scenario), but not together. Empty nodes, on the other hand, can be terminated in bulk, up to 10 nodes at a time (configurable by
--max-empty-bulk-deleteflag.)
What happens when a non-empty node is terminated?
As mentioned above, all pods should be migrated elsewhere. Cluster Autoscaler does this by evicting them and tainting the node, so they aren't scheduled there again.
DaemonSet pods may also be evicted. This can be configured separately for empty
(i.e. containing only DaemonSet pods) and non-empty nodes with --daemonset-eviction-for-empty-nodes
and --daemonset-eviction-for-occupied-nodes flags, respectively.
Note that the default behavior is different on each flag: by default DaemonSet pods eviction will happen only on occupied nodes. Individual DaemonSet pods can also explicitly choose to be evicted (or not). See How can I enable/disable eviction for a specific DaemonSet for more details.
Example 1
Lets consider that this Shoot has currently 2 nodes after scaling up procedure described in section Scaling Up.

We will now remove the pod with high resources requirements by
kubectl delete pod/task-perf (if needed specify kubeconfig of Shoot cluster
and namespace if it is not in default)
After max 5 minutes you should see in logs of cluster-autoscaler-xxxxxx-xxx:
When you see it in the logs for the first time, the node will be deleted in approximately 20 minutes, and the cluster will scale down.
After some time, we can see in the logs that the Autoscaler adds a taint to both nodes, as it considers both of them candidates for deletion.

Within the next 10 minutes, you can observe that one of the nodes is removed and the taint on the remaining node is released.
In the Shoot cluster, you will now see only one node. Scaling down takes longer than scaling up because it is a critical operation, and the Autoscaler wants to ensure that the decrease in load is not just temporary.

Manual Scaling
Manual scaling means editing yaml configuration manifest for Shoots or editing Shoot object in Garden cluster.
It needs to be modified in the field shoot.spec.provider.workers.minimum to the desired
number of nodes but value of the field needs to be lower or equal to shoot.spec.provider.workers.maximum
For downscaling you will decrease the shoot.spec.provider.workers.minimum value to the desired number of nodes (not lower than 1)
Example 2
Then edit object Shoot in Garden cluster.
kubectl edit Shoot/mcm1 -n garden-dev --kubeconfig <kubeconfig of pg-mcm1 garden cluster>
Saving and closing the object's YAML manifest starts reconciliation immediately.