노드 압력 축출
노드 압력 축출 (Node-pressure Eviction)
**노드 압력 축출(node-pressure eviction)**은 kubelet이 노드의 리소스를 회수하기 위해 Pod를 선제적으로 종료하는 과정이에요.
kubelet은 클러스터 노드의 메모리, 디스크 공간, 파일시스템 inode 같은 리소스를 모니터링합니다. 이 리소스 중 하나 이상이 특정 소비 수준에 도달하면 kubelet은 리소스를 회수하고 기아 상태(starvation)를 막기 위해 노드의 하나 이상의 Pod를 선제적으로 실패시킬 수 있어요.
노드 압력 축출 중에 kubelet은 선택된 Pod의 phase를 Failed로 설정하고 그 Pod를 종료합니다.
노드 압력 축출은 API 시작 축출(API-initiated eviction)과 같지 않아요.
kubelet은 구성한 PodDisruptionBudget이나 Pod의 terminationGracePeriodSeconds를 존중하지 않습니다. 소프트 축출 임계값을 사용하면 kubelet은 구성한 eviction-max-pod-grace-period를 존중합니다. 하드 축출 임계값을 사용하면 kubelet은 종료에 0초 유예 기간(즉시 종료)을 사용합니다.
출처: 문서
자가 치유 동작 (Self healing behavior)
kubelet은 최종 사용자 Pod를 종료하기 전에 노드 수준 리소스를 회수하려고 합니다. 예를 들어 디스크 리소스가 부족해지면 사용하지 않는 컨테이너 이미지를 제거합니다.
Pod가 실패한 Pod를 교체하는 워크로드 관리 객체(StatefulSet이나 Deployment 같은)에 의해 관리되면, 컨트롤 플레인(kube-controller-manager)이 축출된 Pod 대신 새 Pod를 만듭니다.
정적 Pod의 자가 치유 (Self healing for static pods)
리소스 압박이 있는 노드에서 정적 Pod(static pod)를 실행 중이라면 kubelet이 그 정적 Pod를 축출할 수 있어요. 정적 Pod는 항상 그 노드에서 Pod를 실행하려는 의도를 나타내므로, kubelet은 교체를 만들려고 합니다.
kubelet은 교체를 만들 때 정적 Pod의 우선순위를 고려합니다. 정적 Pod 매니페스트가 낮은 우선순위를 지정하고, 클러스터 컨트롤 플레인에 정의된 더 높은 우선순위의 Pod가 있으며, 노드가 리소스 압박 상태라면 kubelet이 그 정적 Pod를 위한 공간을 만들지 못할 수 있어요. kubelet은 노드에 리소스 압박이 있더라도 모든 정적 Pod를 계속 실행하려고 시도합니다.
축출 신호와 임계값 (Eviction signals and thresholds)
kubelet은 다음과 같은 다양한 매개변수를 사용해 축출 결정을 내립니다.
- 축출 신호 (Eviction signals)
- 축출 임계값 (Eviction thresholds)
- 모니터링 간격 (Monitoring intervals)
축출 신호 (Eviction signals)
**축출 신호(eviction signal)**는 특정 시점의 특정 리소스의 현재 상태예요. kubelet은 신호를 축출 임계값(노드에서 사용 가능해야 하는 리소스의 최소량)과 비교해 축출 결정을 내립니다.
kubelet은 다음 축출 신호를 사용합니다.
| 축출 신호 | 설명 | Linux 전용 |
|---|---|---|
memory.available |
memory.available := node.status.capacity[memory] - node.stats.memory.workingSet |
|
nodefs.available |
nodefs.available := node.stats.fs.available |
|
nodefs.inodesFree |
nodefs.inodesFree := node.stats.fs.inodesFree |
• |
imagefs.available |
imagefs.available := node.stats.runtime.imagefs.available |
|
imagefs.inodesFree |
imagefs.inodesFree := node.stats.runtime.imagefs.inodesFree |
• |
containerfs.available |
containerfs.available := node.stats.runtime.containerfs.available |
|
containerfs.inodesFree |
containerfs.inodesFree := node.stats.runtime.containerfs.inodesFree |
• |
pid.available |
pid.available := node.stats.rlimit.maxpid - node.stats.rlimit.curproc |
• |
이 표에서 설명(Description) 열은 kubelet이 신호 값을 얻는 방법을 보여줘요. 각 신호는 백분율 또는 리터럴 값을 지원합니다. kubelet은 신호와 관련된 총 용량에 대해 백분율 값을 계산합니다.
메모리 신호 (Memory signals)
Linux 노드에서 memory.available의 값은 free -m 같은 도구가 아니라 cgroupfs에서 파생됩니다. 이것은 중요해요. free -m은 컨테이너에서 작동하지 않고, 사용자가 노드 할당 가능(node allocatable) 기능을 사용하면 리소스 부족 결정이 루트 노드뿐 아니라 cgroup 계층의 최종 사용자 Pod 부분에도 국한되기 때문입니다. 이 스크립트나 cgroupv2 스크립트는 kubelet이 memory.available을 계산하기 위해 수행하는 동일한 단계를 재현합니다. kubelet은 압박 상태에서 메모리를 회수할 수 있다고 가정하므로, 계산에서 inactive_file(비활성 LRU 목록의 파일 백업 메모리 바이트 수)을 제외합니다.
참고: 기능 상태: Kubernetes v1.37부터 Beta, 기본 활성화 (기능 게이트:
HugepageAwareEviction). hugepage가 구성된 노드에서 kubelet은 축출 관리자가 사용하는memory.available에서 노드의 총 hugepage 용량을 뺍니다. 이 조정이 없으면 메모리 cgroup 컨트롤러가 작업 집합에서 hugetlb 할당을 추적하지 않기 때문에 hugepage 예약 RAM이 AvailableBytes를 부풀립니다. 이는 축출을 지연시키고 OOM kill로 이어질 수 있어요. 이전 동작을 복원하려면HugepageAwareEviction기능 게이트를 비활성화하세요.
Windows 노드에서 memory.available의 값은 노드의 CommitLimit에서 노드의 전역 CommitTotal을 빼서 노드의 전역 메모리 커밋 수준(GetPerformanceInfo() 시스템 호출로 조회)에서 파생됩니다. 노드의 페이지 파일 크기가 바뀌면 CommitLimit이 바뀔 수 있다는 점에 유의하세요!
파일시스템 신호 (Filesystem signals)
kubelet은 축출 신호(<identifier>.inodesFree 또는 <identifier>.available)와 함께 사용할 수 있는 세 가지 특정 파일시스템 식별자를 인식합니다.
- nodefs: 노드의 주요 파일시스템으로, 로컬 디스크 볼륨, 메모리가 백업하지 않는 emptyDir 볼륨, 로그 저장, 임시 스토리지 등에 사용됩니다. 예를 들어 nodefs는
/var/lib/kubelet을 포함합니다. - imagefs: 컨테이너 런타임이 컨테이너 이미지(읽기 전용 레이어)를 저장하는 데 사용할 수 있는 선택적 파일시스템입니다. 별도의 containerfs가 없으면 이미지 파일시스템이 컨테이너 쓰기 가능 레이어도 저장합니다.
- containerfs: 컨테이너 런타임이 컨테이너 쓰기 가능 레이어를 저장하는 데 사용할 수 있는 선택적 파일시스템입니다. containerfs를 사용하면 imagefs 파일시스템을 분리해 이미지(읽기 전용 레이어)만 저장하고 그 외에는 아무것도 저장하지 않을 수 있습니다.
이 식별자들은 kubelet이 관찰하는 파일시스템을 설명합니다. 이것들이 항상 세 개의 다른 마운트 지점을 의미하는 것은 아니에요. 일반적인 레이아웃에서는 두 개 또는 세 개의 식별자가 같은 기저 파일시스템을 가리킬 수 있습니다.
참고: 기능 상태: Kubernetes v1.31부터 Beta, 기본 활성화 (기능 게이트:
KubeletSeparateDiskGC). 분리된 이미지 파일시스템 기능은 containerfs에 대한 새 축출 신호, 임계값, 메트릭을 추가합니다. containerfs를 사용하려면 Kubernetes v1.37 릴리스에서KubeletSeparateDiskGC기능 게이트를 활성화해야 합니다. Kubernetes v1.37에서 containerfs 파일시스템 지원을 제공하는 것은 CRI-O(v1.29 이상)뿐입니다.
kubelet은 컨테이너 파일시스템에 대해 세 가지 일반적인 레이아웃을 지원합니다.
- 모든 것이 단일 nodefs에 있는 경우. "rootfs" 또는 간단히 "root"라고도 불러요. 이 레이아웃에서 nodefs, imagefs, containerfs는 모두 같은 기저 파일시스템을 가리킵니다.
- 컨테이너 런타임 저장소가 루트 파일시스템과 분리된 전용 디스크에 있는 경우. 이 레이아웃에서 imagefs와 containerfs는 같은 기저 파일시스템을 가리키며, 이미지 레이어와 컨테이너 쓰기 가능 레이어를 모두 저장합니다. 흔히 "분리 디스크(split disk)"(또는 "별도 디스크(separate disk)") 파일시스템이라고 불러요.
- 컨테이너 쓰기 가능 레이어가 nodefs에 있고, 컨테이너 이미지(읽기 전용 레이어)는 별도의 imagefs에 저장되는 경우. 이 레이아웃에서 containerfs와 nodefs는 같은 기저 파일시스템을 가리킵니다. 흔히 "분리 이미지(split image)" 파일시스템이라고 불러요.
kubelet은 기저 컨테이너 런타임에서 현재 구성을 사용해 이 파일시스템들을 자동으로 발견하려고 하며, 다른 로컬 노드 파일시스템은 무시합니다.
kubelet은 다른 컨테이너 파일시스템이나 스토리지 구성을 지원하지 않으며, 현재 이미지와 컨테이너에 여러 파일시스템을 지원하지 않습니다.
지원 중단된 kubelet 가비지 컬렉션 기능 (Deprecated kubelet garbage collection features)
일부 kubelet 가비지 컬렉션 기능은 축출을 위해 지원 중단(deprecated)됩니다.
| 기존 플래그 | 이유 |
|---|---|
--maximum-dead-containers |
이전 로그가 컨테이너 컨텍스트 밖에 저장되면 지원 중단 |
--maximum-dead-containers-per-container |
이전 로그가 컨테이너 컨텍스트 밖에 저장되면 지원 중단 |
--minimum-container-ttl-duration |
이전 로그가 컨테이너 컨텍스트 밖에 저장되면 지원 중단 |
축출 임계값 (Eviction thresholds)
kubelet이 축출 결정을 내릴 때 사용할 사용자 지정 축출 임계값을 지정할 수 있어요. 소프트 및 하드 축출 임계값을 구성할 수 있습니다.
축출 임계값은 [eviction-signal][operator][quantity] 형태를 가져요. 여기서:
eviction-signal은 사용할 축출 신호입니다.operator는<(보다 작음) 같은 원하는 관계 연산자입니다.quantity는1Gi같은 축출 임계값 양입니다.quantity의 값은 Kubernetes가 사용하는 quantity 표현과 일치해야 합니다. 리터럴 값이나 백분율(%)을 사용할 수 있어요.
예를 들어 노드에 총 10GiB 메모리가 있고 사용 가능한 메모리가 1GiB 아래로 떨어지면 축출을 촉발하고 싶다면, 축출 임계값을 memory.available<10% 또는 memory.available<1Gi 중 하나로 정의할 수 있어요(둘 다 사용할 수는 없습니다).
소프트 축출 임계값 (Soft eviction thresholds)
소프트 축출 임계값은 축출 임계값을 관리자가 지정한 필수 유예 기간(grace period)과 짝지어요. kubelet은 유예 기간이 초과될 때까지 Pod를 축출하지 않습니다. 유예 기간을 지정하지 않으면 kubelet은 시작 시 오류를 반환합니다.
kubelet이 축출 중에 사용할 소프트 축출 임계값 유예 기간과 최대 허용 Pod 종료 유예 기간을 모두 지정할 수 있어요. 최대 허용 유예 기간을 지정하고 소프트 축출 임계값이 충족되면 kubelet은 두 유예 기간 중 더 작은 값을 사용합니다. 최대 허용 유예 기간을 지정하지 않으면 kubelet은 우아한 종료 없이 축출된 Pod를 즉시 죽입니다.
소프트 축출 임계값을 구성하려면 다음 플래그를 사용할 수 있어요.
--eviction-soft:memory.available<1.5Gi같은 축출 임계값 집합. 지정된 유예 기간 동안 유지되면 Pod 축출을 촉발할 수 있어요.--eviction-soft-grace-period:memory.available=1m30s같은 축출 유예 기간 집합. 소프트 축출 임계값이 Pod 축출을 촉발하기 전에 유지되어야 하는 시간을 정의합니다.--eviction-max-pod-grace-period: 소프트 축출 임계값이 충족된 것에 응답해 Pod를 종료할 때 사용할 최대 허용 유예 기간(초).
하드 축출 임계값 (Hard eviction thresholds)
하드 축출 임계값은 유예 기간이 없습니다. 하드 축출 임계값이 충족되면 kubelet은 갈급한 리소스를 회수하기 위해 우아한 종료 없이 Pod를 즉시 죽입니다.
--eviction-hard 플래그를 사용해 memory.available<1Gi 같은 하드 축출 임계값 집합을 구성할 수 있어요.
kubelet은 다음 기본 하드 축출 임계값을 가집니다.
memory.available<100Mi(Linux 노드)memory.available<500Mi(Windows 노드)nodefs.available<10%imagefs.available<15%nodefs.inodesFree<5%(Linux 노드)imagefs.inodesFree<5%(Linux 노드)
이 하드 축출 임계값 기본값은 매개변수 중 어느 것도 변경되지 않은 경우에만 설정됩니다. 어떤 매개변수의 값이라도 변경하면 다른 매개변수의 값은 기본값으로 상속되지 않고 0으로 설정됩니다. 사용자 지정 값을 제공하려면 모든 임계값을 각각 제공해야 해요. kubelet 구성 파일에서 MergeDefaultEvictionSettings를 true로 설정할 수도 있습니다. true로 설정하고 매개변수가 변경되면 다른 매개변수가 0 대신 기본값을 상속합니다.
containerfs.available과 containerfs.inodesFree(Linux 노드) 기본 축출 임계값은 다음과 같이 설정됩니다.
- containerfs와 nodefs가 같은 기저 파일시스템을 가리키면 containerfs 임계값은 nodefs와 같게 설정됩니다.
- containerfs와 imagefs가 같은 기저 파일시스템을 가리키면 containerfs 임계값은 imagefs와 같게 설정됩니다.
containerfs와 관련된 임계값에 대한 사용자 지정 재정의(override)는 지원되지 않습니다. 시도하면 경고가 발행되고, 제공된 사용자 지정 값은 무시됩니다.
축출 모니터링 간격 (Eviction monitoring interval)
kubelet은 구성된 housekeeping-interval(기본값 10초)을 기준으로 축출 임계값을 평가합니다.
노드 컨디션 (Node conditions)
kubelet은 구성된 유예 기간과 무관하게 하드 또는 소프트 축출 임계값이 충족됐기 때문에 노드가 압박 상태임을 반영하는 노드 컨디션을 보고합니다.
kubelet은 축출 신호를 다음과 같이 노드 컨디션에 매핑합니다.
| 노드 컨디션 | 축출 신호 | 설명 |
|---|---|---|
MemoryPressure |
memory.available |
노드의 사용 가능한 메모리가 축출 임계값을 충족함 |
DiskPressure |
nodefs.available, nodefs.inodesFree, imagefs.available, imagefs.inodesFree, containerfs.available, containerfs.inodesFree |
노드의 루트 파일시스템, 이미지 파일시스템, 또는 컨테이너 파일시스템 중 어느 하나의 사용 가능한 디스크 공간과 inode가 축출 임계값을 충족함 |
PIDPressure |
pid.available |
(Linux) 노드의 사용 가능한 프로세스 식별자가 축출 임계값 아래로 떨어짐 |
컨트롤 플레인은 이 노드 컨디션을 테인트(taint)에 매핑하기도 합니다.
kubelet은 구성된 --node-status-update-frequency(기본값 10초)를 기준으로 노드 컨디션을 업데이트합니다.
노드 컨디션 진동 (Node condition oscillation)
경우에 따라 노드가 정의된 유예 기간만큼 유지되지 않고 소프트 축출 임계값 위아래로 진동할 수 있어요. 이는 보고된 노드 컨디션이 true와 false 사이를 계속 전환하게 만들어 나쁜 축출 결정으로 이어집니다.
진동을 방지하려면 --eviction-pressure-transition-period 플래그를 사용할 수 있어요. 이 플래그는 kubelet이 노드 컨디션을 다른 상태로 전환하기 전에 기다려야 하는 시간을 제어합니다. 전환 기간의 기본값은 5분입니다.
노드 수준 리소스 회수 (Reclaiming node level resources)
kubelet은 최종 사용자 Pod를 축출하기 전에 노드 수준 리소스를 회수하려고 합니다.
DiskPressure 노드 컨디션이 보고되면 kubelet은 노드의 파일시스템에 기반해 노드 수준 리소스를 회수합니다.
imagefs 또는 containerfs가 없는 경우 (Without imagefs or containerfs)
노드에 축출 임계값을 충족하는 nodefs 파일시스템만 있으면 kubelet은 다음 순서로 디스크 공간을 확보합니다.
- 죽은 Pod와 컨테이너를 가비지 컬렉션한다.
- 사용하지 않는 이미지를 삭제한다.
imagefs가 있는 경우 (With imagefs)
컨테이너 런타임이 사용할 전용 imagefs 파일시스템이 있으면 kubelet은 다음을 수행합니다.
- nodefs 파일시스템이 축출 임계값을 충족하면 kubelet은 죽은 Pod와 컨테이너를 가비지 컬렉션합니다.
- imagefs 파일시스템이 축출 임계값을 충족하면 kubelet은 모든 사용하지 않는 이미지를 삭제합니다.
imagefs와 containerfs가 있는 경우 (With imagefs and containerfs)
컨테이너 런타임이 사용하도록 구성된 imagefs 파일시스템과 함께 전용 containerfs가 있으면 kubelet은 다음과 같이 리소스를 회수하려고 합니다.
- containerfs 파일시스템이 축출 임계값을 충족하면 kubelet은 죽은 Pod와 컨테이너를 가비지 컬렉션합니다.
- imagefs 파일시스템이 축출 임계값을 충족하면 kubelet은 모든 사용하지 않는 이미지를 삭제합니다.
kubelet 축출을 위한 Pod 선택 (Pod selection for kubelet eviction)
kubelet의 노드 수준 리소스 회수 시도가 축출 신호를 임계값 아래로 가져오지 못하면 kubelet은 최종 사용자 Pod를 축출하기 시작합니다.
kubelet은 Pod 축출 순서를 결정하기 위해 다음 매개변수를 사용합니다.
- Pod의 리소스 사용량이 요청을 초과하는지 여부
- Pod 우선순위
- 요청 대비 Pod의 리소스 사용량
그 결과 kubelet은 다음 순서로 Pod의 순위를 매기고 축출합니다.
- 사용량이 요청을 초과하는 BestEffort 또는 Burstable Pod. 이 Pod들은 우선순위에 따라, 그다음 사용량이 요청을 초과하는 정도에 따라 축출됩니다.
- 사용량이 요청보다 적은 Guaranteed Pod와 Burstable Pod는 우선순위에 따라 마지막에 축출됩니다.
참고: kubelet은 축출 순서를 결정할 때 Pod의 QoS 클래스를 사용하지 않습니다. 메모리 같은 리소스를 회수할 때 가장 가능성 있는 Pod 축출 순서를 추정하는 데 QoS 클래스를 사용할 수 있어요. QoS 분류는 EphemeralStorage 요청에는 적용되지 않으므로, 예를 들어 노드가 DiskPressure 상태라면 위 시나리오가 적용되지 않습니다.
Guaranteed Pod는 모든 컨테이너에 requests와 limits가 지정되고 둘이 같을 때만 보장됩니다. 이 Pod들은 다른 Pod의 리소스 소비 때문에 절대 축출되지 않습니다. 시스템 데몬(예: kubelet과 journald)이 system-reserved나 kube-reserved 할당으로 예약된 것보다 더 많은 리소스를 소비하고, 노드에 요청보다 적은 리소스를 사용하는 Guaranteed 또는 Burstable Pod만 남아 있다면, kubelet은 노드 안정성을 유지하고 다른 Pod에 대한 리소스 기아의 영향을 제한하기 위해 이 Pod 중 하나를 축출해야 합니다. 이 경우 우선순위가 가장 낮은 Pod를 먼저 축출하기로 선택합니다.
정적 Pod를 실행 중이고 리소스 압박에서 축출되는 것을 피하려면 그 Pod의 priority 필드를 직접 설정하세요. 정적 Pod는 priorityClassName 필드를 지원하지 않습니다.
kubelet이 inode나 프로세스 ID 기아에 응답해 Pod를 축출할 때는 inode와 PID에 요청이 없으므로 Pod의 상대 우선순위를 사용해 축출 순서를 결정합니다.
kubelet은 노드에 전용 imagefs 또는 containerfs 파일시스템이 있는지에 따라 Pod를 다르게 정렬합니다.
imagefs 또는 containerfs가 없는 경우 (nodefs와 imagefs가 같은 파일시스템 사용)
- nodefs가 축출을 촉발하면 kubelet은 총 디스크 사용량(로컬 볼륨 + 모든 컨테이너의 로그와 쓰기 가능 레이어)을 기준으로 Pod를 정렬합니다.
imagefs가 있는 경우 (nodefs와 imagefs 파일시스템이 분리됨)
- nodefs가 축출을 촉발하면 kubelet은 nodefs 사용량(로컬 볼륨 + 모든 컨테이너의 로그)을 기준으로 Pod를 정렬합니다.
- imagefs가 축출을 촉발하면 kubelet은 모든 컨테이너의 쓰기 가능 레이어 사용량을 기준으로 Pod를 정렬합니다.
imagefs와 containerfs가 있는 경우 (imagefs와 containerfs가 분리됨)
- containerfs가 축출을 촉발하면 kubelet은 containerfs 사용량(로컬 볼륨 + 모든 컨테이너의 로그와 쓰기 가능 레이어)을 기준으로 Pod를 정렬합니다.
- imagefs가 축출을 촉발하면 kubelet은 주어진 이미지의 디스크 사용량을 나타내는 이미지 저장소 순위를 기준으로 Pod를 정렬합니다.
최소 축출 회수 (Minimum eviction reclaim)
참고: Kubernetes v1.37부터
containerfs.available메트릭에 사용자 지정 값을 설정할 수 없습니다. 이 특정 메트릭의 구성은 구성에 따라 nodefs 또는 imagefs에 설정된 값을 반영하도록 자동으로 설정됩니다.
경우에 따라 Pod 축출은 갈급한 리소스의 작은 양만 회수합니다. 이는 kubelet이 구성된 축출 임계값을 반복해서 충족시키고 여러 축출을 촉발하게 만들 수 있어요.
--eviction-minimum-reclaim 플래그나 kubelet 구성 파일을 사용해 각 리소스에 대한 최소 회수량을 구성할 수 있어요. kubelet이 리소스가 갈급하다는 것을 알아차리면 지정한 양을 회수할 때까지 그 리소스를 계속 회수합니다.
예를 들어 다음 구성은 최소 회수량을 설정합니다.
apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
evictionHard:
memory.available: "500Mi"
nodefs.available: "1Gi"
imagefs.available: "100Gi"
evictionMinimumReclaim:
memory.available: "0Mi"
nodefs.available: "500Mi"
imagefs.available: "2Gi"
이 예시에서 nodefs.available 신호가 축출 임계값을 충족하면 kubelet은 신호가 1GiB 임계값에 도달할 때까지 리소스를 회수한 다음, 사용 가능한 nodefs 저장소 값이 1.5GiB에 도달할 때까지 최소량 500MiB를 계속 회수합니다.
마찬가지로 kubelet은 imagefs.available 값이 102Gi(사용 가능한 컨테이너 이미지 저장소 102GiB)에 도달할 때까지 imagefs 리소스를 회수하려고 합니다. kubelet이 회수할 수 있는 저장소 양이 2GiB보다 작으면 kubelet은 아무것도 회수하지 않습니다.
모든 리소스의 기본 --eviction-minimum-reclaim은 0입니다.
노드 메모리 부족 동작 (Node out of memory behavior)
kubelet이 메모리를 회수하기 전에 노드가 메모리 부족(OOM) 이벤트를 겪으면 노드는 응답을 위해 oom_killer에 의존합니다.
kubelet은 Pod의 QoS를 기반으로 각 컨테이너에 대해 oom_score_adj 값을 설정합니다.
| 서비스 품질 (Quality of Service) | oom_score_adj |
|---|---|
| Guaranteed | -997 |
| BestEffort | 1000 |
| Burstable | min(max(2, 1000 - (1000 × memoryRequestBytes) / machineMemoryCapacityBytes), 999) |
참고: kubelet은
system-node-critical우선순위를 가진 Pod의 모든 컨테이너에 대해서도oom_score_adj값 -997을 설정합니다.
kubelet이 노드가 OOM을 겪기 전에 메모리를 회수하지 못하면, oom_killer는 노드에서 사용 중인 메모리 백분율을 기반으로 oom_score를 계산하고 oom_score_adj를 더해 각 컨테이너의 유효 oom_score를 얻습니다. 그런 다음 가장 높은 점수의 컨테이너를 죽입니다.
이는 스케줄링 요청에 비해 많은 양의 메모리를 소비하는 낮은 QoS Pod의 컨테이너가 먼저 죽는다는 뜻입니다.
Pod 축출과 달리 컨테이너가 OOM kill되면 kubelet은 restartPolicy에 따라 재시작할 수 있어요.
좋은 관행 (Good practices)
다음 섹션들은 축출 구성에 대한 좋은 관행을 설명합니다.
스케줄 가능한 리소스와 축출 정책 (Schedulable resources and eviction policies)
축출 정책으로 kubelet을 구성할 때, 스케줄러가 즉시 메모리 압박을 유발해 축출을 촉발할 Pod를 스케줄하지 않도록 해야 해요.
다음 시나리오를 고려해보세요.
- 노드 메모리 용량: 10GiB
- 운영자가 메모리 용량의 10%를 시스템 데몬(커널, kubelet 등)용으로 예약하려고 함
- 운영자는 시스템 OOM 발생률을 줄이기 위해 메모리 사용률 95%에서 Pod를 축출하려고 함
이렇게 하려면 kubelet을 다음과 같이 실행합니다.
--eviction-hard=memory.available<500Mi
--system-reserved=memory=1.5Gi
이 구성에서 --system-reserved 플래그는 시스템용으로 1.5GiB 메모리를 예약합니다. 이는 총 메모리의 10% + 축출 임계값 양이에요.
노드는 Pod가 요청보다 더 많이 사용하거나, 시스템이 1GiB보다 많은 메모리를 사용해 memory.available 신호가 500MiB 아래로 떨어지고 임계값을 촉발하면 축출 임계값에 도달할 수 있어요.
DaemonSet과 노드 압력 축출 (DaemonSets and node-pressure eviction)
Pod 우선순위는 축출 결정을 내리는 데 주요 요인입니다. kubelet이 DaemonSet에 속한 Pod를 축출하지 않게 하려면 Pod 스펙에서 적절한 priorityClassName을 지정해 그 Pod에 충분히 높은 우선순위를 주세요. 더 낮은 우선순위나 기본값을 사용해 해당 DaemonSet의 Pod가 충분한 리소스가 있을 때만 실행되도록 할 수도 있어요.
알려진 문제 (Known issues)
다음 섹션들은 리소스 부족 처리와 관련된 알려진 문제를 설명합니다.
kubelet이 메모리 압박을 즉시 관찰하지 못할 수 있음 (kubelet may not observe memory pressure right away)
기본적으로 kubelet은 정기적인 간격으로 cAdvisor를 폴링해 메모리 사용량 통계를 수집합니다. 그 창 안에서 메모리 사용량이 빠르게 증가하면 kubelet이 MemoryPressure를 충분히 빨리 관찰하지 못하고 OOM killer가 여전히 호출될 수 있어요.
--kernel-memcg-notification 플래그를 사용해 kubelet에서 memcg 알림 API를 활성화하면 임계값이 넘어갈 때 즉시 알림을 받을 수 있어요.
극단적인 사용률을 추구하는 것이 아니라 합리적인 정도의 오버커밋(overcommit)을 추구한다면, --kube-reserved와 --system-reserved 플래그를 사용해 시스템용 메모리를 할당하는 것이 이 문제에 대한 실행 가능한 해결 방법입니다.
active_file 메모리는 사용 가능한 메모리로 간주되지 않음 (active_file memory is not considered as available memory)
Linux에서 커널은 활성 LRU(least recently used) 목록의 파일 백업 메모리 바이트 수를 active_file 통계로 추적합니다. kubelet은 active_file 메모리 영역을 회수할 수 없는 것으로 취급합니다. 임시 로컬 스토리지를 포함해 블록 백업 로컬 저장소를 집약적으로 사용하는 워크로드의 경우, 파일과 블록 데이터의 커널 수준 캐시 때문에 최근에 접근한 많은 캐시 페이지가 active_file로 계산될 가능성이 높아요. 이 커널 블록 버퍼 중 충분히 많은 수가 활성 LRU 목록에 있으면 kubelet은 이를 높은 리소스 사용으로 관찰하고 노드에 메모리 압박을 겪고 있다고 테인트를 표시해 Pod 축출을 촉발할 수 있습니다.
자세한 내용은 https://github.com/kubernetes/kubernetes/issues/43916 를 참고하세요.
집약적인 I/O 활동을 수행할 가능성이 높은 컨테이너에 메모리 limit과 memory request를 동일하게 설정하면 이 동작을 우회할 수 있어요. 그 컨테이너에 대한 최적의 메모리 limit 값을 추정하거나 측정해야 합니다.
다음 단계 (What's next)
- API 시작 축출 (API-initiated Eviction) 알아보기
- Pod 우선순위와 선점 (Pod Priority and Preemption) 알아보기
- PodDisruptionBudgets 알아보기
- 서비스 품질 (QoS) 알아보기
- Eviction API 확인