[Kubernetes in Action] Automatic scaling of pods and other cluster nodes

2022. 2. 19. 17:54DevOps/Docker & Kubernetes

 

 

 

Overview

파드 내에서 동작하는 애플리케이션은 ReplicaSet이나 Deployment, 다른 Scalable Resource의 replicas필드를 수동으로 조작함으로써 스케일링할 수 있습니다(Horizontal Scaling). 또한 단일 파드의 CPU나 메모리를 requests나 limits설정을 통해 수동으로 스케일링할 수도 있습니다.(Vertical Scaling). 하지만 이러한 수동 스케일링 방식은 급작스럽거나 예측하기 어려운 트래픽 변화에 대해서 민감하고 효율적으로 대응하기 어렵기 때문에 쿠버네티스에서는 CPU usage 등의 메트릭을 사용해서 Automatic Scaling 하는 방법을 제공합니다. 이번 포스팅에서는 이러한 Automatic Scaling 방법에 대해 알아봅니다.

Vertical Scaling의 경우는 공식문서를 참고해주세요. 이번 포스팅에서는 다루지 않습니다

 

 

Horizontal Pod Autoscaling

Horizontal Pod Autoscaling이란 컨트롤러에 의해 관리되는 파드의 복제본(pod replicas)의 숫자를 스케일링하는 것을 의미합니다. (배포되는 파드의 숫자를 변화시킴). 이는 HorizontalPodAutoscaler(HPA)라고 불리는 쿠버네티스 리소스를 통해서 수행할 수 있으며, HPA는 CPU usage등과 같은 메트릭들을 주기적으로(periodically) 체크해서 필요한 파드의 개수를 계산한 다음 replica설정을 가지고 있는 Deployments, ReplicaSet과 같은 리소스의 replica 수를 수정합니다.

 

 

Understanding the autoscaling process

Autoscaling은 다음 순서로 실행됩니다.

  1. Obtaining Pod Metrics (파드 스케일링을 위한 지표 수집)
  2. Calculating the required number of Pods (지표를 만족시키기 위해 필요한 파드의 복제본 개수 계산)
  3. Updating the Desired Replica count on the scaled resource (리소스 업데이트)

 

Obtaining Pod Metrics

Heapster is marked deprecated as of Kubernetes 1.11. Users are encouraged to use metrics-server instead, potentially supplemented by a third-party monitoring solution, such as Prometheus.

교재에서는 Heapster로 표기되었지만 1.11 버전부터는 Heapster는 Deprecated 되었으므로 metrics-server를 사용해야 합니다.

Autoscaler(HPA)가 스케일링을 위한 계산을 수행하고 실제 리소스의 replica를 수정하긴 하지만(API Call), HPA자체가 지표를 주기적으로 수집하는 것은 아닙니다. 파드와 노드에 관련된 지표들(metrics)은 각 노드의 kublet에서 동작하는 cAdvisor라는 Agent에서 수집되며, 이 지표들은 잘 수합되어 Heapster(metrics-server)로 전송됩니다. HPA는 이 metrics server에 주기적으로 쿼리를 요청해서 계산에 필요한 정보를 가져오게 되는 것입니다.

 

 

이렇게 HPA가 계산을 수행하기 위해서는 위의 그림과 같이 Pod -> cAdvisor -> Heapster -> HPA로 이어지는 네트워크 Flow를 타야 하기 때문에 실제로 Scaling이 시작되기까지는 시간이 조금 걸릴 수 있습니다.

 

 

https://github.com/kubernetes-retired/heapster/blob/master/docs/deprecation.md#initial-deprecation-kubernetes-111

 

GitHub - kubernetes-retired/heapster: [EOL] Compute Resource Usage Analysis and Monitoring of Container Clusters

[EOL] Compute Resource Usage Analysis and Monitoring of Container Clusters - GitHub - kubernetes-retired/heapster: [EOL] Compute Resource Usage Analysis and Monitoring of Container Clusters

github.com

 

Calculating the required number of pods

스케일링되어야 할 파드의 개수를 구하기 위해 HPA는 metrics-server로부터 쿼리를 요청해 받아온 정보들을 토대로 계산을 수행합니다. 계산에 필요한 지표가 1개일 경우에는 모든 파드의 metric 값을 합해서 이를 target value(HPA에 지정한 값)로 나누고 올림 해서 만든 integer를 사용합니다. 

 

예를 들어서 아래의 그림처럼 CPU의 target value가 50%이고(유일한 metric) 3개의 파드가 각각 60%, 90%, 50%의 metric을 가지고 있다면 필요한 파드의 개수는 (60 + 90 + 50) / 50 = 200 / 50 = 4개입니다. 

The actual calculation is a bit more involved than this, because it also makes sure the Autoscaler doesn’t thrash around when the metric value is unstable and changes rapidly.

 

계산에 필요한 지표가 2개 이상일 경우에는 각 metric 별로 요구하는 파드의 개수 중 최댓값을 택합니다. 아래의 예시에서 CPU metric을 사용한 경우의 파드의 개수는 4개이고 QPS(Query Per Second) metric을 사용한 경우 파드의 개수는 (15 + 30 + 12) / 20 = 57 / 20 = 2.85 = 3 이므로 max(4, 3)은 4개가 되어 4개로 스케일링하게 됩니다.

 

 

Updating the Desired Replica count on the scaled resource

실제로 HPA가 Scaled Resource Object(ReaplicaSet, Deployments, ReplicationControllers, StatefulSets)의 replicas를 업데이트하는 데 있어서 해당 오브젝트의 구체적인 스펙을 알지 못해도 되도록, HPA와 Scaled Resource Object 사이에는 "Scale"이라는 SubResource를 두어 추상화합니다. 실제로 HPA는 위에서 설명한 계산 메커니즘을 토대로 Scale의 Replica Count만 조절하고 Scale 리소스는 내부의 Object의 Replicas field를 업데이트합니다.

 

 

 

Understanding the whole autoscaling process

실제로 전체 AutoScaling Process는 다음과 같이 이루어집니다.

 

 

 

Scaling Based on CPU Utilization

AutoScaling을 적용하는 Metric중 가장 자주 사용되는 것이 바로 "파드 안에서 실행 중인 프로세스가 소비하는 CPU"입니다. (the amount of CPU consumed by the process running inside your pods). CPU 사용량이 100% 이상인 경우에는 더 이상 요청을 처리할 수 없기 때문에 적절한 Threshold를 설정하고 그 이상 넘어가면 Autoscaling 되도록 Horizontal Pod Autoscaler를 설정하는 것이 일반적입니다.

Always set the target CPU usage well below 100% (and definitely never above 90%) to leave enough room for handling sudden load spikes.

 

 

여기서 CPU 사용량이 100%가 되었다고 했을 때 이는 Request 리소스를 통해 생성된 파드의 Guaranteed CPU Amount를 의미합니다. (이전 포스팅에서 살펴보았듯, Request는 컨테이너가 필요로 하는 최소 CPU와 메모리의 양을 설정합니다.) 따라서 파드 생성 시 CPU Request가 100 milicore이고, Metric을 CPU 30%로 설정했다고 했을 때, 30 milicore이상 파드가 CPU를 사용하게 되면 Autoscaling 되는 것입니다. 이렇게 Request를 기준으로 Autoscaling 하기 때문에 파드를 Autoscaling 하려면 해당 파드에 대한 Request는 반드시 생성되어야 합니다.

 

 

HorizontalPodAutoscaler(HPA)는 다음과 같이 생성할 수 있습니다.

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: kubia
spec:
  maxReplicas: 5
  minReplicas: 1
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: kubia
  targetCPUUtilizationPercentage: 30
status:
  currentReplicas: 3
  desiredReplicas: 0
Always make sure to autoscale Deployments instead of the underlying ReplicaSets. This way, you ensure the desired replica count is preserved across application updates (remember that a Deployment creates a new ReplicaSet for each version). The same rule applies to manual scaling, as well.

Deployments 포스팅에서 다룬 것처럼 Deployment는 생성시에 내부적으로 ReplicaSet을 생성하여 Scaling 합니다. 따라서 HPA를 생성할 때 그 대상(Target)을 ReplicaSet이 아닌 Deployment로 설정하는 것이 좋습니다. ReplicaSet을 Target으로 설정하는 경우 Application이 업데이트될 때 Deployment가 새로운 ReplicaSet을 사용하게 된다면 이때 또 HPA를 ReplicaSet에 붙여야 하기 때문입니다.  

 

 

 

Scaling based on memory consumption

CPU와는 다르게 Memory는 Scaling하기가 쉽지 않습니다. 파드를 더 띄웠다고 해서 기존 파드(Old Pod)가 기존에 물고 있었던 메모리를 Release하지 않을 수도 있기 때문입니다. 이는 파드 레벨이 아닌 애플리케이션 레벨에서 컨트롤해야 하는 요소이며 따라서 애플리케이션 관리자가 이를 적절하게 관리하는 방법을 제공해야 합니다.  쿠버네티스 시스템 쪽에서 할 수 있는 것은 컨테이너를 Kill & Restart 한 후에 메모리를 예전보다 적게 쓰기를 바라는 것뿐입니다. (1.8 이전 버전)

 

쿠버네티스 1.8 버전부터는 Memory Based Autoscaling 방법을 제공하며 사용 방법은 CPU와 동일합니다. 

apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: yeoul-test
spec:
  maxReplicas: 4
  minReplicas: 1
  scaleTargetRef:
    apiVersion: extensions/v1beta1
    kind: Deployment
    name: yeoul-test-dp
  metrics:
  - type: Resource
    resource:
      name: memory
      targetAverageUtilization: 40

 

Scaling based on other custom metrics

HPA의 metrics 필드는 list형태이기 때문에 여러 개의 Metric을 정의해서 한꺼번에 사용할 수 있습니다. (위에서 살펴보았듯, 여러 개의 Metric을 사용하면 각 Metric을 통해 스케일링되어야 하는 파드의 개수 중 최댓값만큼 Autoscaling 됩니다.) 사용할 수 있는 Metric에는 3가지 타입이 있습니다.

 

Resource Metric Type

CPU Utilization과 같이 컨테이너의 Resource Request와 관련된 값들입니다. (CPU, Memory등등)

 

Pods Metric Type

파드와 직접적으로 관련된 값들입니다. QPS(Query Per Second)나 Queue안에 들어있는  Message Count 등입니다.

 

Object Metric Type

Object Metric Type은 파드와 직접적인 연관이 없는 요소들을 Metric으로 삼고자 할 때 사용합니다. 

spec:
  metrics:
  - type: Object
    resource:
      metricName: latencyMillis
      target:
        apiVersion: extensions/v1beta1
        kind: Ingress
        name: frontend
      targetValue: 20
  scaleTargetRef:
    apiVersion: extensions/v1beta1
    kind: Deployment
    name: kubia

 

Scaling down to zero replicas

HPA는 minReplicas값을 0으로 설정하는 것을 허용하지 않습니다. (책이 쓰였을 당시). 지금은 autoscale 명령어로 0을 설정하는 것이 가능하긴 하지만 0으로 설정해도 파드는 1개가 생성됩니다. 파드가 생성되고 아무것도 하지 않아도 0으로 scaledown 되지는 않습니다.

 

 

Horizontal scaling of cluster nodes

앞서 파드의 관점에서 AutoScaling을 살펴보았다면 이번에는 Cluster안의 노드의 관점에서 AutoScaling을 살펴봅니다. 파드 스케일링을 하다 보면 클러스터에 존재하는 노드를 다 사용하거나 여타 이유로 인해 스케줄링이 불가능해지는 상황이 올 수도 있습니다. 클라우드 환경의 경우(GCP, AWS 등등) 자동으로 노드를 생성하도록 노드 레벨에서 AutoScaling을 할 수 있는데 이를 담당하는 AutoScaler를 Cluster Autoscaler라고 합니다.

 

Introducing the Cluster Autoscaler

기존 노드의 리소스로 새로운 파드를 더 이상 생성할 수 없다고 판단되면 Cluster Autoscaler는 추가적인 노드를 프로비저닝 합니다. 또한 긴 시간 동안 특정 노드가 underutilized상태라고 판단되면 해당 노드를 de-provisioning 합니다.

 

Requesting Additional nodes from the cloud infrastructure

새로운 파드가 생성되었는데 이미 존재하는 노드에는 스케줄 할 수 없을 때 Cluster Autoscaler는 추가적인 노드를 프로비저닝 합니다. 새로운 노드를 프로비저닝 했을 때 해당 노드가 새로 생성될 파드를 스케줄 할 수 있어야 하므로(accomodatable) 대부분의 Cloud Provider들은 노드를 Grouping 하며, 새로운 노드를 프로비저닝 할 때, 프로비저닝 할 노드 타입을 명시해야 합니다. 

 

Cluster Autoscaler는 새로운 노드를 프로비저닝 하기 전에 새로 생성될 파드가 특정 노드 그룹에서 정상적으로 생성될 수 있는지를 확인합니다. 여러 개의 옵션이 가능할 경우에는 내부의 알고리즘에 따라 적절한 노드 타입을 선택하여 요청을 보냅니다. 새로운 노드가 프로비저닝 되면 새 노드의 Kublet이 API 서버에 리소스 생성을 요청하며, 해당 노드는 클러스터의 일부가 되어 동작하게 됩니다.

 

 

 

Relinquishing nodes

다음과 같은 조건을 모두 만족하면 해당 노드는 Unnecessary 하다고 판단되어 제거됩니다. (기본적으로 노드에서 실행 중인 파드의 Request가 적고, 다른 노드로 옮겨질 수 있으면 Unnecessary 하다고 판단하는 것입니다.)

 

  • 한 노드에 존재하는 모든 파드의 CPU / Memory Requests가 50% 이하이다.
  • 해당 노드에 System Pod가 없다.
  • 해당 노드에 Unmanaged Pod(수동으로 띄운 관리되지 않은 파드)가 없다.
  • 해당 노드에 LocalStorage가 붙어있는 파드가 없다

 

해당 노드가 Unnecessary하다고 판단되면 해당 노드는 unschedulable 하다고 마크되며, 안의 파드들은 모두 삭제됩니다. 해당 파드들은 모두 ReplicaSet에 의해 관리되고 있으므로(위의 조건에 의해) 삭제된 파드는 다른 노드에서 정상적으로 띄워집니다.

 

 

 

 

 

 

반응형