K8s PriorityClass 프리엠션

PriorityClass란?

Kubernetes PriorityClass는 Pod에 우선순위를 부여하여, 클러스터 리소스가 부족할 때 어떤 Pod를 먼저 스케줄링하고 어떤 Pod를 퇴출(Preemption)할지 결정하는 메커니즘입니다. 프로덕션 워크로드와 배치 작업이 혼재된 환경에서, PriorityClass 없이는 중요한 서비스가 리소스 부족으로 Pending 상태에 빠질 수 있습니다.

Kubernetes 1.14부터 GA(정식)로 지원되며, kube-scheduler의 핵심 스케줄링 전략 중 하나입니다. 이 글에서는 PriorityClass 설정부터 Preemption 동작 원리, 운영 전략까지 깊이 있게 다루겠습니다.

PriorityClass 기본 구조

# 클러스터 레벨 리소스 (네임스페이스 없음)
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: critical-production
value: 1000000          # 높을수록 우선순위 높음 (최대 10억)
globalDefault: false     # true면 모든 Pod의 기본값
preemptionPolicy: PreemptLowerPriority  # 프리엠션 정책
description: "프로덕션 핵심 서비스 전용"
필드 설명 기본값
value 정수 우선순위 값 (높을수록 중요) 0
globalDefault PriorityClass 미지정 Pod에 적용 false
preemptionPolicy PreemptLowerPriority 또는 Never PreemptLowerPriority

시스템 기본 PriorityClass

Kubernetes는 두 개의 내장 PriorityClass를 제공합니다. 이를 사용자 워크로드에 사용하면 안 됩니다.

# 시스템 예약 PriorityClass
$ kubectl get priorityclasses

NAME                      VALUE        GLOBAL-DEFAULT
system-cluster-critical   2000000000   false    # etcd, kube-apiserver 등
system-node-critical      2000001000   false    # kubelet, kube-proxy 등

# 사용자 PriorityClass value 범위: -2,147,483,648 ~ 1,000,000,000
# 10억 초과는 시스템 전용 (system-cluster-critical, system-node-critical)

실전 PriorityClass 계층 설계

운영 환경에서는 워크로드 특성에 따라 3~5단계 우선순위 체계를 설계하는 것이 좋습니다.

# 1단계: 핵심 인프라 (모니터링, 로깅)
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: infrastructure
value: 900000
description: "Prometheus, Fluentd 등 관측 인프라"
---
# 2단계: 프로덕션 서비스
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: production
value: 700000
description: "사용자 트래픽을 직접 처리하는 서비스"
---
# 3단계: 내부 서비스
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: internal
value: 500000
description: "내부 API, 관리 도구"
---
# 4단계: 배치/크론잡
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: batch
value: 100000
description: "배치 처리, 데이터 파이프라인"
---
# 5단계: 기본값 (개발/테스트)
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: default
value: 0
globalDefault: true
description: "개발, 테스트, 실험 워크로드"

Pod에 PriorityClass 적용

apiVersion: apps/v1
kind: Deployment
metadata:
  name: payment-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: payment-api
  template:
    metadata:
      labels:
        app: payment-api
    spec:
      priorityClassName: production  # PriorityClass 지정
      containers:
      - name: payment-api
        image: payment-api:v2.1.0
        resources:
          requests:
            cpu: 500m
            memory: 512Mi
          limits:
            cpu: "1"
            memory: 1Gi

Preemption 동작 원리

Preemption(프리엠션)은 높은 우선순위 Pod가 스케줄링될 수 없을 때, 낮은 우선순위 Pod를 퇴출하여 리소스를 확보하는 메커니즘입니다.

# Preemption 흐름:
# 1. 고우선순위 Pod 생성 → 스케줄 불가 (리소스 부족)
# 2. Scheduler가 각 노드에서 퇴출 후보를 계산
# 3. 최소 퇴출로 스케줄 가능한 노드 선택
# 4. 저우선순위 Pod에 graceful termination 시작
# 5. terminationGracePeriodSeconds 경과 후 강제 종료
# 6. 고우선순위 Pod 스케줄링

# Preemption 비활성화 (대기만 하고 퇴출 안 함)
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: batch-no-preempt
value: 100000
preemptionPolicy: Never   # 퇴출 없이 대기
description: "높은 우선순위지만 다른 Pod를 퇴출하지 않음"

preemptionPolicy: Never는 배치 작업처럼 스케줄링 순서는 앞서되 다른 서비스를 방해하면 안 되는 경우에 유용합니다.

PDB와 Preemption의 관계

Preemption은 PodDisruptionBudget(PDB)를 존중하지만, 완벽하게 보장하지는 않습니다.

# PDB가 있어도 Preemption이 발생할 수 있는 경우:
# - PDB를 존중하면서 퇴출 가능한 조합이 존재할 때
# - PDB 위반 없이는 스케줄 불가능할 때 (최후 수단으로 PDB 무시)

# 핵심 서비스는 반드시 PDB + 높은 PriorityClass 조합
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: payment-api-pdb
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: payment-api
---
# payment-api는 production PriorityClass (700000)
# → 퇴출 대상이 될 확률 자체가 극히 낮음

ResourceQuota와 PriorityClass 연동

ResourceQuotascopeSelector를 사용하면 PriorityClass별로 리소스 제한을 걸 수 있습니다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: production-quota
  namespace: team-a
spec:
  hard:
    cpu: "20"
    memory: 40Gi
    pods: "50"
  scopeSelector:
    matchExpressions:
    - scopeName: PriorityClass
      operator: In
      values:
      - production
      - infrastructure
---
apiVersion: v1
kind: ResourceQuota
metadata:
  name: batch-quota
  namespace: team-a
spec:
  hard:
    cpu: "8"
    memory: 16Gi
    pods: "100"
  scopeSelector:
    matchExpressions:
    - scopeName: PriorityClass
      operator: In
      values:
      - batch
      - default

운영 모니터링

# Preemption 발생 이벤트 확인
kubectl get events --field-selector reason=Preempted -A

# PriorityClass별 Pod 현황
kubectl get pods -A -o custom-columns=
  "NAMESPACE:.metadata.namespace,
   NAME:.metadata.name,
   PRIORITY:.spec.priority,
   CLASS:.spec.priorityClassName,
   STATUS:.status.phase" 
  | sort -t' ' -k3 -rn | head -20

# Pending Pod 중 Preemption 대기 확인
kubectl get pods -A --field-selector status.phase=Pending 
  -o custom-columns="NAME:.metadata.name,PRIORITY:.spec.priority"

운영 주의사항

항목 주의사항 권장 대응
value 간격 너무 촘촘하면 나중에 끼워넣기 어려움 10만 단위 간격 유지
globalDefault 클러스터에 1개만 허용, 중복 시 오류 반드시 하나만 설정
Preemption 연쇄 퇴출된 Pod가 다른 Pod를 퇴출 가능 계층 수를 3~5개로 제한
시스템 값 사용 10억 초과 값을 사용자 Pod에 부여 사용자 범위(~10억) 준수
Spot/Preemptible 노드 클라우드 Spot 노드와 Preemption 혼동 Spot에는 batch PriorityClass 배치

마무리

PriorityClass는 Kubernetes 클러스터의 리소스 배분 정책을 코드로 선언하는 핵심 도구입니다. 핵심 서비스에 높은 우선순위를 부여하고, 배치 작업에 preemptionPolicy: Never를 적용하면 서비스 안정성과 리소스 효율성을 동시에 확보할 수 있습니다. ResourceQuota의 scopeSelector와 조합하면 네임스페이스 단위의 정밀한 리소스 거버넌스까지 구현 가능합니다.

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