티스토리 뷰
Kubernetes 특정 node에 pod 배포하기 - label, nodeSelector, affinity(nodeAffinity, podAffinity)
GodNR 2020. 1. 12. 00:46서론
본 포스팅에서는 Kubernetes 특정 node에 Pod를 배치하는 방법에 대해 살펴보겠습니다.
Kubernetes의 구조는 크게 MaterNode / WorkerNode / InfraNode / Ingress Node 등으로 구분할 수 있습니다.
- MasterNode에는 Kubernetes를 관리하는 관리노드가 설치
- InfraNode에는 각종 Echo System(Monitoring, Logging, Tracing 등)이 설치
- WorkerNode에는 실제 Application이 배포되는 노드
- IngressNode에는 Ingress Controller가 배포되는 노드
...
위와 같은 용도로 Node를 구성하게 됩니다.
예를 들어 Nginx를 사용하는 홈페이지 환경을 Docker 이미지로 생성하여 Kubernetes에 배포할 경우 해당 Pod는 WorkerNode에 구성되어야 합니다.
또한 모니터링을 위해 Prometheus Docker Image를 배포할 경우 InfraNode에 Pod가 배포되어야 합니다.
이와같이 용도에 맞게 Pod를 배치 시킬 수 있는 Label 기능과 Label을 선택할 수 있는 NodeSelector & Affinity에 대해 테스트를 진행하겠습니다.
본문
1. label 관리
1) node label info
kubectl get nodes --show-labels
먼저 현재 구성되어 있는 node들의 label 정보를 확인해 보겠습니다.
[root@kubemaster ~]# kubectl get nodes --show-labels NAME STATUS ROLES AGE VERSION LABELS kubemaster Ready master 16d v1.16.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=kubemaster, kubernetes.io/os=linux,node-role.kubernetes.io/master= kubeworker Ready 16d v1.16.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=kubeworker, kubernetes.io/os=linux [root@kubemaster ~]# |
kubemaster, kubeworker 노드의 label 정보가 위와 같이 구성되어 있으며, 특이점은 MasterNode에는 node-role.kubernetes.io/master= Label이 등록되어 있습니다.
2) node label add
kubectl label nodes [node_name] [key]=[value]
다음으로 label을 추가해 보도록 하겠습니다.
[root@kubemaster ~]# kubectl label nodes kubeworker key=worker beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=kubemaster, kubernetes.io/os=linux,node-role.kubernetes.io/master= beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,key=worker,kubernetes.io/arch=amd64, kubernetes.io/hostname=kubeworker,kubernetes.io/os=linux CalicoIsUp Calico is running on this node KubeletHasSufficientMemory kubelet has sufficient memory available KubeletHasNoDiskPressure kubelet has no disk pressure KubeletHasSufficientPID kubelet has sufficient PID available KubeletReady kubelet is posting ready status |
위와 같이 kubeworker node에 key=worker라는 label이 추가된 것을 확인할 수 있습니다.
3) node label delete
kubectl label nodes [node_name] [key]-
다음으로 추가한 label을 삭제해 보도록 하겠습니다.
[root@kubemaster ~]# kubectl label nodes kubeworker key- node/kubeworker labeled [root@kubemaster ~]# kubectl get nodes --show-labels NAME STATUS ROLES AGE VERSION LABELS kubemaster Ready master 17d v1.16.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=kubemaster, kubernetes.io/os=linux,node-role.kubernetes.io/master= kubeworker Ready 17d v1.16.3 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=kubeworker, kubernetes.io/os=linux [root@kubemaster ~]# |
위와 같이 추가한 key를 삭제할 수 있습니다.
2. Label을 이용한 특정 Node에 Pod 배포
이제부터는 추가한 Label을 Deployment에 적용하는 방법에 대해 살펴보겠습니다.
크게 nodeSelector와 affinity를 이용할 수 있습니다.
1) nodeSelector
아래는 생성한 label을 기준으로 deployment에 nodeSelector를 추가한 yaml 파일입니다.
apiVersion: apps/v1 kind: Deployment metadata: name: wildfly-app-deployment labels: app: wildfly-app spec: replicas: 1 selector: matchLabels: app: wildfly-app template: metadata: labels: app: wildfly-app spec: containers: - name: wildfly-app image: 192.168.56.100:5000/middleware/wildfly/wildfly_custom_image:latest imagePullPolicy: Always ports: - containerPort: 8080 nodeSelector: key: worker --- apiVersion: v1 kind: Service metadata: name: wildfly-app spec: selector: app: wildfly-app ports: - protocol: TCP port: 8080 targetPort: 8080 type: ClusterIP --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: wildfly-app-ingress namespace: default spec: rules: - host: test.nrson.co.kr http: paths: - backend: serviceName: wildfly-app servicePort: 8080 path: / tls: - hosts: secretName: nrson-tls |
위와 같이 deployment에
nodeSelector:
key: worker
형식으로 nodeSelector와 key, value를 지정하면 해당 label을 갖고 있는 node에만 배포가 진행됩니다.
그럼 실제로 배포를 진행해 보도록 하겠습니다.
[root@kubemaster deployment]# kubectl create -f deployment.yaml deployment.apps/wildfly-app-deployment created service/wildfly-app created ingress.extensions/wildfly-app-ingress created [root@kubemaster deployment]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES wildfly-app-deployment-84b77d7c98-5p49p 1/1 Running 0 83s 10.233.104.45 kubeworker [root@kubemaster deployment]# |
위와 같이 key=worker label을 포함하고 있는 kubeworker node에 pods가 배치된 것을 확인할 수 있습니다.
3) affinity
affinity는 뜻 그대로 유연한 Scheduling이 가능하도록 구성할 수 있습니다.
앞서 살펴본 nodeSelector의 경우 label naming을 기준으로 Pod를 특정 노드 또는 특정 노드 그룹에 배포하는 방식을 사용하였다면, affinity는 다양한 조건을 제시할 수 있고 유연하게 처리할 수 있습니다.
affinity는 크게 노드를 기준으로 하는 node affinity와 pod 레벨에서 label을 관리할 수 있는 pod affinity으로 구분할 수 있습니다.
a) node affinity
node affinity가 node level에서 배치를 관리하는다는점에서 nodeSelector와는 비슷하지만 다양한 조건을 명시할 수 있다는 유연성에서 차이가 있습니다.
node affinity는 크게 아래와 같이 4가지로 분류할 수 있습니다.
- requiredDuringSchedulingIgnoredDuringExecution - preferredDuringSchedulingIgnoredDuringExecution - requiredDuringSchedulingRequiredDuringExecution - preferredDuringSchedulingRequiredDuringExecution |
서두의 빨간색 required(hard affinity) & preferred(soft affinity)는 반드시 포함해야 하는지 또는 우선시하되 필수는 아닌지를 결정하는 조건이며, 중간의 파란색 Ignored & Required는 운영 중(Runtime) Node의 Label이 변경되면 무시할 것인지(Ignore) 또는 즉시 Eviction 처리(Required)하여 재기동을 수행할 것인지를 결정합니다.
앞서 nodeSelector로 생성한 pod는 초기화를 진행하고 yaml 파일을 수정해 보도록 하겠습니다.
apiVersion: apps/v1 kind: Deployment metadata: name: wildfly-app-deployment labels: app: wildfly-app spec: replicas: 1 selector: matchLabels: app: wildfly-app template: metadata: labels: app: wildfly-app spec: containers: - name: wildfly-app image: 192.168.56.100:5000/middleware/wildfly/wildfly_custom_image:latest imagePullPolicy: Always ports: - containerPort: 8080 affinity: --- apiVersion: v1 kind: Service metadata: name: wildfly-app spec: selector: app: wildfly-app ports: - protocol: TCP port: 8080 targetPort: 8080 type: ClusterIP --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: wildfly-app-ingress namespace: default spec: rules: - host: test.nrson.co.kr http: paths: - backend: serviceName: wildfly-app servicePort: 8080 path: / tls: - hosts: secretName: nrson-tls |
위와 같이 deployment에 nodeAffinity를 추가하였습니다.
nodeAffinity가 의미하는 내용을 살펴보자면
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: key
operator: In
values:
- worker
requiredDuringSchedulingIgnoredDuringExecution의 경우 아래 요구조건이 일치하는 node에 적용하겠다는 의미로 matchExpressions로 정의 된 key 값이 label의 [key], values 값이 label의 [value]와 일치해야 합니다. 여기서 처음 정의되는 operator라는 항목이 있는데 nodeAffinity에서 사용할 수 있는 operator에는 In 외에도 NotIn, Exists, DoesNotExist, Gt, Lt 등을 활용할 수 있습니다.
가장 많이 사용하는 In의 경우 두개 이상의 values가 명시되어 있을 때 values 중 하나의 값을 가질 경우를 의미합니다.
#참고
a. 여기서 주의 할 점은 2개 이상의 matchExpressions가 정의되어 있을 경우 각각의 matchExpressions를 만족하는 Pod가 기동되지만, matchExpressions 내부의 matching 조건이 여러개 일 경우 모든 조건을 만족해야 pod가 기동된다는 점입니다.
b. nodeAffinity와 반대로 특정 노드에만 할당하지 않도록 하는 nodeAntiAffinity를 지원합니다. matchExpressions를 만족할 경우 해당 노드는 pod를 배포하는 대상에서 제외됩니다.
c. matchExpressions에서 key는 단일 값을 갖지만, values는 여러개의 값이 나열될 수 있습니다.
d. preferred와 required는 스케줄링이 이루어지는 순간 즉 create or apply 시점에만 반영되는 정보입니다. 이를 Pod의 label 정보가 변경되는 순간에 반영할 것인지를 결정하는 ignore or required를 적용할 수 있습니다. Required가 적용될 경우 Node label이 변경되면 즉시 Eviction 상태로 변경될 수 있으니 설정에 유의해야 합니다.
b) pod affinity
pod affinity는 node affinity가 node의 label을 기준으로 기동했던것 처럼 pod의 label을 기준으로 조건을 만족할 경우 pod를 기동하는 방식으로 작성됩니다.
예시 1) requiredDuringSchedulingIgnoredDuringExecution
apiVersion: apps/v1 kind: Deployment metadata: name: wildfly-app-deployment labels: app: wildfly-app spec: replicas: 1 selector: matchLabels: app: wildfly-app template: metadata: labels: app: wildfly-app spec: containers: - name: wildfly-app image: 192.168.56.100:5000/middleware/wildfly/wildfly_custom_image:latest imagePullPolicy: Always ports: - containerPort: 8080 affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - wildfly-app topologyKey: kubernetes.io/hostname --- apiVersion: v1 kind: Service metadata: name: wildfly-app spec: selector: app: wildfly-app ports: - protocol: TCP port: 8080 targetPort: 8080 type: ClusterIP --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: wildfly-app-ingress namespace: default spec: rules: - host: test.nrson.co.kr http: paths: - backend: serviceName: wildfly-app servicePort: 8080 path: / tls: - hosts: secretName: nrson-tls |
podAffinity는 위와 같이 작성됩니다.
내용을 살펴보자면, "pod의 label key가 app이고, label value가 wildfly-app인 pods가 기동된 서버에 topologyKey 값과 같은 node에 해당 deployment을 배포해라" 정도로 이해 할 수 있습니다.
nodeAffinity와 마찬가지로 특정 pod가 배포된 node를 제외하라는 의미의 podAntiAffinity를 지원합니다.
약간 내용이 어렵게 느껴질 수 있어서 이를 도식화 해보도록 하겠습니다.
node와 pod의 label 정보입니다.
[root@kubemaster deployment]# kubectl get nodes --show-labels -o wide beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=kubemaster, kubernetes.io/os=linux,node-role.kubernetes.io/master= beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,key=worker,kubernetes.io/arch=amd64, kubernetes.io/hostname=kubeworker1,kubernetes.io/os=linux kubeworker2 Ready 17d v1.16.3 192.168.56.104 CentOS Linux 7 (Core) 3.10.0-957.el7.x86_64 docker://18.9.7 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,key=compute,kubernetes.io/arch=amd64, kubernetes.io/hostname=kubeworker2,kubernetes.io/os=linux apache-app-deployment-12ab8ce092-paghw 1/1 Running 0 8h 10.233.104.63 kubeworker2 app=apache-app,pod-template-hash=12ab8ce092 |
위와 같은 Label 정보를 기준으로 할 때 yaml 파일로 추가된 deployment가 배치 될 노드를 가정해 보자면, 다음과 같이 정리할 수 있습니다.
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- wildfly-app
topologyKey: kubernetes.io/hostname
labelSelector에 의해 pod의 라벨 정보가 app=wildfly-app인 pods가 기동 된 노드를 찾습니다. (pod label의 빨간색, 파란색 부분 - kubeworker1)
해당 노드의 라벨 정보가 topologyKey에 정의 된 key 값인 kubernetes.io/hostname의 value가 같은 모든 노드에 Pod를 배치(node label의 빨간색, 파란색 부분 - kubeworker1)합니다. 이때 hostname은 모든 서버가 서로 다르므로 wildfly-app이 배포된 kubeworker1에만 배포가 가능하다는 결론이 나옵니다.
한가지 예시를 더 들어보도록 하겠습니다.
예시 2) preferredDuringSchedulingIgnoredDuringExecution
affinity:
podAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 80
preference:
matchExpressions:
- key: disktype
operator: In
values:
- ssd
topologyKey: kubernetes.io/hostname
위와 같이 preferredDuringSchedulingIgnoredDuringExecution을 선택하고 weight로 80이 부여되어 있는것이 예시 1과 다른점입니다.
preferredDuringSchedulingIgnoredDuringExecution는 앞서 잠시 살펴보았지만, required와 달리 반드시 matchExpressions을 만족해야 하는 것은 아닙니다.
다만, preferredDuringSchedulingIgnoredDuringExecution 역시 배치해야 할 노드를 선정해야 할 기준이 있어야 하기에 다음과 같은 우선순위를 갖습니다.
(matchExpressions를 만족하는 노드들 중 weight가 높은 Node > matchExpressions를 만족하는 노드들 중 weight가 낮은 Node > matchExpressions를 만족하지 않는 노드)
이를 활용한다고 하면, Hard Disk로 HDD를 사용하는 서버보다는 SSD를 사용하는 서버에 우선순위를 높게 부여하여 먼저 기동되고 하고 싶을 경우 등에 적용한다면 유용하게 사용할 수 있을 듯 싶습니다.
이와같이 affinity를 응용하여 활용한다면 다음과 같은 케이스에서 사용할 수 있을 듯 합니다.
- Active / Standby 구조로 동작하는 솔루션 이미지 등에 적용하여 Active 솔루션이 설치 된 노드에 중복되어 Standby가 배포되지 않도록 podAntiAffinity를 적용하여 사전에 방지하는 역할
- 이미 배포되어 있는 특정 Pod와 local 통신을 해야 하는 경우 해당 Pod가 배포되어 있는 개수 만큼 Replica를 지정하고 kubernetes.io/hostname을 topologyKey로 지정하여 배포하는 경우
- Kubernetes Cluster 내부에서 Pod를 기준으로 Cluster 단위를 다시한번 쪼개는 역할 등을 수행할 수 있습니다.
결론
이번 포스팅에서는 Label을 활용하여 내가 원하는 Node에 Pod를 배포하는 방법에 대해 살펴보았습니다.
간단히 정리하자면, Kubernetes Node와 Pods는 Label을 갖고 있으며, Label을 기준으로 Pod를 배치할 노드를 선택할 수 있습니다.
특히 nodeSelector를 통해 간단히 추가, 제거가 가능하며, 보다 유연한 기능을 적용하기 위해 affinity를 반영할 수 있습니다.
affinity는 node와 pod의 label을 기준으로 적용하여 node를 선택할 수 있습니다.
그 밖에도 직접 Go Lang을 이용하여 개발한 Pod 스케줄러를 적용할 수도 있습니다.
다양한 Label과 지정 방식을 적용하여 체계적이고 구조화된 Kubernetes를 설계하는 것은 구축을 넘어 운영단계에 접어들었을 때 더욱 빛을 발할 것입니다.
'③ 클라우드 > ⓚ Kubernetes' 카테고리의 다른 글
minikube 5분안에 설치하기 (0) | 2020.03.03 |
---|---|
Ansible을 활용한 kubernetes 운영환경 구축하기 (0) | 2020.02.28 |
Kubernetes Persistent Volume 생성하기 - PV, PVC (1) | 2020.01.05 |
Kubernetes SSL 인증서 적용하기 - TLS, SSL, CAChain, RootCA (4) | 2020.01.04 |
Kubernetes 자원할당 - CPU, MEM, DISK, Replica, HPA (3) | 2019.12.28 |
- Total
- Today
- Yesterday
- node.js
- openstack tenant
- webtob
- OpenStack
- 오픈스택
- JBoss
- JEUS7
- 쿠버네티스
- Docker
- 아키텍처
- openstack token issue
- TA
- jeus
- kubernetes
- wildfly
- Architecture
- SA
- API Gateway
- MSA
- 마이크로서비스
- Da
- 마이크로서비스 아키텍처
- aa
- aws
- apache
- k8s
- nodejs
- SWA
- JEUS6
- git
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |