Pod Eviction과 Priority Class
July 2023 (810 Words, 5 Minutes)
Pod 제거순위와 Priority Class
쿠버네티스는 클러스터 내에서 리소스를 효율적으로 활용하기 위한 다양한 메커니즘을 제공합니다. 이 중에는 노드에서 리소스를 초과 사용할 때 pod를 제거하는 기능이 포함되어 있습니다. 이를 Eviction Policy라고 부릅니다.
node의 상태가 pressure상태에 들어가게되면, node-pressure-eviction
상태에 들어가게 됩니다. Eviction Policy는 노드에서 메모리, CPU 등의 리소스 사용량이 특정 임계값을 초과할 때 발동합니다. 이런 상황에서 쿠버네티스는 노드의 상태를 보호하고, 노드에서 실행 중인 pod에 미치는 영향을 최소화하기 위해 우선적으로 특정 pod를 제거합니다.
- 임계값은
soft-eviction-thresholds
또는hard-eviction-thresholds
에 의해 결정됩니다
node-conditions
의 판단기준이 충족되면 eviction이 시작됩니다.
노드-압박 축출 (node pressure eviction)
node-pressure-eviction
은 kubelet이 노드의 자원을 회수하기 위해 pod를 능동적으로 중단시키는 절차입니다. kubelet은 클러스터 노드의 메모리, 디스크 공간, 파일시스템 inode와 같은 자원을 모니터링합니다. 이러한 자원 중 하나 이상이 특정 소모 수준에 도달하면, kubelet은 하나 이상의 pod를 능동적으로 중단시켜 자원을 회수하고 고갈 상황을 방지할 수 있습니다. 노드-압박 축출 과정에서, kubelet은 eviction시킬 pod의 PodPhase를 Failed로 설정함으로써 pod가 종료됩니다. 노드-압박 축출은 api-eviction
과는 차이가 있습니다. kubelet은 이전에 설정된 PodDisruptionBudget
값이나 pod의 terminationGracePeriodSeconds
값을 따르지 않습니다.
pod 제거 우선 순위는 pod에 부여된 Quality of Service(QoS) 클래스에 따라 결정됩니다. QoS 클래스는 pod가 요구하는 CPU와 메모리 리소스를 바탕으로 결정됩니다. 쿠버네티스는 다음과 같은 3가지 QoS 클래스를 가지고 있습니다.
Guaranteed
: pod가 요구하는 CPU와 메모리 리소스가 모두 지정되었고, 요구량과 상한 값이 일치합니다.Burstable
: pod가 요구하는 CPU나 메모리 리소스 중 적어도 하나가 지정되었지만, 요구량과 상한 값이 일치하지 않습니다.BestEffort
: pod가 요구하는 CPU와 메모리 리소스가 모두 지정되지 않았습니다.
Eviction Policy가 동작되는 상황에서는, BestEffot
클래스에 있는 pod를 제거합니다.
그다음으로 Burstable
클래스에서 가장 많은 리소스를 사용하는 pod를 제거합니다.
Guaranteed
클래스에 속한 pod는 가장 마지막으로 제거됩니다.
따라서 pod에 resource(request, limit)을 설정하는것이 중요합니다. pod에 리소스를 명시적으로 설정해 QoS 클래스를 지정함으로서 pod의 제거 우선순위를 조절 할 수 있습니다.
노드 메모리 부족 시의 동작
node-pressure-eviction
상황에서 kubelet이 메모리 회수를 하기전에 노드에 메모리가 부족하면 노드는 oom_killer를 이용해 메모리를 확보합니다. oom_killer는om_score_adj
에 따라 프로세스를 정리합니다.
oom_score_adj
값은 pod의 우선순위를 나타내는 데 사용되는 Linux 커널 설정값으로, Out-Of-Memory (OOM) killer가 시스템 메모리 부족 상황에서 어떤 프로세스를 먼저 종료할지 결정하는 데 도움을 줍니다. 값이 높을수록 프로세스는 더 먼저 종료 대상이 됩니다.
쿠버네티스에서는 oom_score_adj
값은 파드의 QoS 클래스에 따라 결정됩니다:
- Guaranteed QoS 클래스 :
oom_score_adj
= -997 - Burstable QoS 클래스 :
oom_score_adj
= min(-998, 1000 - (1000 * memoryRequestBytes) / machineMemoryCapacityBytes) - BestEffort QoS 클래스 :
oom_score_adj
= 1000
이렇게 resource를 할당하는것과 할당하지 않았을경우 발생하는 차이점에 대해 알아보았습니다.
kubelet이 node-pressure-eviction
을 동작 시키기 전에 발생 될 수 있는 oom_killer또한 고려해서 설계 해야합니다.
Priority Class
PriorityClass
는 pod가 얼마나 중요한지를 나타내는 우선순위를 정의하는데 사용됩니다. PriorityClass
객체는 우선순위 이름과 해당 우선순위에 대한 정수 값이 포함되어 있습니다. pod 생성 시, pod 스펙에 priorityClassName
을 지정하여 특정 PriorityClass
를 선택할 수 있습니다.
PriorityClass
는 스케줄링 결정을 할 때, 그리고 노드에서 리소스가 부족할 때 Eviction 대상을 선정하는 데에 사용됩니다.
스케줄링 시, 높은 우선순위를 가진 pod는 낮은 우선순위를 가진 pod보다 더 빨리 스케줄링되며, 필요한 경우 노드에서 낮은 우선순위의 pod를 제거하여(Preemption) 공간을 만들 수 있습니다.
노드에서 리소스가 부족할 때 (즉, Eviction이 발생할 때), 쿠버네티스는 우선적으로 낮은 우선순위를 가진 pod를 제거합니다. 즉, 높은 PriorityClass
를 가진 pod는 상대적으로 안전하다고 볼 수 있습니다.
하지만 주의할 점은, PriorityClass
가 QoS 클래스보다 pod 제거 우선 순위에서 높은 우선순위를 가진다는 것입니다. 즉, Guaranteed
QoS 클래스를 가진 pod도, 낮은 PriorityClass
를 가진 pod에 비해 먼저 제거될 수 있습니다. 따라서 중요한 pod에는 적절한 PriorityClass
를 지정하는 것이 중요합니다.
먼저, 높은 우선순위를 가진 PriorityClass
를 만들어야 합니다. 아래는 high-priority
라는 이름의 PriorityClass
를 만드는 YAML 파일의 예입니다:
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: high-priority
value: 1000000
globalDefault: false
description: "This priority class should be used for high priority pods only."
이 YAML 파일을 high-priority.yaml
이라는 이름으로 저장하고, 다음의 kubectl 명령어를 사용하여 PriorityClass
를 생성합니다:
kubectl apply -f high-priority.yaml
그런 다음, jekyll-deployment
라는 Deployment를 관리하는 pod에 이 PriorityClass
를 적용하려면, 해당 Deployment의 YAML 파일을 업데이트해야 합니다. 아래는 업데이트된 Deployment YAML의 예입니다:
apiVersion: apps/v1
kind: Deployment
metadata:
name: jekyll-deployment
namespace: jekyll
spec:
replicas: 3
selector:
matchLabels:
app: jekyll
template:
metadata:
labels:
app: jekyll
spec:
containers:
- name: jekyll
image: jekyll/jekyll:latest
ports:
- containerPort: 4000
priorityClassName: high-priority # Add this line
이렇게 하면 jekyll-deployment
Deployment에 의해 생성되는 모든 pod는 high-priority
PriorityClass
를 가집니다. 이로 인해 이 pod들은 노드에서 리소스 부족 시 제거되는 대상에서 상대적으로 보호받게 됩니다.
YAML 파일을 업데이트한 후에는 다음의 kubectl 명령어를 사용하여 Deployment를 업데이트해야 합니다:
kubectl apply -f jekyll-deployment.yaml
이 명령어를 실행하면 쿠버네티스는 Deployment를 업데이트하고, 변경된 설정을 적용하기 위해 pod를 재생성합니다.
Node pressure eviction과 PriorityClass 둘다 Kubernetes에서 설정하고 관리합니다. 하지만 두개는 다른목적으로 사용되기때문에 두개 사이의 우선순위를 설정하는것은 좋지않습니다.
하지만 이 둘 사이의 상호 작용을 정리해보겠습니다
- QoS 클래스(Quality of Service class): pod가 사용하는 리소스(requests와 limits)에 따라 자동으로 설정됩니다. pod는 Guaranteed, Burstable, BestEffort 중 하나의 QoS 클래스를 가집니다. QoS 클래스는 노드의 리소스가 부족할 때 어떤 파드가 eviction 대상이 될지 결정하는 데 중요한 역할을 합니다. 일반적으로, BestEffort QoS 클래스를 가진 pod가 가장 먼저 eviction 대상이 되고, 그 다음이 Burstable, 그리고 마지막으로 Guaranteed입니다.
- PriorityClass: pod의 중요성을 나타내며, 수동으로 설정이 가능합니다. 높은 PriorityClass를 가진 pod는 낮은 PriorityClass를 가진 pod보다 더 늦게 eviction 대상이 됩니다. 즉, PriorityClass가 높을수록 해당 파드는 노드의 리소스가 부족할 때 eviction에서 더 안전하게 보호됩니다.
따라서, QoS 클래스는 pod의 리소스 사용에 대한 “약속”을 나타내고, PriorityClass는 pod의 “중요성”을 나타냅니다. 이 두 가지 속성은 서로 독립적이지만, 둘 다 eviction 결정에 중요한 역할을 합니다. 만약 높은 PriorityClass를 가진 파드가 BestEffort QoS 클래스를 가진다면, 그 파드는 낮은 PriorityClass를 가진 Guaranteed QoS 클래스 파드보다 더 늦게 eviction 대상이 될 수 있습니다.
사실 클라우드에서 쿠버네티스를 이용한다면 overcommit이 되지않게 설정하고 node를 autoscale할경우 위 상황에 대해 고려 할 일이 없을수도 있습니다.
온프레미스에서 쿠버네티스를 이용할경우 scale을 변경할 수 없기때문에 주어진 환경 안에서 잘 설정/관리 하는것이 좋을것 같습니다.