테인트와 톨러레이션

테인트와 톨러레이션 (Taints and Tolerations)

노드 어피니티(node affinity)는 파드를 노드 집합으로 끌어당기는(선호 또는 하드 요구 사항으로) 파드의 속성이에요. 테인트는 그 반대예요. 테인트는 노드가 파드 집합을 밀어낼 수 있게 해줘요.

톨러레이션(tolerations)은 파드에 적용돼요. 톨러레이션은 스케줄러가 일치하는 테인트가 있는 파드를 스케줄링할 수 있게 해줘요. 톨러레이션은 스케줄링을 허용하지만 보장하지는 않아요. 스케줄러는 자신의 기능의 일부로 다른 파라미터도 평가해요.

테인트와 톨러레이션은 함께 동작해 파드가 부적절한 노드에 스케줄링되지 않도록 보장해요. 하나 이상의 테인트가 노드에 적용되며, 이는 그 노드가 테인트를 톨러레이션하지 않는 어떤 파드도 받아들이지 말아야 한다는 것을 표시해요.

출처: 문서

본문

개념 (Concepts)

kubectl taint로 노드에 테인트를 추가해요. 예를 들어,

kubectl taint nodes node1 key1=value1:NoSchedule

이 명령은 node1에 테인트를 배치해요. 테인트는 key key1, value value1, 테인트 효과(effect) NoSchedule을 가져요. 이는 일치하는 톨러레이션 없이는 어떤 파드도 node1에 스케줄링할 수 없다는 뜻이에요.

위 명령이 추가한 테인트를 제거하려면 다음을 실행할 수 있어요.

kubectl taint nodes node1 key1=value1:NoSchedule-

PodSpec에서 파드에 대한 톨러레이션을 지정해요. 다음 두 톨러레이션 모두 위의 kubectl taint 줄이 만든 테인트와 "일치"하며, 따라서 어느 톨러레이션이든 가진 파드는 node1에 스케줄링될 수 있어요.

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
tolerations:
- key: "key1"
  operator: "Exists"
  effect: "NoSchedule"

기본 쿠버네티스 스케줄러는 특정 파드를 실행할 노드를 선택할 때 테인트와 톨러레이션을 고려해요. 하지만 파드에 대해 .spec.nodeName을 수동으로 지정하면 그 동작은 스케줄러를 우회해요. 그 파드는 선택한 노드에 NoSchedule 테인트가 있더라도 할당한 노드에 바인딩돼요. 이런 일이 발생하고 노드에 NoExecute 테인트도 설정되어 있다면, 적절한 톨러레이션이 없으면 kubelet이 파드를 축출해요.

다음은 몇 가지 톨러레이션이 정의된 파드의 예시예요.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  tolerations:
  - key: "example-key"
    operator: "Exists"
    effect: "NoSchedule"

operator의 기본값은 Equal이에요.

톨러레이션은 키가 같고 효과가 같으며 다음 중 하나일 때 테인트와 "일치"해요.

  • operator가 Exists이거나(이 경우 값이 지정되어서는 안 됨),
  • operator가 Equal이고 값이 같아야 하거나.

참고:

두 가지 특수한 경우가 있어요.

키가 비어 있으면 operator는 Exists여야 하며, 이는 모든 키와 값을 일치시켜요. 동시에 효과는 여전히 일치해야 한다는 점을 주목하세요.

빈 효과는 key key1을 가진 모든 효과와 일치해요.

위 예시는 NoSchedule 효과를 사용했어요. 또는 PreferNoSchedule 효과를 사용할 수 있어요.

effect 필드의 허용 값은 다음과 같아요.

NoExecute - 이는 이미 노드에서 실행 중인 파드에 다음과 같이 영향을 줘요.

  • 테인트를 톨러레이션하지 않는 파드는 즉시 축출됨
  • 톨러레이션 사양에서 tolerationSeconds를 지정하지 않고 테인트를 톨러레이션하는 파드는 영원히 바인딩된 채로 남음
  • 지정된 tolerationSeconds로 테인트를 톨러레이션하는 파드는 지정된 시간 동안 바인딩된 채로 남음. 그 시간이 지나면 노드 수명주기 컨트롤러가 노드에서 파드를 축출함.

NoSchedule - 일치하는 톨러레이션이 없으면 새 파드는 테인트된 노드에 스케줄링되지 않아요. 현재 노드에서 실행 중인 파드는 축출되지 않아요.

PreferNoSchedule - PreferNoScheduleNoSchedule의 "선호" 또는 "소프트" 버전이에요. 제어 플레인은 테인트를 톨러레이션하지 않는 파드를 노드에 두지 않으려고 하겠지만, 보장되지는 않아요.

같은 노드에 여러 테인트를, 같은 파드에 여러 톨러레이션을 둘 수 있어요. 쿠버네티스가 여러 테인트와 톨러레이션을 처리하는 방식은 필터와 같아요. 노드의 모든 테인트로 시작한 다음, 파드가 일치하는 톨러레이션을 가진 것을 무시해요. 남은 무시되지 않은 테인트는 파드에 표시된 효과를 가져요. 특히,

  • 효과가 NoSchedule인 무시되지 않은 테인트가 하나 이상 있으면 쿠버네티스는 그 노드에 파드를 스케줄링하지 않아요.
  • 효과가 NoSchedule인 무시되지 않은 테인트가 없지만 효과가 PreferNoSchedule인 무시되지 않은 테인트가 하나 이상 있으면 쿠버네티스는 그 노드에 파드를 스케줄링하지 않으려고 해요.
  • 효과가 NoExecute인 무시되지 않은 테인트가 하나 이상 있으면 파드는 노드에서 축출되고(이미 노드에서 실행 중인 경우), 노드에 스케줄링되지 않아요(아직 노드에서 실행 중이 아닌 경우).

예를 들어 노드를 이렇게 테인트한다고 상상해 보세요.

kubectl taint nodes node1 key1=value1:NoSchedule
kubectl taint nodes node1 key1=value1:NoExecute
kubectl taint nodes node1 key2=value2:NoSchedule

그리고 파드가 두 개의 톨러레이션을 갖는 경우:

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoSchedule"
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"

이 경우 파드는 세 번째 테인트와 일치하는 톨러레이션이 없기 때문에 그 노드에 스케줄링될 수 없어요. 하지만 테인트가 추가될 때 이미 노드에서 실행 중이라면 계속 실행될 수 있어요. 세 번째 테인트만 파드가 톨러레이션하지 않는 유일한 것이기 때문이에요.

일반적으로 효과가 NoExecute인 테인트가 노드에 추가되면, 테인트를 톨러레이션하지 않는 파드는 즉시 축출되고 톨러레이션하는 파드는 결코 축출되지 않아요. 하지만 NoExecute 효과의 톨러레이션은 테인트가 추가된 후 파드가 노드에 얼마나 오래 바인딩되어 있을지를 지시하는 선택적 tolerationSeconds 필드를 지정할 수 있어요. 예를 들어,

tolerations:
- key: "key1"
  operator: "Equal"
  value: "value1"
  effect: "NoExecute"
  tolerationSeconds: 3600

이는 이 파드가 실행 중이고 일치하는 테인트가 노드에 추가되면, 파드가 3600초 동안 노드에 바인딩되어 있다가 축출된다는 뜻이에요. 그 시간 전에 테인트가 제거되면 파드는 축출되지 않아요.

숫자 비교 연산자 (Numeric comparison operators)

이 기능을 사용하려면 (클러스터 관리자나) 클러스터의 모든 관련 구성 요소에서 TaintTolerationComparisonOperators 기능 게이트를 활성화해야 해요.

기능 게이트 활성화/비활성화에 대한 자세한 내용은 '기능 게이트 활성화 또는 비활성화' 문서를 참고하세요.

EqualExists 외에도 숫자 비교 연산자(GtLt)를 사용해 정수 값을 가진 테인트와 일치시킬 수 있어요. 이는 신뢰성 수준이나 SLA 계층으로 노드를 일치시키는 것 같은 임계값 기반 스케줄링에 유용해요.

  • Gt는 테인트 값이 톨러레이션 값보다 클 때 일치해요.
  • Lt는 테인트 값이 톨러레이션 값보다 작을 때 일치해요.

숫자 연산자의 경우 톨러레이션과 테인트 값 모두 유효한 정수여야 해요. 어느 값이든 정수로 파싱할 수 없으면 그 톨러레이션은 일치하지 않아요.

참고:

예를 들어 노드가 서비스 수준 계약(SLA)을 나타내는 값으로 테인트되어 있다면,

kubectl taint nodes node1 servicelevel.organization.example/agreed-service-level=950:NoSchedule

파드는 SLA가 900보다 큰 노드를 톨러레이션할 수 있어요.

apiVersion: v1
kind: Pod
metadata:
  name: nginx-numeric-toleration
  labels:
    env: test
spec:
  containers:
    - name: nginx
      image: nginx
      imagePullPolicy: IfNotPresent
  tolerations:
    - key: "servicelevel.organization.example/agreed-service-level"
      operator: "Gt"
      value: "900"
      effect: "NoSchedule"

이 톨러레이션은 950 > 900이므로(테인트 값이 Gt 연산자에 대한 톨러레이션 값보다 큼) node1의 테인트와 일치해요. 마찬가지로 Lt 연산자를 사용해 테인트 값이 톨러레이션 값보다 작은 테인트와 일치시킬 수 있어요.

tolerations:
- key: "servicelevel.organization.example/agreed-service-level"
  operator: "Lt"
  value: "1000"
  effect: "NoSchedule"

참고:

숫자 비교 연산자를 사용할 때:

  • 톨러레이션과 테인트 값 모두 유효한 부호 있는 64비트 정수여야 해요(0으로 시작하는 숫자(예: "0550")는 허용되지 않음).
  • 값이 정수로 파싱될 수 없으면 톨러레이션은 일치하지 않아요.
  • 숫자 연산자는 모든 테인트 효과(NoSchedule, PreferNoSchedule, NoExecute)와 동작해요.
  • 숫자 연산자가 있는 PreferNoSchedule의 경우: 파드의 톨러레이션이 숫자 비교를 충족하지 않으면(예: Gt 사용 시 테인트 값 < 톨러레이션 값), 스케줄러는 노드에 더 낮은 우선순위를 주지만 더 나은 옵션이 없으면 여전히 스케줄링할 수 있어요.

경고:

TaintTolerationComparisonOperators 기능 게이트를 비활성화하기 전에:

  • 컨트롤러의 핫 루프(hot-loop)를 피하기 위해 Gt 또는 Lt 연산자를 사용하는 모든 워크로드를 식별해야 해요.
  • 모든 워크로드 컨트롤러 템플릿을 Equal 또는 Exists 연산자를 사용하도록 업데이트해야 해요.
  • Gt 또는 Lt 연산자를 사용하는 보류 중인 파드를 모두 삭제해야 해요.
  • 검증 오류의 스파이크에 대해 apiserver_request_total 메트릭을 모니터링해야 해요.

예시 사용 사례 (Example Use Cases)

테인트와 톨러레이션은 파드를 노드에서 멀리 유도하거나 실행되지 말아야 할 파드를 축출하는 유연한 방법이에요. 몇 가지 사용 사례는 다음과 같아요.

  • 전용 노드(Dedicated Nodes): 특정 사용자 집합의 독점 사용을 위해 노드 집합을 전용으로 만들고 싶다면, 그 노드에 테인트를 추가하고(kubectl taint nodes nodename dedicated=groupName:NoSchedule 같은) 그들의 파드에 대응하는 톨러레이션을 추가할 수 있어요(이는 사용자 정의 어드미션 컨트롤러를 작성하는 것이 가장 쉬울 수 있어요). 톨러레이션이 있는 파드는 테인트된(전용) 노드뿐 아니라 클러스터의 다른 어떤 노드도 사용할 수 있어요. 노드를 그들에게 전용으로 만들고 그들만 전용 노드를 사용하도록 하고 싶다면, 같은 노드 집합에 테인트와 유사한 라벨(예: dedicated=groupName)을 추가하고, 어드미션 컨트롤러가 파드가 dedicated=groupName 라벨이 있는 노드에만 스케줄링되도록 노드 어피니티를 추가해야 해요.
  • 특수 하드웨어가 있는 노드(Nodes with Special Hardware): 소수의 노드가 특수 하드웨어(예: GPU)를 가진 클러스터에서는 특수 하드웨어가 필요하지 않은 파드를 그 노드에서 멀리 두어, 나중에 도착하는 특수 하드웨어가 필요한 파드를 위한 공간을 남기는 것이 바람직해요. 이는 특수 하드웨어가 있는 노드를 테인트하고(kubectl taint nodes nodename special=true:NoSchedule 또는 kubectl taint nodes nodename special=true:PreferNoSchedule), 특수 하드웨어를 사용하는 파드에 대응하는 톨러레이션을 추가해 이룰 수 있어요. 전용 노드 사용 사례처럼, 톨러레이션을 사용자 정의 어드미션 컨트롤러로 적용하는 것이 가장 쉬울 수 있어요. 예를 들어 특수 하드웨어를 나타내는 데 확장 리소스(Extended Resources)를 사용하고, 특수 하드웨어 노드를 확장 리소스 이름으로 테인트하고, ExtendedResourceToleration 어드미션 컨트롤러를 실행하는 것을 권장해요. 이제 노드가 테인트되어 있으므로 톨러레이션이 없는 파드는 그들에 스케줄링되지 않아요. 하지만 확장 리소스를 요청하는 파드를 제출하면, ExtendedResourceToleration 어드미션 컨트롤러가 자동으로 올바른 톨러레이션을 파드에 추가하고 그 파드는 특수 하드웨어 노드에 스케줄링돼요. 이렇게 하면 특수 하드웨어 노드가 그러한 하드웨어를 요청하는 파드에 전용으로 사용되고, 수동으로 파드에 톨러레이션을 추가할 필요가 없음을 보장할 수 있어요.
  • 테인트 기반 축출(Taint based Evictions): 노드 문제가 있을 때 파드별로 구성 가능한 축출 동작으로, 다음 섹션에서 설명해요.

테인트 기반 축출 (Taint based Evictions)

노드 컨트롤러는 특정 조건이 참일 때 노드를 자동으로 테인트해요. 다음 테인트는 내장되어 있어요.

  • node.kubernetes.io/not-ready: 노드가 준비되지 않음. 이는 NodeCondition Ready가 "False"인 것에 대응해요.
  • node.kubernetes.io/unreachable: 노드가 노드 컨트롤러에서 도달할 수 없음. 이는 NodeCondition Ready가 "Unknown"인 것에 대응해요.
  • node.kubernetes.io/memory-pressure: 노드에 메모리 압력이 있음.
  • node.kubernetes.io/disk-pressure: 노드에 디스크 압력이 있음.
  • node.kubernetes.io/pid-pressure: 노드에 PID 압력이 있음.
  • node.kubernetes.io/network-unavailable: 노드의 네트워크를 사용할 수 없음.
  • node.kubernetes.io/unschedulable: 노드가 스케줄 불가능함.
  • node.cloudprovider.kubernetes.io/uninitialized: kubelet이 "외부" 클라우드 제공자로 시작되면 이 테인트는 노드에 사용 불가로 표시하기 위해 설정돼요. cloud-controller-manager의 컨트롤러가 이 노드를 초기화하면 kubelet이 이 테인트를 제거해요.

노드를 드레인해야 하는 경우, 노드 컨트롤러 또는 kubelet이 NoExecute 효과로 관련 테인트를 추가해요. 이 효과는 node.kubernetes.io/not-readynode.kubernetes.io/unreachable 테인트에 대해 기본으로 추가돼요. 장애 조건이 정상으로 돌아오면 kubelet 또는 노드 컨트롤러가 관련 테인트를 제거할 수 있어요.

어떤 경우에 노드가 도달할 수 없으면 API 서버는 그 노드의 kubelet과 통신할 수 없어요. 파드를 삭제하기로 한 결정은 API 서버와의 통신이 다시 수립될 때까지 kubelet에 전달될 수 없어요. 그동안 삭제 예정인 파드는 분할된 노드에서 계속 실행될 수 있어요.

참고:

파드에 대해 tolerationSeconds를 지정해 파드가 실패하거나 응답하지 않는 노드에 얼마나 오래 바인딩되어 있는지를 정의할 수 있어요.

예를 들어 로컬 상태가 많은 애플리케이션을 네트워크 파티션 상황에서 오래 동안 노드에 바인딩된 채 두고 싶을 수 있어요. 파티션이 복구되기를 바라며 파드 축출을 피하고 싶은 경우죠. 그 파드에 설정하는 톨러레이션은 다음과 같을 수 있어요.

tolerations:
- key: "node.kubernetes.io/unreachable"
  operator: "Exists"
  effect: "NoExecute"
  tolerationSeconds: 6000

참고:

쿠버네티스는 사용자나 컨트롤러가 그 톨러레이션을 명시적으로 설정하지 않는 한, node.kubernetes.io/not-readynode.kubernetes.io/unreachable에 대해 tolerationSeconds=300으로 톨러레이션을 자동으로 추가해요.

이 자동 추가된 톨러레이션은 이러한 문제 중 하나가 감지된 후 5분 동안 파드가 노드에 바인딩된 채로 유지된다는 뜻이에요.

DaemonSet 파드는 다음 테인트에 대해 tolerationSeconds 없는 NoExecute 톨러레이션으로 생성돼요.

  • node.kubernetes.io/unreachable
  • node.kubernetes.io/not-ready

이렇게 하면 DaemonSet 파드가 이러한 문제로 인해 결코 축출되지 않도록 보장해요.

참고:

조건으로 노드 테인트하기 (Taint Nodes by Condition)

제어 플레인은 노드 컨트롤러를 사용해 노드 조건에 대해 NoSchedule 효과로 테인트를 자동으로 만들어요.

스케줄러는 스케줄링 결정을 할 때 노드 조건이 아니라 테인트를 확인해요. 이는 노드 조건이 스케줄링에 직접 영향을 주지 않도록 보장해요. 예를 들어 DiskPressure 노드 조건이 활성화되면 제어 플레인은 node.kubernetes.io/disk-pressure 테인트를 추가하고 영향을 받는 노드에 새 파드를 스케줄링하지 않아요. MemoryPressure 노드 조건이 활성화되면 제어 플레인은 node.kubernetes.io/memory-pressure 테인트를 추가해요.

대응하는 파드 톨러레이션을 추가해 새로 생성된 파드에 대해 노드 조건을 무시할 수 있어요. 제어 플레인은 또한 BestEffort 이외의 QoS 클래스를 가진 파드에 node.kubernetes.io/memory-pressure 톨러레이션을 추가해요. 이는 쿠버네티스가 Guaranteed 또는 Burstable QoS 클래스의 파드(메모리 요청이 설정되지 않은 파드도 포함)를 메모리 압력을 견딜 수 있는 것으로 취급하기 때문이며, 새 BestEffort 파드는 영향을 받는 노드에 스케줄링되지 않아요.

DaemonSet 컨트롤러는 DaemonSet이 깨지지 않도록 모든 데몬에 다음 NoSchedule 톨러레이션을 자동으로 추가해요.

  • node.kubernetes.io/memory-pressure
  • node.kubernetes.io/disk-pressure
  • node.kubernetes.io/pid-pressure (1.14 이상)
  • node.kubernetes.io/unschedulable (1.10 이상)
  • node.kubernetes.io/network-unavailable (호스트 네트워크만)

이 톨러레이션을 추가하면 역호환성이 보장돼요. DaemonSet에 임의의 톨러레이션을 추가할 수도 있어요.

장치 테인트와 톨러레이션 (Device taints and tolerations)

전체 노드를 테인트하는 대신, 클러스터가 특수 하드웨어를 관리하기 위해 동적 리소스 할당을 사용할 때 관리자는 개별 장치를 테인트할 수도 있어요. 장점은 테인트를 정확히 고장났거나 유지보수가 필요한 하드웨어에 표적화할 수 있다는 것이에요. 톨러레이션도 지원되며 장치를 요청할 때 지정할 수 있어요. 테인트처럼 이것들은 같은 할당된 장치를 공유하는 모든 파드에 적용돼요.

더 알아보기 (Learn more)