[CloudNeta] EKS Best Practices - Scalability: Kubernetes Scaling Theory
이 문서는 AWS EKS Best Practices Guide의 Scalability 섹션 중 Kubernetes Scaling Theory 페이지를 분석한 글입니다. Kubernetes 클러스터의 확장성을 이해하기 위한 이론적 토대를 제공합니다.
Nodes vs. Churn Rate
Kubernetes의 확장성을 논의할 때, 흔히 단일 클러스터의 노드 수에 초점을 맞춥니다. 하지만 이는 확장성을 이해하는 데 가장 유용한 지표가 아닐 때가 많습니다.
예시:
- 5,000개 노드 클러스터에 고정된 수의 Pod가 있다면, 초기 셋업 이후에는 컨트롤 플레인에 큰 부하를 주지 않습니다.
- 반면 1,000개 노드 클러스터에서 1분 이내에 10,000개의 단기 Job을 생성하면, 컨트롤 플레인에 지속적인 부하가 발생합니다.
노드 수가 아닌 **변화율(Rate of Change)**로 확장성을 바라보아야 합니다. 특정 시간(보통 Prometheus 기본값인 5분 간격) 내에 발생하는 변화율이 무엇을 튜닝해야 원하는 스케일을 달성할 수 있는지 더 잘 알려줍니다.
Thinking in Queries Per Second
Kubernetes의 각 컴포넌트에는 다음 단계의 컴포넌트를 과부하로부터 보호하는 메커니즘이 있습니다:
- Kubelet - API 서버 호출을 특정 속도로 제한하는 플래그 보유
- Scheduler
- Kube Controller Manager
- API server
이러한 보호 메커니즘은 일반적으로 QPS(Queries Per Second) 단위로 표현됩니다.
QPS 설정을 변경할 때는 매우 신중해야 합니다. 하나의 병목을 제거하면(예: Kubelet의 QPS 증가) 하류 컴포넌트에 영향을 미칩니다. 특정 속도 이상에서 시스템을 과부하시킬 수 있으므로, 서비스 체인의 각 부분을 이해하고 모니터링하는 것이 핵심입니다.
API 서버는 API Priority and Fairness라는 더 복잡한 시스템을 도입하여 별도로 구성할 수 있습니다.
일부 메트릭은 올바른 용도처럼 보이지만 실제로는 다른 것을 측정합니다. 예를 들어 kubelet_http_inflight_requests는 Kubelet의 메트릭 서버에 대한 요청만 측정하며, Kubelet에서 API 서버로의 요청 수를 측정하는 것이 아닙니다. 이로 인해 Kubelet의 QPS 플래그를 잘못 구성할 수 있습니다. 특정 Kubelet의 감사 로그(audit logs)를 쿼리하는 것이 더 신뢰할 수 있는 방법입니다.
Scaling Distributed Components
EKS는 관리형 서비스이므로, Kubernetes 컴포넌트를 두 가지 범주로 나눌 수 있습니다:
graph LR
subgraph AWS_MANAGED["AWS 관리 영역"]
ETCD[etcd]
KCM[Kube Controller Manager]
SCHED[Scheduler]
end
subgraph API["API Server"]
APIS[API Server
API Priority & Fairness
고객 설정 가능]
end
subgraph CUSTOMER["고객 설정 가능 영역"]
KUBELET[Kubelet]
RUNTIME[Container Runtime]
CNI[네트워크 드라이버
CNI]
CSI[스토리지 드라이버
CSI]
end
ETCD --- APIS
KCM --- APIS
SCHED --- APIS
APIS --- KUBELET
KUBELET --- RUNTIME
KUBELET --- CNI
KUBELET --- CSI
style AWS_MANAGED fill:#232f3e,color:#fff,stroke:#ff9900,stroke-width:2px
style API fill:#1a472a,color:#fff,stroke:#ff9900,stroke-width:2px
style CUSTOMER fill:#0d3b66,color:#fff,stroke:#3b82f6,stroke-width:2px| 구분 | 컴포넌트 | 관리 주체 |
|---|---|---|
| AWS 관리 | etcd, Kube Controller Manager, Scheduler | AWS |
| 중간 | API Server (API Priority and Fairness 설정은 고객이 가능) | AWS (설정은 고객) |
| 고객 설정 가능 | Kubelet, Container Runtime, CNI/CSI 등 AWS API 호출 오퍼레이터 | 고객 |
Upstream and Downstream Bottlenecks
서비스를 모니터링할 때, 양방향(upstream/downstream) 메트릭을 살펴보며 병목을 찾아야 합니다.
Kubelet을 예로 들면:
- Upstream: API Server
- Downstream: Container Runtime (Containerd)
How many Pods per Node
Upstream에서 지원하는 노드당 Pod 수는 110개입니다. 하지만 실제 프로덕션 워크로드는 upstream 스케일링 테스트에서 검증된 것보다 복잡합니다. Kubelet이 Containerd 런타임을 "따라갈 수 있는지(keeping up)" 확인해야 합니다.
sequenceDiagram
participant K as Kubelet
participant C as Containerd
loop Pod 상태 모니터링
K->>C: Pod 상태 요청
C-->>K: Pod 상태 응답
end
Note over K,C: Pod 상태 변경이 너무 빠르면
요청이 타임아웃됩니다단순화하면, Kubelet은 컨테이너 런타임(Containerd)으로부터 Pod의 상태를 가져옵니다. 너무 많은 Pod가 너무 빠르게 상태 변경을 하면, 컨테이너 런타임에 대한 요청이 타임아웃될 수 있습니다.
Kubernetes는 끊임없이 진화하고 있으며, 이 서브시스템은 현재 변경이 진행 중입니다.
참고: KEP-3386
PLEG Duration 모니터링
Pod Lifecycle Event Generation(PLEG) duration 메트릭에서 **타임아웃 값에 평평한 선(flat line)**이 보이면, 해당 노드가 한계를 넘었다는 의미입니다.
아래 PromQL 쿼리로 확인할 수 있습니다:
increase(kubelet_pleg_relist_duration_seconds_bucket{instance="$instance"}[$__rate_interval])
타임아웃 발생 시 대응
타임아웃이 관찰되면, 원인을 해결한 후 다음 단계로 진행해야 합니다:
- 노드당 Pod 수를 줄이기
- 높은 재시도 볼륨을 유발하는 에러를 찾기 (재시도는 Churn Rate에 영향)
고정된 숫자(예: 110개)에 의존하기보다, 메트릭을 사용하여 해당 노드가 할당된 Pod의 Churn Rate를 처리할 수 있는지 판단하는 것이 최선입니다.
Scale by Metrics
메트릭으로 시스템을 최적화하는 것은 오래된 개념이지만, Kubernetes 여정 초기에 종종 간과됩니다.
특정 숫자(예: 노드당 110개 Pod)에 집중하는 대신, 시스템의 병목을 찾아주는 메트릭을 찾는 데 노력을 집중해야 합니다. 이러한 메트릭의 올바른 임계값을 이해하면, 시스템이 최적으로 구성되었다는 높은 확신을 가질 수 있습니다.
The Impact of Changes
문제를 해결할 때 흔히 저지르는 실수는 의심스러워 보이는 첫 번째 메트릭이나 로그 에러에 집중하는 것입니다.
앞서 Kubelet 타임아웃을 봤을 때, Kubelet의 초당 전송 속도를 증가시키는 등 무작위 조치를 시도할 수 있습니다. 하지만 에러가 발생한 지점의 하류(downstream) 전체를 먼저 살펴보는 것이 현명합니다.
각 변경은 목적이 있어야 하고, 데이터에 의해 뒷받침되어야 합니다.
Kubelet의 하류 컴포넌트:
graph TD
KUBELET[Kubelet] --> CONTAINERD[Containerd Runtime
Pod 에러]
KUBELET --> DS_CSI[DaemonSet: Storage Driver
CSI]
KUBELET --> DS_CNI[DaemonSet: Network Driver
CNI]
DS_CSI --> EC2_API[EC2 API]
DS_CNI --> EC2_API
style KUBELET fill:#232f3e,color:#fff,stroke:#ff9900,stroke-width:2px
style CONTAINERD fill:#0d3b66,color:#fff,stroke:#3b82f6,stroke-width:2px
style DS_CSI fill:#1a472a,color:#fff,stroke:#059669,stroke-width:2px
style DS_CNI fill:#1a472a,color:#fff,stroke:#059669,stroke-width:2px
style EC2_API fill:#7c3aed,color:#fff,stroke:#8b5cf6,stroke-width:2px노드를 너무 밀집하게 bin packing하면 여러 지점에서 에러가 발생할 수 있습니다.
워크로드에 적합한 노드 크기를 설계할 때, 이러한 신호들은 간과하기 쉽지만 시스템에 불필요한 압력을 가하여 스케일과 성능을 모두 제한할 수 있습니다.
The Cost of Unnecessary Errors
Kubernetes 컨트롤러는 에러 상황에서 재시도에 탁월하지만, 이에는 비용이 따릅니다:
- 재시도는 Kube Controller Manager 등의 컴포넌트에 대한 압력을 증가시킵니다.
- 스케일 테스트에서 에러를 모니터링하는 것이 중요합니다.
에러가 적을수록 시스템의 문제를 더 쉽게 발견할 수 있습니다. 주요 운영(업그레이드 등) 전에 클러스터가 에러 프리 상태인지 주기적으로 확인하면, 예기치 않은 이벤트 발생 시 로그 트러블슈팅이 간소화됩니다.
Expanding Our View
1,000개 이상의 노드를 가진 대규모 클러스터에서는 개별 병목을 하나씩 찾는 것이 비현실적입니다. PromQL의 topk 함수를 사용하면 데이터셋에서 가장 높은 값을 찾을 수 있습니다.
클러스터의 모든 Kubelet이 포화 상태인지 확인하기 위해, Kubelet이 **이벤트를 폐기(discard)**하고 있는지 살펴봅시다:
topk(3, increase(kubelet_pleg_discard_events{}[$__rate_interval]))
쿼리 분석:
| 구성요소 | 설명 |
|---|---|
$__rate_interval |
Grafana 변수. 4개의 샘플을 확보하기 위한 변수로, 모니터링의 복잡한 주제를 간단한 변수로 우회합니다. |
topk |
상위 결과만 반환합니다. |
3 |
결과를 3개 노드로 제한합니다. 클러스터 전체 메트릭에 유용한 함수입니다. |
{} |
필터 없음. 일반적으로 스크래핑 규칙의 job 이름을 넣지만, 이름이 다양하므로 비워둡니다. |
Splitting the Problem in Half
시스템의 병목을 해결하기 위해, upstream 또는 downstream에 문제가 있음을 보여주는 메트릭을 찾아 문제를 반으로 나누는 접근법을 사용합니다. 이는 메트릭 데이터를 표시하는 방식의 핵심 원칙이기도 합니다.
이 프로세스의 좋은 시작점은 API Server입니다. API Server를 통해 문제가 다음 중 어디에 있는지 파악할 수 있습니다:
- 클라이언트 애플리케이션 측 문제
- 컨트롤 플레인 측 문제
graph LR
CLIENT[클라이언트 애플리케이션
Kubelet, Operators 등] -->|upstream| API[API Server
관측 지점]
API -->|downstream| CP[컨트롤 플레인
etcd, KCM, Scheduler]
style API fill:#ff9900,color:#000,stroke:#232f3e,stroke-width:3px
style CLIENT fill:#0d3b66,color:#fff,stroke:#3b82f6,stroke-width:2px
style CP fill:#232f3e,color:#fff,stroke:#ff9900,stroke-width:2px정리
| 원칙 | 설명 |
|---|---|
| 노드 수보다 변화율 | 클러스터의 확장성은 노드 수가 아닌 단위 시간당 변화율(Churn Rate)로 이해해야 합니다. |
| QPS 기반 사고 | 각 컴포넌트의 QPS 보호 메커니즘을 이해하고, 하나의 병목 제거가 하류에 미치는 영향을 고려해야 합니다. |
| 메트릭 기반 판단 | 고정 숫자(110 pods/node 등)가 아닌 PLEG duration 등의 메트릭으로 실제 한계를 파악해야 합니다. |
| 데이터 기반 변경 | 각 변경은 목적이 있어야 하며, 데이터에 의해 뒷받침되어야 합니다. |
| 문제 이분법 | API Server를 관측 지점으로 삼아 문제를 upstream/downstream으로 나누어 추적합니다. |
| 에러 비용 인식 | 불필요한 에러와 재시도는 시스템 전체에 압력을 가하므로, 에러 프리 상태를 유지하는 것이 중요합니다. |