일반적인 Kubernetes 성능 병목 현상 문제 해결

CPU 제한, 메모리 OOMKill, 스케줄링 지연 등 일반적인 Kubernetes 성능 병목 현상을 체계적으로 진단하고 해결하는 방법을 배웁니다. 이 가이드는 리소스 요청 조정, HPA 스케일링 최적화, 기본 클러스터 제약 조건 식별을 위한 실행 가능한 명령과 모범 사례를 제공하여 최적의 애플리케이션 성능을 보장합니다.

일반적인 Kubernetes 성능 병목 현상 문제 해결

Kubernetes 성능 문제는 거의 "Kubernetes가 느리다"고 스스로 알리지 않습니다. 배포가 중단되거나, API가 갑자기 5xx 오류를 반환하거나, 큐가 비워지지 않거나, 사용자가 지연 시간을 불평하는 동안 포드가 정상으로 보이는 상황을 목격하게 됩니다. 클러스터는 그 이야기의 일부일 뿐이지만, 일관된 명령 세트로 검사할 수 있는 부분입니다.

요령은 노드 크기나 복제본 수로 바로 뛰어드는 것을 피하는 것입니다. 먼저 어떤 종류의 병목 현상이 있는지 결정하십시오: CPU 제한, 메모리 압력, 스케줄링 지연, 느린 스케일링, 네트워크 지연, 스토리지 지연, 또는 예상보다 더 많은 작업을 수행하는 애플리케이션.

1단계: 증상 식별

특정 구성 요소를 자세히 살펴보기 전에 관찰된 성능 저하를 명확히 정의하십시오. 일반적인 증상은 종종 다음 범주 중 하나에 속합니다:

  • 느린 배포/업데이트: 포드 생성에 과도한 시간이 걸리거나 롤링 업데이트가 중단됩니다.
  • 응답 없는 애플리케이션: 포드는 실행 중이지만 애플리케이션 수준 트래픽(예: 높은 지연 시간, 5xx 오류)에 응답하지 못합니다.
  • 높은 리소스 급증: 노드 또는 특정 배포 전반에 걸쳐 설명할 수 없는 CPU 또는 메모리 사용률 급증이 발생합니다.
  • 스케줄링 지연: 새 포드가 Pending 상태에 무기한 남아 있습니다.

2단계: 리소스 제약 조건 진단 (CPU 및 메모리)

리소스 관리 부실은 Kubernetes 성능 문제의 가장 빈번한 원인입니다. 잘못 설정된 요청 및 제한은 제한 또는 OOMKill로 이어집니다.

1. 리소스 사용량 및 제한 확인

kubectl describekubectl top을 사용하여 영향을 받는 애플리케이션의 리소스 할당을 검사하는 것부터 시작하십시오.

실행 가능한 확인: 메트릭 서버가 보고하는 실제 사용량과 requestslimits를 비교하십시오.

# 네임스페이스의 모든 포드에 대한 리소스 사용량 가져오기
kubectl top pods -n <namespace>

# 특정 포드의 리소스 요청/제한 확인
kubectl describe pod <pod-name> -n <namespace>

또한 소유 워크로드를 검사하여 문제가 하나의 포드에 영향을 미치는지 아니면 전체 Deployment에 영향을 미치는지 이해하십시오:

kubectl get deploy <deployment-name> -n <namespace> -o yaml
kubectl get pods -n <namespace> -l app=<label> -o wide

하나의 포드만 느리고 다른 포드와 다른 노드에 있는 경우 노드 수준 압력이 더 가능성이 높습니다. 모든 복제본이 느린 경우 리소스 설정, 다운스트림 종속성 또는 애플리케이션 동작에 더 주의를 기울여야 합니다.

2. CPU 제한

컨테이너의 CPU 사용량이 정의된 limit에 반복적으로 도달하면 커널이 이를 제한하여 노드 자체에 사용 가능한 용량이 있더라도 심각한 지연 시간 급증을 초래합니다. 이는 종종 일반적인 CPU 부족으로 오인됩니다.

진단 팁: kubectl top노드에서 100% CPU 사용량을 표시하지 않는 경우에도 높은 지연 시간 응답을 찾으십시오. 제한은 컨테이너별로 발생합니다.

더 깊은 확인을 위해 메트릭 시스템이 컨테이너 CPU 제한 메트릭을 노출하는 경우 이를 사용하십시오. Prometheus 기반 설정에서 팀은 종종 요청 지연 시간과 함께 제한된 CPU 주기와 같은 메트릭을 관찰합니다. 원시 CPU 사용량만으로는 제한을 숨길 수 있습니다. 컨테이너가 전체 노드 코어를 사용하는 것처럼 보이기 전에 제한될 수 있기 때문입니다.

해결 방법:

  • 워크로드가 합법적으로 더 많은 처리 능력을 필요로 하는 경우 CPU limit를 늘리십시오.
  • 애플리케이션이 busy-waiting 상태인 경우 단순히 제한을 늘리는 대신 애플리케이션 코드를 최적화하십시오.
  • 일부 지연 시간에 민감한 서비스의 경우 플랫폼 정책에 부합한다면 CPU 요청은 유지하면서 CPU 제한을 제거하는 것을 고려하십시오. 이렇게 하면 스케줄러에 유용한 배치 정보를 제공하면서도 강제 제한을 피할 수 있습니다.

3. 메모리 압력 및 OOMKill

컨테이너가 메모리 limit를 초과하면 Kubernetes는 OOM(Out-Of-Memory) kill을 시작하여 컨테이너를 반복적으로 다시 시작합니다.

진단: 포드 상태에서 빈번한 재시작을 확인하고(kubectl get podsRESTARTS 열 확인) OOMKilled 이벤트에 대한 로그를 검사하십시오.

# OOMKill에 대한 최근 이벤트 확인
kubectl get events --field-selector involvedObject.name=<pod-name> -n <namespace>

해결 방법:

  • OOMKill이 빈번한 경우 즉시 메모리 limit를 늘리십시오.
  • 장기적인 수정을 위해 애플리케이션을 프로파일링하여 메모리 누수를 찾아 수정하거나 힙 크기를 줄이십시오.

메모리는 CPU와 다르게 동작합니다. CPU는 제한될 수 있으며 프로세스는 계속 느리게 실행됩니다. 메모리 제한 위반은 일반적으로 프로세스가 종료되는 것으로 끝납니다. 이로 인해 메모리 문제는 재시작, 연결 끊김, 콜드 캐시, 실패한 진행 중인 요청과 같은 안정성 문제처럼 보입니다.

모범 사례: 요청을 현명하게 설정하십시오. 리소스 requests가 예상 최소 사용량에 합리적으로 가깝게 설정되었는지 확인하십시오. requests가 너무 낮으면 스케줄러가 노드를 초과 할당하여 모든 포드가 동시에 요구 사항에 도달할 때 경합이 발생할 수 있습니다.

3단계: 스케줄링 병목 현상 조사

포드가 Pending 상태로 남아 있으면 스케줄러가 적합한 노드를 찾지 못하는 문제입니다.

1. Pending 포드 분석

kubectl describe pod를 사용하여 Pending 포드의 Events 섹션을 읽으십시오. 이 섹션에는 일반적으로 스케줄링 실패에 대한 명확한 설명이 포함되어 있습니다.

일반적인 스케줄러 메시지:

  • 0/3 nodes are available: 3 Insufficient cpu. (노드 용량 문제)
  • 0/3 nodes are available: 3 node(s) had taint {dedicated: infra}, that the pod didn't tolerate. (Taints/Tolerations 불일치)
  • 0/3 nodes are available: 1 node(s) had taint {NoSchedule: true}, that the pod didn't tolerate. (노드 압력 또는 유지 관리)

2. 클러스터 리소스 포화

CPU/메모리 부족으로 스케줄링이 지연되는 경우 클러스터에 충분한 총 용량이 부족합니다.

해결 방법:

  • 클러스터에 노드를 더 추가하십시오.
  • 잘못 구성된 리소스 요청으로 인해 노드 사용률이 인위적으로 높지 않은지 확인하십시오(2단계 참조).
  • 클라우드 공급자에서 실행 중인 경우 **Cluster Autoscaler (CA)**를 사용하여 Pending 포드가 누적될 때 동적으로 노드를 추가하십시오.

Cluster Autoscaler가 활성화되어 있지만 노드가 추가되지 않는 경우 클라우드 공급자가 손상되었다고 가정하기 전에 해당 로그를 읽으십시오. Autoscaler는 노드 그룹이 최대 크기에 도달했거나, Pending 포드에 충족할 수 없는 제약 조건이 있거나, 할당량이 새 인스턴스를 방지하기 때문에 노드 추가를 거부할 수 있습니다.

4단계: 스케일링 메커니즘의 성능 문제

자동 스케일링은 신속하게 반응해야 하지만 Horizontal Pod Autoscaler (HPA) 또는 Vertical Pod Autoscaler (VPA)의 잘못된 구성이 문제를 일으킬 수 있습니다.

1. Horizontal Pod Autoscaler (HPA) 지연

HPA는 Metrics Server가 정확한 CPU/메모리 사용률 또는 사용자 정의 메트릭을 보고하는 데 의존합니다.

진단 단계:

  1. Metrics Server 상태 확인: Metrics Server가 실행 중이고 액세스 가능한지 확인하십시오.
    kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes"
    
  2. HPA 상태 확인: HPA 구성 및 최근 이벤트를 검사하십시오.
    kubectl describe hpa <hpa-name> -n <namespace>
    
    메트릭 소스를 사용할 수 없거나 스케일링 결정 루프가 제대로 작동하는지 나타내는 메시지를 찾으십시오.

병목 현상: 사용자 정의 메트릭이 사용되는 경우 외부 메트릭 공급자가 올바르게 작동하고 HPA가 유용한 결정을 내릴 수 있을 만큼 자주 데이터를 보고하는지 확인하십시오.

HPA는 반응적입니다. 메트릭이 이를 반영하지 않는 한 트래픽 급증이 오고 있음을 알지 못합니다. 갑작스러운 버스트가 있는 워크로드의 경우 더 높은 최소 복제본, 더 빠른 사용자 정의 메트릭, 큐 기반 스케일링 또는 알려진 이벤트 전 사전 스케일링이 필요할 수 있습니다.

2. Vertical Pod Autoscaler (VPA) 상호 작용

VPA는 리소스 요청을 자동으로 조정하지만, 특히 재시작을 용납할 수 없는 상태 저장 애플리케이션의 경우 조정 단계에서 포드를 자주 재시작하거나 크기를 조정하면 성능 불안정을 유발할 수 있습니다.

권장 사항: 먼저 VPA를 Recommender 모드로 사용하거나 updateMode: "Off"를 사용하여 자동 적용 없이 권장 사항만 관찰함으로써 불필요한 크기 조정 중단을 완화하십시오.

5단계: 네트워크 및 스토리지 성능

컴퓨팅 리소스가 괜찮아 보일 때 네트워킹 또는 영구 스토리지가 병목 지점일 수 있습니다.

1. CNI (Container Network Interface) 문제

포드 간(특히 노드 간) 통신이 느리거나 간헐적으로 실패하는 경우 CNI 플러그인이 과부하되었거나 잘못 구성되었을 수 있습니다.

문제 해결:

  • CNI 데몬셋 포드(예: Calico, Flannel)의 로그를 확인하십시오.
  • 다른 노드의 포드 간에 ping 또는 curl을 사용하여 기본 연결을 테스트하십시오.

2. Persistent Volume (PV) 지연

디스크 I/O(데이터베이스, 로깅 시스템)에 크게 의존하는 애플리케이션은 기본 Persistent Volume의 지연 시간이 높으면 성능 저하를 겪습니다.

실행 가능한 확인: 프로비저너 유형(예: AWS EBS gp3 대 io1)을 확인하고 볼륨이 필요한 IOPS/처리량 사양을 충족하는지 확인하십시오.

스토리지 경고: 기본 디스크 성능 특성을 이해하지 않고 표준 hostPath 볼륨에서 고처리량 데이터베이스를 직접 실행하지 마십시오. 까다로운 워크로드에는 관리형 클라우드 스토리지 솔루션 또는 고성능 로컬 스토리지 프로비저너를 사용하십시오.

노드 수준 병목 현상

때로는 노드의 모든 포드가 동시에 느려집니다. 이것은 하나의 Deployment만 바라보는 것을 멈추고 노드를 검사해야 하는 신호입니다.

kubectl describe node <node-name>
kubectl top node <node-name>
kubectl get pods --all-namespaces -o wide | grep <node-name>

MemoryPressure, DiskPressurePIDPressure 조건을 찾으십시오. 디스크 압력은 애플리케이션 증상이 명백한 디스크 오류보다는 느린 시작, 이미지 풀 실패 또는 축출일 수 있기 때문에 간과하기 쉽습니다.

노드 자체에서 액세스 권한이 있는 경우 다음을 확인하십시오:

df -h
iostat -x 1
free -h
journalctl -u kubelet --since "30 minutes ago"

관리형 Kubernetes 서비스는 직접 노드 액세스를 제한할 수 있지만 동일한 아이디어가 적용됩니다: 공급자 메트릭, kubelet 이벤트 및 노드 조건을 사용하여 노드가 공유 병목 현상인지 결정하십시오.

제어 플레인 및 API 압력

대부분의 애플리케이션 지연 시간은 Kubernetes API 서버로 인해 발생하지 않습니다. 웹 요청은 일반적으로 모든 사용자 요청에서 API 서버를 호출하지 않습니다. 그러나 제어 플레인 압력은 운영 성능을 저하시킬 수 있습니다: 느린 롤아웃, 지연된 스케줄링, 느린 엔드포인트 업데이트 또는 뒤처지는 컨트롤러.

증상은 다음과 같습니다:

  • kubectl 명령이 클러스터 전체에서 느립니다.
  • 배포가 포드를 생성하는 데 평소보다 오래 걸립니다.
  • 컨트롤러가 원하는 상태보다 뒤쳐집니다.
  • 이벤트에 반복적인 API 시간 초과가 표시됩니다.

문제가 일반 애플리케이션 트래픽에 영향을 미치는지 아니면 클러스터 운영에 영향을 미치는지 확인하십시오. 롤아웃과 스케줄링만 느린 경우 제어 플레인을 관리하는 클러스터에서 API 서버 상태, 컨트롤러 관리자 동작, 어드미션 웹훅 및 etcd 상태를 살펴보십시오.

어드미션 웹훅은 특별한 주의가 필요합니다. 느리거나 사용할 수 없는 웹훅은 노드에 충분한 용량이 있더라도 포드 생성을 지연시킬 수 있습니다. 롤아웃이 생성 시점에 중단되고 이벤트에 웹훅 호출이 언급된 경우 노드 크기를 조정하기 전에 웹훅 서비스를 조사하십시오.

실용적인 문제 해결 순서

사용자가 볼 수 있는 증상부터 시작하십시오:

  • 느린 HTTP 요청: 앱 지연 시간, CPU 제한, 메모리 재시작, 다운스트림 지연 시간 및 네트워크 경로를 비교하십시오.
  • 느린 포드 시작: 이미지 풀 시간, 스케줄링 이벤트, 볼륨 연결 시간 및 초기화 컨테이너를 확인하십시오.
  • Pending 포드: 요청, 노드 용량, 테인트, 선호도, 할당량 및 오토스케일러 제한을 확인하십시오.
  • 주기적인 지연 시간 급증: CPU 제한, 가비지 수집, 시끄러운 이웃, 스토리지 지연 시간 및 HPA 스케일링 타이밍을 확인하십시오.
  • 무작위 재시작: OOMKilled, 활성 프로브, 노드 압력 및 이전 컨테이너의 애플리케이션 로그를 확인하십시오.

그런 다음 한 번에 한 계층씩 증명하거나 제거하십시오. 예를 들어, 지연 시간 급증이 CPU 제한과 정확히 일치하면 강력한 단서가 있습니다. CPU, 메모리, 네트워크 및 스토리지가 모두 안정적으로 보이는 동안 지연 시간 급증이 발생하면 병목 현상이 애플리케이션 내부 또는 Kubernetes 외부의 다운스트림 서비스에 있을 수 있습니다.

추측 없는 요청 및 제한 조정

잘못된 리소스 설정은 많은 성능 문제를 만듭니다:

  • 요청이 너무 낮음: 스케줄러가 너무 많은 사용 중인 포드를 동일한 노드에 패킹합니다.
  • 요청이 너무 높음: 실제 사용량이 적당함에도 포드가 Pending 상태로 유지됩니다.
  • CPU 제한이 너무 낮음: 지연 시간에 민감한 앱이 제한됩니다.
  • 메모리 제한이 너무 낮음: 컨테이너가 느려지는 대신 종료됩니다.
  • 요청이 전혀 없음: 스케줄링의 예측 가능성이 떨어지고 중요한 워크로드가 시끄러운 이웃과 심하게 경쟁할 수 있습니다.

최근 프로덕션 메트릭을 시작점으로 사용한 다음 정상적인 급증을 위한 여유 공간을 남겨 두십시오. Java, Node.js, Go, Python 및 데이터베이스 유사 워크로드의 경우 메모리 동작이 매우 다를 수 있으므로 컨테이너 이미지 크기가 비슷해 보인다고 해서 한 서비스의 제한을 다른 서비스로 복사하지 마십시오.

다음 단계

최고의 Kubernetes 성능 조사는 좋은 의미에서 지루합니다: 증상을 정의하고, 포드를 확인하고, 노드를 확인하고, 스케일링을 확인한 다음 네트워크와 스토리지를 확인하십시오. kubectl describekubectl top은 시작에 불과하지만, 일반적으로 어떤 방향으로 추적할 가치가 있는지 알려줍니다.

  1. 강력한 Resource Quotas를 구현하여 시끄러운 이웃이 중요한 애플리케이션을 굶주리지 않도록 하십시오.
  2. 포드 재시작 횟수를 정기적으로 검토하여 미묘한 OOM 또는 실패하는 애플리케이션 동작을 조기에 포착하십시오.
  3. 원시 사용량뿐만 아니라 CPU 제한 메트릭을 구체적으로 추적하는 Prometheus/Grafana 대시보드를 활용하십시오.