Node Affinity는 Kubernetes에서 Pod의스케줄링 제약 조건을 정의하는 메커니즘
특정 조건을 가진 노드에만 Pod이 스케줄링되도록 설정
노드의 특정 집합에만 pod를 스케줄하도록 지시.
- nodeSelector: selector filed에 명시된 모든 LABEL이 포함되어야 배치됨
- nodeAffinity: 특정 노드에만 Pod가 실행되도록 유도
- spec.affinity.nodeAffinity 필드에서 설정된다.
- 요구조건: 조건이 맞는 파드에 가중치를 줘서 가중치가 가장 높은 파드에 배치
- 필수 조건: requiredDuringSchedulingIgnoredDuringExecution
- 조건이 만족되지 않으면 Pod는 해당 노드에 스케줄링되지 않음
- 노드 조건이 나중에 바뀌더라도 Pod는 계속 실행됨(스케줄링 시점에만 고려)
- 선호 조건: preferredDuringSchedulingIgnoredDuringExecution
- 조건을 만족하는 노드에 우선적으로 스케줄링하지만, 조건을 만족하지 않아도 다른 노드에 스케줄링 가능
- IgnoredDuringExecution의 의미는 운영중에 node affinity에 어떤 변화가 발생하더라도 일단 schedule되면 impact가 없다는 것을 의미한다.
- Planned Affinity: requiredDuringScheduleingRequiredDuringExecution
- Affinity rule에 어긋나면 바로 제거한다.
root@master:~/pod-scheduling# kubectl label node node2.example.com disktype=ssd
node/node2.example.com labeled
root@master:~/pod-scheduling# kubectl get nodes -L gpu,disktype
NAME STATUS ROLES AGE VERSION GPU DISKTYPE
master.example.com Ready control-plane 8d v1.29.3
node1.example.com Ready <none> 8d v1.29.3 true
node2.example.com Ready <none> 8d v1.29.3 true ssd
root@master:~/pod-scheduling# cat tensorflow-gpu-ssd.yaml
apiVersion: v1
kind: Pod
metadata:
name: tensorflow-gpu-ssd
spec:
containers:
- name: tensorflow
image: tensorflow/tensorflow:nightly-jupyter
ports:
- containerPort: 8888
protocol: TCP
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- {key: disktype, operator: Exists} # key가 disktype이 존재하는 노드
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 10
preference:
matchExpressions:
- {key: gpu, operator: In, values: ["true"]} # 조건이 맞으면 10점 누적
- {key: disktype, operator: In, values: ["ssd"]} # 조건이 맞으면 10점 누적
root@master:~/pod-scheduling# kubectl apply -f tensorflow-gpu-ssd.yaml
pod/tensorflow-gpu-ssd created
# disktype이 존재하면서, 가중치가 20인 Node2에서 실행되는 것 확인
Every 2.0s: kubectl get pod -o wide master.example.com: Mon Mar 25 00:46:12 2024
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
tensorflow-gpu-ssd 1/1 Running 0 3m56s 10.36.0.1 node2.example.com <none> <none>
# 레이블 초기화
root@master:~/pod-scheduling# kubectl label node node{1,2}.example.com disktype-
label "disktype" not found.
node/node1.example.com not labeled
node/node2.example.com unlabeled
root@master:~/pod-scheduling# kubectl label node node{1,2}.example.com gpu-
node/node1.example.com unlabeled
node/node2.example.com unlabeled
Node Affinity와 Taints/Tolerations 비교
Node Affinity | Taints와 Tolerations | |
적용 방식 | 특정 조건의 노드를 선호하거나 필수 조건으로 설정 | 특정 노드에 대해 Pod 배치 제한 또는 허용 |
주요 목적 | Pod 배치를 위한 선호 조건 설정 | 노드에 배치된 Pod을 강제 제한 또는 예외 허용 |
복잡성 | 조건식으로 복잡한 조건 설정 가능 | 단순한 제약 및 예외 설정 |
# Pod 스케줄링
- podAffinity: pod를 더 가깝게 배치하기 (같은 노드에 붙여서 실행)
- podAntiAffinity: pod를 더 멀리 배치하기 (다른 노드에서 실행)
- PodAffinity 요구조건
- 엄격한 요구: requiredDuringSchedulingIgnoredDuringExecution
- 선호도 요구: preferredDuringSchedulingIgnoredDuringExecution
- topologyKey
- 노드 label을 이용해 pod의 Affinity와 AntiAffinity를 설정할 수 있는 또 하나의 기준
- 쿠버네티스는 Pod를 스케줄링 할 때 먼저 pod의 라벨을 기준으로 대상 노드를 찾고, 이후 topologyKey필드를 확인해 해당 노드가 원하는 노드인지 확인.
# backend 파드 실행
[master ~]$kubectl run backend -l app=backend --image=busybox -- sleep 999999
pod/backend created
[master ~]$kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
backend 1/1 Running 0 13s app=backend
[master ~/Affinity]$kubectl get pods --show-labels -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
backend 1/1 Running 0 14m 10.4.0.50 gke-cluster-1-default-pool-697aeef7-5nx7 <none> <none> app=backend
root@master:~/pod-scheduling# cat pod-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 5
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: backend
topologyKey: kubernetes.io/hostname
containers:
- name: main
image: busybox
args:
- sleep
- "99999"
root@master:~/pod-scheduling# kubectl apply -f pod-affinity.yaml
deployment.apps/frontend created
Every 2.0s: kubectl get pods -o wide master.example.com: Fri Apr 12 16:23:13 2024
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
backend 1/1 Running 0 2m55s 10.36.0.1 node2.example.com <none> <none>
frontend-b8b7bcd6f-5x4z9 1/1 Running 0 28s 10.36.0.5 node2.example.com <none> <none>
frontend-b8b7bcd6f-8bjkg 1/1 Running 0 28s 10.36.0.2 node2.example.com <none> <none>
frontend-b8b7bcd6f-9jkqv 1/1 Running 0 28s 10.36.0.4 node2.example.com <none> <none>
frontend-b8b7bcd6f-f6t6z 1/1 Running 0 28s 10.36.0.6 node2.example.com <none> <none>
frontend-b8b7bcd6f-phtf9 1/1 Running 0 28s 10.36.0.3 node2.example.com <none> <none>
# 파드 5개 전부 node2에서 실행 중인 것 확인 가능
# AntiAffinity
root@master:~/pod-scheduling# cat pod-antiaffinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 5
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
affinity:
podAntiAffinity: # 실행되고 있는 노드와 분리시켜주세요
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: backend
topologyKey: kubernetes.io/hostname
containers:
- name: main
image: busybox
args:
- sleep
- "99999"
root@master:~/pod-scheduling# kubectl apply -f pod-antiaffinity.yaml
deployment.apps/frontend created
Every 2.0s: kubectl get pods -o wide master.example.com: Fri Apr 12 16:33:19 2024
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
backend 1/1 Running 0 13m 10.36.0.1 node2.example.com <none> <none>
frontend-55f7fddc89-2lrw5 1/1 Running 0 71s 10.47.0.3 node3.example.com <none> <none>
frontend-55f7fddc89-6dm7c 1/1 Running 0 71s 10.47.0.2 node3.example.com <none> <none>
frontend-55f7fddc89-74tzr 1/1 Running 0 71s 10.44.0.1 node1.example.com <none> <none>
frontend-55f7fddc89-ngswg 1/1 Running 0 71s 10.47.0.1 node3.example.com <none> <none>
frontend-55f7fddc89-qtg7n 1/1 Running 0 71s 10.44.0.2 node1.example.com <none> <none>
# 2번 노드를 제외하고 실행시켜 준다.
# 실습
Create a new deployment named red with the nginx image and 2 replicas, and ensure it gets placed on the controlplane node only.
$ kubectl create deployment red --image=nginx --replicas=2 --dry-run=client -o yaml
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
resources: {}
status: {}
$ kubectl create deployment red --image=nginx --replicas=2 --dry-run=client -o yaml > red.yaml
controlplane ~ ➜ cat red.yaml
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:
affinity: # 필요 부분 수정
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: Exists
containers:
- image: nginx
name: nginx
resources: {}
status: {}
반응형
'Container > Kubernetes' 카테고리의 다른 글
[K8S] cordon & drain (0) | 2024.04.13 |
---|---|
[K8S] Taint & Toleration (0) | 2024.04.12 |
[K8S] Secret (0) | 2024.03.20 |
[K8S] Canary Deployment (0) | 2024.03.20 |
[K8S] Annotation (0) | 2024.03.17 |