HPA (Horizontal Pod Autoscaler)는 Deployment나 StatefulSet 등의 워크로드의 레플리카 갯수를 수요에 맞게 자동으로 스케일링하도록 한다.
수평 스케일링은 레플리카를 늘리고 줄이는 것을 의미한다. 통상적으로 레플리카를 늘리는 것을 scale-out, 줄이는 것을 scale-in 이라고 부른다. 수직 스케일링이라는 것도 있는데, 이는 레플리카를 조절하는 것이 아닌 CPU 및 메모리 등 서버의 스펙을 늘리거나 줄이는 것을 의미한다. (이는 scale-up / scale-down 으로 불린다.)
HPA 동작 방식
쿠버네티스에는 HPA를 지원하기 위해 HorizontalPodAutoscaler API와 이에 대응하는 컨트롤러를 제공한다. HorizontalPodAutoscaler에서는 파드 수평 스케일링의 대한 정보를 기술할 수 있으며 대표적으로는 다음 정보를 기술한다.
- HPA를 적용시킬 워크로드
- HPA 적용 기준 (메트릭)
- 최소 / 최대 레플리카 갯수
- 오토스케일링 세부 행위
HorizontalPodAutoscaler에 대응하는 컨트롤러는 kube-controller-manager
에 구현이 되어있으며, 컨트롤러는 주기적으로 클러스터에 선언된 HorizontalPodAutoscaler을 확인한다. HorizontalPodAutoscaler를 확인하는 주기는 kube-controller-manager
의 --horizontal-pod-autoscaler-sync-period
파라미터에 의해 결정된다.
컨트롤러는 주기적으로 다음과 같이 HorizontalPodAutoscaler를 확인하여 파드 오토스케일링을 진행한다.
- 각 주기마다 컨트롤러는 HorizontalPodAutoscaler에 지정된 타겟을 찾는다. 타겟은 결국 파드집합이다. (
spec.scaleTargetRef
) - HorizontalPodAutoscaler에서 지정된 메트릭을 확인하여, 타겟 파드의 리소스 사용률 평균을 구한다. (
spec.metrics
)- 여기서 리소스는 기본적으로는 CPU, Memory 가 있고, 그 외의 다른 커스텀 메트릭을 정의하여 사용할 수도 있다.
- 타겟 파드의 리소스 사용률에 따라 아래의 식에 따라 레플리카 갯수를 변경한다. 여기서 메트릭 값에 해당하는 것은 파드 리소스의
requests
이며limits
은 관계가 없다.
원하는 레플리카 수 = ceil[현재 레플리카 수 * ( 현재 메트릭 값 / 원하는 메트릭 값 )]
- ex) 메모리를 기반으로 사용율을 따질 때, 파드 메모리 requests가 1Gi 이고, 실제 메모리 사용량이 512Mi 라면 사용률은 0.5이다.
예를 들어, 레플리카의 갯수가 3이고, CPU 사용율 기준을 80%으로 설정하였을 때 평균 CPU 사용율에 따른 레플리카 수 변화는 다음과 같다.
현재 레플리카 수 | 현재 CPU 사용율 | 원하는 CPU 사용율 | 원하는 레플리카 수 | 계산 식 |
---|---|---|---|---|
3 | 75% | 80% | 3 | ceil(3*75/80) = ceil(2.81) |
3 | 90% | 80% | 4 (scale-out) | ceil(3*90/80) = ceil(3.375) |
4 | 67.5% | 80% | 4 | Ceil(4*67.5/80) = ceil(3.375) (scale-out 직후) |
4 | 65% | 80% | 4 | Ceil(4*65/80) = ceil(3.25) |
4 | 50% | 80% | 3 (scale-in) | Ceil(4*50/80) = ceil(2.5) |
3 | 66.7% | 80% | 3 | Ceil(3*66.7/80) = ceil(2.5) (scale-in 직후) |
3 | 45% | 80% | 2 (scale-in) | Ceil(3*45)=ceil(1.69) |
2 | 67.5% | 80% | 2 | Ceil(2*67.5)=ceil(1.69) (scale-in 직후) |
Metric과 Metric Server
위의 동작방식에서 컨트롤러는 HorizontalPodAutoscaler에 지정된 메트릭에 대한 타겟 파드의 리소스 사용율을 구해야 하는데, 이 메트릭은 당연히 컨트롤러가 읽을 수 있어야 한다. 쿠버네티스에서는 컨트롤러가 HPA에서 사용할 수 있는 메트릭을 가져오기 위해서 지정된 API를 사용하는데, 이 종류로는 metrics.k8s.io
, custom.metrics.k8s.io
, external.metrics.k8s.io
가 있다. 다르게 말하면, metrics.k8s.io
, custom.metrics.k8s.io
, external.metrics.k8s.io
API를 사용할 수 있어야지만 HPA 기능을 사용할 수 있다.
하지만, 기본적으로는 쿠버네티스 클러스터에 metrics.k8s.io
, custom.metrics.k8s.io
, external.metrics.k8s.io
API는 제공되어있지 않다. 클러스터에서 지원하는 API Resource를 확인하기 위해서 아래 명령어를 사용해보자. 항목에 위의 API가 없다면 당장은 HPA 기능은 사용할 수 없을 것이다.
$ kubectl api-resources
# metrics가 포함된 API Resources 필터링
$ kubectl api-resources | grep metrics
위의 API는 애드온에 의해 제공되며, 별도로 애드온을 설치해야 한다. 가장 간단하게는 Metrics Server를 설치하면 해당 API Resource를 사용할 수 있게 된다. 아래의 명령어를 통해 간단히 설치할 수 있다.
$ kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
만약 metrics-server 설치 시 아래 에러가 발생한다면 deployment에 arguments를 하나 추가해야 한다.
E0104 09:04:26.156491 1 scraper.go:149] "Failed to scrape node" err="Get \"https://192.168.35.167:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 192.168.35.167 because it doesn't contain any IP SANs" node="k8s-cp1"
E0104 09:04:26.159954 1 scraper.go:149] "Failed to scrape node" err="Get \"https://192.168.35.2:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 192.168.35.2 because it doesn't contain any IP SANs" node="k8s-wn1"
E0104 09:04:26.160729 1 scraper.go:149] "Failed to scrape node" err="Get \"https://192.168.35.183:10250/metrics/resource\": tls: failed to verify certificate: x509: cannot validate certificate for 192.168.35.183 because it doesn't contain any IP SANs" node="k8s-wn2"
$ kubectl edit deploy metrics-server -n kube-system
spec:
template:
spec:
containers:
- args:
- ...
- --kubelet-insecure-tls # 이 옵션 추가
metrics-server를 성공적으로 설치하였다면 api-resources 항목이 추가될 것이다.
$ kubectl api-resources | grep metrics
nodes metrics.k8s.io/v1beta1 false NodeMetrics
pods metrics.k8s.io/v1beta1 true PodMetrics
- metrics-server는
metrics.k8s.io
API만 제공한다. custom.metrics.k8s.io
는 사용자 정의 메트릭으로, 별도의 메트릭 백엔드에 대한 어댑터 서버에서 제공한다. 대표적으로는 prometheus-adapter 가 있다. prometheus를 사용하는 경우라면 이것을 사용하여 커스텀 메트릭을 활용한 HPA도 사용할 수 있다.external.metrics.k8s.io
은 외부 메트릭으로 이것도 메트릭 어댑터 서버가 필요하다. prometheus-adapter는 해당 API 또한 제공한다.
보너스로 metrics-server를 생성하였다면 kubectl top
명령어를 사용하여 파드, 노드의 리소스 사용량을 확인할 수 있다.
$ kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
k8s-cp1 108m 5% 1159Mi 61%
k8s-wn1 32m 1% 1505Mi 39%
k8s-wn2 45m 2% 1590Mi 41%
$ kubectl top pod
NAME CPU(cores) MEMORY(bytes)
todo-api-f75444874-hhzvd 5m 292Mi
HPA 적용하기
metrics-server 까지 설치하였다면 HPA를 사용할 준비는 완료되었다. 이제 실제로 HPA를 적용해보자. 아래 명령어를 통해 간단히 HorizontalPodAutoscaler를 생성할 수 있다.
$ kubectl autoscale deploy/todo-api --cpu-percent=50 --min=1 --max=5
horizontalpodautoscaler.autoscaling/todo-api autoscaled
- CPU 기반 HPA를 적용하였으며, 평균 CPU 사용율 50%을 기준으로 오토스케일이 이루어진다.
- 오토스케일링에 의해 결정되는 최소 레플리카는 1개, 최대 레플리카는 5대로 설정하였다.
- 적용시킬 대상은 todo-api Deployment이며, 해당 파드의 CPU Requests는 정의되어 있어야 한다.
HPA 추가 후에 아래 명령어로 HPA를 확인할 수 있다.
$ kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
todo-api Deployment/todo-api 1%/50% 1 5 1 114s
HPA를 생성한 후 해당 파드에 100ms 주기로 계속 요청하여 부하테스트를 간단히 진행해보자.
$ while true; do; curl -XGET http://todo-api.beer1.com/todoList; sleep 0.1; done
- ingress를 만들어서 todo-api.beer1.com 도메인으로 요청오는 경우 todo-api 서비스로 라우팅하도록 구성하였다.
- 물론 port-forward 명령어를 통해 파드에 요청을 날리는 것도 가능하지만, 레플리카가 여러개로 늘어나더라도 하나의 파드에만 부하가 가기 떄문에 HPA 부하 테스트로는 적합하지 않을 수 있다. (port-forward svc도 마찬가지다.)
아래 명령어를 사용하여 hpa의 변화를 관찰해보자.
$ kubectl get hpa -w
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
todo-api Deployment/todo-api 5%/50% 1 5 1 50m
todo-api Deployment/todo-api 37%/50% 1 5 1 50m
todo-api Deployment/todo-api 86%/50% 1 5 1 50m # scale-out 시작
todo-api Deployment/todo-api 44%/50% 1 5 2 50m
todo-api Deployment/todo-api 26%/50% 1 5 2 51m
todo-api Deployment/todo-api 80%/50% 1 5 2 51m # scale-out 시작
todo-api Deployment/todo-api 51%/50% 1 5 4 51m
todo-api Deployment/todo-api 28%/50% 1 5 4 51m # 부하테스트 종료
todo-api Deployment/todo-api 5%/50% 1 5 4 52m
todo-api Deployment/todo-api 7%/50% 1 5 4 52m
todo-api Deployment/todo-api 6%/50% 1 5 4 52m
todo-api Deployment/todo-api 5%/50% 1 5 4 53m
todo-api Deployment/todo-api 4%/50% 1 5 4 53m
todo-api Deployment/todo-api 6%/50% 1 5 4 53m
todo-api Deployment/todo-api 5%/50% 1 5 4 53m
todo-api Deployment/todo-api 8%/50% 1 5 4 54m
todo-api Deployment/todo-api 6%/50% 1 5 4 54m
todo-api Deployment/todo-api 15%/50% 1 5 4 55m
todo-api Deployment/todo-api 5%/50% 1 5 4 55m
todo-api Deployment/todo-api 7%/50% 1 5 4 55m
todo-api Deployment/todo-api 5%/50% 1 5 4 55m
todo-api Deployment/todo-api 6%/50% 1 5 4 56m
todo-api Deployment/todo-api 5%/50% 1 5 4 56m # scale-in 시작
todo-api Deployment/todo-api 7%/50% 1 5 2 56m
todo-api Deployment/todo-api 6%/50% 1 5 2 57m
todo-api Deployment/todo-api 5%/50% 1 5 2 57m
todo-api Deployment/todo-api 12%/50% 1 5 2 57m
todo-api Deployment/todo-api 5%/50% 1 5 2 58m
todo-api Deployment/todo-api 7%/50% 1 5 2 59m
todo-api Deployment/todo-api 5%/50% 1 5 2 59m
todo-api Deployment/todo-api 4%/50% 1 5 2 59m
todo-api Deployment/todo-api 5%/50% 1 5 2 60m # scale-in 시작
todo-api Deployment/todo-api 6%/50% 1 5 1 60m
- 첫 번째 scale-out이 시작되는 시점은 리소스 사용율이 86%가 되는 시점이다. 이 때는
ceil(1*86/50) = ceil(1.72)
이기 때문에 레플리카가 2대로 조정된다. - 두 번째 scale-out이 시작되는 시점은 레플리카 2일 때 리소스 사용율이 80%가 되는 시점이다. 이 때는
ceil(2*80/50) = ceil(3.2)
이기 때문에 레플리카가 4대로 조정된다. - 첫 번째로 scale-in이 시작되는 시점은 부하테스트가 종료되고 약 5분 뒤에 시작이 된다. 이 때는
ceil(4*5/50) = ceil(0.4)
여서 레플리카가 1대여야 하는데 2대로 조정된다. 그리고 이미 부하테스트가 종료된 후에 CPU 사용율이 줄어들었기 때문에 더 빨리 레플리카가 줄어들어야 하는데 그렇지 않았다. - 두 번째로 scale-in이 시작되는 시점은 첫 번째 scale-in이 시작된 후 약 4분 뒤에 시작되었다.
테스트를 통해 알 수 있었던 것은 계산 식 결과 scale-out이 되는 경우는 즉각 반영되지만 scale-in이 되는 경우는 즉각적으로 반영이 되지 않는다. 이유는 scale-in의 경우에는 약간의 보정이 들어가는데, kube-controller-manager의 --horizontal-pod-autoscaler-downscale-stabilization
옵션 떄문이다.
리소스 사용률이 scale-in 조건에 만족하는 경우 해당 옵션의 값만큼 시간이 지난 후에도 여전히 scale-in 조건에 만족한다면 그 때 실제로 scale-in을 진행한다. 기본값으로는 5분(5m
) 이며 scale-in이 점진적으로 발생하여 급격히 변동하는 메트릭 값의 영향을 완만하게 해준다.
- 여기서 생각해봐야 할 점은 scale-out은 즉시이고, scale-in은 평가시간을 길게 잡는것으로 되어있으며 두 행위에 대한 차이가 있다.
- scale-out을 하는 경우, 실질적으로 가용 레플리카가 늘어나는 시점은 추가 레플리카가 모두 ready 상태가 되는 시점이다. 즉, scale-out이 즉시 되는 것이 아니기 때문에 부하가 많이 발생하면 즉시 레플리카를 요청하는게 유리하기 때문이 아닐까 생각한다.
- scale-in 조건을 만족하는 경우, 아주 운좋게 평가 시점에만 리소스 사용량이 감소했다면 scale-in이 즉시 일어난 이후에는 여전히 요청이 많을 것이며 다시 scale-out을 해야하는 경우가 발생할 수 있다. scale-out -> ready 상태로 가는 시간적 비용이 따르기 떄문에 scale-in에 대한 평가는 오래하는 것이 유리할 것으로 보고 있다.
마무리
지금까지 쿠버네티스 파드 오토 스케일링 기능에 대해 알아보았다. 이번 장에서 알아본 내용은 아주 기본적인 내용이며, 추후에는 HPA 각각에 대한 scale-out 및 scale-in에 대한 세부 동작을 정의하는 방법과, 커스텀 리소스를 사용하여 오토스케일링을 하는 방식을 다뤄볼 예정이다.
2025.01.05 - [DevOps/Kubernetes] - Kubernetes HPA (파드 오토스케일링) [2] - 동작 제어
'DevOps > Kubernetes' 카테고리의 다른 글
Kubernetes HPA (파드 오토스케일링) [2] - 동작 제어 (0) | 2025.01.05 |
---|---|
Kubernetes Scheduling - Node Affinity (0) | 2024.05.02 |
Kubernetes Scheduling 소개 및 NodeSelector (1) | 2024.05.01 |
[1] 쿠버네티스 확장 - kubectl plugin (0) | 2023.10.29 |
Helm Chart Repository 만들기 (2) - Harbor OCI registry (0) | 2023.10.12 |
댓글