Assign
Update : 2025-01-30
Assign 소개.
Pod의 효과적인 할당을 위한 기술 전략과 작동 방식 및 다양한 방법들에 대해 소개합니다.필요에 따라 특정 노드에서만 실행하거나 특정 노드에서 실행하는 것에 우선 순위를 두도록 제한할 수 있습니다. 스케쥴러가 자동으로 적절한 배치를 수행하므로 일반적으로 이러한 제한들은 필요하지 않지만, 더 세밀한 제어가 필요한 경우가 프로덕션에서 발생할 수 있습니다.
이번 실습에서는 이러한 요구 조건에 맞추어 Pod에 대한 할당을 중점적으로 다룹니다.
NodeSelector
1.소개.
NodeSelector는 가장 간단한 노드 선택 방법입니다. NodeSelector는 PodSpec 필드에서 선택이 가능하며, Key-value 페어로 지정하게 됩니다. Pod가 Node에 실행 될 수 있으려면, Node에 표시된 Key-Value 형태의 Lable이 존재해야 합니다. 이것은 다중으로 지정할 수도 있습니다.
"metadata": {
"labels": {
"key1" : "value1",
"key2" : "value2"
}
}
2.Node 의 Label 확인.
이미 yaml로 정의된 EKS LAB을 eksctl로 배포시에, 각 Worker Node들에 Lable이 정의 되어 있었습니다.
cat ~/environment/myeks/eksworkshop-cluster-3az.yaml
whchoi-cluster.yaml의 내용을 살펴 봅니다.
#생략
nodeGroups:
- name: ng1-public
instanceType: m5.xlarge
desiredCapacity: 3
minSize: 3
maxSize: 9
volumeSize: 30
volumeType: gp2
amiFamily: AmazonLinux2
#
#Public-worker-node에 대한 label을 정의
#
labels:
nodegroup-type: "frontend-workloads"
ssh:
publicKeyPath: "/home/ec2-user/environment/eksworkshop.pub"
allow: true
iam:
attachPolicyARNs:
withAddonPolicies:
autoScaler: true
cloudWatch: true
ebs: true
fsx: true
efs: true
- name: ng2-private
instanceType: m5.xlarge
desiredCapacity: 3
privateNetworking: true
minSize: 3
maxSize: 9
volumeSize: 30
volumeType: gp2
amiFamily: AmazonLinux2
#
#Private-worker-node에 대한 label을 정의
#
labels:
nodegroup-type: "backend-workloads"
ssh:
publicKeyPath: "/home/ec2-user/environment/eksworkshop.pub"
allow: true
iam:
withAddonPolicies:
autoScaler: true
cloudWatch: true
ebs: true
fsx: true
efs: true
#이하 생략
현재 설정되어 있는 Node, Pods의 label은 아래 명령을 통해 확인이 가능합니다.
kubectl get node --show-labels -o wide
사전에 정의해 둔 "nodegroup-type=frontend-workloads" 의 노드들을 출력해 봅니다.
kubectl get nodes -l nodegroup-type=frontend-workloads
아래와 같은 출력 결과를 볼 수 있습니다.
whchoi98:~ $ kubectl get nodes -l nodegroup-type=backend-workloads
NAME STATUS ROLES AGE VERSION
ip-10-11-16-31.ap-northeast-2.compute.internal Ready <none> 90m v1.16.12-eks-904af05
ip-10-11-55-30.ap-northeast-2.compute.internal Ready <none> 88m v1.16.12-eks-904af05
ip-10-11-90-240.ap-northeast-2.compute.internal Ready <none> 85m v1.16.12-eks-904af05
3.새로운 Label 정의 .
한 개의 Node Name을 선택하고, label을 지정합니다. (예. assigntest=01)
kubectl label nodes $(kubectl get nodes -l nodegroup-type=frontend-workloads -o jsonpath='{.items[0].metadata.name}') disktype=ssd
정상적으로 라벨링 되었는지 확인합니다.
kubectl get nodes -l disktype=ssd
4. 특정 레이블의 노드에 Pod 배포.
새로운 디렉토리와 namespace를 만들고 nginx 배포를 위한 매니페스트 파일을 간단하게 작성합니다. 해당 Pod는 생성될때 앞서 label에서 정의된 disktype=ssd 가 추가된 worker node로 배포됩니다.
mkdir ~/environment/nodeselector
kubectl create namespace nodeselector
cat <<EoF > ~/environment/nodeselector/pod-nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
env: test
namespace: nodeselector
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
nodeSelector:
disktype: ssd
EoF
생성한 매니페스트 파일을 통해 nginx app을 배포합니다.
kubectl -n nodeselector apply -f ~/environment/nodeselector/pod-nginx.yaml
정상적으로 배포되었는지 확인합니다. 앞서 지정한 worker node로 배포되어야 합니다.
kubectl get nodes --show-labels | grep disktype
kubectl -n nodeselector get pods -o wide
아래는 출력 결과 예시입니다.
$ kubectl get nodes --show-labels | grep disktype
ip-10-11-16-31.ap-northeast-2.compute.internal Ready <none> 120m v1.16.12-eks-904af05 alpha.eksctl.io/cluster-name=eksworkshop,alpha.eksctl.io/instance-id=i-0ca4ddf4adea01038,alpha.eksctl.io/nodegroup-name=ng1-public,beta.kubernetes.io/arch=amd64,beta.kubernetes.io/instance-type=m5.xlarge,beta.kubernetes.io/os=linux,disktype=ssd,failure-domain.beta.kubernetes.io/region=ap-northeast-2,failure-domain.beta.kubernetes.io/zone=ap-northeast-2a,kubernetes.io/arch=amd64,kubernetes.io/hostname=ip-10-11-16-31.ap-northeast-2.compute.internal,kubernetes.io/os=linux,nodegroup-type=frontend-workloads
$ kubectl -n nodeselector get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 4m18s 10.11.28.19 ip-10-11-16-31.ap-northeast-2.compute.internal <none> <none>
Affinity 와 Anti-Affinity
1.소개.
nodeSelector
는 파드를 특정 label이 있는 노드로 배하는 매우 간단한 방법을 제공합니. 하지만 Affinity/Anti-Affinity 기능은 조금 더 유연하게 파드를 노드에 배치하는 방법을 제공합니다. 주요 개선 사항은 다음과 같습니다 .
Affinity/Anti-Affinity 언어가 더 다양하게 표현할 수 있습니다 . 언어는 논리 연산자인 AND 연산으로 작성된 정확한 매칭 항목 이외에 더 많은 매칭 규칙을 제공합니다 .
규칙이 엄격한 요구 사항이 아니라 "required-hard affinity / preferred - soft affinity" 규칙을 나타낼 수 있기에 스케줄러가 규칙을 만족할 수 없더라도, 파드가 계속 유연하게 스케줄 되도록 합니다.
노드 자체에 label을 붙이기보다는 노드(또는 다른 토폴로지 도메인)에서 실행 중인 다른 파드의 label을 제한할 수 있습니다. 이를 통해 어떤 파드가 함께 위치할 수 있는지와 없는지에 대한 규칙을 적용할 수 있습니다 .
Affinity 기능은 "노드 Affinity" 와 "파드 Affinity/Anti-Affinity" 두 종류의 Affinity로 구성됩니다. "노드 Affinity" 는 기존 nodeSelector
와 비슷하지만(그러나 위에서 나열된 첫째와 두 번째 이점이 있습다.), "파드 Affinity/Anti-Affinity" 위에서 나열된 세번째 항목에 설명된 대로 노드 label이 아닌 파드 label에 대해 제한되고 위에서 나열된 첫 번째와 두 번째 속성을 가집니다 .
2.Node Affinity
앞서 소개한 nodeSelector 와 유사합니다. 노드 label을 기반으로 다양한 조건들을 명시할 수 있다는 점에서 차이가 있습니다.
현재 requiredDuringSchedulingIgnoredDuringExecution
와 preferredDuringSchedulingIgnoredDuringExecution
로 부르는 두 가지 종류의 노드 어피니티가 있습니다 .
requiredDuringSchedulingIgnoredDuringExecution
파드가 노드에 스케줄 되도록 반드시 규칙을 만해야 하는 것(nodeSelector
와 같으나 보다 상세 구문을 사용해서)을 지정하고, preferredDuringSchedulingIgnoredDuringExecution
는 스케줄러가 시도하려고는 하지만, preferences 를 지정한다는 점에서 이를 각각 "엄격함(hard)" 과 "유연함(soft)" 으로 생각할 수 있습니다.
이름의 "IgnoredDuringExecution" 부분은 nodeSelector
작동 방식과 유사하게 노드의 레이블이 런타임 중에 변경되어 파드의 Affinity 규칙이 더 이상 충족되지 않아도 파드가 여전히 그 노드에서 동작한다는 의미입니다 .
향후에는 referredDuringSchedulingIgnoredDuringExecution
와 같은 requiredDuringSchedulingIgnoredDuringExecution
를 제공할 계획입니다 .
따라서 requiredDuringSchedulingIgnoredDuringExecution
의 예로는 "인텔 CPU가 있는 노드에서만 파드 실행"이 될 수 있고, preferredDuringSchedulingIgnoredDuringExecution
의 예로는 "장애 조치 영역 XYZ에 파드 집합을 실행하려고 하지만, 불가능하다면 다른 곳에서 일부를 실행하도록 허용"이 있을 것이다.
아래와 같이 pod 배포를 위한 새로운 매니페스트 파일을 작성합니다.
Node Affinity는 다음과 같은 구문의 의미를 가지고 있습니다.
required - 반드시 포함되어야 함.
preferred - 우선하지만 필수는 아님.
Ingnored - Runtime 중에 Node Label 변경시 무시
DuringScheduling 뒤에 오는 required - Runtime 중에 NodeLabel 변경시 즉시 재기동.
mkdir ~/environment/affinity
cat <<EoF > ~/environment/affinity/pod-with-node-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: azname
operator: In
values:
- ap-northeast-2a
- ap-northeast-2b
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: another-node-label-key
operator: In
values:
- another-node-label-value
containers:
- name: with-node-affinity
image: us.gcr.io/k8s-artifacts-prod/pause:2.0
EoF
예제에서 operator:In 연산자를 사용하였습니다. In, NotIn, Exists, DoesNotExist, Gt, Lt 등 다양한 연산자를 사용할 수 있습니다. nodeAffinity에 대한 아래 몇가지 내용을 알아 둘 필요가 있습니다.
2개 이상의 matchExpressions가 선언되어 있는 경우에는 , 모든 조건이 만족해야 Pod가 배포됩니다. 또한 matchExpressions 내부의 선언된 조건이 여러개 있는 경우에도 모든 조건을 만족해야 Pod가 배포됩니다.
key는 고유의 값 한개를 선언하지만, value는 예제 처럼 여러개를 선언할 수 있습니다. (value는 하나의 조건만 만족해도 됩니다.)
preferred와 required는 create,apply 시점에만 반영됩니다.
이제 아래와 같이 새로운 namespace를 만들고 , 적용해 봅니다.
kubectl create namespace affinity
kubectl -n affinity apply -f ~/environment/affinity/pod-with-node-affinity.yaml
아래 명령을 통해 Pod의 배포 상태를 확인합니다.
kubectl -n affinity get pods -o wide
Node의 Label 이 azname=ap-northeast-2a, 2b를 가지고 있는 것이 아직 없기 때문에 pending 상태입니다.
whchoi98:~ $ kubectl -n affinity get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
with-node-affinity 0/1 pendingng 0 2m4s <none> ip-10-11-16-31.ap-northeast-2.compute.internal <none> <none>
이제 node에 nodAffinity를 위한 label을 지정합니다. 노드 어피니티는 PodSpec의 affinity
필드의 nodeAffinity
필드에서 지정됩니다.
kubectl label nodes $(kubectl get nodes -l nodegroup-type=frontend-workloads -o jsonpath='{.items[0].metadata.name}') azname=ap-northeast-2a
이제 정상적으로 pod가 생성되는 지 확인합니다.
$ kubectl -n affinity get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
with-node-affinity 1/1 Running 0 2m10s 10.11.24.52 ip-10-11-16-31.ap-northeast-2.compute.internal <none> <none>
3.Pod Affinity
PodAffinity를 활용하면 ReplicaSets, StatefulSets, Deployments 등과 함께 좀 더 상세하고 유용하게 사용할 수 있습니다. 예를 들면 동일 노드에 목적에 따라 함께 사용될 Pod들을 함께 배치 할 수 있습니다.
이번 랩에서는 아래에서 처럼 Pod를 배포하려고 합니다.
worker node 1 - Webserver1 - RedisCache1
worker node2 - Webserver2 - RedisCache2
worker node3 - Webserver3 - RedisCache3
앞서 생성한 namespace에 아래와 같은 RedisCache 매니페스트 파일을 생성합니다.
cat <<EoF > ~/environment/affinity/redis-with-node-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis-cache
namespace: affinity
spec:
selector:
matchLabels:
app: store
replicas: 3
template:
metadata:
labels:
app: store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: redis-server
image: redis:3.2-alpine
EoF
매니페스트 파일에는 다음과 같은 정보들을 담고 있습니다.
App : redis-cache
replica : 3개
label : app=store
podAntiAffinity : app=store
앞서 생성한 namespace에 아래와 같은 nginx WebServer 매니페스트 파일을 생성합니다.
cat <<EoF > ~/environment/affinity/web-with-node-affinity.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
namespace: affinity
spec:
selector:
matchLabels:
app: web-store
replicas: 3
template:
metadata:
labels:
app: web-store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-store
topologyKey: "kubernetes.io/hostname"
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- store
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-app
image: nginx:1.12-alpine
EoF
매니페스트 파일에는 다음과 같은 정보들을 담고 있습니다.
App : web-server (alpine nginx 1.12)
replica : 3개
label : app=web-store
podAntiAffinity : app=web-store
podAffinity: app=store
이와 같은 배포전략은 여러개의 앱을 요구 조건에 따라 특정 노드에 묶어서 배포할 때, 매우 효과적이며 강력한 배포전략 기술입니다.
이제 만들어진 매니페스트 파일을 배포합니다.
kubectl apply -f ~/environment/affinity/redis-with-node-affinity.yaml
kubectl apply -f ~/environment/affinity/web-with-node-affinity.yaml
정상적으로 배포되었는지 확인해 봅니다.
kubectl -n affinity get pods -o wide
정상적으로 배포되었다면 아래와 같은 결과를 볼 수 있습니다. Web-Server와 Redis-Cache 서버는 모두 분산되어 배포되었습니다.
whchoi98:~ $ kubectl -n affinity get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-cache-6bc7d5b59d-c2cmr 1/1 Running 0 18s 10.11.94.33 ip-10-11-90-240.ap-northeast-2.compute.internal <none> <none>
redis-cache-6bc7d5b59d-d729w 1/1 Running 0 18s 10.11.120.83 ip-10-11-114-132.ap-northeast-2.compute.internal <none> <none>
redis-cache-6bc7d5b59d-pt9vc 1/1 Running 0 18s 10.11.150.121 ip-10-11-146-170.ap-northeast-2.compute.internal <none> <none>
web-server-655bf8bdf4-7pvsr 1/1 Running 0 18s 10.11.73.255 ip-10-11-90-240.ap-northeast-2.compute.internal <none> <none>
web-server-655bf8bdf4-ghgm8 1/1 Running 0 18s 10.11.111.44 ip-10-11-114-132.ap-northeast-2.compute.internal <none> <none>
web-server-655bf8bdf4-rmzkk 1/1 Running 0 18s 10.11.153.163 ip-10-11-146-170.ap-northeast-2.compute.internal <none> <none>
with-node-affinity 1/1 Running 0 173m 10.11.24.52 ip-10-11-16-31.ap-northeast-2.compute.internal <none> <none>
Last updated
Was this helpful?