개요

K8s 환경에서 어플리케이션이 특정 노드에서만 실행 되게끔 하고싶을 수 있다. 기본적으로는 클라우드 환경에서 k8s가 스케쥴링으로 자원을 고려하여 POD들을 배치하지만 더 세밀하고 클러스터를 관리하고 싶을 수 있다. 예를 들어 어플리케이션을 특정 SSD 노드들에 배치한다던가 같은 렉의 노드들로 배치하고 싶은 일이 있을 수 있다.

아래 내용은 Kubernetes 관리 commandline tool인 kubectl에 대한 사전지식을 전제로 한다. kubectl에 대한 가이드는 kubectl 설치하기를 참고하자.

0. 요약

요약하자면 PM장비(물리장비)가 아래 이름과 같이 3대 있다고 가정하자. 이 장비들에 Label을 붙인뒤 이 LabelPod이 배치(deployment)되게 요청하면 된다. 순서3의 deploy 요청 화살표는 편의상 개념적으로 표시한것이다.

1. 노드에 Label 정보 추가하기

1-1. 노드에 Label 추가하기

kubectl 명령어를 이응하여 아래와 같이 노드에 label을 추가해보자.

$ kubectl get nodes # k8s 클러스터의 노드 정보 보기
$ kubectl label nodes <node-name> <label-key>=<label-value> # label

실제 예를 들면 아래와 같다.

kubectl label nodes kubernetes-foo-node-1.c.a-robinson.internal disktype=ssd

label 삭제는 아래와 같이 -를 붙여 가능하다.

$ kubectl label nodes <node-name> <label-key>-

1-2. 노드 Label 검증하기

아래와 같이 검증이 가능하다.

$ kubectl get nodes --show-labels # 복수장비
$ kubectl describe node "nodename" # 단일장비

1-3. 노드 label 실제 적용해보기

실제로 구축된 클러스터에서 아래와 같이 node를 label하고 확인해보자.

worker는 현재 3대로 구성되어있다.
– search-so-dev52
– search-so-dev54
– search-so-dev55

이 세대의 장비에 2대씩 조합하여 aurochsapp, log2mat, stargate 로 장비그룹을 label해본다.

테스트를 위해 아래의 그림과 같이 장비 3대에 3개의 label을 2대씩 조합하여 구성해본다.

실제 명령어는 아래와 같이 입력하면 된다.

$ kubectl label nodes search-so-dev52 aurochsapp=large
node/search-so-dev52 labeled
$ kubectl label nodes search-so-dev54 aurochsapp=large
node/search-so-dev54 labeled
$ kubectl label nodes search-so-dev54 log2mat=large
node/search-so-dev54 labeled
$ kubectl label nodes search-so-dev55 log2mat=large
node/search-so-dev55 labeled
$ kubectl label nodes search-so-dev55 stargate=large
node/search-so-dev55 labeled
$ kubectl label nodes search-so-dev52 stargate=large
node/search-so-dev52 labeled

아래와 같이 node별 label을 확인 할 수 있고 nodetype=aurochs-app을 확인 할 수 있다.

$ kubectl get nodes --show-labels
NAME                                STATUS   ROLES    AGE   VERSION   LABELS
sample-cluster-ingress-1   Ready    node     16d   v1.11.5   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=sample-cluster-ingress-1,node-role/lb=true,node-role.kubernetes.io/node=true
sample-cluster-master-1    Ready    master   17d   v1.11.5   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=sample-cluster-master-1,node-role.kubernetes.io/master=true
sample-cluster-master-2    Ready    master   17d   v1.11.5   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=sample-cluster-master-2,node-role.kubernetes.io/master=true
sample-cluster-master-3    Ready    master   17d   v1.11.5   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=sample-cluster-master-3,node-role.kubernetes.io/master=true
search-so-dev52                     Ready    node     16d   v1.11.5   aurochsapp=large,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=search-so-dev52,node-role/worker=true,node-role.kubernetes.io/node=true,stargate=large
search-so-dev54                     Ready    node     16d   v1.11.5   aurochsapp=large,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=search-so-dev54,log2mat=large,node-role/worker=true,node-role.kubernetes.io/node=true
search-so-dev55                     Ready    node     16d   v1.11.5   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=search-so-dev55,log2mat=large,node-role/worker=true,node-role.kubernetes.io/node=true,stargate=large

2. nodeSelector

nodeSelector는 가장 심플하게 POD이 실행될 노드를 선택하는 방법이다.

2-1. POD 명세 작성

nginx를 띄우는 예제 POD이다. 주목할 점은 nodeSelector로 POD이 실행될 노드를 지정한 것이다.

아래와 같이 지정하면 1-1에서 정의한 label 인 disktype=ssd가 설정된 노드에서만 POD이 실행될것이다.

pods/pod-nginx.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    env: test
spec:
  containers:
  - name: nginx
    image: nginx
    imagePullPolicy: IfNotPresent
  nodeSelector:
    disktype: ssd

2-2. POD 실행 및 검증하기

아래와 같이 POD을 실행하면 label이 설정된 노드에서 POD이 실행된다.

kubectl create -f https://k8s.io/examples/pods/pod-nginx.yaml

아래 명령어를 통해 pod이 어떤 노드에서 실행되는지 확인 할 수 있다.

kubectl get pods -o wide

2-3. POD 실제 적용해보기

starport docker app을 aurochsapp=large, log2mat=large, stargate=large로 각각 띄워보기로 한다.

aurochsapp-deploy.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: aurochsapp
  labels:
    env: test
spec:
  replicas: 2
  template:
    metadata:
      labels:
        env: test
    spec:
      containers:
      - name: nginx
        image: 원하는이미지
      nodeSelector:
        aurochsapp: "large"
$ kubectl create -f aurochsapp-deploy.yaml
$ kubectl get pods -o wide
NAME                          READY     STATUS    RESTARTS   AGE       IP             NODE
aurochsapp-6fc75fb7b9-lsss8   1/1       Running   0          5m        10.230.5.187   search-so-dev52
aurochsapp-6fc75fb7b9-nvt5j   1/1       Running   0          5m        10.230.6.62    search-so-dev54

같은 맥락으로 log2matstargate도 생성하고 나면 아래와 같은 결과를 볼 수 있다.

$ kubectl get pods -o wide
NAME                          READY     STATUS    RESTARTS   AGE       IP             NODE
aurochsapp-6fc75fb7b9-lsss8   1/1       Running   0          10m       10.230.5.187   search-so-dev52
aurochsapp-6fc75fb7b9-nvt5j   1/1       Running   0          10m       10.230.6.62    search-so-dev54
log2mat-58974b9cc7-bnblv      1/1       Running   0          2m        10.230.4.196   search-so-dev55
log2mat-58974b9cc7-g8lvk      1/1       Running   0          2m        10.230.6.121   search-so-dev54
stargate-5df4488b85-fbcjn     1/1       Running   0          1m        10.230.5.48    search-so-dev52
stargate-5df4488b85-r5hc8     1/1       Running   0          1m        10.230.4.50    search-so-dev55

3. Affinity

nodeSelector 대비 더 Rich한 표현식으로 노드를 선택하고 싶으면 Affinityinter pod affinity를 이용하면 된다.

k8s는 nodeSelector보다 이 방식을 권장하고 있다.

이 방식의 장점은 아래와 같다.

  1. 표현식이 좀 더 다양하다.(exact match의 AND조합을 벗어남)
  2. hard한 명시보다 soft/preference 설정에 가깝게 바꿔준다.
  3. 제약조건을 node 자체보다 POD간에 부여하는 방식으로 즉 서로 같이 배치될수 있는 POD과 아닌 POD을 정의할 수 있음

affinity는 크게 node affinityinter-pod affinity/anti-affinity로 나뉜다.
Node AffinitynodeSelector와 유사한 반면 inter-pod affinity/anti-affinity는 pod label을 다룬다고 보면 된다.

nodeSelectornode affinity로 훗날 대체될 예정이다.

3-1. Node Affinity

nodeSelector와 유사하지만 POD이 어떤 노드에는 스케쥴 될 수 있는지 정의한다.
현재 두가지 타입의 node affinity가 있는데 아래의 4가지이다.

requiredpreferred는 조건을 반드시 충족해야 하는지 선호되는지 정도의 차이다.
IgnoredDuringExecutionRequiredDuringExecution의 차이는 Runtime에 Node label이 바뀌더라도 무시할것인지 즉시 eviction할것인지에 대한 스케쥴링 정책이다.

  • requiredDuringSchedulingIgnoredDuringExecution
  • preferredDuringSchedulingIgnoredDuringExecution
  • requiredDuringSchedulingRequiredDuringExecution
  • preferredDuringSchedulingRequiredDuringExecution

예시를 하나 들자면 requiredDuringSchedulingIgnoredDuringExecution반드시 intel CPU를 가진 노드에 POD을 배포해라.가 될 수 있고 preferredDuringSchedulingIgnoredDuringExecution가급적 XYZ zone의 노드에 배포되었으면 좋겠지만 안된다면 다른데 배포해라. 정도이다.

node affinity에 대한 예시코드는 아래와 같다.

pods/pod-with-node-affinity.yaml

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

위에서 설명하지 않은 명세에 관한 추가정보는 아래와 같은것들이 있다.

operator종류는 아래의 것들이 가능하다.

  • In
  • NotIn
  • Exists
  • DoesNotExist
  • Gt
  • Lt

nodeSelectornodAffinity 조합에 따른 POD 배포 조건은 아래와 같다.
– 만약 nodeSelectornodeAffinity가 둘다 명시 -> 두조건 모두 충족해야함
– 여러개의 nodeSelectorTermsnodeAffinity 타입으로 명시 -> nodeSelectorTerms중 하나만 충족해도 됨
– 여러개의 matchExpressionsnodeSelectorTerms 아래 명시 -> 모든 조건 충족해야함
– affinity selection은 스케쥴링때만 적용되고 런타임으로 적용되지 않음

weightpreferred...으로 시작하는 조건에 가중치를 부여하는 것으로 1~100의 값으로 정의된다.

3-2. Node Affinity 실제 적용해보기

aurochsapp-nodeaffinity-deploy.yaml로 예제를 만들었다.
기존 nodeSelector와 문법만 바뀌고 동일하게 node label이 aurochsapp=large인 node를 찾고 여기에 Pod을 배치한다.

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: aurochsapp
  labels:
    env: test
spec:
  replicas: 2
  template:
    metadata:
      labels:
        env: test
    spec:
      containers:
      - name: nginx
        image: 원하는이미지
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: aurochsapp
                operator: In
                values:
                - large

위와 같은 Pod명세로 deployment를 만들고 pod을 확인해보면 아래와 같이 원하는 node들로 배포가 된것을 확인 할 수 있다.

$ kubectl create -f aurochsapp-nodeaffinity-deploy.yaml
deployment "aurochsapp" created
$ kubectl create -f log2mat-nodeaffinity-deploy.yaml
deployment "log2mat" created
$ kubectl create -f stargate-nodeaffinity-deploy.yaml
deployment "stargate" created

$ kubectl get pods -o wide
NAME                          READY     STATUS              RESTARTS   AGE       IP             NODE
aurochsapp-547946678b-jpjj7   1/1       Running   0          18h       10.230.6.59    search-so-dev54
aurochsapp-547946678b-svdzd   1/1       Running   0          18h       10.230.5.160   search-so-dev52
log2mat-5b5758964-fqc4v       1/1       Running   0          18h       10.230.6.140   search-so-dev54
log2mat-5b5758964-ppnfx       1/1       Running   0          18h       10.230.7.126   search-so-dev55
stargate-6464cc9647-6w525     1/1       Running   0          18h       10.230.5.228   search-so-dev52
stargate-6464cc9647-k2n9n     1/1       Running   0          18h       10.230.7.180   search-so-dev55

4. Inter-pod affinity and anti-affinity

POD 간에 affinity, anti-affinity를 명시할 수 있는데 현재로써는 node affinity도 충분하다고 판단하여 내용을 유심히 보지 않았다.

5. 참고자료