Horizontal Pod AutoScaler (HPA)
HorizontalPodAutoscaler로 Kubernetes 워크로드를 자동 조정하여 자원 할당 최적화 및 비용 절감 실현.
개요
HorizontalPodAutoscaler(약어: HPA)는 워크로드 리소스(예: 디플로이먼트 또는 스테이트풀셋)를 자동으로 업데이트하며, 워크로드의 크기를 수요에 맞게 자동으로 스케일링하는 것을 목표로 합니다. 수평 스케일링은 부하 증가에 대해 파드를 더 배치하는 것을 뜻하며 이는 수직 스케일링(쿠버네티스에서는, 해당 워크로드를 위해 이미 실행 중인 파드에 더 많은 자원(예: 메모리 또는 CPU)를 할당하는 것)과는 다르게 동작합니다.
부하량이 줄어들고, 파드의 수가 최소 설정값 이상인 경우, HorizontalPodAutoscaler는 워크로드 리소스(디플로이먼트, 스테이트풀셋, 또는 다른 비슷한 리소스)에게 스케일 다운을 지시한다.
이 문서는 예제 웹 앱의 크기를 자동으로 조절하도록 HorizontalPodAutoscaler를 설정하는 예시를 다루며. 이 예시 워크로드는 PHP 코드를 실행하는 아파치 httpd이다.
HPA (Horizontal Pod Autoscaler) 핵심 정리
graph LR
A[메트릭 모니터링] --> B[파드 수 자동조정] --> C[리소스 최적화]
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
minReplicas: 1 # 최소 파드 수
maxReplicas: 10 # 최대 파드 수
metrics:
- type: Resource # CPU/Memory
target: 50% # 목표 사용률
[계산 공식]
필요한 파드 수 = ceil(현재 파드 수 × (현재 메트릭/목표 메트릭))
[주요 메트릭]
- CPU 사용률
- 메모리 사용률
- Custom Metric
[모니터링]
kubectl get hpa
테스트 환경 설정
Pre-install
이 예제를 실행하기 위해, 클러스터에 Metrics Server가 배포 및 구성되어 있어야 합니다. 쿠버네티스 Metrics Server는 클러스터의 kubelet으로부터 리소스 메트릭을 수집하고, 수집한 메트릭을 쿠버네티스 API를 통해 노출시키며, 메트릭 수치를 나타내는 새로운 종류의 리소스를 추가하기 위해 API Service를 사용할 수 있습니다.
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
고가용성 모드
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/high-availability-1.21+.yaml
Metrics Server를 실행하는 방법을 보려면 metrics-server 문서를 참고해주세요.
각 필요한 설정들을 변경하셔서 구성하시는 것을 권장드립니다.
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
name: bjchoi-seoul-eks-2025-01-06
region: ap-northeast-2
version: "1.30"
# self-managed 컴포넌트
iam:
withOIDC: true
serviceAccounts:
# ALB Controller를 위한 ServiceAccount
- metadata:
name: aws-load-balancer-controller
namespace: kube-system
labels:
app.kubernetes.io/component: controller
attachPolicyARNs:
- "arn:aws:iam::759320821027:policy/AWSLoadBalancerControllerIAMPolicy_bjchoi"
roleName: 20250106_AWSLoadBalancerControllerIAMPolicy_bjchoi
roleOnly: false
vpc:
id: vpc-007f6f62fffdf5b23
clusterEndpoints:
privateAccess: true
publicAccess: true
publicAccessCIDRs: [1.214.42.138/32, 14.38.23.41/32]
subnets:
public:
bjchoi-test-subnet-pub-ap-northeast-2a:
id: subnet-0f2ea19aba9663c70
bjchoi-test-subnet-pub-ap-northeast-2c:
id: subnet-044a364a5a8b672e7
managedNodeGroups:
- name: bjchoi-eks-node
maxPodsPerNode: 110
labels: { name: worker-node }
instanceType: m5.large
desiredCapacity: 1
maxSize: 4
volumeSize: 20
volumeType: gp3
amiFamily: Ubuntu2204
subnets:
- bjchoi-test-subnet-pub-ap-northeast-2a
- bjchoi-test-subnet-pub-ap-northeast-2c
securityGroups:
attachIDs: [sg-0b5a887d93708252c]
iam:
attachPolicyARNs:
- arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
- arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
- arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
tags:
Name: bjchoi-eks-node
owner: bjchoi
addons:
- name: kube-proxy
- name: coredns
- name: vpc-cni
# configurationValues: |
# env:
# ENABLE_PREFIX_DELEGATION: "true"
# WARM_PREFIX_TARGET: "1"
- name: aws-ebs-csi-driver
- name: amazon-cloudwatch-observability
eksctl create cluster -f ./<CLUSTER_CONFIG_FILE_NAME>.yaml
kubectl apply -f https://k8s.io/examples/application/php-apache.yaml
HorizontalPodAutoscaler 생성
생성하는 방법은 두 가지가 존재합니다.
kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache cpu: 0%/50% 1 10 1 91s
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache # 타겟 디플로이먼트 이름
minReplicas: 1 # --min=1
maxReplicas: 10 # --max=10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50 # --cpu-percent=50
부하 증가 테스트
kubectl run -i --tty load-generator --rm --image=busybox:1.28 --restart=Never -- /bin/sh -c "while sleep 0.01; do wget -q -O- http://php-apache; done"
kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache cpu: 116%/50% 1 10 5 4m47s
정상적으로 동작하는 것을 확인하였다면 종료합니다
부하 발생 중지
이후 hpa를 확인해보면 순차적으로 감소되고 있는 것을 확인할 수 있습니다.
kubectl get hpa php-apache --watch
php-apache Deployment/php-apache cpu: 0%/50% 1 10 7 13m
php-apache Deployment/php-apache cpu: 0%/50% 1 10 4 14m
php-apache Deployment/php-apache cpu: 0%/50% 1 10 1 14m
Custom metric 기반 오토스케일링 구성
autoscaling/v2
로 HPA를 추가하여 HorizontalPodAutoscaler
를 구성할 수 있습니다.
apiVersion: apps/v1 # Deployment는 apps/v1 사용
kind: Deployment
metadata:
name: php-apache
spec:
selector:
matchLabels:
run: php-apache
replicas: 1
template:
metadata:
labels:
run: php-apache
spec:
containers:
- name: php-apache
image: registry.k8s.io/hpa-example
ports:
- containerPort: 80
resources:
limits:
cpu: 500m
requests:
cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
name: php-apache
labels:
run: php-apache
spec:
ports:
- port: 80
selector:
run: php-apache
---
apiVersion: autoscaling/v2 # HPA 추가
kind: HorizontalPodAutoscaler
metadata:
name: php-apache
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
minReplicas: 1
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
autoscaling/v2를 장점은 다음과 같습니다
[1. 다양한 메트릭 타입 지원]
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
metrics:
- type: Resource # 기본 리소스 메트릭 (CPU/Memory) -> 기본 제공되는 Metrics-server로 사용합니다.
- type: Pods # 파드 메트릭 -> 초당 요청 수, 큐 길이 등
- type: Object # 외부 오브젝트 메트릭 -> Ingress, Service 등의 메트릭 사용
- type: External # 외부 메트릭 -> CloudWatch, Prometheus 등 외부 메트릭
- type: ContainerResource # 컨테이너 리소스 메트릭 -> 파드 내 특정 컨테이너만 모니터링
[2. 비즈니스 메트릭 기반 스케일링]
spec:
metrics:
- type: External
external:
metric:
name: queue_messages # 예: 메시지 큐 길이
selector:
matchLabels:
queue: "workers"
target:
type: AverageValue
averageValue: 30
[3. 복수 메트릭 조합]
spec:
metrics:
- type: Resource # CPU 사용률
resource:
name: cpu
target:
type: Utilization
averageUtilization: 50
- type: Pods # 동시에 큐 길이도 체크
pods:
metric:
name: messages_count
target:
type: AverageValue
averageValue: 10
하지만 이러한 custom metric을 사용하기 위해선 prometheus같은 metric collector가 필요할 수 있습니다.