Autoscaling in a Managed Kubernetes cluster
Autoscaling is unavailable:
- for node groups with GPU without drivers;
- node groups on dedicated servers.
In a Managed Kubernetes cluster, you can use Cluster Autoscaler or Karpenter to autoscale node groups. They help optimize cluster resource utilization—the number of nodes in a group will automatically decrease or increase depending on the cluster load. Cluster Autoscaler is installed automatically when a cluster is created; to start using it, you just need to enable it. Karpenter must be installed and configured separately.
When using autoscaling tools, keep the recommendations in mind.
For pod autoscaling in Managed Kubernetes, Metrics Server is used.
Recommendations
For optimal autoscaling performance, we recommend:
- do not use more than one autoscaling tool at the same time;
- make sure the project has quotas for vCPU, RAM, GPU, and disk capacity to create the maximum number of nodes in the group;
- specify resource requests in the manifests for pods. For more information, see the Resource Management for Pods and Containers instruction in the Kubernetes documentation;
- configure PodDisruptionBudget for pods that cannot tolerate interruptions. This will help avoid downtime during migration between nodes;
- do not manually modify node resources through the control panel. Cluster Autoscaler and Karpenter will not accommodate these changes;
- when using Cluster Autoscaler check that nodes in the group have the same configuration and labels.
Autoscaling using Cluster Autoscaler
Cluster Autoscaler does not need to be installed in a cluster—it is installed automatically when a cluster is created. To use Cluster Autoscaler in a cluster, enable node group autoscaling. After autoscaling is enabled, default settings are used, but you can configure Cluster Autoscaler for each node group.
Operating principle
Cluster Autoscaler works with existing node groups and pre-selected configurations. If a node group is in the ACTIVE status, Cluster Autoscaler checks every 10 seconds for pods in the PENDING status and analyzes the load—pod requests for vCPU, RAM, and GPU. Depending on the check results, nodes are added or deleted. During this process, the node group switches to PENDING_SCALE_UP or PENDING_SCALE_DOWN. The cluster status during autoscaling is ACTIVE. For more information about cluster statuses, see the View cluster status instruction.
The minimum and maximum number of nodes in a group can be set when enabling autoscaling —Cluster Autoscaler will only change the number of nodes within these limits. If there are at least two working nodes remaining in other node groups of the cluster, you can configure autoscaling to zero nodes.
Adding a node
If there are pods in the PENDING status and the cluster lacks free resources to place them, the required number of nodes is added to the cluster. In a cluster with Kubernetes version 1.28 and higher, Cluster Autoscaler will work across several groups at once and distribute nodes evenly.
For example, you have two node groups with autoscaling enabled. The cluster load increases, requiring four nodes to be added. Two new nodes will be created simultaneously in each node group.
In a cluster with Kubernetes version 1.27 or lower, nodes are added one at a time per check cycle.
Deleting a node
If there are no pods in the PENDING status, Cluster Autoscaler checks the amount of resources requested by the pods.
If the requested resource amount for pods on a node is less than 50% of the node's resources, Cluster Autoscaler marks the node as unnecessary. If the resource request amount on the node does not increase within 10 minutes, Cluster Autoscaler checks whether the pods can be migrated to other nodes.
Cluster Autoscaler will not move pods and, consequently, will not delete a node if one of the following conditions is met:
- pods use PodDisruptionBudget;
- In kube-system pods there is no PodDisruptionBudget;
- pods are created without a controller — for example, Deployment, ReplicaSet, StatefulSet;
- pods use local storage;
- there are no resources on other nodes for the pod requests;
- there is a mismatch of nodeSelector, affinity and anti-affinity rules, or other parameters.
You can allow such pods to be moved — to do this, add the following annotation:
cluster-autoscaler.kubernetes.io/safe-to-evict: "true"
If there are no restrictions, the pods will be migrated, and the underutilized nodes will be removed. Nodes are removed one by one per check cycle.
Autoscaling to zero nodes
In a node group, you can configure autoscaling to zero nodes—all nodes in the group are removed when the load is low. The node group card with all settings is not removed. When the load increases, nodes can be added to this group again.
Autoscaling to zero nodes works only if at least two working nodes remain in other node groups of the cluster. Working nodes must remain in the cluster to host system components necessary for cluster operation.
For example, autoscaling to zero nodes will not work if the cluster has:
- two node groups with one working node in each;
- one node group with two working nodes.
When there are no nodes in a group, you do not pay for unused resources.
Enable autoscaling using Cluster Autoscaler
If you set the minimum number of nodes in a group higher than the current number, it will not increase to the lower bound immediately. The node group will only scale after pods appear in the PENDING status. Similarly, for the upper bound of nodes in a group—if the current number of nodes is greater than the upper bound, removal will only start after checking the pods.
You can enable autoscaling using Cluster Autoscaler in the control panel, via the Managed Kubernetes API or via Terraform.
- In the control panel, on the top menu, click Products and select Managed Kubernetes.
- Open the cluster page → Cluster Resources tab.
- In the menu of the node group, select Change number of nodes.
- In the Number of nodes field, open the With autoscaling tab.
- Set the minimum and maximum number of nodes in the group—the node count will only change within this range. For fault-tolerant operation of system components, we recommend using at least two working nodes in the cluster. Nodes can be in different groups.
- Click Save.
Configure Cluster Autoscaler
You can configure Cluster Autoscaler separately for each node group.
Parameters, their descriptions, and default values can be found in the Cluster Autoscaler parameters table. If you do not specify a parameter in the manifest, the default value will be used.
Example manifest:
apiVersion: v1
kind: ConfigMap
metadata:
name: cluster-autoscaler-nodegroup-options
namespace: kube-system
data:
config.yaml: |
150da0a9-6ea6-4148-892b-965282e195b0:
scaleDownUtilizationThreshold: 0.55
scaleDownUnneededTime: 7m
zeroOrMaxNodeScaling: true
e3dc24ca-df9d-429c-bcd5-be85f8d28710:
scaleDownGpuUtilizationThreshold: 0.25
ignoreDaemonSetsUtilization: true
Here, 150da0a9-6ea6-4148-892b-965282e195b0 and e3dc24ca-df9d-429c-bcd5-be85f8d28710 are the unique identifiers (UUIDs) of the node groups in the cluster. You can view them in the control panel: in the top menu, click Products ⟶ Managed Kubernetes ⟶ Kubernetes section ⟶ cluster page ⟶ copy the UUID above the node group card, next to the pool segment.
Cluster Autoscaler parameters
Autoscaling using Karpenter
Operating principle
Karpenter is a flexible cluster autoscaling tool. Unlike Cluster Autoscaler, Karpenter uses existing node groups but can also create new ones.
Karpenter can select an optimal node configuration, taking into account not only technical parameters but also cost. Karpenter chooses the cheapest option suitable for the current load. This is possible because Karpenter integrates directly with the OpenStack API, which is used to create cloud platform resources.
If the cluster is in the ACTIVE status, Karpenter checks for pods (Pod) in the PENDING status and analyzes the load—pod requests for vCPU, RAM, and GPU. Depending on the check results, node groups and nodes are added or removed. Karpenter can only remove nodes and node groups that it has created.
During this process, the cluster switches to PENDING_SCALE_UP or PENDING_SCALE_DOWN. The cluster status during autoscaling is ACTIVE. For more information about cluster statuses, see the View cluster status instruction.
Install Karpenter
-
In the control panel, on the top menu, click Products and select Managed Kubernetes.
-
Open the cluster page → Settings tab.
-
Click Download kubeconfig. Downloading the kubeconfig file is unavailable if the cluster status is
PENDING_CREATE,PENDING_ROTATE_CERTS,PENDING_DELETE, orERROR. -
Export the path to the kubeconfig file to the
KUBECONFIGenvironment variable:export KUBECONFIG=<path>Specify
<path>— the path to the kubeconfigcluster_name.yamlfile, where<cluster_name>is the cluster name. -
Export the Managed Kubernetes cluster ID to the
ClusterIDenvironment variable:export ClusterID=<cluster_id>Specify
<cluster_id>— Managed Kubernetes cluster ID; you can view it in the control panel: on the top menu, click Products → Managed Kubernetes → cluster page → copy the ID below the cluster name, next to the region and pool. -
Install Karpenter using Helm:
helm install karpenter-helmrelease oci://ghcr.io/selectel/mks-charts/karpenter: \--namespace kube-system \--set controller.settings.clusterID=$ClusterIDBy default, the latest available version is installed. Available versions can be viewed in the mks-charts repository of the Selectel documentation on GitHub.
Configure Karpenter
To configure autoscaling using Karpenter, set up NodePool and NodeClass objects.
NodePool describes the rules for selecting and scaling nodes. For example:
- what types of nodes can be created;
- with what configurations (flavors) and resources;
- when these nodes can be deleted or recreated.
Each NodePool refers to a specific NodeClass. In configurations (flavors) with network volumes, the NodeClass defines the infrastructure parameters of the network volumes that will be used by the nodes in the cluster. A single cluster can have several different NodeClasses, for example, differing by the type or size of the network volume. Configurations (flavors) with a local boot disk can also be used, but the local disk parameters are determined by the selected configuration.
More about NodePool in the NodePools article of the Karpenter documentation.
1. Check cluster compliance with requirements
-
Ensure that the Kubernetes version is 1.28 or higher. You can upgrade the cluster version.
-
Ensure that the cluster has at least one node with at least 2 vCPU and 4 GiB RAM. For optimal Karpenter performance, we recommend adding two nodes to the cluster, each with at least 2 vCPU and 4 GiB RAM;
-
Ensure that autoscaling is disabled.
-
Ensure that auto-repair is disabled.
2. Create a NodeClass
-
Create a
nodeclass.yamlYAML file with a manifest for the NodeClass object.Example of a NodeClass manifest for a network volume of type Universal:
apiVersion: karpenter.k8s.selectel/v1alpha1kind: SelectelNodeClassmetadata:name: defaultspec:disk:categories:- universalsizeGiB: 30Where:
universal— network volume type;30— size of the network volume in GB.
-
Apply the manifest:
kubectl apply -f nodeclass.yaml
3. Create a NodePool
-
Create a
nodepool.yamlfile with a manifest for the NodePool object. The description of all parameters, except for therequirementsblock parameters, can be found in the NodePools instruction in the Karpenter documentation. The description of therequirementsblock parameters is in the Parameters of the requirements block in NodePool.Example of a NodePool manifest when using the Calico network plugin
apiVersion: karpenter.sh/v1kind: NodePoolmetadata:name: defaultspec:template:spec:nodeClassRef:name: defaultkind: SelectelNodeClassgroup: karpenter.k8s.selectelrequirements:- key: topology.kubernetes.io/zoneoperator: Invalues: ['ru-7a', 'ru-7b']- key: node.kubernetes.io/instance-typeoperator: Invalues: ['SL1.1-2048', 'SL1.2-4096', 'SL1.2-8192']- key: karpenter.sh/capacity-typeoperator: Invalues: ['on-demand']expireAfter: 720hdisruption:consolidationPolicy: WhenEmptyOrUnderutilizedconsolidateAfter: 0slimits:cpu: '1000'memory: 1000GiExample of a NodePool manifest when using the Cilium network plugin
apiVersion: karpenter.sh/v1kind: NodePoolmetadata:name: defaultspec:template:spec:nodeClassRef:name: defaultkind: SelectelNodeClassgroup: karpenter.k8s.selectelstartupTaints:- key: node.cilium.io/agent-not-readyvalue: "true"effect: NoExecute- key: node.cilium.io/agent-not-readyeffect: NoSchedulerequirements:- key: topology.kubernetes.io/zoneoperator: Invalues: ['ru-7a', 'ru-7b']- key: node.kubernetes.io/instance-typeoperator: Invalues: ['SL1.1-2048', 'SL1.2-4096', 'SL1.2-8192']- key: karpenter.sh/capacity-typeoperator: Invalues: ['on-demand']expireAfter: 720hdisruption:consolidationPolicy: WhenEmptyOrUnderutilizedconsolidateAfter: 0slimits:cpu: '1000'memory: 1000Gi -
Apply the manifest:
kubectl apply -f nodepool.yaml
Parameters of the requirements block in NodePool
In the NodePool object, in the requirements block, the requirements for the nodes to be created are described.