[Yandex Cloud documentation](../../index.md) > [Tutorials](../index.md) > [Container infrastructure](index.md) > Managed Service for Kubernetes > Setting up and testing scaling > Horizontal scaling of an application in a cluster

# Horizontal application scaling in a Yandex Managed Service for Kubernetes cluster

# Horizontal scaling of an application in a cluster


Managed Service for Kubernetes supports several types of [autoscaling](../../managed-kubernetes/concepts/autoscale.md). In this tutorial, you will learn to configure [cluster](../../managed-kubernetes/concepts/index.md#kubernetes-cluster) autoscaling using a combination of Cluster Autoscaler and Horizontal Pod Autoscaler.

* [Scaling based on CPU utilization](#cpu-autoscaling).
* [Scaling based on the number of application requests](#rps-autoscaling).

If you no longer need the resources you created, [delete them](#clear-out).

{% note warning %}

While running, the total number of [group nodes](../../managed-kubernetes/concepts/index.md#node-group) may increase to six. Make sure you have enough [folder resources](../../managed-kubernetes/concepts/limits.md) to follow the steps provided in this tutorial.

{% endnote %}


## Required paid resources {#paid-resources}

The support cost for this solution includes:

* Fee for using the master and outgoing traffic in a Managed Service for Kubernetes cluster (see [Managed Service for Kubernetes pricing](../../managed-kubernetes/pricing.md)).
* Fee for using computing resources, OS, and storage in cluster nodes (VMs) (see [Compute Cloud pricing](../../compute/pricing.md)).
* Fee for a public IP address for cluster nodes (see [Virtual Private Cloud pricing](../../vpc/pricing.md#prices-public-ip)).
* Key Management Service fee for the number of active key versions (with `Active` or `Scheduled For Destruction` for status) and completed cryptographic operations (see [Key Management Service pricing](../../kms/pricing.md)).


## Getting started {#before-you-begin}

1. If you do not have the Yandex Cloud CLI yet, [install and initialize it](../../cli/quickstart.md#install).

   The folder used by default is the one specified when [creating](../../cli/operations/profile/profile-create.md) the CLI profile. To change the default folder, use the `yc config set folder-id <folder_ID>` command. You can also specify a different folder for any command using `--folder-name` or `--folder-id`. If you access a resource by its name, the search will be limited to the default folder. If you access a resource by its ID, the search will be global, i.e., through all folders based on access permissions.

1. [Install Helm](https://helm.sh/docs/intro/install/).
1. [Create these service accounts](../../iam/operations/sa/create.md) for the [master](../../managed-kubernetes/concepts/index.md#master) and node groups, and [assign them the listed roles](../../iam/operations/sa/assign-role-for-sa.md):
   * `sa-k8s-master` for cluster management:
     * `k8s.clusters.agent`: To manage a Kubernetes cluster.
     * `load-balancer.admin`: To manage a [network load balancer](../../network-load-balancer/index.md).
   * `sa-k8s-nodes` for node group management:
     * `container-registry.images.puller`: To pull images from [Yandex Container Registry](../../container-registry/index.md).
1. [Create a network](../../vpc/quickstart.md) named `k8s-network` to host your cluster. Select **Create subnets** when creating it.

1. [Create security groups](../../managed-kubernetes/operations/connect/security-groups.md) for the Managed Service for Kubernetes cluster and its node groups.

    {% note warning %}
    
    The configuration of security groups determines performance and availability of the cluster and the services and applications running in it.
    
    {% endnote %}

1. [Create an encryption key](../../kms/operations/key.md#create):
   * **Name**: `k8s-symetric-key`.
   * **Encryption algorithm**: `AES-128`.
   * **Rotation period, days**: `365 days`.
1. [Create a Managed Service for Kubernetes cluster](../../managed-kubernetes/operations/kubernetes-cluster/kubernetes-cluster-create.md) with the following settings:
   * **Service account for resources**: `sa-k8s-master`.
   * **Service account for nodes**: `sa-k8s-nodes`.
   * **Encryption key**: `k8s-symetric-key`.
   * **Release channel**: `RAPID`.
   * **Public address**: `Auto`.
   * **Type of master**: `Highly available`.
   * **Cloud network**: `k8s-network`.
   * **Security groups**: Select the previously created security groups containing the rules for service traffic and Kubernetes API access.
   * **Сilium CNI**: `Enabled`.
1. [Create two node groups](../../managed-kubernetes/operations/node-group/node-group-create.md) with the following settings in the `ru-central1-a` and `ru-central1-b` availability zones:
   * Under **Scaling**:
     * **Type**: `Automatic`.
     * **Minimum number of nodes**: `1`.
     * **Maximum number of nodes**: `3`.
     * **Initial number of nodes**: `1`.
   * Under **Network settings**:
     * **Public address**: `Auto`.
     * **Security groups**: Select the previously created security groups containing the rules for service traffic, internet access to services, and SSH access to nodes.
     * **Location**: `ru-central1-a` or `ru-central1-b`.

1. [Install kubect](https://kubernetes.io/docs/tasks/tools/install-kubectl) and [configure it to work with the new cluster](../../managed-kubernetes/operations/connect/index.md#kubectl-connect).

## Scaling based on CPU utilization {#cpu-autoscaling}

In this section, you will learn how to configure cluster autoscaling based on CPU load.
1. Create a file named `k8s-autoscale-CPU.yaml` that contains the configuration for the test application, load balancer, and [Horizontal Pod Autoscaler](../../managed-kubernetes/concepts/autoscale.md#hpa):

   {% cut "k8s-autoscale-CPU.yaml" %}

   ```yaml
   ---
   ### Deployment
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: nginx
     labels:
       app: nginx
   spec:
     replicas: 1
     selector:
       matchLabels:
         app: nginx
     template:
       metadata:
         name: nginx
         labels:
           app: nginx
       spec:
         containers:
           - name: nginx
             image: registry.k8s.io/hpa-example
             resources:
               requests:
                 memory: "256Mi"
                 cpu: "500m"
               limits:
                 memory: "500Mi"
                 cpu: "1"
   ---
   ### Service
   apiVersion: v1
   kind: Service
   metadata:
     name: nginx
   spec:
     selector:
       app: nginx
     ports:
       - protocol: TCP
         port: 80
         targetPort: 80
     type: LoadBalancer
   ---
   ### HPA
   apiVersion: autoscaling/v1
   kind: HorizontalPodAutoscaler
   metadata:
     name: nginx
   spec:
     scaleTargetRef:
       apiVersion: apps/v1
       kind: Deployment
       name: nginx
     minReplicas: 1
     maxReplicas: 10
     targetCPUUtilizationPercentage: 20
   ```

   {% endcut %}

1. Create the objects:

   ```bash
   kubectl apply -f k8s-autoscale-CPU.yaml
   ```

1. In a separate window, start Kubernetes component load monitoring:

   ```bash
   watch kubectl get pod,svc,hpa,nodes -o wide
   ```

1. Run the following command to simulate a workload:

   ```bash
   URL=$(kubectl get service nginx -o json \
     | jq -r '.status.loadBalancer.ingress[0].ip') && \
     while true; do wget -q -O- http://$URL; done
   ```

   {% note tip %}

   To increase the load and speed up the scenario, run multiple simulations in separate windows.

   {% endnote %}

    {% note info %}
    
    If you cannot access the resource at the specified URL, [make sure](../../managed-kubernetes/operations/connect/security-groups.md) the security groups for the Managed Service for Kubernetes cluster and its node groups are configured correctly. If a rule is missing, [add it](../../vpc/operations/security-group-add-rule.md).
    
    {% endnote %}

   Within a few minutes, Horizontal Pod Autoscaler will increase the number of pods on the nodes due to rising CPU usage. As soon as existing cluster resources become inadequate to satisfy the `requests` value, Cluster Autoscaler will scale up nodes in the groups.
1. Stop simulating the workload. Over the next few minutes, the number of nodes and pods will drop back to the initial value.

## Scaling based on the number of application requests {#rps-autoscaling}

In this section, you will learn how to configure cluster autoscaling based on the number of application requests (_Requests Per Second, RPS_).

### How it works {#how-it-works}

1. The ingress controller sends information on the number of application requests to [Prometheus](https://prometheus.io/).
1. Prometheus generates and publishes the `nginx_ingress_controller_requests_per_second` metric for the number of application requests per second.

   For creating this metric, the `values-prom.yaml` Prometheus configuration file includes the following rule:

   ```yaml
   rules:
     groups:
       - name: Ingress
         rules:
           - record: nginx_ingress_controller_requests_per_second
             expr: rate(nginx_ingress_controller_requests[2m])
   ```

1. Based on this metric, the [autoscaling tools](../../managed-kubernetes/concepts/autoscale.md) adjust the number of pods and nodes.

### Installing objects {#create-objects}

1. Clone the GitHub repository with the up-to-date configuration files:

   ```bash
   git clone https://github.com/yandex-cloud-examples/yc-mk8s-autoscaling-solution.git && \
   cd yc-mk8s-autoscaling-solution
   ```

1. Add the Helm repositories with the ingress controller and Prometheus monitoring system:

   ```bash
   helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx && \
   helm repo add prometheus-community https://prometheus-community.github.io/helm-charts && \
   helm repo update
   ```

1. Install the ingress controller:

   ```bash
   helm upgrade \
     --install rps ingress-nginx/ingress-nginx \
     --values values-ingr.yaml
   ```

1. Install Prometheus:

   ```bash
   helm upgrade \
     --install prometheus prometheus-community/prometheus \
     --values values-prom.yaml
   ```

1. Install the [Prometheus adapter](https://github.com/kubernetes-sigs/prometheus-adapter) that enables the autoscaling tools to retrieve metrics from Prometheus:

   ```bash
   helm upgrade \
     --install prometheus-adapter prometheus-community/prometheus-adapter \
     --values values-prom-ad.yaml
   ```

1. Create a test application, Ingress rule, and [Horizontal Pod Autoscaler](../../managed-kubernetes/concepts/autoscale.md#hpa):

   ```bash
   kubectl apply -f k8s_autoscale-RPS.yaml
   ```

   Once these objects are created, Prometheus will add a new metric called `nginx_ingress_controller_requests_per_second`. Prometheus will start monitoring that metric only after traffic goes through the ingress controller.
1. Send several test requests to the ingress controller:

   ```bash
   URL=$(kubectl get service rps-ingress-nginx-controller -o json \
     | jq -r '.status.loadBalancer.ingress[0].ip') && \
     curl --header "Host: nginx.example.com" http://$URL
   ```

    {% note info %}
    
    If you cannot access the resource at the specified URL, [make sure](../../managed-kubernetes/operations/connect/security-groups.md) the security groups for the Managed Service for Kubernetes cluster and its node groups are configured correctly. If a rule is missing, [add it](../../vpc/operations/security-group-add-rule.md).
    
    {% endnote %}

1. Make sure the `nginx_ingress_controller_requests_per_second` metric is available:

   ```bash
   kubectl get --raw /apis/custom.metrics.k8s.io/v1beta1 | jq . | \
     grep ingresses.networking.k8s.io/nginx_ingress_controller_requests_per_second
   ```

   Result:

   ```text
   "name": "ingresses.networking.k8s.io/nginx_ingress_controller_requests_per_second",
   ```

### Testing autoscaling {#test-rps-autoscaling}

1. In a separate window, start Kubernetes component load monitoring:

   ```bash
   watch kubectl get pod,svc,hpa,nodes -o wide
   ```

1. Run the following command to simulate a workload:

   ```bash
   URL=$(kubectl get service rps-ingress-nginx-controller -o json \
     | jq -r '.status.loadBalancer.ingress[0].ip') && \
     while true; do curl --header "Host: nginx.example.com" http://$URL; done
   ```

    {% note info %}
    
    If you cannot access the resource at the specified URL, [make sure](../../managed-kubernetes/operations/connect/security-groups.md) the security groups for the Managed Service for Kubernetes cluster and its node groups are configured correctly. If a rule is missing, [add it](../../vpc/operations/security-group-add-rule.md).
    
    {% endnote %}

   Within a few minutes, Horizontal Pod Autoscaler will increase the number of pods on the nodes due to increasing number of application requests. As soon as existing cluster resources become inadequate to satisfy the `requests` value, Cluster Autoscaler will scale up nodes in the groups.
1. Stop simulating the workload. Over the next few minutes, the number of nodes and pods will drop back to the initial value.

## Delete the resources you created {#clear-out}

Delete the resources you no longer need to avoid paying for them:

1. [Delete the Kubernetes cluster](../../managed-kubernetes/operations/kubernetes-cluster/kubernetes-cluster-delete.md).
1. If you used static public IP addresses to access your cluster or nodes, release and [delete](../../vpc/operations/address-delete.md) them.