requests와 limits 값을 정하는 기준: 운영에서 통하는 규칙

5 분 소요

requests는 스케줄링용 예약, limits는 상한이라는 개념은 K8s 중급 #4에서 다뤘습니다. 그런데 현장의 질문은 개념이 아니라 “그래서 얼마로 적는가“입니다. 이 글은 값을 정하는 실무 규칙과, 값이 잘못됐을 때 나는 사고 유형을 정리합니다. Pod PendingHPA 편에서 미뤄 둔 숙제이기도 합니다.

requests: 실측의 p95 근처에서 시작합니다 #

requests는 스케줄러에게 하는 약속이자 노드에서 경합이 났을 때 보장받는 몫입니다. 그래서 기준은 희망이 아니라 실측입니다.

  • 평시 부하에서 1〜2주 사용량을 관측하고, CPU는 평균과 p95 사이, 메모리는 p95〜피크 근처를 출발점으로 잡습니다. 메모리를 더 보수적으로 잡는 이유는, CPU 부족은 느려질 뿐이지만 메모리 부족은 프로세스가 죽기 때문입니다.
  • 감으로 시작해야 한다면 작게 시작해 올립니다. 반대 방향(크게 잡고 줄이기)은 잘 일어나지 않아서, 과대 예약이 화석처럼 남습니다.
  • VPA를 권고(Off/Initial) 모드로 붙이면 실측 기반 권고값을 공짜로 얻습니다. auto 모드로 켜지 않아도 산정 도구로서 충분히 유용합니다.

requests가 실측보다 크게 잡히면 어떤 사고가 나는지는 Pod Pending 편의 2번에서 봤습니다. 클러스터 실사용률은 낮은데 예약이 가득 차서 스케줄이 막히고, 노드 비용은 예약 기준으로 나갑니다. 과대 requests는 눈에 안 보이는 고정비입니다.

CPU limit: 걸지 않는 것이 기본값이 될 수 있습니다 #

CPU limit은 논쟁이 있는 항목이고, 논점은 스로틀링입니다. CPU limit은 cgroup의 시간 할당(quota) 방식이라, limit에 도달한 컨테이너는 다음 주기까지 강제로 멈춥니다. 이 스로틀링이 p99 지연 스파이크의 단골 원인입니다. 실사용이 limit 근처도 아닌데 멀티스레드 앱의 순간 버스트가 주기 안에서 quota를 소진해 스로틀되는, 직관에 반하는 사고도 있습니다.

실무 규칙은 이렇게 잡을 수 있습니다.

  • requests를 제대로 잡았다면 CPU limit은 생략을 기본값으로 검토합니다. CPU는 압축 가능한 자원이라 limit이 없어도 경합 시 requests 비율로 공평하게 나뉘고, 남는 CPU는 쓰는 것이 이득입니다.
  • CPU limit이 필요한 경우는 멀티테넌트에서 이웃의 폭주를 강하게 격리해야 할 때, 벤치마크 등 일관된 환경이 필요할 때입니다. 이때는 스로틀링 지표(container_cpu_cfs_throttled_periods_total)를 함께 관측합니다.
  • 스로틀링이 관측되면 limit을 올리거나 없애는 것이 먼저이고, 그다음이 앱의 스레드 수 조정입니다.

memory limit: 반대로, 거는 것이 기본입니다 #

메모리는 CPU와 달리 압축 불가능한 자원입니다. 뺏을 방법이 죽이는 것뿐이라서, limit 없는 컨테이너의 메모리 누수는 노드 전체를 위협하고, 노드 압박 시 커널 OOM 킬러가 어떤 프로세스를 죽일지 예측하기 어려워집니다. memory limit을 걸면 적어도 “누수 난 그 컨테이너가, 명확한 OOMKilled 사유로” 죽습니다. 사고를 없애는 게 아니라 사고를 진단 가능하게 만드는 설정입니다.

  • 값은 p95〜피크에 여유를 얹어(예: 피크의 120〜150%) 잡고, OOMKilled가 관측되면 누수인지 정상 요구 증가인지를 먼저 가립니다. 컨테이너 안 free가 호스트 값을 보여 주는 착시와 cgroup OOM의 구분은 메모리 편에서 다뤘습니다.
  • requests와 limits를 같게(Guaranteed) 두는 선택은 중요 워크로드의 예측 가능성을 확보합니다. 다음 절의 축출 순서 때문입니다.

QoS 클래스: 노드가 몰릴 때 누가 먼저 쫓겨나는가 #

requests·limits의 조합이 Pod의 QoS 클래스를 정하고, 노드 메모리가 부족해질 때 축출(eviction) 순서에 영향을 줍니다.

클래스조건압박 시
Guaranteed모든 컨테이너 requests=limits가장 마지막까지 보호
Burstablerequests < limits (또는 일부만 설정)중간: requests 초과 사용량이 클수록 불리
BestEffort둘 다 없음가장 먼저 축출

규칙은 단순합니다. 죽으면 안 되는 것(데이터베이스, 핵심 API)은 Guaranteed로, 일반 서비스는 Burstable로, BestEffort는 프로덕션에 두지 않습니다. BestEffort는 설정을 “안 한” 결과로 생기므로, LimitRange로 네임스페이스 기본값을 강제해 실수로 생기는 것을 막는 편이 안전합니다.

운영 루틴: 한 번 정하고 끝나지 않습니다 #

사용 패턴은 배포마다 바뀌므로, 산정은 루틴이 필요합니다.

  1. 관측 기반 재조정 주기를 둡니다(분기 또는 주요 릴리스 후). requests 대비 실사용, 스로틀링, OOMKilled 세 가지를 봅니다. 대시보드 구성은 K8s 고급 #5의 스택으로 충분합니다.
  2. 양쪽 사고 유형을 지표로 감시합니다. 과대 예약(클러스터 예약률은 높고 실사용은 낮음)과 과소 설정(스로틀링·OOMKilled 발생)은 반대 방향의 같은 문제입니다.
  3. HPA와의 상호작용을 기억합니다. HPA 편에서 봤듯 타깃 퍼센트의 분모가 requests라서, requests를 조정하면 스케일 동작도 함께 변합니다. 둘은 항상 세트로 리뷰합니다.

정리 #

  • requests는 실측(CPU는 평균〜p95, 메모리는 p95〜피크)에서 출발하고, VPA 권고 모드를 산정 도구로 씁니다. 과대 requests는 보이지 않는 고정비입니다.
  • CPU limit은 스로틀링 비용을 이해하고 선택합니다. requests가 제대로면 생략이 합리적인 기본값이고, 걸었다면 스로틀링 지표를 함께 봅니다.
  • memory limit은 거는 것이 기본입니다. 사고를 막는 게 아니라 OOMKilled라는 진단 가능한 형태로 만드는 설정입니다.
  • 중요 워크로드는 Guaranteed, 일반은 Burstable, BestEffort는 LimitRange로 원천 차단합니다.
  • 산정은 일회성이 아니라 루틴입니다. 예약률, 스로틀링, OOMKilled 세 지표로 분기마다 재조정합니다.
X