2026. 4. 6. 02:47ㆍ카테고리 없음
3. EKS Scaling 소개
- EKS에서 스케일링은 단순히 Pod 수를 늘리는 문제만이 아니라, 애플리케이션 튜닝, Pod 리소스 조정, 노드 증설, 이벤트 기반 확장까지 함께 고려해야 합니다.
- 어떤 워크로드인지에 따라 HPA, VPA, KEDA, Cluster Autoscaler, Karpenter 같은 도구의 역할이 달라집니다.
주요 스케일링 기술은 다음과 같습니다.
- Application tuning 애플리케이션 자체의 병목을 줄이기 위한 프로세스 튜닝입니다.
- VPA Vertical Pod Autoscaler와 In-Place Pod Resource Resize를 통해 Pod 1개의 CPU/메모리 크기를 조정합니다.
- HPA CPU/메모리 같은 메트릭을 기준으로 Pod 수를 늘리거나 줄입니다. 요청 분산을 위해 보통 Load Balancer가 함께 필요합니다.
- KEDA CPU/메모리가 아니라 큐 길이, Kafka 메시지 수, Cron 스케줄 같은 이벤트를 기준으로 확장합니다.
- CA/CAS Cluster Autoscaler는 Pod가 더 이상 스케줄되지 못할 때 노드를 동적으로 추가/삭제합니다.
- AWS 제공 기타 옵션
- Karpenter: 워크로드에 맞춰 노드를 빠르게 프로비저닝
- Fargate: 서버리스 방식으로 컨테이너 실행
- Auto Mode: AWS가 노드 운영 일부를 대신 관리

스케일링 전략 의사결정 프레임워크

- 먼저 "우리 서비스가 정말 초고속 반응형 스케일링이 필요한가?"를 판단해야 합니다.
- "트래픽 급증 시 사용자 에러 방지"라는 같은 목표라도, 실제로는 여러 접근법이 존재하며 대부분은 초고속 확장보다 예측형/복원력 중심 접근이 더 비용 효율적입니다.
| 1. 반응형 고속화 | Karpenter + KEDA + Warm Pool | 5-45초 | $40K-190K | 매우 높음 | 극소수 미션 크리티컬 |
| 2. 예측형 스케일링 | CronHPA + Predictive Scaling | 사전 확장 (0초) | $2K-5K | 낮음 | 패턴 있는 대부분의 서비스 |
| 3. 아키텍처 복원력 | SQS/Kafka + Circuit Breaker | 스케일링 지연 허용 | $1K-3K | 중간 | 비동기 처리 가능한 서비스 |
| 4. 적정 기본 용량 | 기본 replica 20-30% 증설 | 불필요 (이미 충분) | $5K-15K | 매우 낮음 | 안정적인 트래픽 |
4. Horizontal Pod Autoscaler (HPA)
HPA 소개
링크: Docs
- Kubernetes에서 애플리케이션의 Pod 복제본 수를 자동으로 늘리고 줄이는 기능을 수평 Pod 자동 확장(HPA) 이라고 합니다.
- HPA는 CPU 사용률, 메모리, 커스텀 메트릭 등을 관찰하여 원하는 상태에 맞게 replica 수를 조정합니다.
- 예를 들어 CPU 목표값을 50%로 두면, 평균 CPU 사용률이 50%를 넘을 때 replica를 늘리고 다시 안정화되면 줄입니다.
- 이 동작은 minReplicas, maxReplicas, 그리고 scale up/down 안정화 시간의 영향을 받습니다.
샘플 애플리케이션 배포
먼저 HPA 실습용 샘플 애플리케이션을 배포합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-apache
spec:
selector:
matchLabels:
run: php-apache
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
kubectl apply -f https://k8s.io/examples/application/php-apache.yaml
kubectl exec -it deploy/php-apache -- cat /var/www/html/index.php
<?php
$x = 0.0001;
for ($i = 0; $i <= 1000000; $i++) {
$x += sqrt($x);
}
echo "OK!";
?>
부하 발생과 모니터링
부하를 발생시키기 전에 HPA와 Pod 상태를 모니터링할 터미널을 준비합니다.
# 터미널 1
watch -d 'kubectl get hpa,pod; echo; kubectl top pod; echo; kubectl top node'
# 터미널 2
kubectl exec -it deploy/php-apache -- top
# 터미널 3
kubectl exec -it curl -- sh -c 'for i in $(seq 1 5); do while true; do curl -s php-apache; sleep 1; done & done; wait'

HPA 생성
cat <<EOF | kubectl apply -f -
apiVersion: autoscaling/v2
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:
averageUtilization: 50
type: Utilization
EOF
CLI 한 줄로 생성할 수도 있습니다.
kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
HPA 생성 후 상태를 확인합니다.
kubectl describe hpa
kubectl get hpa php-apache -o yaml | kubectl neat
예시 출력:
Name: php-apache
Namespace: default
Reference: Deployment/php-apache
Metrics: ( current / target )
resource cpu on pods (as a percentage of request): 0% (1m) / 50%
Min replicas: 1
Max replicas: 10
Deployment pods: 1 current / 1 desired
Conditions:
AbleToScale True
ScalingActive True
ScalingLimited False
Events: <none>
지속적으로 부하를 주면 replica가 증가하고, 부하를 중단한 뒤 일정 시간이 지나면 다시 줄어듭니다.
kubectl exec curl -- sh -c 'while true; do curl -s php-apache; sleep 0.01; done'
- 기본적으로 scale up은 비교적 빠르게 반응합니다.
- scale down은 기본 안정화 시간 때문에 약 5분 정도 뒤에 일어날 수 있습니다.
- 이는 순간적인 트래픽 하락에 과민 반응하지 않도록 하기 위한 동작이며, 운영 환경에서는 워크로드 특성에 맞게 조정합니다.
HPA 사용 시 주의사항
- HPA가 CPU 사용률을 계산하려면 Deployment에 resources.requests가 반드시 있어야 합니다.
- metrics-server가 정상 동작하지 않으면 HPA는 CPU/메모리 기반 스케일링을 수행할 수 없습니다.
- HPA는 평균값 기반으로 판단하므로, 일부 Pod만 과부하인 상황을 놓칠 수 있습니다.
- CPU/메모리만으로 대응이 어려운 이벤트성 워크로드는 KEDA 같은 이벤트 기반 확장이 더 적합할 수 있습니다.
참고 링크:
- [kubernetes] hpa 사용시 주의할 점
- k8s HPA 동작 방식 및 이슈들 정리
- 쿠버네티스 운영에서 HPA만으로는 충분하지 않은 이유
- HPA 알고리즘
- Kubernetes Autoscaling with HPA
5. VPA - Vertical Pod Autoscaler
VPA 소개
- VPA(Vertical Pod Autoscaler) 는 Pod의 resources.requests 값을 자동으로 최적화하는 도구입니다.
- HPA가 Pod 수를 늘리는 수평 확장이라면, VPA는 Pod 1개의 CPU/메모리 크기를 조정하는 수직 확장입니다.
- VPA는 실제 리소스 사용량을 분석해 적정 요청값을 계산하고, 필요하면 Pod를 재시작하여 새 리소스 요청값을 반영합니다.
주의할 점:
- HPA와 VPA를 CPU/메모리 기준으로 동시에 사용하면 충돌이 발생할 수 있습니다.
- VPA는 Pod 재시작을 유발할 수 있으므로 순간적인 서비스 영향이 생길 수 있습니다.
- updateMode: Off로 두면 권장값만 계산하고 실제 반영은 하지 않습니다.
Goldilocks

Goldilocks는 VPA 권장값을 보기 쉽게 보여주는 도구입니다.
- 뜻: "너무 크지도 작지도 않은, 딱 적당한 상태"
- VPA 권장치를 기반으로 적절한 CPU/메모리 요청값을 찾는 데 유용합니다.
참고 링크:
Goldilocks 설치
kubectl create ns javajmx-sample
kubectl label ns javajmx-sample goldilocks.fairwinds.com/enabled=true
kubectl describe ns javajmx-sample
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
helm upgrade --install goldilocks fairwinds-stable/goldilocks \
--namespace goldilocks \
--create-namespace \
--set vpa.enabled=true
kubectl get all -n goldilocks
예시 출력:
NAME READY STATUS RESTARTS AGE
pod/goldilocks-controller-6cb5d68986-qjq72 1/1 Running 0 35s
pod/goldilocks-dashboard-fcf885596-56w6d 1/1 Running 0 35s
pod/goldilocks-dashboard-fcf885596-m4xz7 1/1 Running 0 35s
pod/goldilocks-vpa-admission-controller-5dd4f64f9c-fl4sw 1/1 Running 0 35s
pod/goldilocks-vpa-recommender-967dd4dcb-ls5nn 1/1 Running 0 35s
샘플 앱 배포와 권장치 확인
kubectl apply -f https://raw.githubusercontent.com/aws-observability/aws-o11y-recipes/main/sandbox/javajmx/example/sample-javajmx-app.yaml
kubectl get vpa -n javajmx-sample
예시 출력:
NAME MODE CPU MEM PROVIDED AGE
goldilocks-tomcat-example Off 15m 126805489 True 6m49s
초기에는 VPA 컴포넌트가 완전하지 않으면 PROVIDED=False로 보일 수 있습니다. 이 경우 VPA CRD/RBAC/컨트롤러 구성을 별도로 확인해야 합니다.


순정 VPA 설치
kubectl apply -f https://raw.githubusercontent.com/kubernetes/autoscaler/refs/heads/master/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/autoscaler/refs/heads/master/vertical-pod-autoscaler/deploy/vpa-rbac.yaml
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
./hack/vpa-up.sh
kubectl get pods -n kube-system | grep vpa
kubectl get crd | grep autoscaling
예시 출력:
vpa-admission-controller-5879bdc979-k5jvg 1/1 Running 0 35s
vpa-recommender-59c7769c67-g8wrb 1/1 Running 0 39s
vpa-updater-64888c8777-q7cf5 1/1 Running 0 41s
VPA 핵심 컴포넌트는 다음과 같습니다.
컴포넌트역할
| vpa-recommender | 파드의 리소스 사용량을 분석해 적정 requests 값을 계산 |
| vpa-updater | 현재 값과 권장값 차이가 크면 파드를 재시작해 반영 |
| vpa-admission-controller | 새 파드 생성 시 권장값을 자동 적용 |
VPA 동작 확인
kubectl apply -f examples/hamster.yaml
kubectl get vpa -w
예시 출력:
NAME MODE CPU MEM PROVIDED AGE
hamster-vpa Auto 587m 250Mi True 36s
kubectl describe pod | grep Requests: -A2
kubectl get events --sort-by=".metadata.creationTimestamp" | grep VPA
예시 이벤트:
Normal EvictedByVPA pod/hamster-7996dbb57-t8kvx Pod was evicted by VPA Updater to apply resource recommendation.
Normal EvictedPod verticalpodautoscaler/hamster-vpa VPA Updater evicted Pod hamster-7996dbb57-t8kvx to apply resource recommendation.
참고:
- UpdateMode "Auto"는 deprecated 경고가 표시될 수 있습니다.
- 최신 버전에서는 Recreate, Initial, InPlaceOrRecreate 같은 명시적인 모드를 사용하는 편이 좋습니다.
6. KEDA - Kubernetes Event-driven Autoscaling
KEDA 소개

KEDA는 특정 이벤트를 기반으로 Pod를 자동으로 스케일링하는 도구입니다.
- HPA는 보통 CPU/메모리 같은 리소스 메트릭을 기준으로 동작합니다.
- KEDA는 Kafka 메시지 수, SQS 큐 길이, Cron 스케줄 등 외부 이벤트를 기준으로 스케일링할 수 있습니다.
- 이벤트가 없을 때 replica를 0까지 줄일 수 있어 비용 효율이 좋습니다.
- 내부적으로는 HPA를 생성해서 동작하며, 사용자는 ScaledObject를 선언하면 됩니다.

KEDA 주요 컴포넌트:
컴포넌트역할
| keda-operator | 이벤트를 감지하고 Deployment를 0↔N으로 스케일 |
| keda-operator-metrics-apiserver | 외부 이벤트를 HPA가 읽을 수 있는 메트릭으로 변환 |
| keda-admission-webhooks | ScaledObject 설정 검증 |
metrics API 확인
먼저 metrics-server API가 정상 노출되는지 확인합니다.
kubectl get --raw "/apis/metrics.k8s.io" | jq
예시 출력:
{
"kind": "APIGroup",
"apiVersion": "v1",
"name": "metrics.k8s.io",
"versions": [
{
"groupVersion": "metrics.k8s.io/v1beta1",
"version": "v1beta1"
}
],
"preferredVersion": {
"groupVersion": "metrics.k8s.io/v1beta1",
"version": "v1beta1"
}
}
참고:
# CPU/Memory 메트릭은 기존 metrics-server가 담당하고,
# KEDA metrics-server는 외부 이벤트 소스 메트릭을 노출합니다.
# https://keda.sh/docs/2.16/operate/metrics-server/
KEDA 설치
helm repo add kedacore https://kedacore.github.io/charts
helm repo update
helm install keda kedacore/keda \
--version 2.16.0 \
--namespace keda \
--create-namespace \
-f keda-values.yaml
kubectl get crd | grep keda
kubectl get all -n keda
kubectl get validatingwebhookconfigurations keda-admission-webhooks -o yaml | kubectl neat
kubectl get podmonitor,servicemonitors -n keda
kubectl get apiservice v1beta1.external.metrics.k8s.io -o yaml
확인 포인트:
- scaledobjects.keda.sh, scaledjobs.keda.sh 같은 CRD가 생성되어야 합니다.
- keda-operator, keda-operator-metrics-apiserver, keda-admission-webhooks Pod가 모두 Running이어야 합니다.
- v1beta1.external.metrics.k8s.io APIService 상태가 Available=True여야 합니다.

Cron 기반 ScaledObject 실습
먼저 keda 네임스페이스에 샘플 애플리케이션을 배포합니다.
kubectl apply -f https://k8s.io/examples/application/php-apache.yaml -n keda
kubectl get pod -n keda
이제 Cron 기반 ScaledObject를 생성합니다.
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: php-apache-cron-scaled
spec:
minReplicaCount: 0
maxReplicaCount: 2
pollingInterval: 30
cooldownPeriod: 300
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: php-apache
triggers:
- type: cron
metadata:
timezone: Asia/Seoul
start: "0,15,30,45 * * * *"
end: "5,20,35,50 * * * *"
desiredReplicas: "1"
kubectl apply -f keda-cron.yaml -n keda
watch -d 'kubectl get ScaledObject,hpa,pod -n keda'
ScaledObject 상태를 실시간으로 보면 활성화 구간에서 ACTIVE=True로 바뀌고, KEDA가 자동으로 HPA를 생성하는 것을 볼 수 있습니다.
kubectl get ScaledObject -n keda -w
NAME SCALETARGETKIND SCALETARGETNAME MIN MAX READY ACTIVE FALLBACK PAUSED TRIGGERS AUTHENTICATIONS AGE
php-apache-cron-scaled apps/v1.Deployment php-apache 0 2 True Unknown False Unknown 17s
php-apache-cron-scaled apps/v1.Deployment php-apache 0 2 True Unknown False Unknown 30s
php-apache-cron-scaled apps/v1.Deployment php-apache 0 2 True True False Unknown 30s
php-apache-cron-scaled apps/v1.Deployment php-apache 0 2 True True False Unknown 60s
php-apache-cron-scaled apps/v1.Deployment php-apache 0 2 True False False Unknown 90s
kubectl get ScaledObject,hpa,pod -n keda
NAME SCALETARGETKIND SCALETARGETNAME MIN MAX READY ACTIVE FALLBACK PAUSED TRIGGERS AUTHENTICATIONS AGE
scaledobject.keda.sh/php-apache-cron-scaled apps/v1.Deployment php-apache 0 2 True False False Unknown
kubectl get hpa -o jsonpath="{.items[0].spec}" -n keda | jq
{
"maxReplicas": 2,
"metrics": [
{
"external": {
"metric": {
"name": "s0-cron-Asia-Seoul-00,15,30,45xxxx-05,20,35,50xxxx",
"selector": {
"matchLabels": {
"scaledobject.keda.sh/name": "php-apache-cron-scaled"
}
}
},
"target": {
"averageValue": "1",
"type": "AverageValue"
}
},
"type": "External"
}
],
"minReplicas": 1,
"scaleTargetRef": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"name": "php-apache"
}
}
예시 출력:
NAME SCALETARGETKIND SCALETARGETNAME MIN MAX READY ACTIVE FALLBACK PAUSED TRIGGERS AUTHENTICATIONS AGE
php-apache-cron-scaled apps/v1.Deployment php-apache 0 2 True True False Unknown cron Unknown 30s
{
"maxReplicas": 2,
"metrics": [
{
"external": {
"metric": {
"name": "s0-cron-Asia-Seoul-00,15,30,45xxxx-05,20,35,50xxxx",
"selector": {
"matchLabels": {
"scaledobject.keda.sh/name": "php-apache-cron-scaled"
}
}
},
"target": {
"averageValue": "1",
"type": "AverageValue"
}
},
"type": "External"
}
],
"minReplicas": 1,
"scaleTargetRef": {
"apiVersion": "apps/v1",
"kind": "Deployment",
"name": "php-apache"
}
}
정리:
- 사용자는 ScaledObject만 선언하지만, 실제 스케일링은 KEDA가 만든 HPA가 수행합니다.
- HPA와 달리 외부 이벤트를 직접 트리거로 사용할 수 있습니다.
- idle 상태에서는 replica를 0까지 내릴 수 있다는 점이 큰 차이입니다.

정리 후 삭제:
kubectl delete scaledobject php-apache-cron-scaled -n keda
kubectl delete deploy,svc php-apache -n keda
helm uninstall keda -n keda
7 Cluster Proportional Autoscaler (CPA)

설치
helm upgrade --install cluster-proportional-autoscaler \
cluster-proportional-autoscaler/cluster-proportional-autoscaler
테스트용 Deployment를 하나 준비합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
resources:
limits:
cpu: "100m"
memory: "64Mi"
requests:
cpu: "100m"
memory: "64Mi"
ports:
- containerPort: 80
kubectl apply -f cpa-nginx.yaml
CPA 값은 다음처럼 정의할 수 있습니다.
config:
ladder:
nodesToReplicas:
- [1, 1]
- [2, 2]
- [3, 3]
- [4, 3]
- [5, 5]
options:
namespace: default
target: "deployment/nginx-deployment"
helm upgrade --install cluster-proportional-autoscaler \
cluster-proportional-autoscaler/cluster-proportional-autoscaler \
-f cpa-values.yaml \
--namespace kube-system
kubectl describe cm cluster-proportional-autoscaler -n kube-system
노드 수를 늘려서 Deployment replica 변화도 확인할 수 있습니다.
export ASG_NAME=$(aws autoscaling describe-auto-scaling-groups \
--query "AutoScalingGroups[].AutoScalingGroupName" \
--output text | tr '\t' '\n' | grep "myeks")
aws autoscaling update-auto-scaling-group \
--auto-scaling-group-name "${ASG_NAME}" \
--min-size 5 \
--desired-capacity 5 \
--max-size 5
aws autoscaling describe-auto-scaling-groups \
--query "AutoScalingGroups[? Tags[? (Key=='eks:cluster-name') && Value=='myeks']].[AutoScalingGroupName, MinSize, MaxSize, DesiredCapacity]" \
--output table
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hamster-7996dbb57-tcgjq 1/1 Running 0 41m 192.168.21.13 ip-192-168-21-237.ap-northeast-2.compute.internal <none> <none>
hamster-7996dbb57-z58wf 1/1 Running 0 42m 192.168.12.81 ip-192-168-14-65.ap-northeast-2.compute.internal <none> <none>
nginx-deployment-57f959d4d7-cvkvd 1/1 Running 0 6m21s 192.168.14.4 ip-192-168-14-236.ap-northeast-2.compute.internal <none> <none>
nginx-deployment-57f959d4d7-k8wsp 1/1 Running 0 4m31s 192.168.22.55 ip-192-168-21-237.ap-northeast-2.compute.internal <none> <none>
nginx-deployment-57f959d4d7-wtfkj 1/1 Running 0 4m31s 192.168.14.92 ip-192-168-14-65.ap-northeast-2.compute.internal <none> <none>
예시 결과:
- 노드 수 변화에 맞춰 nginx-deployment replica가 3개로 늘어났습니다.
- 각 Pod가 서로 다른 노드에 분산된 것도 함께 확인할 수 있습니다.
정리 후 삭제:
helm uninstall cluster-proportional-autoscaler -n kube-system
kubectl delete -f cpa-nginx.yaml
참고:
- CPU/Memory 기반 정책도 구성할 수 있습니다.
coresToReplicas:
- [1, 1]
- [64, 3]
- [512, 5]
- [1024, 7]
- [2048, 10]
- [4096, 15]
- 관련 글: Blog