우리 작업 시스템에서 쿠버네티스 오버헤드를 최소화한 방법 (새 탭에서 열림)
Datadog은 기존 작업 시스템을 Kubernetes로 이전하는 과정에서 CPU 사용량은 증가하고 작업 처리 속도는 40-50% 저하되는 성능 퇴행 문제를 겪었습니다. 이를 해결하기 위해 VM과 Kubernetes 간의 정밀한 비교 실험을 설계하고, 지표 측정 방식과 리소스 할당(Resource Requests) 설정을 최적화하여 성능을 이전 수준으로 복구했습니다. 본 분석을 통해 Kubernetes 오버헤드의 실체를 파악하고, 고성능 워크로드를 위한 포드 배치 전략을 도출했습니다. ### 실험 환경의 통제와 정렬 * **환경 변수 통일**: 성능 차이의 원인을 정확히 규명하기 위해 VM과 Kubernetes 클러스터의 인스턴스 유형(c5.2xlarge), 커널 버전(3.13.0-141), 실행 스크립트를 동일하게 맞추어 비교 대상을 단일화했습니다. * **배치 구조 최적화**: 기존 VM 방식과 유사하게 하나의 포드 내에 하나의 부모 프로세스와 그 자식 워커들을 배치하여, 노드당 부모 프로세스 수와 포드 수가 일치하도록 구성했습니다. ### 측정 지표의 재정의: Idle CPU의 중요성 * **Load Average의 한계**: Kubernetes에서는 상태 확인 등을 위한 배경 프로세스가 빈번하게 실행되는데, 이는 실제 CPU 사용량과 무관하게 Load Average 수치를 비정상적으로 높여 시스템이 바쁜 것처럼 오해하게 만듭니다. * **Idle CPU 활용**: 프로세스 개수가 아닌 '실제 CPU가 일하지 않는 시간'을 측정하는 Idle CPU 지표를 선택함으로써, 시스템의 남은 용량을 더 정확하게 파악하고 성능 분석의 신뢰도를 높였습니다. * **처리량(Throughput) 중심**: 배치 작업의 특성에 맞춰 지연 시간(Latency)보다는 30초당 완료된 작업 수라는 처리량 지표를 핵심 성능 지표로 설정했습니다. ### 리소스 요청(Resource Requests) 및 스케줄링 튜닝 * **스케줄링 병목 해결**: 초기에 각 포드가 1 Core CPU를 요청하도록 설정했을 때, 노드당 4개의 포드만 배치되는 과소 활용 문제가 발생했습니다. 목표치인 노드당 6개 포드 배치를 위해 CPU 요청을 100m으로, 메모리 요청을 500MB로 대폭 낮췄습니다. * **단일 리소스 기준 권장**: 여러 리소스(CPU, 메모리 등)의 요청 값을 모두 엄격하게 잡으면 스케줄링이 복잡해지므로, 하나의 주된 리소스를 기준으로 배치를 유도하고 나머지는 실제 필요량에 가깝게 설정하는 것이 효율적임을 확인했습니다. * **Request와 Limit의 구분**: `request`는 스케줄링을 위한 최소 보장치이며, 실제 실행 중의 제약은 `limit`이 담당하므로 `request`를 낮추는 것이 실행 성능에 부정적인 영향을 주지 않는다는 점을 활용했습니다. ### 포드별 오버헤드의 실체 분석 * **프로세스 구조**: `pstree`를 통해 분석한 결과, 포드당 오버헤드는 주로 컨테이너 런타임인 `containerd-shim`에서 발생했습니다. * **CPU 및 메모리 비용**: 실험 결과 포드당 CPU 오버헤드는 무시할 수 있는 수준이었으며, 메모리는 포드당 약 24MB(containerd-shim 및 pause 컨테이너 포함) 수준으로 측정되었습니다. * **결론적 선택**: 오버헤드가 크지 않기 때문에, 관리 효율성을 위해 포드 하나에 여러 부모 프로세스를 억지로 집어넣기보다 '포드당 1 부모 프로세스' 구조를 유지하는 것이 더 유리하다는 결론을 내렸습니다. Kubernetes로 이전 시 발생하는 성능 저하는 플랫폼 자체의 문제라기보다 잘못된 리소스 요청 설정과 지표 해석에서 기인하는 경우가 많습니다. 노드당 포드 밀도를 최적화하기 위해 `Resource Requests`를 전략적으로 낮게 설정하고, 시스템의 부하를 판단할 때는 Load Average 대신 Idle CPU를 관찰함으로써 VM에 근접한 성능을 확보할 수 있습니다.