Container Orchestration/Kubernetes

Kubernetes 어피니티

Somaz 2022. 5. 13. 17:06
728x90
반응형

overview

 

쿠버네티스 클러스터의 어피니티를 사용해 쿠버네티스 파드를 할당하는 방법을 공부하려고 한다.

 

쿠버네티스에서는 사용자가 파드를 어떤 노드에 배포할 것인지 아래의 기능을 통해 설정할 수 있다.

 

  • 노드 셀렉터(NodeSelector)
  • 어피니티(Affinity)
    • 노드 어피니티(Node Affinity)
    • 파드 어피니티(Pod Affinity)
    • 안티 어피니티(Anti Affinity)
  • 테인트& 톨러레이션(Taint)
  • 커든(Cordon)
  • 드레인(Drain)

 

1. NodeSelector

 

파드의 .spec 필드에 노드셀렉터를 이용하여 어떤 노드에서 실행될지 설정한다.

  • 가장 간단한 스케줄링 옵션임
  • Key-Value 쌍으로 설정한다.

우선 노드셀렉터를 적용하려면 원하는 노드의 노드레이블에 어떤 Key-Value 쌍이 있는지 확인해야 한다. 해당 명령어로 확인할 수 있다.

$ kubectl get nodes --show-lables
NAME      STATUS    ROLES    AGE     VERSION        LABELS
worker0   Ready     <none>   1d      v1.13.0        ...,kubernetes.io/hostname=worker0

 

 

Add a label to a Node

$ kubectl label nodes docker-desktop disktype=ssd
node/docker-desktop labeled

> kubectl get nodes --show-lables
NAME      STATUS    ROLES    AGE     VERSION        LABELS
worker0   Ready     <none>   1d      v1.13.0        ...,**disktype=ssd**,kubernetes.io/hostname=worker0
  • worker0에 disktype=ssd을 가지는 Key-Value쌍의 레이블 추가한다.

 

 

NodeSelector 예제

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd #위에서 추가한 노드레이블의 Key-Value 쌍을 입력

 

 

 

2. Affinity

 

affinity는 Pod를 특정 Node에 배포되도록 하는 정책이다. affinity는 Node를 기준으로 하는 Node affinity와, 다른 Pod가 배포된 위치(node) 를 기준으로 하는 Pod affinity 두 가지가 있다

nodeaffinity는 nodeselector와 비슷하게 node의 label 기반으로 pod를 스케줄링한다.

 

node affinity와 nodeselector를 함께 설정할 수도 있는데 이때는 두개의 조건을 모두 만족해야 스케줄링한다.

 

node affinity에는 두 가지 필드가 존재한다.

 

  • requiredDuringSchedulingIgnoredDuringExecution
  • : 스케줄링하는 동안 꼭 필요한 조건
  • preferredDuringSchedulingIgnoredDuringExecution
  • : 스케줄링하는 동안 만족하면 좋은 조건 (굳이 조건을 만족하지 않아도 된다.)

위의 두 조건으로 파드가 이미 스케줄링되어 있는 상태에서는 해당 노드의 조건이 바뀌어도 무시된다.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd            
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent

operator의 필드설명

required~필드와 preferred~필드에서 설정할 수 있는 operator 필드의 설명이다.

  • In
  • : .values[] 필드에 설정한 값 중 레이블에 있는 값과 일치하는 것이 하나라도 있는지 확인한다.
  • NotIn
  • : In과는 반대로 .values[] 필드에 있는 값 모두와 맞지 않는지 확인한다.
  • Exists
  • : .key필드에 설정한 값이 레이블에 있는지만 확인한다.
  • DoesNotExist
  • : Exists와 반대로 .key 필드 값이 없는지만 확인
  • Gt (Greater than)
  • : .values[] 필드에 설정된 값보다 큰 숫자형 데이터인지를 확인한다. (초과)
  • Lt (Lower than)
  • : .values[] 필드에 설정된 값보다 작은 숫자형 데이터인지를 확인한다. (미만)

 

Node affinity 

Node affinity는 Pod가 특정 node로 배포되도록 하는 기능이다.

 

예전에 label에서 설명했던 node selector 도 node의 label과 pod의 selector label이 매칭되는 node에만 배포하도록 하기 때문에, 사실상 Node affinity와 같은 기능을 한다 

 

Node affinity는 Hard affinity와 Soft affinity가 있다. Node affinity는 Pod가 조건이 딱 맞는 node 에만 배포되도록 하는 기능이고,

 

Soft affinity는 Pod가 조건에 맞는 node에 되도록(반드시가 아니라)이면 배포되도록 하는 기능이다. 앞에서 언급한 node selector는 Hard affinity에 해당한다. 

 

 

아래는 Pod 설정 YAML에서 node affinity를 적용한 예제이다. 

pod-with-node-affinity.yaml docs/concepts/configuration  
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

출처 https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity

 

Assigning Pods to Nodes

You can constrain a Pod so that it can only run on particular set of node(s). There are several ways to do this and the recommended approaches all use label selectors to facilitate the selection. Generally such constraints are unnecessary, as the scheduler

kubernetes.io

 

 

 

requiredDuringSchedulingIgnoredDuringExecution는 Hard affinity 정의 이다. nodeSelectorTerms 부분에, matchExpressions을 사용하여, label set-based selector 문법을 이용하면 된다.

 

위의 예제는 node에 label key “kubernetes.io/e2e-az-name” 의 값이 eze-az1 이나 eze-az2 인 node를 선택하도록 하는 설정이다. 

 

Node affinity는 여러 affinity를 동시에 적용할 수 있는데, 위의 예제에서는 두 개의 Affinity를 정의하였다.

 

두번째 Affinity는 Soft affinity로, preferredDuringSchedulingIgnoredDuringExecution: 으로 정의한다.

 

Soft affinity는 앞서 언급한것과 같이 조건에 맞는 node로 되도록이면 배포될 수 있도록 그 node로 배포 선호도를 주는 기능이다.

 

이때 weight 필드를 이용해서 선호도를 조정할 수 있는데, weight은 1~100이고, node 의 soft affinity의 weight 값들을 합쳐서 그 값이 높은 node를 우선으로 고려하도록 우선 순위를 주는데 사용할 수 있다. 

 

특정 Node로 배포되게 하는 Affinity 설정도 있지만, 반대로 특정 Node로 배포되는 것을 피하도록 하는 AntiAffinity라는 설정도 있다.

 

nodeAffinity 대신 nodeAntiAffinity라는 Notation을 사용하면 되고, Affinity와는 다르게 반대로, 조건에 맞는 Node를 피해서 배포하도록 된다.. 

 

Inter-Pod affinity

Node affinity가 node의 label을 기준으로 Pod가 배포될 node는 선택한다면, Inter pod affinity는 기존에 배포된 Pod를 기준으로 해서, 배포될 node를 결정한다. 

 

Pod affinity는 데이타 베이스의 Master / Slave pod 가 다른 node 에 배포되도록 하기 위해서 master pod가 배포된 node를 피해서 배포하게 한다던가.

 

클러스터 시스템에서 클러스터를 이루는 각각의 Pod가 다른 node에 배포도록 하는 등에 전략에 사용할 수 있다. 


Node affinity와 마찬가지로 Hard affinity와 Soft affinity가 있다.

 

Node affinity아 마찬가지로 requiredDuringSchedulingIgnoredDuringExecution 로 hard affinity를,preferredDuringSchedulingIgnoredDuringExecution로 soft affinity를 정의한다.

 

Node를 선택할때 Inter pod affinity는 node affinity와 다르게 topology key 라는 것을 사용한다. 

 

Pod affinity는 Pod affinity에 의해서 해당 node를 선택한 후에, 그 node의 label을 하나 선택한다.

 

선택하는 label은 topology key로 지정하는데, 이 topology key에 매칭 되는 node 들을 배포 대상으로 선택한다.

 

Node affinity와 마찬가지로 특정 Pod와 같이 배포되는 것을 피하는 AntiAffinity 도 있다. Pod affinity는 podAffinity라는 notation을 사용하고, Anti affinity는 PodAntiAffinity라는 Notation을 사용한다. 

 

개념이 복잡하기 때문에 아래 예제를 보면서 이해해보도록 하자



apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: security
            operator: In
            values:
            - S1
        topologyKey: cloud/zone
  containers:
  - name: with-pod-affinity
    image: k8s.gcr.io/pause:2.0

출처  https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity

 

Assigning Pods to Nodes

You can constrain a Pod so that it can only run on particular set of node(s). There are several ways to do this and the recommended approaches all use label selectors to facilitate the selection. Generally such constraints are unnecessary, as the scheduler

kubernetes.io

 

위와 같이 Pod 설정 파일이 있다고 했을때, 배포는 다음 그림과 같다. 

PodAffinity에서 requiredDuringSchedulingIgnoredDuringExecution 로 Hard affinity를 정의하였다.

 

이렇게 되면, 기존에 배포된 Pod 중에서, key가 “security”이고, value가 “S1”인, Pod1이 배포된 node인 Node 1을 기준으로 하는데, topologyKey로 정의된 cloud/zone label의 값”z1”을 기준 Node인 Node 1에서 읽어서,

 

Node 들 중에 label이 “cloud/zone=z1” 인 Node 들만을 후보로 선택해서, Node 1, Node 2 를 배포 가능 Node 로 선택한다.

Inter-Pod affinity 예제

다른 예제를 보자. 아래 예제는 redis-cache Pod를 3개 배포하는데, 각각 다른 Node에 분산되서 배포되도록 하는 예제이다. 

apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis-cache
spec:
  selector:
    matchLabels:
      app: store
  replicas: 3
  template:
    metadata:
      labels:
        app: store
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - store
            topologyKey: "kubernetes.io/hostname"
      containers:
      - name: redis-server
        image: redis:3.2-alpine

출처 https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity

 

Assigning Pods to Nodes

You can constrain a Pod so that it can only run on particular set of node(s). There are several ways to do this and the recommended approaches all use label selectors to facilitate the selection. Generally such constraints are unnecessary, as the scheduler

kubernetes.io

PodAntiAffinity를 사용한 예로, requiredDuringSchedulingIgnoredDuringExecution 를 이용한 Hard affinity이다.

 

Pod의 label이 “app=store”가 있는 Pod가 배포되어 있는 Node 중에서 topologyKey가 kubernetes.io/hostname (쿠버네티스에서 자동으로 미리 저장하는 label로 Node의 이름을 정의하는 label 이다.) 로 되어 있기 때문에,

 

node의 kubernetes.io/hostname label 값이 이 Node와 다른 Node를 배포 타겟으로 설정한다.

 

즉 “app=store” 이름으로 배포된 Pod가 없는 Node에 배포하는 설정이다.

 

이 Redis pod는 “app=store” 라는 label을 가지고 있는 Pod이기 때문에,

 

이미 Node에 이 Redis Pod가 배포되어 있으면, 그 Node에는 배포되지 않기 때문에 Redis Pod가 배포되지 않은 다른 Node에 중첩되서 배포되지 않도록 해준다. 



 

이 상태에서 nginx 서버를 배포해보자. Nginx pod를 3개 배포하는데, 각각을  서로 다른 node에 배포하도록 하되, 대신 redis가 배포된 node에 배포하도록 하는 설정이다.  

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
spec:
  selector:
    matchLabels:
      app: web-store
  replicas: 3
  template:
    metadata:
      labels:
        app: web-store
    spec:
      affinity:
        podAntiAffinity: 
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app    #----------------------------①
                operator: In
                values:
                - web-store #----------------------------②
            topologyKey: "kubernetes.io/hostname"
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app    #----------------------------③
                operator: In
                values:
                - store     #----------------------------④
            topologyKey: "kubernetes.io/hostname" #------⑤
      containers:
      - name: web-app
        image: nginx:1.16-alpine

출처  https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity

 

Assigning Pods to Nodes

You can constrain a Pod so that it can only run on particular set of node(s). There are several ways to do this and the recommended approaches all use label selectors to facilitate the selection. Generally such constraints are unnecessary, as the scheduler

kubernetes.io

예시 템플릿 설명

① : 안티어피니티를 설정할 label의 key값으로 app을 설정하였다.

 

② : ①에서 설정한 key값에 대응되는 value값으로 web-store를 설정하였다.

⇒ 즉 레이블이 app: web-store 로 설정된 파드가 속한 노드를 피해서 파드를 배포한다는 설정이다.

 

③ : 파드어피니티를 설정할 label의 key값으로 app을 설정하였다.

 

④ : ③에서 설정한 key값에 대응되는 value값으로 store를 설정하였다.

⇒ 즉 레이블이 app: store 로 설정된 파드가 속한 노드를 찾아서 함께 파드를 배포한다는 설정이다.

 

⑤ : topologyKey는 노드의 레이블을 이용하여 파드 어피니티와 안티 어피니티를 설정할 수 있는 또 하나의 기준이다.

  • spec.affinity 필드에 podAffinity, podAntiAffinity를 사용한다는 점 외에는 ****노드 어피니티의 설정방법과 거의 같다.

 

먼저 podAntiAffinity로 “app:web-store” Pod가 배포되어 있는 node를 찾은 후에, 그 Node이 kubernetes.io/hostname 를 topologyKey로 해서, 그 node들을 제외한다.

 

즉 “app:web-store” 라벨을 가지고 있는 Pod 들이 배포된 Node를 제외하는 설정이다. 그런데, 이 Pod는 label을 “app:web-store” 라벨을 가지도록 되어 있기 때문에,

 

이 Pod가 배포되어 있는 Node에 배포하지 말고, 다른 Node에 분산해서 배포하라는 내용이다.

 

여기에 PodAffinity로 “app:store”인 Pod가 배포되어 있는 Node에서 topologyKey로 “kubernetes.io/hostname”을 사용하였기 때문에, “app:store”인 라벨을 가지고 있는 Pod가 배포되어 있는 Node를 찾아서 배포하라는 내용이다.

 

다시 말해서 “app:store”인 Pod와 같은 Node에 배포하라는 의미이다. 

 

아래는 실행결과 인데, web-server가 각각 다른 Node에 겹치지 않고 분리되서 부탁되어 있는 것을 볼 수 있고,

 

redis-cache Pod도 역시 서로 다른 Node에 겹치지 않게 배포되어 있는 것을 확인할 수 있다. 그리고 web-server와 redis-cache pod들은 하나씩 같은 Node에 배포된것을 확인할 수 있다. 

NAME                           READY     STATUS    RESTARTS   AGE       IP           NODE
redis-cache-1450370735-6dzlj   1/1       Running   0          8m        10.192.4.2   kube-node-3
redis-cache-1450370735-j2j96   1/1       Running   0          8m        10.192.2.2   kube-node-1
redis-cache-1450370735-z73mh   1/1       Running   0          8m        10.192.3.1   kube-node-2
web-server-1287567482-5d4dz    1/1       Running   0          7m        10.192.2.3   kube-node-1
web-server-1287567482-6f7v5    1/1       Running   0          7m        10.192.4.3   kube-node-3
web-server-1287567482-s330j    1/1       Running   0          7m        10.192.3.2   kube-node-2

 

 

topologyKey

topologyKey는 해당 필드에 설정된 Key:(any value)의 레이블 쌍을 가지는 노드에 대해서만 균등한 분배를 한다.

  • 위의 예제에서는 kubernetes.io/hostname이 Key값에 해당한다. 즉 kubernetes.io/hostname:(any value)로 노드를 찾음.

실제 쿠버네티스 클러스터를 운영하다보면 여러가지 기준으로 스케줄링을 해야할 경우가 생기는데 topologyKey를 이용하여 스케줄링을 쉽게 설정할 수 있다.

 

3. Taint & Toleration

 

특정 노드에 테인트를 설정할 수있는데 테인트를 설정하게된 노드에는 파드들을 스케줄링 하지 않는다.

  • 테인트를 설정한 노드에 파드들을 스케줄링 하려면 톨러레이션(toleration)을 설정해야함.

테인트와 톨러레이션은 주로 노드를 특정 역할만 하도록 만들때 자주 사용한다.

테인트는 크게 키, 값, 효과 세가지로 구성할 수 있다.

$ kubectl taint nodes 노드이름 키=값:효과

# 노드에 테인트 설정
$ kubectl taint nodes docker-desktop key01=value01:NoSchedule
node/docker-desktop tainted

# 노드의 테인트 확인 
$ kubectl describe nodes docker-desktop$ kubectl taint nodes 노드이름 키=값:효과

 

테인트 삭제는 아래의 명령어로 할수 있다.

$ kubectl taint nodes 노드이름 키=값:효과-

 

테인트가 설정된 노드에 파드를 실행시키려면 톨러레이션을 설정해야 한다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kubernetes-simple-app
  labels:
    app: kubernetes-simple-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kubernetes-simple-app
  template:
    metadata:
      labels:
        app: kubernetes-simple-app
    spec:
      containers:
      - name: kubernetes-simple-app
        image: arisu1000/simple-container-app:latest
        ports:
        - containerPort: 8080
      tolerations:
      - key: "key01"
        value: "value01"
        operator: "Equal"
        effect: “NoSchedule"
  • tolerations의 필드의 key와 value는 테인트에 설정했던 값을 적어준다.

 

tolerations필드 설명

톨러레이션에서 설정할수 있는 설정값에 대한 설명이다.

  • key : 테인트를 설정할 때 사용했던 key값을 적어준다.
    • 첫 문자는 영문이나 숫자로 시작해야한다.
    • 최대 253자까지 작성할 수 있다.
  • value : 테인트를 설정할 때 사용했던 value값을 적어준다.
    • 최대 63자까지 작성할 수 있다.
  • operator : 톨러레이션을 어떠한 조건에 의해 설정될 것인지 선택한다.
    • Equal : key, value, effect 필드 값이 테인트의 설정값과 모두 같은지 확인함.
    • Exists : 어떤 테인트 설정이 있든 파드를 스케줄링해서 실행한다.
  • effect : 파드를 스케줄링 할 조건을 설정한다.
    • NoSchedule : 톨러레이션 설정이 없다면 스케줄링 하지 않는다.
    • PreferNoSchedule : 톨러레이션 설정이 없다면 스케줄링 하지 않는다.
      하지만! 클러스터 안의 자원이 부족하다면 테인트를 설정한 노드에서도 파드를 스케줄링 할 수있다.
    • NoExecute : 톨러레이션 설정이 없다면 스케줄링 하지 않고 기존에 배포되어 있는 파드들도 톨러레이션 설정이 없다면 종료시킨다.

 

4. cordon

 

커든(cordon)은 특정 노드에 추가로 파드를 스케줄링하지 않게하는 명령이다.

 

커든 설정은 아래의 명령어로 설정할 수 있다.

 

$ kubectl cordon 노드이름

 

# docker-desktop노드에 커든설정
$ kubectl cordon docker-desktop

# 커든이 설정되었으므로 파드는 Pending 상태로 스케줄링되지 않는다.
$ kubectl scale deploy kubernetes-simple-app --replicas2

 

커든 설정을 제거할면 아래의 명령어로 설정할 수 있다.

$ kubectl uncordon 노드이름

 

drain

드레인(drain)은 지정된 노드에 있는 모든 파드들을 다른 노드로 이동시키는 명령이다.

  • 드레인 설정은 주로 해당 노드를 유지보수(커널 업그레이드, 하드웨어 유지 관리 등)를 하거나 Managed로 사용중인 경우는 해당 노드를 반납하고자 할 때 주로 사용한다.

 

드레인의 특징

  • 드레인은 기본적으로 graceful하게 파드들을 종료시킨다. (즉시 종료되는 것이 아니고 기존의 작업을 정리한 후 종료함)
  • DaemonSet으로 실행한 파드들에 드레인 설정을 하려면 --ignore-daemonsets=true 옵션과 함께 명령을 실행해야 한다.
  • 컨트롤러를 이용하지 않는 파드들에도 드레인 설정을 할 수없다. 하려면 --force 옵션과 함께 명령을 실행해야 한다.

드레인 설정은 아래의 명령어로 설정할 수 있다.

 

$ kubectl drain 노드이름
  • 드레인 설정을 한 노드의 파드들은 클러스터 안에 존재하는 노드로 스케줄링에 설정되어있는 스코어링에 따라 스케줄링된다.
 
 

 

참고문서

 

https://kubernetes.io/ko/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity

 

노드 어피니티를 사용해 노드에 파드 할당

이 문서는 쿠버네티스 클러스터의 특정 노드에 노드 어피니티를 사용해 쿠버네티스 파드를 할당하는 방법을 설명한다. 시작하기 전에 쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이

kubernetes.io

 

https://kubernetes.io/ko/docs/concepts/scheduling-eviction/assign-pod-node

 

노드에 파드 할당하기

특정한 노드(들) 집합에서만 동작하도록 파드를 제한할 수 있다. 이를 수행하는 방법에는 여러 가지가 있으며 권장되는 접근 방식은 모두 레이블 셀렉터를 사용하여 선택을 용이하게 한다. 보통

kubernetes.io

 

 

728x90
반응형