DevOps/Kubernetes

[Kubernetes] HPA : Horisontal Pod AutoScaler (파드의 오토 스케일링)

TTOII 2022. 6. 4. 12:24
728x90

🚀 HPA : Horisontal Pod AutoScaler

Horizontal Pod Autoscaling | Kubernetes

지금까지 우리는 kubectl scale, apply, replace, edit, patch 등의 명령어를 이용해서 ReplicaSet, Deployment 또는 StatefulSet과 같은 리소스들의 replicas 개수를 변경할 수 있었다.

 

앞선 방식은 스케일링을 수동으로 하는 것이고 Horisontal Pod AutoScaler(HPA)는 자동 스케일링하는 것이다.

HPA : Deployment, ReplicaSet, StatefulSet의 복제본 개수를 조정
세가지의 공통점은 모두 replicas를 사용한다는 것이다.

  • Scale out(Horisontal) : 개수를 여러개 세팅하는 것
  • Scale up(Vertical) : 크기를 늘리는 것
    • VM의 경우 : cpu, memory 양을 더 많이 할당해 구성하는 것
    • BM의 경우 : cpu, memory HW를 더 많이 장착하는 것

📌 AutoScaling 가능한 대상

  • Pod
    • HPA
    • VPA : Vertical Pod Autoscaler
  • Node
    • ClusterAutoScaler

VPA나 ClusterAutoScaler는 클라우드 환경에서만 가능하다.
VPA는 수직적으로 파드의 크기를 조정하는 것인데 여기서 수직적의 의미는 무엇인가 ?
requests, limits를 의미하며 이것을 실시간으로 조정할 수 있는 것을 VPA라고 한다.

VPA는 add-on이라서 클라우드에서 따로 구성해야 한다.

 

ClusterAutoScaler는 필요하다면 워커 노드를 더 많이 자동으로 할당할 수 있다.
모든 클라우드 환경에서 지원하며 마찬가지로 add-on이다.

AWS의 AutoScaling 기능을 쿠버네티스에 접목시킨 개념이다.

 

ReplicaSet, Deployment, StatefulSet로 파드를 만들었는데 더이상 모든 노드에 리소스 용량이 없어서 배치할 수 없거나 아주 큰 용량의 파드를 요청하면 ClusterAutoScaler는 워커 노드를 만들어준다.

즉, 워커 노드의 수를 늘이고 줄일 수 있다.

 

 vagrant@k8s-node1  ~/autoscaling/hpa  kubectl api-resources | grep hpa
horizontalpodautoscalers          hpa          autoscaling/v1                         true         HorizontalPodAutoscaler

shortname은 hpa autoscaling 그룹의 v1이다. 네임스페이스를 사용한다.

 

 vagrant@k8s-node1  ~/autoscaling/hpa  kubectl explain hpa.spec

FIELDS:
   maxReplicas  <integer> -required-

   minReplicas  <integer>

   scaleTargetRef       <Object> -required-

   targetCPUUtilizationPercentage       <integer>
  • maxReplicas : 최대 복제본 개수
  • minReplicas : 최소 복제본 개수, 기본값은 1이며 0도 설정 가능하다.
  • scaleTargetRef : 스케일링을 하기위한 참조 대상, 즉 Deploy, RS, STS
  • targetCPUUtilizationPercentage : cpu 사용률이 얼마를 넘어가면 스케일링 할 것인가

 vagrant@k8s-node1  ~/autoscaling/hpa  kubectl explain hpa.spec.scaleTargetRef
FIELDS:
   apiVersion   <string>

   kind <string> -required-

   name <string> -required-

kind : Deploy, RS, STS 중에 어떤 종류인가 ?
name : 해당되는 리소스의 이름

 

🚀 알고리즘 세부 정보

현재 replicas : 3이고 CPU : 50%를 넘어가면 스케일링하라는 기준을 만들어놨다고 하자
그리고 현재 메트릭 값이 70%라고 하자

가장 기본적인 관점에서, HorizontalPodAutoscaler 컨트롤러는 원하는(desired) 메트릭 값과 현재(current) 메트릭 값 사이의 비율로 작동한다.

원하는 레플리카 수 = ceil[현재 레플리카 수 * ( 현재 메트릭 값 / 원하는 메트릭 값 )]

그러면 3 * (100 / 50) = 6개
HPA는 RS에게 replicas를 6으로 바꾸라고 한다. 그러면 spec.replicas 값을 3 → 6으로 자동으로 고친다.

그러나 maxReplicas값은 넘을 수 없다.

 

maxReplicas는 10개, minReplicas는 1개, targetCPUUtilizationPercentage를 70%이며

현재 3개라고 가정하면 70% 미만이되면 일정시간이 지났을 때 스케일인을 하게된다.

3 1로 즉, minReplicas값까지 떨어뜨린다. 비용을 줄이기 위해서이다.

 

🚀 워크로드 스케일링의 안정성

오토 스케일링의 최악의 상황은 targetCPUUtilizationPercentage를 70%로 맞춰 놓았을 때 계속 임계값을 넘나드는 상황이다.

그런 상황은 스케일인과 스케일아웃이 무한 반복되는 현상이 나타날 수 있다.

 

너무 잦은 스케일링을 하게되면 App에 역효과가 날 수 있다.
스케일링 하는데 리소스가 많이 소모된다. → 오버헤드가 발생한다.

이런 현상은 thrashing 또는 flapping 라고 한다.

 

생각보다 이런 현상이 자주 발생한다. 우리가 기준을 잡을 때 일반적인 사용량보다 조금 높게 잡기 때문이다.

용자가 조금만 많아지거나 적어지면 구간 사이를 계속 flapping 한다.

 

안정화 윈도우 설정

behavior:
  scaleDown:
    stabilizationWindowSeconds: 300

기본적으로 설정된 시간은 300초이다.
70%의 기준이 넘어가도 300초가 지나야한다.
이때 최악의 상황은 70%를 넘었는데 299초의 순간 임계값보다 떨어진다면 스케일링하지 않는 것이다.

윈도우라는 것은 특정 간격을 의미한다.

 

🚀 실습

직접 구성해보자
myweb-deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myweb-deploy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: myweb
          image: ghcr.io/c1t1d0s7/go-myweb:alpine
          ports:
            - containerPort: 8080

 

myweb-hpa.yaml

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: myweb-hpa
spec:
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myweb-deploy
 vagrant@k8s-node1  ~/cont/sts3  kubectl get deploy,rs,po,hpa
NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/myweb-deploy             2/2     2            2           2m26s
deployment.apps/nfs-client-provisioner   1/1     1            1           2d7h

NAME                                                DESIRED   CURRENT   READY   AGE
replicaset.apps/myweb-deploy-657f957c85             2         2         2       2m26s
replicaset.apps/nfs-client-provisioner-758f8cd4d6   1         1         1       2d7h

NAME                                          READY   STATUS    RESTARTS   AGE
pod/myweb-big                                 0/1     Pending   0          85m
pod/myweb-deploy-657f957c85-862lz             1/1     Running   0          2m26s
pod/myweb-deploy-657f957c85-mvmmw             1/1     Running   0          2m26s
pod/nfs-client-provisioner-758f8cd4d6-wpjbt   1/1     Running   0          2d7h

NAME                                            REFERENCE                 TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/myweb-hpa   Deployment/myweb-deploy   <unknown>/50%   1         10        2          2m26s

HPA가 참조하는 리소스(myweb-deploy)가 보인다.
<unknown>/50% : 현재 값은 약간의 시간이 지나야 숫자로 나타나며 50% 기준에 맞춰져 있는 것이다.
최소, 최대, 현재 파드의 개수를 보여준다.

 

파드는 kubelet이 관리한다. kubelet이 파드의 사용량을 측정한다.
kubelet 내에는 cadvisor App이 포함되어 있다.

cadvisor가 파드의 리소스 사용량을 측정해서 가져오고 HPA에게 전달한다.

 

 vagrant@k8s-node1  ~/cont/sts3  kubectl get hpa             
NAME        REFERENCE                 TARGETS         MINPODS   MAXPODS   REPLICAS   AGE
myweb-hpa   Deployment/myweb-deploy   <unknown>/50%   1         10        2          6m13s

 vagrant@k8s-node1  ~/autoscaling/hpa  kubectl describe hpa myweb-hpa
Events:
  Type     Reason          Age                  From                       Message
  ----     ------          ----                 ----                       -------
  Warning  FailedGetScale  11s (x9 over 2m11s)  horizontal-pod-autoscaler  no matches for kind "Deployment" in group ""

하지만 시간을 기다려도 <unknown>에 숫자가 나타나지 않을 때는 YAML 파일에 scaleTargetRef.apiVersion값이 세팅되어 있는지 확인해야한다.

 

Conditions:
  Type           Status  Reason                   Message
  ----           ------  ------                   -------
  AbleToScale    True    SucceededGetScale        the HPA controller was able to get the target's current scale
  ScalingActive  False   FailedGetResourceMetric  the HPA was unable to compute the replica count: failed to get cpu utilization: missing request for cpu

Events:
  Type     Reason                        Age                     From                       Message
  ----     ------                        ----                    ----                       -------
  Warning  FailedComputeMetricsReplicas  3m42s (x12 over 6m27s)  horizontal-pod-autoscaler  invalid metrics (1 invalid out of 1), first error is: failed to get cpu utilization: missing request for cpu
  Warning  FailedGetResourceMetric       87s (x21 over 6m27s)    horizontal-pod-autoscaler  failed to get cpu utilization: missing request for cpu

수정한 결과 AbleToScale 타겟을 찾았음을 알 수 있다.
FailedGetResourceMetric 오류가 발생했으며

Events 목록에서 보면 failed to get cpu utilization: missing request for cpu라는 메세지가 출력된다.

 

아무리 기다려도 <unknown> 상태에서 측정되지 않는다. 측정이 되지 않으면 스케일링 자체가 불가능하다.

대상이되는 시스템에 requests가 세팅되어 있어야 한다.

 

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myweb-deploy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: myweb
          image: ghcr.io/c1t1d0s7/go-myweb:alpine
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: 200m
            limits:
              cpu: 200m
 vagrant@k8s-node1  ~/autoscaling/hpa  kubectl get hpa
NAME        REFERENCE                 TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
myweb-hpa   Deployment/myweb-deploy   0%/50%    1         10        2          22m
 vagrant@k8s-node1  ~/autoscaling/hpa  kubectl describe deployments.apps myweb-deploy 
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  23m   deployment-controller  Scaled up replica set myweb-deploy-657f957c85 to 2
  Normal  ScalingReplicaSet  73s   deployment-controller  Scaled up replica set myweb-deploy-9c9c8bbf to 1
  Normal  ScalingReplicaSet  71s   deployment-controller  Scaled down replica set myweb-deploy-657f957c85 to 1
  Normal  ScalingReplicaSet  71s   deployment-controller  Scaled up replica set myweb-deploy-9c9c8bbf to 2
  Normal  ScalingReplicaSet  69s   deployment-controller  Scaled down replica set myweb-deploy-657f957c85 to 0

스케일링이 되고 있는 것을 확인할 수 있다.

 

 vagrant@k8s-node1  ~/autoscaling/hpa  kubectl get deploy,rs,po,hpa
NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/myweb-deploy             2/2     2            2           25m
deployment.apps/nfs-client-provisioner   1/1     1            1           2d8h

NAME                                                DESIRED   CURRENT   READY   AGE
replicaset.apps/myweb-deploy-657f957c85             0         0         0       25m
replicaset.apps/myweb-deploy-9c9c8bbf               2         2         2       3m35s
replicaset.apps/nfs-client-provisioner-758f8cd4d6   1         1         1       2d8h

NAME                                          READY   STATUS    RESTARTS   AGE
pod/myweb-deploy-9c9c8bbf-47prm               1/1     Running   0          3m35s
pod/myweb-deploy-9c9c8bbf-9tl59               1/1     Running   0          3m33s
pod/nfs-client-provisioner-758f8cd4d6-wpjbt   1/1     Running   0          2d8h

NAME                                            REFERENCE                 TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/myweb-hpa   Deployment/myweb-deploy   0%/50%    1         10        2          25m

 

🚀 과부하 테스트

HorizontalPodAutoscaler 연습 | Kubernetes

 vagrant@k8s-node1  ~/autoscaling/hpa  kubectl get pod    
NAME                                      READY   STATUS    RESTARTS   AGE
myweb-deploy-6dcf6c95c6-8k59t             1/1     Running   0          19s
myweb-deploy-6dcf6c95c6-nnwff             1/1     Running   0          19s
nfs-client-provisioner-758f8cd4d6-wpjbt   1/1     Running   0          2d8h
 vagrant@k8s-node1  ~/autoscaling/hpa  kubectl exec myweb-deploy-6dcf6c95c6-8k59t -- sha256sum /dev/zero

파드에 부하를 걸어준다.

 

 vagrant@k8s-node1  ~/autoscaling/hpa  kubectl top pods
NAME                                      CPU(cores)   MEMORY(bytes)   
myweb-deploy-6dcf6c95c6-8k59t             201m         1Mi             
myweb-deploy-6dcf6c95c6-nnwff             0m           1Mi             
nfs-client-provisioner-758f8cd4d6-wpjbt   2m           10Mi 

부하를 걸어준 파드는 더이상 cpu 사용량이 늘어나지 않는다.
현재 총 2개의 파드 중에 하나의 파드가 cpu 200m을 모두 사용하고 있으며

다른 하나의 파드는 사용량이 없으므로 전체 실행할 수 있는 용량 400m 중에 200m을 사용하고 있어 스케일링이 되지 않는다.

 

 vagrant@k8s-node1  ~/autoscaling/hpa  kubectl exec myweb-deploy-6dcf6c95c6-nnwff -- sh
a256sum /dev/zero

여기서 다른 하나의 파드에도 부하를 걸어준다.

 

Every 1.0s: kubectl get po,hpa                           k8s-node1: Thu May 26 13:50:36 2022

NAME                                          READY   STATUS    RESTARTS   AGE
pod/myweb-deploy-6dcf6c95c6-8k59t             1/1     Running   0          5m17s
pod/myweb-deploy-6dcf6c95c6-nnwff             1/1     Running   0          5m17s
pod/myweb-deploy-6dcf6c95c6-qnkvf             1/1     Running   0          77s
pod/myweb-deploy-6dcf6c95c6-wl2w4             1/1     Running   0          92s
pod/nfs-client-provisioner-758f8cd4d6-wpjbt   1/1     Running   0          2d8h

NAME                                            REFERENCE                 TARGETS   MINPODS
  MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/myweb-hpa   Deployment/myweb-deploy   50%/50%   1
  10        4          5m17s

그리고 일정 시간이 지나게되면 스케일링이 진행되고 복제본 개수를 늘린다.
2개의 파드 모두가 cpu 200m씩을 사용하고 있기 때문에 1개의 파드가 추가될 것이다.

추가된 1개의 파드를 고려하더라도 전체 실행할 수 있는 용량 600m 중에 400m을 사용하고 있어 약 66%를 사용하게 된다. 때문에 또 다시 1개의 파드가 추가된다.

결국 복제본 개수가 4개가 되며 전체 실행 가능한 cpu 용량이 800m이 되고

그 중 2개의 파드가 200m씩 사용하고 있으므로 50%가 된다.

과부하 테스트를 종료하면 cpu 사용률이 떨어질 것이고 최대 300초 동안 기다렸다가 스케일인을 한다.

스케일 아웃 : 180초
스케인 인 : 300초

 

🚀 v2beta2

 vagrant@k8s-node1  ~/autoscaling/hpa  kubectl api-versions | grep auto       
autoscaling/v1
autoscaling/v2beta1
autoscaling/v2beta2

우리는 autoscaling/v1을 사용하고 있으며 다른 버전도 존재한다.

 

 vagrant@k8s-node1  ~/autoscaling/hpa  kubectl explain hpa.spec --api-version autoscaling/v2beta2

FIELDS:
   behavior     <Object>

   maxReplicas  <integer> -required-

   metrics      <[]Object>

   minReplicas  <integer>

   scaleTargetRef       <Object> -required-

v2beta2에 관련된 리소스를 볼 수 있다.

behavior는 v1에 존재하지 않는다. 사용하기 위해서는 v2beta2 버전을 사용해야 한다.

targetCPUUtilizationPercentage대신 metrics가 있다.

 

v2beta2부터 개선된 사항으로 cpu뿐만 아니라 다양한 메트릭을 참조할 수 있다.
물론 스케일링시에는 cpu를 제일 많이 체크한다.

web 또는 DB에서 요청과 응답을 할 때 cpu가 높아지는게 대부분이기 때문이다.

메모리 DB가 아닌 이상 메모리가 높아지는 경우는 없다.

그럼에도 cpu 뿐만 아니라 다양한 다른 메트릭을 체크할 수 있도록 만들었다.

 

간단한 예제를 만들어보자
myweb-hpa-v2beta2.yaml

apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: myweb-hpa
spec:
  minReplicas: 1
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization # 사용률(%)
          avarageUtilization: 50 # 평균 사용률
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myweb-deploy

 

myweb-hpa.yaml

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: myweb-hpa
spec:
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myweb-deploy

myweb-hpa-v2beta2.yamlmyweb-hpa.yaml은 똑같이 작동한다.

728x90