kubernetes CKA study (10) - Taints and Tolerations, Node Selector, Taints and Tolerations vs Node Affinity

이동명·2023년 12월 20일
0

kubernetes CKA study

목록 보기
10/37
post-thumbnail

Taints and Tolerations

taint 및 tolerations의 개념은 초보자에게는 약간 혼란 스러울 수 있습니다. 따라서 사람에게 접근하는 벌레의 비유를 사용하여 taint 과 tolerations이 무엇인지 이해해보겠습니다. 벌레가 사람에게 상륙하는 것을 방지하기 위해, 우리는 사람에게 구충제 스프레이를 뿌립니다. 여기서 구충제 스프레이는 taint를 의미합니다. 벌레는 구충제 스프레이의 냄새를 참지 못하기 때문에 사람에게 다가가지 못합니다. 즉, taint가 버그를 제거한 것입니다. 그러나 이 냄새에 내성이 있는 다른 벌레가 있을 수 있습니다. 그 벌레에게는 taint가 효과가 없는 것이죠. 결국 그 벌레는 사람에게 착륙합니다. 벌레가 사람에게 착륙하는 것을 결정하는 두 가지가 있습니다.

  • 사람에게 taint가 있는가

  • 벌레가 그 taint를 얼만큼 참을 수 있는가, 쿠버네티스로 돌아가서, 비유에서의 사람은 노드이고 벌레는 파드입니다. taint 과 tolerations은 클러스터에 대한 보안 또는 침입과 아무 상관이 없습니다. taint 및 tolerations는 어떤 파드가 노드에 스케쥴될수 있는지에 대한 제한을 설정하는 데 사용됩니다.

세 개의 워커 노드가 있는 간단한 클러스터를 보겠습니다. 노드의 이름은 1, 2, 3 입니다. 또한 이러한 노드에 배포할 수 있는 파드 세트가 있습니다. 파드들은 A, B, C, D라고 하겠습니다. 파드가 생성되면 Kubernetes 스케줄러는 이러한 파드를 사용 가능한 워커 노드에 배치하려고 시도합니다. 현재로서는 아무 제한이 없기때문에 스케줄러는 모든 노드에 균등하게 균형을 맞추어 파드를 배치합니다.

이제 노드 1에 특정 use case나 애플리케이션을 위한 전용 리소스가 있다고 가정해봅시다. 그렇다면 노드1에는 특정 애플리케이션에 속하는 파드만 배치하고 싶습니다. 우리의 요구사항은 아래 두 가지 입니다.

  • 노드1 에 원치 않는 파드는 배치되지 않는다.

  • 노드1 에는 특정한 파드만 배치된다.

첫째, 해당 노드에 taint를 배치하여 모든 파드가 그 노드에 배치되는 것을 방지합니다. 이 taint를 blue라고 부릅시다. 파드는 디폴트로 tolerations를 가지고 있지 않습니다. 따로 명시해주지 않을 경우에는 어떠한 파드도 taint가 있는 곳에 갈 수 없습니다. 따라서 현재 어떤 파드도 taint blue를 참을 수 없기 때문에 노드1에는 어떤 파드도 배치될 수 없습니다. 이것으로 우리의 요구사항의 절반을 해결했습니다.

나머지 요구사항의 절반은 특정한 파드를 해당 노드에 배치하는 것입니다. 이를 위해서는 어떤 파드가 특정한 taint에 tolerant한지(참을 수 있는지) 지정해야 합니다. 지금 케이스에서는, 파드 D만 노드1에 배치되는 것을 허용하고 싶습니다. 그렇다면 파드 D에 toleration을 추가합니다. 이제 파드 D는 blue taint를 참을 수 있게 됩니다. 따라서 스케쥴러가 이 파드를 노드1에 배치하려고 할 때, 배치가 가능해집니다. 노드1은 blue taint에 대한 toleration를 가지고 있는 파드만 허용할 수 있습니다. 이것이 taints와 toleration가 존재할 때 파드가 스케쥴되는 방식입니다. 스케쥴러는 파드 A를 노드1에 배치하려고 하지만 노드1의 taint에 의해 허용되지 않고, 노드2로 이동합니다. 그리고 스케쥴러는 파드B를 노드1에 배치하려고 시도합니다. 그러나 다시 taint에 의해 파드B는 허용되지 않고, 다음 free 노드인 노드3에 배치됩니다. 그런 다음, 스케쥴러는 파드C를 노드1에 배치하려고 시도하지만, 다시 허용되지 않고 노드2에서 끝납니다. 그리고 마지막으로 스케쥴러는 파드D를 노드1에 배치하려고 합니다. 파드D는 노드1에 대한 toleration이 있으므로 허용됩니다. taint는 노드에 설정되고, toleration는 파드에 설정된다는 것을 기억해주세요.

Taints

그래서 이것을 어떻게 적용할 수 있을까요? kubectl taint nodes 커맨드를 사용하여 노드에 taint를 설정합니다. 커맨드 뒤에 key-value쌍으로 노드이름과 taint를 지정하면 됩니다.

kubectl taint nodes <node-name> key=value:taint-effect

Example

kubectl taint nodes node1 app=blue:NoSchedule

예를 들어 노드를 blue 애플리케이션 전용으로 지정하려는 경우, key-value 쌍은 app=blue가 됩니다.

taint effect는 taint에 toleration가 없는 파드를 어떻게 처리할 것인지를 결정합니다. 3가지 종류의 taint effect가 있습니다.

  • NoSchedule: 앞에서 우리가 말한 것 처럼 toleration가 없는 파드는 taint가 있는 노드에 스케쥴링 되지 않습니다.

  • PreferNoSchedule: 시스템이 되도록이면 taint가 있는 노드에 파드를 배치하지 않으려하지만 가능은 합니다.

  • NoExcuse: toleration이 없는 새로운 파드는 노드에 배치되지 않으며, 이미 노드에 존재하는 파드는 toleration가 없다면 축출됩니다. 이미 노드에 존재하던 파드는 노드에 taint가 설정되기 전에 스케쥴링되었을 것입니다.

예제 커맨드는 노드 1에 taint를 설정하는 커맨드입니다. key-value 쌍은 app=blue이며, taint effect는 NoSchedule입니다.

Tolerations

파드에 toleration을 추가하려면 먼저 파드 Definition file을 가져와야 합니다. difinition file의 spec섹션에 tolerations라는 섹션을 추가합니다. taint를 생성했을 때 사용한 값과 동일하게 입력합니다. value는 "blue"이고, effect는 "NoSchedule"입니다. toleration섹션의 모든 값은 쌍따옴표로 감싸야 하는 것을 기억하세요.

apiVersion: v1
kind: Pod
metadata:
	name: myapp-pod
spec:
	containers:
		- name: nginx-container
		image: nginx
	tolerations:
		- key: "app"
		operator: "Equal"
		value: "blue"
		effect: "NoSchedule"

이제 파드가 toleration를 가지고 생성되거나 toleration을 가지도록 업데이트될 때, effect에 따라 스케쥴링이 안되거나 기존 노드에서 제거될 것입니다.

Taint - NoExcute

NoExcuse taint effect에 대해 더 알아보겠습니다. 예를 들어 3개의 노드가 있습니다. 지금은 아무런 taint나 toleration이 없기 때문에 평소와 같은 방식으로 스케쥴링이 이루어집니다.

이제 노드1을 특정 애플리케이션 전용으로 사용하기로 결정했습니다. 노드에 taint를 애플리케이션 이름으로 설정하고 특정 애플리케이션에 속하는 파드D 에도 toleration을 추가했습니다. taint effect는 NoExcuse로 설정했습니다.

taint effect가 발생하면 파드 C는 노드에서 제거되게 됩니다. 제거된 파드 C는 죽습니다. 파드D는 blue taint에 toleration을 가지고 있기 때문에 노드에서 계속 실행됩니다.

이제 다시 taints와 toleration을 구성했던 원래 시나리오로 돌아가겠습니다. taints와 tolerations은 노드가 특정한 파드를 허용하는 것에 대한 제한을 의미한다는 것을 기억하세요. 이 케이스에서는 노드 1은 오직 파드 D만 수용할 수 있습니다. 그러나 파드D가 항상 노드1에 있다는 것을 보장하지는 않습니다. 다른 두 노드에 taints가 적용되지 않았기 때문에, 파드 D는 다른 두 노드에도 배치될 수 있습니다. 따라서 taints와 tolerations가 파드가 특정 노드로 간다는 것을 말하는 것이 아님을 기억하세요. 대신, 노드가 특정 toleration을 가지고 있는 파드만 허용한다는 것을 의미합니다.

마지막으로 흥미로운 사실 하나를 살펴보겠습니다. 지금까지는 우리는 워커 노드만 언급했습니다. 그러나 클러스터에는 마스터 노드도 있습니다. 마스터노드는 파드를 호스팅하고 모든 관리 소프트웨어를 실행할 수 있습니다. 이제 스케쥴러가 마스터 노드의 파드를 스케쥴링하지 않는다는 것을 눈치채셨는지 모르겠습니다. 왜 그럴까요? Kubernetes 클러스터가 처음 설정되면 마스터 노드에 taint가 자동으로 설정되어 파드가 마스터 노드에 스케쥴링되지 않도록 합니다. 우리는 이 것을 확인할수도 있고 필요하면 동작을 수정할 수도 있습니다.

그러나 best pratice는 마스터 서버에 애플리케이션 워크로드를 배포하지 않는 것입니다. 이 taint를 보려면 kubectl describe node kubemaster 명령을 실행하십시오. taint섹션을 보려면 kubectl describe node kubemaster |grep Taint를 실행하면 됩니다. 아래와 같이 taint가 설정된 것을 볼 수 있습니다.

테스트 통과 완료


Node Selector

간단한 예시로 시작하겠습니다. 3개의 노드가 있는 클러스터가 있으며 그 중 2개는 더 낮은 하드웨어 리소스를 가진 더 작은 노드입니다. 다른 하나는 더 높은 리소스로 구성된 더 큰 노드입니다. 다양한 종류의 워크로드가 클러스터에서 실행 중입니다. 더 큰 노드를 더 높은 마력이 필요한 데이터 처리 워크로드를 전용으로 사용하려고 합니다.

그러나 현재 기본 설정에서는 모든 파드는 모든 노드로 이동할 수 있습니다. 이 경우 파드 C는 노드 2 또는 3로 배치되어서 죽을 수 있습니다. 이를 해결하기 위해, 파드가 특정 노드에서만 실행되도록 제한을 설정할 수 있습니다. 두 가지 방법이 있습니다. 첫 번째는 노드 선택기를 사용하는 것입니다. 간단하고 쉬운 방법입니다. 이를 위해 이전에 생성한 파드 definition 파일을 살펴봅니다. 이 파일에는 데이터 처리 이미지와 함께 파드를 생성하기 위한 간단한 정의가 있습니다.

apiVersion: v1
kind: Pod
metadata:
 name: myapp-pod
spec:
 containers:
 - name: data-processor
   image: data-processor
 nodeSelector:
  size: Large

이 파드가 더 큰 노드에서 실행되도록 제한하려면 spec섹션에 nodeSelector라는 새 속성을 추가합니다. size: Large를 입력하세요. 잠시만요, size: Large는 어디서 나온 것일까요? 그리고 Kubernetes는 어떤 것이 큰 노드인지 어떻게 알 수 있을까요? size와 large의 key-value쌍은 사실 노드의 label입니다. 스케줄러는 이 레이블을 사용하여 파드를 배치할 올바른 노드를 식별합니다. label과 selector에 대해서는 서비스, Replicasets, Deployment 등 Kubernetes 과정 전반에 걸쳐 여러 번 보게 되는 주제입니다. 이와 같이 Node Selector에서 레이블을 사용하려면 파드를 만들기 전에 먼저 노드에 레이블을 지정해야 합니다.

Label Nodes

이제 돌아가서 노드에 레이블을 지정하는 방법을 살펴보겠습니다. 노드에 레이블을 지정하려면 아래 포맷의 커맨드를 사용하면 됩니다.

kubectl label nodes <node-name> <label-key>=<label-value>

Example

kubectl label nodes node-1 size=Large

이제 노드에 레이블을 지정했으므로 파드 생성으로 돌아갈 수 있습니다.

apiVersion: v1
kind: Pod
metadata:
 name: myapp-pod
spec:
 containers:
 - name: data-processor
   image: data-processor
 nodeSelector:
  size: Large

Node Selector가 size: Large로 설정되어 있습니다.

포드가 생성되면, 원하는 대로 노드 1에 배치됩니다.

Node Selector - Limitations

Node Selector는 우리의 목적에 부합했지만 한계가 있습니다. 우리는 단일 레이블과 selector를 사용했습니다. 그러나 만약 요구 사항이 훨씬 더 복잡하다면 어떨까요? 예를 들어, "Large노드 나 Medium노드에 파드를 배치하는 것"과 같은 것, "Small이 아닌 노드에 파드를 배치하는 것"과 같은 것과 같은 상황입니다. Node Selector로는 이를 달성할 수 없습니다.

이를 위해 Node affinity 및 anti-affinity 기능이 나타났습니다.

Node Affinity

Node Affinity의 주요 기능은 파드가 특정한 노드에서 호스팅되는 것을 보장하는 것입니다.

예를 들어 위와 같은 상황에서 대용량 데이터 처리 파드를 노드1에 배치되도록 하고 싶습니다. 이 작업은 지난 강의에서 배운 Node Selector를 통해 간단하게 해결할 수 있습니다. 그러나 Node Selector는 "Large노드 나 Medium노드에 파드를 배치하는 것"과 같은 것, "Small이 아닌 노드에 파드를 배치하는 것"과 같은 것과 같은 advance expressions을 제공할 수 없습니다.

Node Affinity 기능은 advance expressions을 제공합니다.

Node Affinity로 Large노드 나 Medium노드에 파드를 배치하기

아래 pod definition file은 위에서 구현한 NodeSelector를 사용한 definition file과 동일한 내용입니다. 둘 다 큰 노드에 파드를 배치한다는 똑같은 일을 합니다.

apiVersion: v1
kind: Pod
metadata:
 name: myapp-pod
spec:
 containers:
 - name: data-processor
   image: data-processor
 affinity:
   nodeAffinity:
     requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: size
            operator: In
            values: 
            - Large
            - Medium

spec 아래에 affinity 가 있고, 또 그 아래에 nodeAffinity가 있습니다. 그 아래엔 문장처럼 보이는 긴 필드 requiredDuringSchedulingIgnoredDuringExecution가 있습니다. 이 아래에 nodeSelectorTerms가 있고 여기에서 key-value 쌍을 지정합니다. key-value쌍을 더 자세히 보겠습니다.

  • key: size

  • operator: In

  • values:

    • Large

    • Medium

위 key-value쌍은 size가 Large이거나 Medium인 노드에 파드를 배치(In)하라는 의미입니다.

Node Affinity 로 Small이 아닌 노드에 파드를 배치하기

만약 Small이 아닌 노드에 파드를 배치하기 원한다면, 아래와 같이 operator: NotIn 을 사용하면 됩니다.

apiVersion: v1
kind: Pod
metadata:
 name: myapp-pod
spec:
 containers:
 - name: data-processor
   image: data-processor
 affinity:
   nodeAffinity:
     requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: size
            operator: NotIn
            values: 
            - Small

Node Affinity 로 Small이 아닌 노드에 파드를 배치하기

만약 우리가 노드의 레이블을 Large와 Medium만 설정하고 Small은 레이블을 설정하지 않았다면, 레이블이 존재하는 노드에만 파드를 배치함으로써 파드를 Large노드나 Medium노드에 배치할 수 있을 것입니다. 이럴 때는 operator: Exists를 사용하면 됩니다.

apiVersion: v1
kind: Pod
metadata:
 name: myapp-pod
spec:
 containers:
 - name: data-processor
   image: data-processor
 affinity:
   nodeAffinity:
     requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: size
            operator: Exists

Node Affinity Types

그런데 만약, NodeAffinity와 Node가 일치하지 않는다면 어떻게 될까요? 예를 들어, Large 노드에 파드를 배치하라고 했는데 Large라는 레이블이 붙은 노드가 없는 경우나, 파드가 이미 배치되어 있는데 누군가 노드의 레이블을 변경하는 경우입니다. 이 때 파드가 노드에 계속 남아 있을까요? 이러한 것들에 대한 설명은 긴 문장 같이 생긴 Node Affinity Type이 해줍니다. Node Affinity Type은 Node Affinity와 관련된 스케쥴러의 동작을 정의합니다.

현재 Available Node Affinity Type은 두 가지 입니다.

  • requiredDuringSchedulingIgnoredDuringExecution

  • preferredDuringScheduilingIgnoredDuringExecution
    그리고 추가적으로 Planned Node Affinity Type이 하나 더 있습니다.

  • requiredDuringSchedulingRequiredDuringExecution

  • Available

    • requiredDuringSchedulingIgnoredDuringExecution
    • preferredDuringSchedulingIgnoredDuringExecution
  • Planned

    • requiredDuringSchedulingRequiredDuringExecution
    • preferredDuringSchedulingRequiredDuringExecution

Node Affinity Types States

파드의 라이프사이클에는 Node Affinity에 대해 두 가지 상태가 있습니다. DuringScheduling과 DuringExecution입니다.

DuringScheduling

DuringScheduling은 Pod가 존재하지 않고 처음 생성된 상태입니다. 파드가 처음 생성될 때 지정된 Node Affinity 규칙이 파드를 올바른 노드에 배치하는 것은 당연합니다. 이제 일치하는 레이블이 있는 노드를 사용할 수 없으면 어떻게 될까요? 예를 들어 노드에 Large 레이블을 지정하는 것을 잊은 경우 입니다.
여기에서 Node Affinity Type이 작용합니다.

첫 번째 Type인 Required을 선택하면 스케줄러는 지정된 Node Affinity을 사용하여 파드를 노드에 배치하도록 지시합니다. 노드를 찾을 수 없으면 파드가 예약되지 않습니다. 이 타입은 파드의 배치가 중요한 경우에 사용됩니다. 일치하는 노드가 없으면 파드가 예약되지 않습니다.

그러나 파드 배치가 워크로드를 실행하는 것보다 덜 중요하다고 가정해 보겠습니다. 이 경우 일치하는 노드를 찾을 수 없는 경우, 스케줄러는 단순히 Node Affinity 규칙을 무시하고 사용 가능한 노드에 파드를 배치합니다.
이 방법은 스케줄러에게 "스케쥴러야, 파드를 일치하는 노드에 배치하도록 최선을 다해봐. 하지만 정말 찾을 수 없다면 아무데나 둬." 라고 말하는 것과 같습니다.

DuringExecution

DuringExecution은 파드가 실행 중이고, 노드 레이블 변경과 같이 Node Affinity도에 영향을 미치는 환경 변경이 발생한 상태입니다. 예를 들어 관리자가 우리가 이전에 설정한 size=Large라는 레이블을 노드에서 제거했다고 가정해 보겠습니다. 이제 노드에서 실행 중인 파드는 어떻게 될까요? 아래에서 볼 수 있듯이, 두 가지 Available Node Affinity Type는 DuringExecution 값이 Ignored로 설정되어 있습니다. 즉, 파드는 계속 실행되며 Node Affinity의 변경 사항은 일단 예약되면 파드에 영향을 미치지 않습니다.

Planned Node Affinity Type은 DuringExecution 부분에서 차이가 있습니다. RequiredDuringExecution이라는 새 옵션이 나타났으며, 이 옵션은 Node Affinity을 충족하지 않는 노드에서 실행 중인 파드를 제거합니다. 즉, Large 노드에서 실행 중인 파드는 Large 레이블이 노드에서 제거되면 제거되거나 종료됩니다.


테스트 도중 기억에 남는 문제를 기록해놓겠다.

Create a new deployment named red with the nginx image and 2 replicas, and ensure it gets placed on the controlplane node only.

Use the label key - node-role.kubernetes.io/control-plane - which is already set on the controlplane node.

(controlplane 노드에서만 pod 배치하기)

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: red
  name: red
spec:
  replicas: 2
  selector:
    matchLabels:
      app: red
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: red
    spec:
      containers:
      - image: nginx
        name: nginx

      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: node-role.kubernetes.io/control-plane
                operator: Exists

두번째 문제 지문은 기억 안나지만..

(labels의 key,value를 이용하여 node01 에서만 pod 배치하기)

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: blue
  name: blue
spec:
  replicas: 3
  selector:
    matchLabels:
      app: blue
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: blue
    spec:
      containers:
      - image: nginx
        name: nginx
      affinity:
         nodeAffinity:
           requiredDuringSchedulingIgnoredDuringExecution:
             nodeSelectorTerms:
             - matchExpressions:
               - key: color
                 operator: In
                 values:
                 -  blue

테스트 통과 완료


Taints and Tolerations vs Node Affinity

taint, toleration 및 Node Affinity에 대해 배웠으므로 이번 강의에서 재미있는 연습을 통해 두 개념을 함께 연결해 보겠습니다.

파란색, 빨간색 및 녹색의 세 가지 색상으로 각각 3개의 노드와 3개의 파드가 있습니다.

궁극적인 목표는 파란색 파드를 파란색 노드에, 빨간색 파드를 빨간색 노드에, 마찬가지로 녹색 파드를 녹색 노드에 배치하는 것입니다.

모두 동일한 Kubernetes 클러스터 안에 있습니다. 우리는 노드에 다른 색의 파드가 배치되는 것도, 파드가 다른 색의 노드에 배치되는 것도 원하지 않습니다.

Taint and Toleration

먼저 taint 및 toleration을 사용하여 이 문제를 해결해 보겠습니다.
파란색, 빨간색, 녹색으로 노드에 taint를 적용한 다음, 각 색상을 허용하도록 pod에 toleration을 설정합니다. 이제 파드가 생성되면 노드는 올바른 toleration을 가진 파드만 수용할 수 있습니다. 따라서 녹색 파드는 녹색 노드로, 파란색 파드는 파란색 노드로 가게 됩니다.

그러나 taint 및 toleration은 파드가 같은 색 노드만 선호한다고 보장하지 않습니다.

따라서 빨간색 파드는 taint 또는 toleration 세트가 없는 다른 노드 중 하나에서 끝날 수 있습니다. 이것은 우리가 원하는 것이 아닙니다.

Node Affinity

Node Affinity를 사용하여 동일한 문제를 해결해 보겠습니다. Node Affinity를 사용하여 먼저 해당 색상(파란색, 빨간색 및 녹색)으로 노드에 레이블을 지정합니다. 그런 다음 파드에 Node Selector를 설정하여 파드를 노드에 연결합니다.
이렇게 하면 파드는 올바른 노드로 가게 됩니다. 그러나 다른 파드가 색이 있는 노드에 배치되지 않는다는 보장은 없습니다. 이 경우 다른 파드 중 하나가 우리 노드에 있을 가능성이 있습니다. 이것 또한 우리가 바라는 것이 아닙니다.

Taints/Tolerations and Node Affinity

따라서 taint, toleration 및 Node Affinity 규칙의 조합을 함께 사용하여 특정 파드에 대한 노드를 완전히 전용으로 설정할 수 있습니다.
먼저 taint 및 toleration을 사용하여 다른 파드가 노드에 배치되지 않도록 한 다음, Node Affinity를 사용하여 파드가 다른 노드에 배치되지 않도록 합니다.

업로드중..

profile
Web Developer

0개의 댓글