Horizontal Pod AutoScaler (HPA)

HorizontalPodAutoscaler로 Kubernetes 워크로드를 자동 조정하여 자원 할당 최적화 및 비용 절감 실현.

Horizontal Pod AutoScaler (HPA)
Photo by Growtika / Unsplash

개요

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가 필요할 수 있습니다.

Reference