8.6 오토스케일링
이 Part의 앞 장들은 주어진 Pod 수와 노드 수 안에서 자리를 정하는 이야기였다. 오토스케일링은 그 수 자체를 부하에 따라 움직인다. 축은 셋이다. HPA(HorizontalPodAutoscaler)는 Pod 개수를, VPA(VerticalPodAutoscaler)는 Pod 하나의 크기(requests)를, Cluster Autoscaler와 Karpenter는 노드 수를 조정한다. 세 축은 층이 달라 함께 동작하지만, 조합을 잘못 고르면 서로의 판단 근거가 어긋난다.
HPA, 메트릭에서 replica 수로
HPA controller는 주기적으로(기본 15초) 대상 워크로드의 메트릭을 확인하고 replica 수를 다시 계산한다. 메트릭 소스는 세 갈래다. CPU와 메모리 같은 Resource 메트릭은 metrics-server가 제공하고, 애플리케이션 고유 지표(초당 요청 수 등)는 custom metrics API로, 클러스터 바깥 지표(메시지 큐 길이 등)는 external metrics API로 들어온다. Resource 메트릭의 Utilization 목표는 requests 대비 백분율이다. 분모가 limits도 노드 용량도 아닌 requests라는 사실이 뒤에 나오는 여러 동작의 전제가 된다.
계산식은 하나다.
desiredReplicas = ceil(currentReplicas × currentMetric / targetMetric)replica 3개가 목표 사용률 70%에서 실제 140%를 기록하면 ceil(3 × 140/70) = 6으로 늘린다. 현재와 목표의 비율 차이가 tolerance(기본 10%) 안이면 변경하지 않는다.
방향에 따라 반응 속도가 비대칭이다. scale-up은 기본 즉시 반영되지만, scale-down에는 기본 300초의 안정화 윈도(stabilization window)가 있다. 윈도 안에서 계산된 권장값 중 가장 큰 값을 쓰기 때문에, 메트릭이 출렁여도 replica 수가 곧바로 따라 내려가며 요동치는 일이 억제된다. 더 세밀한 통제는 behavior 필드로 한다.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
behavior:
scaleDown:
stabilizationWindowSeconds: 600
policies:
- type: Pods
value: 1
periodSeconds: 60위 behavior는 축소를 60초에 1개씩으로 제한하고 안정화 윈도를 10분으로 늘린 구성이다. scaleUp 쪽에도 같은 문법으로 Percent와 Pods 정책, 정책이 여럿일 때의 선택 기준(selectPolicy)을 지정한다.
VPA, 그리고 HPA와의 동거 조건
VPA는 Pod의 실사용량 이력에서 requests 권장값을 계산하고, 설정에 따라 자동 적용까지 한다. Kubernetes 본체가 아니라 별도로 설치하는 프로젝트이고, requests 변경은 전통적으로 Pod 재생성을 동반해 왔다. 8.2에서 본 대로 requests는 스케줄링과 QoS 판정의 입력이므로, 실측과 동떨어진 requests를 데이터 기반으로 교정하는 도구로서 가치가 있다.
문제는 HPA와의 조합이다. 둘 다 CPU나 메모리를 기준으로 삼으면 같은 신호에 두 controller가 동시에 반응한다. HPA의 사용률은 requests가 분모인데 VPA가 그 분모를 바꾼다. VPA가 requests를 올리면 HPA가 보는 사용률이 내려가 replica가 줄고, 줄어든 replica에 부하가 몰리면 VPA가 다시 requests를 올리는 되먹임이 생긴다. 공식 문서도 CPU, 메모리 기준의 HPA와 VPA를 같은 워크로드에 함께 쓰지 말라고 안내한다. HPA를 custom 메트릭이나 external 메트릭 기준으로 두면 두 controller의 신호가 분리되어 병용이 성립한다.
노드 층, Cluster Autoscaler와 Karpenter
replica를 아무리 늘려도 노드에 자리가 없으면 새 Pod는 Pending에 머문다. 이 층을 담당하는 것이 노드 오토스케일러다.
Cluster Autoscaler는 스케줄링 실패로 Pending에 빠진 Pod를 감지하면, 미리 정의된 노드 그룹(클라우드의 인스턴스 그룹 단위) 가운데 하나를 골라 크기를 늘린다. 그룹 안의 노드는 같은 스펙이라는 전제로 배치를 시뮬레이션하므로, 다른 인스턴스 타입이 필요하면 그룹을 따로 만들어 두어야 한다. 축소는 사용률이 낮은 노드의 Pod 전부가 다른 노드로 옮겨진다고 판단될 때 그 노드를 비우고 제거하는 방식이다.
Karpenter는 노드 그룹이라는 중간 단위를 거치지 않는다. Pending Pod의 요구(requests, affinity, taint 조건)를 직접 읽고 그에 맞는 인스턴스 타입을 그때그때 골라 노드를 생성한다. 워크로드 스펙이 다양할수록 노드를 수요에 맞추기 쉽고, consolidation 기능이 배치를 재조정해 덜 쓰는 노드를 줄인다. 어느 쪽이든 클라우드 제공자와의 연동이 전제라서, 온프레미스 환경에서는 선택지가 제한된다.
스케일 아웃이 안 될 때 보는 곳
HPA를 선언했는데 replica가 늘지 않는 상황에는 반복되는 원인 몇 가지가 있다. 진단의 시작은 kubectl describe hpa다.
kubectl describe hpa webTARGETS 열이 <unknown>이면 메트릭 자체가 없는 것이다. metrics-server가 설치되지 않았거나 비정상이면 Resource 메트릭 기반 HPA는 아무 판단도 하지 못하고, 이벤트에 FailedGetResourceMetric이 남는다. kubectl top pod가 동작하는지가 빠른 확인법이다.
메트릭은 있는데 사용률이 계산되지 않으면 requests 미설정을 의심한다. Utilization 목표는 requests가 분모라서, 대상 Pod의 컨테이너에 CPU requests가 없으면 백분율 계산이 성립하지 않는다. 8.2에서 requests 없는 클러스터가 무너지는 방식을 보았는데, 오토스케일링 불능도 그 목록에 추가된다.
maxReplicas 도달은 조용한 원인이다. HPA는 상한에서 멈추고, describe 출력의 조건에 ScalingLimited가 True로 표시된다.
마지막은 HPA가 desired를 올렸는데 실제 Pod 수가 늘지 않는 경우다. 새 Pod가 Pending이면 문제는 HPA 층이 아니라 노드 층이다. 노드 오토스케일러가 없거나, 노드 그룹이 최대 크기에 도달했거나, Pending Pod의 조건(taint, affinity)을 만족하는 노드를 만들 그룹이 없는 경우가 여기 해당한다. kubectl get pod로 Pending 여부를 확인하고 8.1의 FailedScheduling 이벤트로 내려가면 층이 구분된다.
정리
HPA는 requests 대비 사용률을 계산식 하나로 replica 수에 반영하고, 안정화 윈도와 behavior로 반응 속도를 통제한다. VPA는 requests를 교정하는 별개의 축이라 같은 메트릭의 HPA와 조합하면 서로의 분모를 흔들고, 노드 층의 확장은 Cluster Autoscaler나 Karpenter의 몫이다. 스케일 아웃이 멈췄을 때는 메트릭, requests, replica 상한, 노드 용량 순으로 층을 내려가며 확인한다.