Skip to content

Latest commit

 

History

History
74 lines (50 loc) · 8.94 KB

File metadata and controls

74 lines (50 loc) · 8.94 KB

v1.35 — Kubernetes In-Place Pod Resize: Pod를 재시작하지 않고 CPU/메모리를 바꾸는 법 (KEP-1287, GA)

메타데이터

항목 내용
프로젝트 Kubernetes
관련 릴리스 v1.27(alpha) → v1.33(beta) → v1.35 "Timbernetes"(GA, 2025-12-17)
KEP KEP-1287: In-Place Update of Pod Resources (SIG Node)
최초 구현 PR kubernetes/kubernetes#102884
발견 출처 Kubernetes v1.35 릴리스 블로그, In-Place Pod Resize Graduates to Stable
검증 근거 KEP README (kubernetes/enhancements) 실제 설계 문서

개요

Kubernetes v1.35에서 **Pod의 CPU/메모리 리소스 요청·제한을 Pod를 재시작하지 않고 바꿀 수 있는 기능(In-Place Pod Resize)**이 GA(정식 기능)로 승격됐다. v1.27에서 alpha로 시작해 8개 릴리스에 걸쳐 점진적으로 안정화된, 꽤 오래 걸린 기능이다. 핵심은 "리소스 스펙을 바꾼다"는 선언과 "그게 실제로 컨테이너에 적용됐다"는 사실 사이를 kubelet이 여러 단계로 나눠 추적하는 상태 기계를 도입한 것이다.

사전 지식 — 왜 지금까지는 Pod를 재시작해야만 했는가

Kubernetes의 원래 모델에서 Pod.Spec.Containers[i].Resources(CPU/메모리 요청량과 제한량)는 생성 시점에 고정되는 필드였다. 이 값은 스케줄러가 Pod를 어느 노드에 배치할지 결정하는 데 쓰이고, kubelet이 컨테이너 런타임(containerd 등)에게 cgroup 제한을 설정할 때도 이 값을 그대로 쓴다. 문제는 이 필드가 **불변(immutable)**으로 취급됐다는 점이다 — 값을 바꾸려면 Pod 객체 자체를 지우고 새로 만들어야 했다.

이게 왜 문제가 되냐면, VPA(Vertical Pod Autoscaler) 같은 도구가 "이 Pod는 메모리를 더 써야 한다"고 판단해도 실제로 적용하려면 Pod를 죽이고 다시 띄워야 했다 — stateful 워크로드(DB, 캐시)나 오래 걸리는 배치 작업에는 이게 그대로 다운타임이나 작업 재시작 비용으로 이어진다. cgroup 자체는 리눅스 커널 레벨에서 실행 중에 limit을 바꿀 수 있는 기능을 이미 제공하는데, Kubernetes API 레벨에서 "Spec은 불변"이라는 제약이 그 능력을 못 쓰게 막고 있었던 셈이다.

변경 분석 — "선언"과 "실제 적용" 사이를 4단계로 쪼갠 상태 기계

KEP-1287의 핵심 설계는, 리소스 값 하나를 네 단계로 구분해서 추적하는 것이다:

  1. Desired — 사용자가 .spec.containers[i].resources에 선언한 값
  2. Allocated — kubelet이 "이 노드에서 이 값을 감당할 수 있다"고 판단해 로컬에 체크포인트해둔 값 (status.containerStatuses[i].allocatedResources)
  3. Actuated — 그 값이 컨테이너 런타임(CRI)에게 실제로 전달된 상태
  4. Actual — 런타임이 cgroup 등에서 실제로 보고하는 값 (status.containerStatuses[i].resources)

기존에는 "Spec == 실제 실행 중인 값"이라는 암묵적 전제가 있었는데, 이 전제가 깨지면서 Spec을 바로 바꿔도 즉시 적용되는 게 아니라 별도의 승인·적용 절차를 거친다는 게 이번 변경의 본질이다.

[변경 전]
┌──────────────┐   생성 시 고정   ┌───────────────────┐
│ Pod.Spec     │ ───────────────▶ │ 컨테이너 실제 실행값 │
│ (Resources)  │  (바꾸려면 재생성) │ (cgroup limit)     │
└──────────────┘                 └───────────────────┘

[변경 후]
┌──────────┐  /resize  ┌───────────┐  admission  ┌───────────┐  CRI     ┌────────┐
│ Desired  │ ────────▶ │ Allocated │ ──────────▶ │ Actuated  │ ───────▶ │ Actual │
│ (Spec)   │           │ (체크포인트)│  (노드 여유 검사) │ (런타임 전달) │          │ (cgroup)│
└──────────┘           └───────────┘             └───────────┘          └────────┘

변경 경로는 /resize 서브리소스로 한정된다 — Pod 전체를 PATCH하는 게 아니라, 리소스 변경 전용 API 엔드포인트를 따로 둔 것이다. 이 요청이 들어오면 kubelet은 다음을 계산한다: *"현재 이 노드에 있는 다른 모든 Pod의 allocated 리소스 합 + 이번에 새로 요청하는 값"*이 노드의 allocatable을 넘는지. 넘지 않으면 그 자리에서 승인(Allocated 단계로 진입)하고, 지금은 부족하지만(예: 다른 Pod가 내려가면 여유가 생기는 경우) 이론적으로는 가능하면 PodResizePending(reason=Deferred)으로 대기시키고, 노드 용량 자체를 초과해서 원천적으로 불가능하면 PodResizePending(reason=Infeasible)으로 즉시 거부한다. 승인됐지만 아직 런타임에 실제로 반영되지 않은 중간 상태는 PodResizeInProgress 조건으로 노출된다.

설계 결정 및 트레이드오프

왜 "런타임이 재시작으로 리사이즈를 흉내내는 것"을 명시적으로 금지했는가: KEP는 "리소스를 조정하기 위해 런타임이 일부러 컨테이너를 재시작해서는 안 되며, 재시작이 꼭 필요하다면 차라리 에러를 반환해야 한다"고 규정한다. 만약 이걸 허용하면, 사용자 입장에서는 "in-place resize"라고 믿고 썼는데 실제로는 컨테이너가 재시작되어 상태(메모리 캐시, TCP 커넥션 등)를 잃는 상황이 조용히 발생할 수 있다 — 기능의 핵심 약속(무중단)을 깨는 실패를 숨기지 말고 명시적으로 드러내는 쪽을 택한 것이다.

왜 리소스별로 재시작 정책을 따로 둘 수 있게 했는가(ResizePolicy): 모든 애플리케이션이 CPU/메모리 변경을 무중단으로 받아들일 수 있는 건 아니다(예: 메모리 제한 변경 시 내부 캐시 크기를 재계산해야 하는 앱). 그래서 리소스 타입(cpu/memory)별로 NotRequired(가능하면 무중단 적용, 기본값)와 RestartContainer(재시작해서 적용) 중 선택하게 했다 — 이렇게 하면 프레임워크는 "가능하면 무중단"을 기본 지향점으로 유지하면서도, 재시작이 실제로 필요한 워크로드를 강제로 무중단 처리하려다 앱을 오작동시키는 상황을 피한다. 여러 리소스에 서로 다른 정책이 붙어 충돌하면 RestartContainer가 우선한다 — 안전한 쪽(더 확실하게 적용되는 쪽)으로 fallback하는 선택이다.

왜 QoS 클래스를 리사이즈 전후로 고정했는가: Kubernetes의 QoS 클래스(Guaranteed/Burstable/BestEffort)는 리소스 요청·제한 값의 관계(요청==제한인지 등)로 파생되는데, 이는 스케줄러의 축출(eviction) 우선순위 결정에 쓰이는 중요한 불변 조건이다. 리사이즈로 QoS 클래스가 바뀔 수 있게 허용하면, 이미 "Guaranteed라서 안전하다"고 가정한 다른 로직(예: 노드 압박 시 축출 순서)이 깨질 수 있다. 그래서 리사이즈 요청이 QoS 클래스를 바꾸는 결과를 낳으면 그 요청 자체를 검증 단계에서 막는다 — 기능 하나를 위해 시스템 전체의 다른 불변 조건을 희생하지 않는 선택이다.

더 살펴볼 점

  • VPA(Vertical Pod Autoscaler)가 이 GA 기능을 기본 적용 모드로 바꾸기까지 얼마나 걸릴까?
  • Deferred 상태로 오래 대기하는 리사이즈 요청에 대한 타임아웃/재시도 정책은 어떻게 동작할까?
  • StatefulSet처럼 순서가 중요한 워크로드에서 리사이즈가 배포 순서와 어떻게 상호작용할까?

참고 자료

참고 외 별도 확인 링크

본문 참고 자료로 충분했다.