Kubernetes 환경에 affinity, anti-affinity 적용하기

2021. 7. 4. 20:20개발/Kubernetes

728x90
반응형

개요

  • kubernetes 환경에서 작업을 하다보면 특정 node에 pod가 쏠리는 현상이 발생할 수 있다.
  • 이를 방지하기 위해서 affinity 설정을 추가할 수 있다.

Node isolation / restriction

Affinity를 설명하기 이전에 isolation과, restriction에 대해서 설명한다.

  • node object에 label을 추가하게 되면, 특정 노드 또는 노드 그룹에 pod가 배치되도록 할 수 있다.
  • node 자체에는 표준 label, annotation, taints들이 존재한다.
  • NodeRestriction admission plugin이 있는데, 이 plugin을 활성화하게 되면, 노드를 쉽게 격리시킬 수 있다.
  • 특정 node의 label을 사용하여 nodeSelector를 지정할 수 있다.

Affinity, Anti-affinity란

  • nodeSelector보다 확장된 방식으로, 특정 노드에 pod를 위치시키게 고정하거나 또는 제외시킬 수 있다.
  • 기존 nodeSelector의 경우에는 AND 조건으로 매칭을 지원하는 반면 affinity는 더 많은 매칭 규칙을 제공한다.
  • 엄격한 요구사항이 아닌 선호 (preference)로 설정이 가능하다. 따라서 조건을 만족 못시키더라도 POD가 계속 스케줄 될 수 있도록 한다.
  • 노드 자체의 레이블로도 가능하지만 해당 노드의 다른 pod의 레이블을 제한할 수도 있다. ( 이를 통해 어떤 pod가 함께 위치할 수 있도록 설정할 수 있다. )

Affinity 종류

1. node affinity

  • nodeSelector와 유사하다. 노드 레이블을 기반으로 POD를 스케줄할 수 있는 노드를 제한할 수 있다.
  • 현재는 requiredDuringSchedulingIgnoredDuringExecution ( hard mode ), preferredDuringSchedulingIgnoredDuringExecution (soft mode) 이 두 종류가 있다.

2. scheduling profile당 node affinity

  • 해당 내용은 node affinity plugin에 추가하는 형태로 pod 자체의 affinity 뿐만아니라 addedAffinity를 설정 가능하다. (추가 조건 같은 개념)
  • 하지만 이렇게 기본적으로 셋팅되면 까먹기도 쉽고, 예상치 못한 상황이 벌어질 수 있으므로 주의해서 사용하도록 하자.

Isolation vs Affinity

결국 pod가 어떤 node에 위치할 수 있는가에 대한 설정을 할 수 있으나 affinity가 좀 더 유연한 방식을 지원한다고 볼 수 있다. (하지만 단순한 설정만 필요하다면 node selector 방식으로 하는 것이 관리 측면에서 편할 것이라고 생각한다.)

실습

  1. Affinity가 어떤 내용인지는 개략적으로 이해했으니, 실제로 어떤 방식으로 동작이 되는지 한 번 살펴보자.
  2. 현재는 scheduling profile당 affinity는 따로 설정안하고, hard와 soft로 해보고 테스트를 진행해보도록 하자.
  3. node는 총 2대 준비하고 테스트를 진행해보자.

1. Multi node 환경 셋팅

# malgogi-test라는 cluster를 생성한다.
# docker runtime에서는 ingress addon을 사용할 수 없어 vm mode로 수행한다.
minikube start --nodes 2 --vm=true -p malgogi-test 

 # 이미 생성된 경우
 minikube node add 2 -p malgogi-test

 # ingress addon 설치 ( 굳이 할 필요 없습니다. )
 minikube addons enable ingress

 # node 확인
 kubectl get nodes

 # nginx ingress 확인 ( 굳이 할 필요 없습니다. )
 kubectl get pods -n kube-system

2. deployment 구성

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-world
  namespace: default
  labels:
    app: hello-world
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello-world
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values:
                      - hello-world
              topologyKey: kubernetes.io/hostname
      containers:
        - name: hello-world
          image: nginx
          resources:
            requests:
              cpu: 10m
              memory: 0.5G
          ports:
            - containerPort: 80
          livenessProbe:
            httpGet:
              path: /
              port: 80
            timeoutSeconds: 3
            initialDelaySeconds: 300
            failureThreshold: 3
            periodSeconds: 30

3. svc 구성

kind: Service
apiVersion: v1
metadata:
  name: hello-world-svc
  namespace: default
spec:
  selector:
    app: hello-world
  ports:
    - port: 80
      targetPort: 80
  type: NodePort

 

4. 결과 확인

4.1. hard mode (requiredDuringSchedulingIgnoredDuringExecution)

우선 우리는 아래와 같이 hard mode로 설정을 했다.

affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values:
                      - hello-world
              topologyKey: kubernetes.io/hostname

그리고 실행을 해보면

kubectl apply -f malgogi-test.yaml

아래와 같이 각각 node에 pod가 할당된 것을 볼 수 있다.

 

그렇다면 이제 3개로 pod를 늘려보면 어떻게 될까?

replica option 3개로 늘리고, 적용을 하면 다음과 같은 event를 볼 수 있다.

0/2 nodes are available: 2 node(s) didn't match pod affinity/anti-affinity, 2 node(s) didn't match pod anti-affinity rules.

4.2. soft mode (preferredDuringSchedulingIgnoredDuringExecution)

즉 affinity rule에 의해서 더이상 할당할 수 있는 node가 없기 때문에 아예 할당이 되지 않는다.

그렇다면 requiredDuringSchedulingIgnoredDuringExecution => preferredDuringSchedulingIgnoredDuringExecution로 변경해보자.
여러개의 값들이 다르므로 아래와 같이 설정을 변경해야한다.

      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values:
                        - hello-world
                topologyKey: kubernetes.io/hostname

이렇게 적용을 하고, 다시 실행해보면 3개가 할당이 되고, 4개로 늘리게 될 경우 각각 node당 2대씩 할당된 것을 볼 수 있다.

출처

728x90
반응형