본문 바로가기

Container/Kubernetes

[K8S] Affinity & antiAffinity

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