K8s 리소스 Right-Sizing 실전

리소스 설정이 왜 어려운가

Kubernetes에서 requestslimits를 잘못 설정하면 두 가지 문제가 발생합니다. 너무 높으면 클러스터 리소스가 낭비되고 비용이 증가합니다. 너무 낮으면 OOMKilled, CPU 스로틀링으로 서비스 장애가 발생합니다. 대부분의 팀이 “넉넉하게” 설정하고 잊어버리지만, 실제 사용량과의 괴리가 40~70%에 달하는 경우가 흔합니다.

이 글에서는 requests vs limits의 정확한 의미, QoS 클래스별 OOM 순서, 메트릭 기반 Right-Sizing 방법, CPU 스로틀링 진단, 그리고 VPA·Goldilocks 자동 추천까지 심층적으로 다룹니다.

requests vs limits 정확한 이해

이 두 값은 완전히 다른 목적을 가집니다:

구분 requests limits
역할 스케줄링 기준 — 노드 배치 결정 런타임 상한 — 초과 시 제한/종료
CPU 초과 다른 Pod가 안 쓰면 더 사용 가능 스로틀링 (강제 대기)
메모리 초과 다른 Pod가 안 쓰면 더 사용 가능 OOMKilled (즉시 종료)
미설정 시 BestEffort QoS → 가장 먼저 퇴거 무제한 사용 가능
resources:
  requests:
    cpu: 250m       # 0.25 코어 — "최소 이만큼은 보장해줘"
    memory: 256Mi   # 256MB — "최소 이만큼은 보장해줘"
  limits:
    cpu: "1"        # 1 코어 — "이 이상은 절대 못 써"
    memory: 512Mi   # 512MB — "이 이상 쓰면 OOMKilled"

QoS 클래스와 OOM 우선순위

노드 메모리가 부족하면 kubelet이 Pod를 QoS 클래스 순서대로 종료합니다:

# 1. Guaranteed — 가장 마지막에 종료
# requests == limits (CPU, 메모리 모두)
resources:
  requests:
    cpu: 500m
    memory: 512Mi
  limits:
    cpu: 500m        # requests와 동일
    memory: 512Mi    # requests와 동일

# 2. Burstable — 중간 우선순위
# requests < limits (또는 일부만 설정)
resources:
  requests:
    cpu: 250m
    memory: 256Mi
  limits:
    cpu: "1"         # requests보다 높음
    memory: 512Mi

# 3. BestEffort — 가장 먼저 종료
# requests/limits 모두 미설정
resources: {}

# 퇴거 순서: BestEffort → Burstable(사용량 높은 순) → Guaranteed

메트릭 기반 Right-Sizing

현재 리소스 설정이 적절한지 실제 사용량 데이터로 판단합니다:

# 1. 현재 리소스 사용량 확인
kubectl top pods -n production --sort-by=cpu
kubectl top pods -n production --sort-by=memory

# 2. requests 대비 실제 사용 비율
kubectl top pods -n production -o json | jq '
  .items[] | {
    name: .metadata.name,
    cpu_used: .containers[0].usage.cpu,
    mem_used: .containers[0].usage.memory
  }'

# 3. Prometheus로 장기 메트릭 분석 (7일)
# CPU: 평균 사용량
avg by (pod) (
  rate(container_cpu_usage_seconds_total{
    namespace="production",
    container!="POD"
  }[5m])
) / on(pod) group_left()
kube_pod_container_resource_requests{
  resource="cpu"
}

# 메모리: P99 사용량 (피크 기준)
quantile_over_time(0.99,
  container_memory_working_set_bytes{
    namespace="production",
    container!="POD"
  }[7d]
) / on(pod) group_left()
kube_pod_container_resource_requests{
  resource="memory"
}

CPU 스로틀링 진단

CPU limits를 너무 낮게 설정하면 스로틀링이 발생하여 응답 지연이 생깁니다. OOMKilled와 달리 Pod가 재시작되지 않아 발견하기 어렵습니다:

# CPU 스로틀링 확인
# throttled_periods: 스로틀링 발생 횟수
# throttled_time: 스로틀링으로 대기한 총 시간

# Prometheus 쿼리: 스로틀링 비율
rate(container_cpu_cfs_throttled_periods_total{
  namespace="production"
}[5m])
/
rate(container_cpu_cfs_periods_total{
  namespace="production"
}[5m])

# 30% 이상이면 CPU limits 증가 필요
# → 응답 지연의 숨은 원인인 경우가 많음

# 해결 방법들:
# 1) CPU limits 증가
# 2) CPU limits 제거 (requests만 설정)
# 3) Pod 수평 확장 (HPA)

논쟁: 많은 팀이 CPU limits를 아예 설정하지 않는 전략을 채택합니다. Google의 내부 권장사항이기도 합니다:

# CPU limits 미설정 전략 (권장하는 팀 多)
resources:
  requests:
    cpu: 250m        # 스케줄링과 QoS용
    memory: 256Mi
  limits:
    # cpu: 생략!     # 스로틀링 방지 — 유휴 CPU 활용
    memory: 512Mi    # 메모리 limits는 반드시 설정 (OOM 방지)

Goldilocks로 자동 추천

Goldilocks는 VPA의 추천 값을 대시보드로 시각화해줍니다:

# Goldilocks 설치
helm repo add fairwinds-stable https://charts.fairwinds.com/stable
helm install goldilocks fairwinds-stable/goldilocks -n goldilocks --create-namespace

# 네임스페이스에 Goldilocks 활성화
kubectl label ns production goldilocks.fairwinds.com/enabled=true

# VPA 객체가 자동 생성되어 추천값 수집 시작
# 대시보드에서 각 Deployment별 추천 requests/limits 확인
kubectl -n goldilocks port-forward svc/goldilocks-dashboard 8080:80
# → http://localhost:8080

Goldilocks 대시보드는 각 컨테이너에 대해 Lower Bound, Target, Upper Bound, Uncapped Target 4가지 추천값을 보여줍니다:

추천 유형 설명 활용
Lower Bound 최소 권장값 requests 하한선
Target 최적 추천값 requests 설정 기준
Upper Bound 상위 안전 마진 limits 설정 기준
Uncapped Target LimitRange 무시 추천 실제 필요량 참고

Right-Sizing 실전 공식

# 권장 공식:
# CPU requests  = P99 사용량 (7일 기준)
# CPU limits    = 미설정 또는 requests × 3~5배
# Memory requests = P99 사용량 × 1.2 (20% 마진)
# Memory limits   = requests × 1.5~2배

# 예시: 7일간 CPU P99=200m, Memory P99=180Mi
resources:
  requests:
    cpu: 200m          # P99 사용량
    memory: 220Mi      # 180Mi × 1.2
  limits:
    # cpu: 미설정      # 스로틀링 방지
    memory: 440Mi      # 220Mi × 2

# 주기적 리뷰 (월 1회)
# 1. Goldilocks 대시보드 확인
# 2. 스로틀링 메트릭 확인
# 3. OOMKilled 이벤트 확인
# 4. requests/limits 조정

비용 절감 효과 측정

# 현재 낭비량 계산 (Prometheus)
# CPU 낭비: requests - 실제 사용량
sum(
  kube_pod_container_resource_requests{resource="cpu", namespace="production"}
  -
  rate(container_cpu_usage_seconds_total{namespace="production"}[5m])
)

# 메모리 낭비
sum(
  kube_pod_container_resource_requests{resource="memory", namespace="production"}
  -
  container_memory_working_set_bytes{namespace="production"}
)

# 일반적으로 Right-Sizing 후:
# - 클러스터 노드 수 20~40% 감소
# - 월 클라우드 비용 25~35% 절감
# - OOMKilled/스로틀링 90% 이상 감소

마무리

Kubernetes 리소스 Right-Sizing은 비용 최적화와 서비스 안정성을 동시에 달성하는 핵심 운영 활동입니다. CPU limits 제거로 스로틀링을 방지하고, 메모리 limits로 OOM을 막으며, Goldilocks와 Prometheus 메트릭으로 데이터 기반 의사결정을 하면 효율적인 클러스터를 운영할 수 있습니다.

관련 글로 K8s HPA 오토스케일링 심화K8s VPA 리소스 자동 튜닝도 함께 참고하세요.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux