[Kubernetes] HPA : Horisontal Pod AutoScaler (파드의 오토 스케일링)
🚀 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
- 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, STStargetCPUUtilizationPercentage
: 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.yaml
과 myweb-hpa.yaml
은 똑같이 작동한다.