Kubernetes Pod 배치전략, Affinity에 대해 이해하고 실습해보기

Bakumando·2022년 5월 29일
1

Kubernetes

목록 보기
16/17

들어가기에 앞서...


0. 블로깅 목적

  • Affinity의 정의 및 종류를 알아보고 이해한다.
  • Pod 배치 전략 중 nodeAffinity에 대해 이해하고 실습한다.
  • Pod 배치 전략 중 podAffinity에 대해 이해하고 실습한다.
  • Pod 배치 전략 중 podAntiAffinity에 대해 이해하고 실습한다.

1. Affinity의 정의 및 종류를 알아보고 이해한다.

1) Affinity?

  • Affinity란 선호도란 뜻이다. Pod는 항상 Node에서 띄워져야 하는데, 이러한 배치를 함에 있어 선호하는 Node나 Pod를 설정할 수 있게끔 하는 리소스이다.

2) Affinity 종류

  • nodeAffinity는 어떤 Node를 선호할 것인가? 에 관련한 리소스이다. 즉, Pod를 배치할 때 어떤 Node에 스케쥴링할지 설정을 해준다.
  • podAffinity는 Pod가 배치될 때, 실행 중인 Pod들 중에 선호하는 Pod를 찾아 해당 Pod와 동일한 Node로 배치하는 걸 설정해준다.
  • podAnitAffinity는 실행 중인 Pod들 중에, 선호하지 않은 Pod가 실행 중인 Node는 피해서 배치를 하겠다는 걸 설정해준다.

2. Pod 배치 전략 중 nodeAffinity에 대해 이해하고 실습한다.

1) nodeAffinity?

  • 선호하는 노드를 설정하는 방법으로, nodeSelector 보다 확장된 Label Selector 기능을 지원한다. 그래서 좀 더 실무환경에 적합한 배Pod 배치 전략이다.
  • matchExpressions 사용 가능하다. (In, NotIn, Exists, DoesNotExist, Gt, Lt 등의 옵션이 있다.)
  • 여러 유즈케이스에 활용 가능한 2가지 옵션이 있는데. Hard, Soft로 나뉜다. 매우 조건이 길기 때문에 2등분해서 의미를 이해하면 좋다.
    • 반드시 충족해야 하는 조건 (Hard)
      • requiredDuringSchedulingIgnoredDuringExecution
        • 즉, 스케쥴링되는 워크로드에는 필수 조건이고, 실행 중인 워크로드는 조건을 무시한다는 의미이다.
    • 선호하는 조건 (Soft)
      • preferredDuringSchedulingIgnoredDuringExecution
        • 즉, 스케쥴링되는 워크로드에는 선호 조건이고, 실행 중인 워크로드는 조건을 무시한다는 의미이다.
    • 용어 설명:
      • IgnoredDuringExecution: 실행 중인 워크로드에 대해서는 해당 규칙을 무시한다.
      • RequiredDuringExecution: 위와 반대개념으로 실행 중인 워크로드에 대해서 해당 규칙을 반드시 필요로 한다.


2) nodeAffinity 실습

  • 말했듯 minikube 멀티 클러스터 환경에서 진행된다.
  • selector 경로에 node-affinity 디렉토리를 만든다.
  • yml, sh 파일을 준비한다.

required.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-affinity-required
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      name: hello
      labels:
        app: hello
    spec:
      containers:
      - name: nginx
        image: nginxdemos/hello:plain-text
        ports:
        - name: http
          containerPort: 80
          protocol: TCP
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: team
                operator: In
                values:
                - blue
                - red
  • nodeAffinity 룰만 잘 이해하면 된다.
  • 스케쥴링 될때는 필수적으로 적용될 조건이지만 실행 중인 노드에는 무시한다.
  • operator In은 or 연산자라고 이해하면 된다. 즉, key가 team이고, value가 blue 혹은 red인 조건을 필수로 한다.

preferred.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: node-affinity-preferred
spec:
  replicas: 4
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      name: hello
      labels:
        app: hello
    spec:
      containers:
      - name: nginx
        image: nginxdemos/hello:plain-text
        ports:
        - name: http
          containerPort: 80
          protocol: TCP
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 40
            preference:
              matchExpressions:
              - key: team
                operator: In
                values:
                - green
  • 여기도 nodeAffinity 룰만 잘 이해하면 된다.
  • 스케쥴링 될때는 선호되는 조건이지만 실행 중인 노드에는 무시한다.
  • operator In은 or 연산자라고 이해하면 된다. 즉, key가 team이고, value가 green인 조건을 선호 한다.
  • preferred는 weight가 있고, 각각에 matchExpressions을 넣는 구조로 되어 있다.
    • weight는 0~100 사이의 값을 가질 수 있다. 각각의 rule을 기반으로 weight를 설정해서 점수를 줌으로써 Pod가 어떤 Node에 최종 배치될지 우선순위를 설정하는 것으로 이해하면 된다.
    • 즉, weight는 여러개 설정할 수 있고, 점수를 매겨 우선순위를 결정하는 데 쓰이는 것이다.
    • 근데 참고로 weight는 이번 실습에 특별히 적용되는 건 없다. 그냥 preferred 구조를 이해시키기 위한 설명 용도로만 언급하였다.

set-node-labels.sh

kubectl label node minikube --overwrite team=green
kubectl label node minikube-m02 --overwrite team=red
kubectl label node minikube-m03 --overwrite team=blue
  • 특정 node 각각에 label(key-valu)를 지정하는 sh 스크립트이다.

실습을 진행해보자.

  • 싱글 노드 환경이라면 현재 환경을 제거하고, minikube start --nodes 3 --driver=docker 커맨드로 3개의 멀티 노드 환경을 구성해준다.
  • node-affinity 디렉토리에서 실습을 진행한다.
  • 참고로 권한 문제가 발생하면 다음 커맨드를 입력한다.
    • chmod +x set-node-labels.sh

  • 우선 watch 모드 실행을 위해 2개의 터미널을 새로 띄운다.
  • watch kubectl get pod
    • 실습 중에 1번 watch 터미널이라고 하겠다.
    • Pod의 상태를 실시간으로 볼 수 있다. 아직 아무것도 존재하지 않는다.
  • watch kubectl get nodes --label-columns team
    • 실습 중에 2번 watch 터미널이라고 하겠다.
    • Node의 상태를 실시간으로 볼 수 있. 특정 label 컬럼도 볼 수 있도록 옵션을 주었다.
    • 현재 node가 3종류 임을 알 수 있다. (minikube, minikube-m02, minikube-m03)
    • 다만 아직 label의 team key에 할당된 value는 없다.

  • set-node-labels.sh
    • 일단 Node label 세팅을 진행해줘야 한다.
    • 2번 watch 터미널에 TEAM 레이블이 업데이트 된 걸 볼 수 있다.

  • kubectl apply -f required.yml
    • apply하면 1번 watch 터미널에 새로운 Pod들이 생성 및 실행된 걸 확인할 수 있다.
    • 각각이 어떤 Node에 할당되었는지도 보인다. Node의 label 설정을 blue 혹은 red로만 해뒀기 때문에, minikube 02, minikube 03에만 할당이 된 것이다.
    • kubectl delete -f required.yml로 제거해준다.

  • kubectl apply -f preferred.yml
    • apply하면 1번 watch 터미널에 4개의 Pod들이 생성 및 실행된 걸 확인할 수 있다.
    • Node의 label 설정을 green으로만 해뒀기 때문에, minikube에만 할당이 되었다.
    • kubectl delete -f preferred.yml로 제거해준다.

3. Pod 배치 전략 중 podAffinity에 대해 이해하고 실습한다.

1) podAffinity?

  • 선호하는 파드를 설정하는 방법으로, 사용법은 nodeAffinity와 거의 동일하다.
  • 역시 여러 유즈케이스에 활용 가능한 2가지 옵션을 제공하며, Hard, Soft로 나뉜다. nodeAffinity와 동일하여 자세한 설명은 생략한다.
    • 반드시 충족해야 하는 조건 (Hard)
      • requiredDuringSchedulingIgnoredDuringExecution
    • 선호하는 조건 (Soft)
      • preferredDuringSchedulingIgnoredDuringExecution
  • 그러나 podAffinity의 가장 중요한 부분은 다음의 개념이다.
  • 토폴로지 키 (Topology Key)
    • 쿠버네티스 Node에 설정된 Label에 대해서, Label Selector를 수행할 노드의 범위를 결정한다.
    • Topology Key는 노드의 레이블 key를 설정하는 것이며, 어떠한 값을 key name으로 넣어도 상관없지만 다음과 같은 3가지 key를 주로 쓴다.
      • 노드 단위: kubernetes.io/hostname
      • 존 단위: topology.kubernetes.io/zone
        • AZ(Availablity Zone: 가용영역)
      • 리전 단위: topology.kubernetes.io/region
        • 말그대로 지역 (서울, 도쿄 등)

      => AWS EKS 기준으로 3가지 key는 노드를 띄우면 그냥 기본 설정 된다. (단, minikube에서는 노드 단위 key만 기본 설정이 되고, 존과 리전은 안된다.)



2) podAffinity 실습

  • minikube 멀티 클러스터 환경에서 진행된다.
  • selector 경로에 pod-affinity 디렉토리를 만든다.
  • yml, sh 파일을 준비한다. (sh는 기존 nodeAffiniy와 같다.)

mysql.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      name: mysql
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mariadb:10.7
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: bakumando
        - name: MYSQL_DATABASE
          value: kubernetes
        ports:
        - name: http
          containerPort: 3306
          protocol: TCP
  • podAffinity 테스트를 위해 특정 node에 Pod 하나를 띄울 필요가 있다. mysql Pod를 준비해보았다.

required.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity-required
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      name: hello
      labels:
        app: hello
    spec:
      containers:
      - name: nginx
        image: nginxdemos/hello:plain-text
        ports:
        - name: http
          containerPort: 80
          protocol: TCP
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - mysql
            topologyKey: kubernetes.io/hostname
            # topologyKey: topology.kubernetes.io/zone
            # topologyKey: topology.kubernetes.io/region
  • nodeAffinity와 비교하면 podAffinity를 사용했다는 점과 topologyKey가 추가되었다는 점이 다르다.
  • 조건은 app key가 mysql인 것을 필수로 한다는 정의이다.
  • topologyKey로는 노드 단위의 범위 설정만 적용을 하였다.

preferred.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-affinity-preferred
spec:
  replicas: 4
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      name: hello
      labels:
        app: hello
    spec:
      containers:
      - name: nginx
        image: nginxdemos/hello:plain-text
        ports:
        - name: http
          containerPort: 80
          protocol: TCP
      affinity:
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - mysql
              topologyKey: kubernetes.io/hostname
              # topologyKey: topology.kubernetes.io/zone
              # topologyKey: topology.kubernetes.io/region
  • required.yml와 비슷하게 정의되어있다.
  • weight는 마찬가지로 실습에 특별히 활용하진 않을 것이다.

실습을 진행해보자.

  • 기본 환경구성과 초반 세팅은 nodeAffinity 테스트와 같다.
  • sh 파일 실행까지 동일하게 진행하고 다음 순서를 따르면 된다.

  • kubectl apply -f mysql.yml
    • mysql.yml을 apply 해준다. 1번 watch 터미널에 생성되었다.

  • kubectl get node --show-labels
    • 각각의 노드들이 가진 label이 출력되는 걸 확인할 수 있다.
    • minikube 노드들 모두 kubernetes.io/hostname를 가지고 있음을 알 수 있다.

  • kubectl apply -f required.yml
    • 1번 watch 터미널을 보면 Pod가 추가되었다.
    • required.yml에 동일한 노드 범위로 배치되게끔 설정을 해두었기 때문에, label이 app:mysql인 Pod가 배치되어 있는 노드에 required의 Pod들이 전부 생성된 것이다.
    • 테스트가 잘 성공했으니, kubectl delete -f required.yml 제거해준다.

  • kubectl apply -f preferred.yml
    • 1번 watch 터미널을 보면 mysql Pod와 같은 노드 범위에 preferred Pod들이 모두 추가되었다.
    • preferred.yml도 required.yml의 원리와 같다.
    • 테스트가 잘 성공했으니, kubectl delete -f preferred.yml 로 마무리 한다.

4. Pod 배치 전략 중 podAntiAffinity에 대해 이해하고 실습한다.

1) podAntiAffinity?

  • 선호하지 않는 파드를 설정하는 방법으로, podAffinity를 podAntiAffinity로만 변경하면 사용법 동일하다.
  • 역시 여러 유즈케이스에 활용 가능한 2가지 옵션을 제공하며, Hard, Soft로 나뉜다. podAffinity와 동일하여 자세한 설명은 생략한다.
    • 반드시 충족해야 하는 조건 (Hard)
      • requiredDuringSchedulingIgnoredDuringExecution
    • 선호하는 조건 (Soft)
      • preferredDuringSchedulingIgnoredDuringExecution
  • 토폴로지 키 (Topology Key)도 podAffinity와 같은 방식이다. 자세한 건 3번 챕터(podAffinity)를 참고하면 된다.


2) podAntiAffinity 실습

  • minikube 멀티 클러스터 환경에서 진행된다.
  • selector 경로에 pod-anti-affinity 디렉토리를 만든다.
  • yml, sh 파일을 준비한다.
    • sh파일과 mysql.yml은 기존 podAffiniy와 같다.)

required.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-anti-affinity-required
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      name: hello
      labels:
        app: hello
    spec:
      containers:
      - name: nginx
        image: nginxdemos/hello:plain-text
        ports:
        - name: http
          containerPort: 80
          protocol: TCP
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - mysql
            topologyKey: kubernetes.io/hostname
            # topologyKey: topology.kubernetes.io/zone
            # topologyKey: topology.kubernetes.io/region
  • podAffinity => podAntiAffinity로 바꿔주기만 했다. 이제 podAffinity 방식과 반대로 label에 매칭된 대상은 같은 노드 범위에 묶이지 않게 될 것이다.

preferred.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pod-anti-affinity-preferred
spec:
  replicas: 4
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      name: hello
      labels:
        app: hello
    spec:
      containers:
      - name: nginx
        image: nginxdemos/hello:plain-text
        ports:
        - name: http
          containerPort: 80
          protocol: TCP
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - mysql
              topologyKey: kubernetes.io/hostname
              # topologyKey: topology.kubernetes.io/zone
              # topologyKey: topology.kubernetes.io/region
  • required와 거의 동일한 명세로 이루어져있다.

실습을 진행해보자.

  • 기본 환경구성과 초반 세팅은 nodeAffinity, podAffinity와 같다.
  • podAffinity 실습의 mysql.yml 파일 실행까지 동일하게 진행하고 다음 순서를 따르면 된다.

  • kubectl apply -f required.yml
    • mysql Pod가 띄워져있는 minikube-m02 노드를 제외하고, 다른 노드 범위에만 required의 Pod들이 실행되었음을 알 수 있다.
    • kubectl delete -f required.yml 지우고 다음으로 넘어가자.

  • kubectl apply -f preferred.yml
    • requierd와 마찬가지로 mysql Pod가 있는 노드를 제외한, 다른 노드 범위에만 preferred의 Pod들이 실행되었음을 알 수 있다.
    • kubectl delete -f preferred.yml 지우고 마무리 한다.
profile
그렇게 바쿠만도는 개발에 퐁당 빠지고 말았답니다.

0개의 댓글