개요
K8s 환경에서 어플리케이션이 특정 노드에서만 실행 되게끔 하고싶을 수 있다. 기본적으로는 클라우드 환경에서 k8s가 스케쥴링으로 자원을 고려하여 POD들을 배치하지만 더 세밀하고 클러스터를 관리하고 싶을 수 있다. 예를 들어 어플리케이션을 특정 SSD 노드들에 배치한다던가 같은 렉의 노드들로 배치하고 싶은 일이 있을 수 있다.
아래 내용은 Kubernetes 관리 commandline tool인 kubectl에 대한 사전지식을 전제로 한다. kubectl에 대한 가이드는 kubectl 설치하기를 참고하자.
0. 요약
요약하자면 PM장비(물리장비)가 아래 이름과 같이 3대 있다고 가정하자. 이 장비들에 Label을 붙인뒤 이 Label로 Pod이 배치(deployment)되게 요청하면 된다. 순서3의 deploy 요청 화살표는 편의상 개념적으로 표시한것이다.

1. 노드에 Label 정보 추가하기
1-1. 노드에 Label 추가하기
kubectl 명령어를 이응하여 아래와 같이 노드에 label을 추가해보자.
$ kubectl get nodes # k8s 클러스터의 노드 정보 보기
$ kubectl label nodes <node-name> <label-key>=<label-value> # label
실제 예를 들면 아래와 같다.
label 삭제는 아래와 같이 -를 붙여 가능하다.
1-2. 노드 Label 검증하기
아래와 같이 검증이 가능하다.
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이 실행된다.
아래 명령어를 통해 pod이 어떤 노드에서 실행되는지 확인 할 수 있다.
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
같은 맥락으로 log2mat과 stargate도 생성하고 나면 아래와 같은 결과를 볼 수 있다.
$ 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한 표현식으로 노드를 선택하고 싶으면 Affinity나 inter pod affinity를 이용하면 된다.
k8s는 nodeSelector보다 이 방식을 권장하고 있다.
이 방식의 장점은 아래와 같다.
- 표현식이 좀 더 다양하다.(exact match의 AND조합을 벗어남)
- hard한 명시보다
soft/preference설정에 가깝게 바꿔준다. - 제약조건을 node 자체보다 POD간에 부여하는 방식으로 즉 서로 같이 배치될수 있는 POD과 아닌 POD을 정의할 수 있음
affinity는 크게 node affinity와 inter-pod affinity/anti-affinity로 나뉜다.
Node Affinity는 nodeSelector와 유사한 반면 inter-pod affinity/anti-affinity는 pod label을 다룬다고 보면 된다.
nodeSelector는 node affinity로 훗날 대체될 예정이다.
3-1. Node Affinity
nodeSelector와 유사하지만 POD이 어떤 노드에는 스케쥴 될 수 있는지 정의한다.
현재 두가지 타입의 node affinity가 있는데 아래의 4가지이다.
required와 preferred는 조건을 반드시 충족해야 하는지 선호되는지 정도의 차이다.
IgnoredDuringExecution와 RequiredDuringExecution의 차이는 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
nodeSelector 및 nodAffinity 조합에 따른 POD 배포 조건은 아래와 같다.
– 만약 nodeSelector와 nodeAffinity가 둘다 명시 -> 두조건 모두 충족해야함
– 여러개의 nodeSelectorTerms이 nodeAffinity 타입으로 명시 -> nodeSelectorTerms중 하나만 충족해도 됨
– 여러개의 matchExpressions이 nodeSelectorTerms 아래 명시 -> 모든 조건 충족해야함
– affinity selection은 스케쥴링때만 적용되고 런타임으로 적용되지 않음
weight는 preferred...으로 시작하는 조건에 가중치를 부여하는 것으로 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도 충분하다고 판단하여 내용을 유심히 보지 않았다.