쿠버네티스 pod가 실행되는 node 선택하기 (feat. nodeName, taint, affinity, toleration)
pod를 특정 node에서 실행시시켜야 하는 경우가 있다. (GPU 등 특정 HW가 있는 node거나, node별로 용도가 구분되어 있는 경우) 이를 위한 쿠버네티스의 기능을 정리해봤다.
1. node 이름으로 선택하기 - NodeName
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
nodeName: kube-01
가장 단순한 방법이다. pod의 spec에 nodeName을 정의하면 해당 node에서 실행된다. 다만 nodeName과 일치하는 node가 없을 경우 자동으로 삭제되는 등 아래와 같은 제약이 있어서 잘 사용되지는 않는다. (실제로 잘못된 nodeName으로 pod를 스케줄링했더니 1분 정도 pending 상태가 유지되다가 pod가 사라졌다.)
만약 명명된 노드가 없으면, 파드가 실행되지 않고 따라서 자동으로 삭제될 수 있다.
만약 명명된 노드에 파드를 수용할 수 있는 리소스가 없는 경우 파드가 실패하고, 그 이유는 다음과 같이 표시된다. 예: OutOfmemory 또는 OutOfcpu.
클라우드 환경의 노드 이름은 항상 예측 가능하거나 안정적인 것은 아니다.
2. node label 로 선택하기(기본) - NodeSelector
node의 label 을 통해 node를 선택한다. 이를 통해서 node를 그룹화하거나 용도별로 구분할 수 있다. (ssd, hdd, gpu 등)
nodeSelector를 사용하기 위해서는 아래와 같은 작업이 필요하다.
- node의 label 확인 (또는 추가)
- pod의 yaml에 spec.nodeSelector를 정의
아래와 같이 node의 label을 확인한다 (또는 추가)
kubectl get no --show-labels # node의 label 확인
kubectl label no [node] key=value # node에 label 추가
아래와 같이 pod의 yaml 파일내에 nodeSelector와 label 을 정의한다.
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd
3. node label 으로 선택하기(고급) - Affinity
Affinity는 node label을 활용한다는 점은 nodeSelector와 유사하지만, In/NotIn/Gt 등 좀 더 유연하게 node label을 비교할 수 있고, 향후 스케줄링 되는 pod외에 현재 실행되고 있는 pod를 제한할 수 있다는 차이점이 있다.
nodeSelector 는 파드를 특정 레이블이 있는 노드로 제한하는 매우 간단한 방법을 제공한다. 어피니티/안티-어피니티 기능은 표현할 수 있는 제약 종류를 크게 확장한다. 주요 개선 사항은 다음과 같다.
1. 어피니티/안티-어피니티 언어가 더 표현적이다. 언어는 논리 연산자인 AND 연산으로 작성된 정확한 매칭 항목 이외에 더 많은 매칭 규칙을 제공한다.
2. 규칙이 엄격한 요구 사항이 아니라 "유연한(soft)"/"선호(preference)" 규칙을 나타낼 수 있기에 스케줄러가 규칙을 만족할 수 없더라도, 파드가 계속 스케줄되도록 한다.
3. 노드 자체에 레이블을 붙이기보다는 노드(또는 다른 토폴로지 도메인)에서 실행 중인 다른 파드의 레이블을 제한할 수 있다. 이를 통
해 어떤 파드가 함께 위치할 수 있는지와 없는지에 대한 규칙을 적용할 수 있다.
Affinity를 사용하기 위해서는 아래와 같은 작업이 필요하다.
- node의 label 확인 (또는 추가)
- pod의 yaml에 spec.affinity 정의
아래는 샘플 yaml 파일이다
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/e2e-az-name
operator: In
values:
- e2e-az1
- e2e-az2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: k8s.gcr.io/pause:2.0
Affinity 방식은 아래 2가지 방법이 가능하다.
- requiredDuringSchedulingIgnoredDuringExecution: 파드가 노드에 스케줄되도록 반드시 규칙을 만족해야 하는 것
- preferredDuringSchedulingIgnoredDuringExecution: 스케줄러가 시도하려고는 하지만, 보증하지 않는 선호(preferences)
Operator는 다음이 가능하다.
- In, NotIn, Exists, DoesNotExist, Gt, Lt. NotIn, DoesNotExist
4. 다른 pod의 위치에 따라 node 선택하기 - podAffinity, podAntiAffinity
web, was 를 동일한 node에 위치시키고 싶을때는 어떻게 해야할까? 이때 사용할수 있는 것이 podAffinity/podAntiAffinity 이다.
(세부내용 추후 추가..)
5. 원하는 pod만 실행시키기 - Taint, Toleration
지금까지 node label을 사용해 pod가 node를 선택하는 방법을 설명했다. 이 방법의 단점은 pod에 nodeSelector 나 Affinity가 없다면 node label과 상관없이 해당 node에서 실행될 수 있다는 점이다. 따라서 pod의 spec과 상관없이 node에서 실행되는것을 방지할수 있는 방법이 필요하고, 이것이 taint와 toleration이다. node에 taint를 설정하면 toleration을 설정한 pod 외에는 해당 node에서 실행되지 않는다.
이를 위해서는 아래와 같은 작업이 필요하다.
- node에 taint를 설정
- pod의 spec.tolerations를 설정
아래는 node에 taint를 설정하는 방법이다.
kubectl taint nodes node1 key1=value1:NoSchedule
taint 는 key, value, effect로 이루어져 있으며, effect는 아래 3가지 경우가 가능하다.
- NoSchedule 이펙트가 있는 무시되지 않은 테인트가 하나 이상 있으면 쿠버네티스는 해당 노드에 파드를 스케줄하지 않는다.
- PreferNoSchedule 이펙트가 있는 무시되지 않은 테인트가 하나 이상 있으면 쿠버네티스는 파드를 노드에 스케쥴하지 않으려고 시도 한다
- NoExecute 이펙트가 있는 무시되지 않은 테인트가 하나 이상 있으면 파드가 노드에서 축출되고(노드에서 이미 실행 중인 경우), 노드에서 스케줄되지 않는다(아직 실행되지 않은 경우).
아래는 spec.tolerations의 예제이다.
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"