K8s Taint·Toleration 노드 제어

Taint와 Toleration이란?

Kubernetes의 Taint와 Toleration은 특정 노드에 Pod가 스케줄링되는 것을 제어하는 메커니즘이다. Taint는 노드에 “오염”을 표시하여 일반 Pod를 거부하고, Toleration은 Pod에 “내성”을 부여하여 오염된 노드에서도 실행할 수 있게 한다. Node Affinity가 “이 노드에 가고 싶다”라면, Taint/Toleration은 “이 노드에 오지 마라”의 역방향 제어다.

이 글에서는 Taint/Toleration의 핵심 동작부터 GPU 노드 전용화, 스팟 인스턴스 관리, 노드 유지보수, 멀티테넌트 격리까지 실전 운영 패턴을 다룬다.

Taint 기본 문법과 Effect

# Taint 추가
kubectl taint nodes worker-01 dedicated=gpu:NoSchedule

# Taint 확인
kubectl describe node worker-01 | grep Taints

# Taint 제거 (키 뒤에 - 추가)
kubectl taint nodes worker-01 dedicated=gpu:NoSchedule-

Taint는 key=value:effect 형식이며, effect는 세 가지가 있다:

Effect 동작 사용 시점
NoSchedule 새 Pod 스케줄링 거부 (기존 Pod 유지) 전용 노드 격리
PreferNoSchedule 가능하면 스케줄링 피함 (soft 제약) 선호도 기반 분산
NoExecute 새 Pod 거부 + 기존 Pod도 퇴거 노드 유지보수, 장애 격리

Toleration 설정

Pod가 Taint된 노드에서 실행되려면 매칭되는 Toleration을 선언해야 한다.

apiVersion: v1
kind: Pod
metadata:
  name: gpu-training-job
spec:
  tolerations:
    # 정확한 매칭 (Equal 연산자)
    - key: "dedicated"
      operator: "Equal"
      value: "gpu"
      effect: "NoSchedule"

    # 키만 매칭 (Exists 연산자 — 값 무관)
    - key: "node-role"
      operator: "Exists"
      effect: "NoSchedule"

    # NoExecute + tolerationSeconds: 300초 후 퇴거
    - key: "node.kubernetes.io/unreachable"
      operator: "Exists"
      effect: "NoExecute"
      tolerationSeconds: 300
  containers:
    - name: trainer
      image: pytorch/pytorch:2.0-cuda11.8
      resources:
        limits:
          nvidia.com/gpu: 1

실전 패턴 1: GPU 노드 전용화

GPU 노드는 비싸므로 GPU 워크로드만 실행해야 한다. Taint로 일반 Pod를 차단하고, GPU Pod에만 Toleration을 부여한다.

# GPU 노드에 Taint 설정
kubectl taint nodes gpu-node-01 nvidia.com/gpu=present:NoSchedule
kubectl taint nodes gpu-node-02 nvidia.com/gpu=present:NoSchedule

# GPU 노드에 라벨도 추가 (Affinity와 함께 사용)
kubectl label nodes gpu-node-01 gpu-type=a100
kubectl label nodes gpu-node-02 gpu-type=a100
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ml-inference
spec:
  replicas: 3
  selector:
    matchLabels:
      app: ml-inference
  template:
    metadata:
      labels:
        app: ml-inference
    spec:
      # Taint 허용
      tolerations:
        - key: "nvidia.com/gpu"
          operator: "Equal"
          value: "present"
          effect: "NoSchedule"
      # + Affinity로 GPU 노드에만 배치
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
              - matchExpressions:
                  - key: gpu-type
                    operator: In
                    values: ["a100", "v100"]
      containers:
        - name: inference
          image: registry.example.com/ml-inference:v2
          resources:
            limits:
              nvidia.com/gpu: 1
            requests:
              cpu: "2"
              memory: 8Gi

핵심: Toleration만으로는 해당 노드에 “반드시” 가는 것이 아니다. Toleration은 “갈 수 있다”는 허용이며, “반드시 가라”는 Node Affinity로 보완해야 한다.

실전 패턴 2: 스팟 인스턴스 관리

스팟(Spot/Preemptible) 노드는 언제든 회수될 수 있으므로, 중요하지 않은 워크로드만 배치한다.

# 스팟 노드에 Taint (클라우드 프로바이더가 자동 추가하기도 함)
kubectl taint nodes spot-node-01 cloud.google.com/gke-spot=true:NoSchedule

---
# 스팟 허용 + 회수 대비 graceful shutdown
apiVersion: apps/v1
kind: Deployment
metadata:
  name: batch-processor
spec:
  replicas: 10
  template:
    spec:
      tolerations:
        - key: "cloud.google.com/gke-spot"
          operator: "Equal"
          value: "true"
          effect: "NoSchedule"
        # 스팟 회수 시 120초 유예
        - key: "cloud.google.com/gke-spot"
          operator: "Equal"
          value: "true"
          effect: "NoExecute"
          tolerationSeconds: 120
      terminationGracePeriodSeconds: 120
      containers:
        - name: processor
          image: registry.example.com/batch:v1
          lifecycle:
            preStop:
              exec:
                command: ["/bin/sh", "-c", "kill -SIGTERM 1 && sleep 100"]

실전 패턴 3: 노드 유지보수 (Cordon + Drain)

노드 유지보수 시 kubectl drain은 내부적으로 NoExecute Taint를 설정하여 Pod를 퇴거시킨다.

# Step 1: Cordon — 새 스케줄링 차단 (NoSchedule Taint 추가)
kubectl cordon worker-03

# Step 2: Drain — 기존 Pod 퇴거
kubectl drain worker-03 
  --ignore-daemonsets 
  --delete-emptydir-data 
  --grace-period=60 
  --timeout=300s

# Step 3: 유지보수 완료 후 Uncordon
kubectl uncordon worker-03

중요: DaemonSet Pod는 drain 시 퇴거되지 않는다 (--ignore-daemonsets). 모니터링 에이전트 등 DaemonSet은 노드에 남아야 하기 때문이다.

실전 패턴 4: 멀티테넌트 노드 격리

# 테넌트 A 전용 노드
kubectl taint nodes pool-a-01 tenant=team-a:NoSchedule
kubectl taint nodes pool-a-02 tenant=team-a:NoSchedule

# 테넌트 B 전용 노드
kubectl taint nodes pool-b-01 tenant=team-b:NoSchedule

---
# 테넌트 A의 워크로드
apiVersion: apps/v1
kind: Deployment
metadata:
  name: team-a-api
  namespace: team-a
spec:
  template:
    spec:
      tolerations:
        - key: "tenant"
          operator: "Equal"
          value: "team-a"
          effect: "NoSchedule"
      nodeSelector:
        tenant-pool: "team-a"
      containers:
        - name: api
          image: registry.example.com/team-a-api:v1

시스템 Taint: K8s 내장 Taint

Kubernetes는 노드 상태에 따라 자동으로 Taint를 추가한다.

Taint Key 조건 Effect
node.kubernetes.io/not-ready 노드 NotReady 상태 NoExecute
node.kubernetes.io/unreachable 노드와 통신 불가 NoExecute
node.kubernetes.io/memory-pressure 메모리 압박 NoSchedule
node.kubernetes.io/disk-pressure 디스크 압박 NoSchedule
node.kubernetes.io/pid-pressure PID 고갈 NoSchedule
node.kubernetes.io/unschedulable kubectl cordon 실행됨 NoSchedule

기본적으로 Pod는 not-readyunreachable에 대해 tolerationSeconds: 300(5분)의 내장 Toleration을 가진다. 이 시간을 줄이면 장애 노드에서 더 빠르게 Pod가 재배치된다.

# 빠른 페일오버: 30초 후 퇴거
tolerations:
  - key: "node.kubernetes.io/not-ready"
    operator: "Exists"
    effect: "NoExecute"
    tolerationSeconds: 30
  - key: "node.kubernetes.io/unreachable"
    operator: "Exists"
    effect: "NoExecute"
    tolerationSeconds: 30

Taint + PodDisruptionBudget 조합

NoExecute Taint로 Pod를 퇴거할 때 PDB(PodDisruptionBudget)와 함께 사용하면 가용성을 보장하면서 안전하게 노드를 비울 수 있다.

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: api-pdb
spec:
  minAvailable: 2  # 최소 2개 Pod 유지
  selector:
    matchLabels:
      app: api-server

안티패턴과 주의점

안티패턴 문제점 해결책
모든 Pod에 와일드카드 Toleration Taint 무력화, 격리 무의미 필요한 Pod에만 명시적 Toleration
Toleration만 사용 (Affinity 없이) 다른 노드에도 스케줄링됨 Toleration + Node Affinity 조합
tolerationSeconds 미설정 NoExecute에서 영원히 실행 적절한 유예 시간 설정
Drain 시 PDB 미설정 모든 Pod 동시 퇴거, 서비스 중단 PDB로 최소 가용 Pod 보장

마무리

Taint와 Toleration은 Kubernetes 스케줄링의 방어적 제어 메커니즘이다. Node Affinity가 Pod를 끌어당기는 “당김”이라면, Taint/Toleration은 Pod를 밀어내는 “밀침”이다. GPU 전용화, 스팟 관리, 노드 유지보수, 멀티테넌트 격리 등 실전 시나리오에서 Affinity 스케줄링과 함께 사용하면 클러스터 리소스를 정밀하게 제어할 수 있다.

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