AWS EKS에서 Node의 EC2 타입과 --max-pods 설정간 관계
본 기사에서는 AWS EKS의 노드의 EC2 인스턴스 타입에 따라 kubelet에서 최대로 Pod를 배치할 수 있는 —max-pods 설정간 관계에 대해 설명합니다. 또한 kubelet의 --max-pods 설정이 Pod 배치에 미치는 영향에 대해 설명합니다. 이 기사에서는 Pod 배치에 영향을 미치는 다른 리소스에 대해서는 고려하지 않습니다.
AWS EKS 클러스터에는 Pod를 하나 이상 배치할 수 있는 EC2 Node가 포함되어 있습니다. EC2 인스턴스의 유형이 다양한 만큼 Node 역시 다양한 유형의 인스턴스를 사용할 수 있습니다. 인스턴스 유형 별 컴퓨팅, 메모리, 스토리지 및 네트워크 기능들이 다양하게 제공되나 이 기사에서는 탄력적 네트워크 인터페이스(Elastic Network Interface)에 대해 집중적으로 다루어보겠습니다.
각 EC2 인스턴스는 유형별로 사용 가능한 네트워크 인터페이스(ENI)와 각 네트워크 인터페이스 별 프라이빗 IP 주소가 각각 다르게 지정되어 있습니다. [1]
예를 들어 m5.large 타입의 인스턴스의 경우, 최대 사용 가능한 네트워크 인터페이스는 3개, 각 네트워크 인터페이스 별로 프라이빗 IPv4는 10개 그리고 프라이빗 IPv6는 10개로 지정되어 있습니다.
$ aws ec2 describe-instance-types --filters "Name=instance-type,Values=m5.large" --query "InstanceTypes[].{Type: InstanceType, MaxENI: NetworkInfo.MaximumNetworkInterfaces, IPv4addr: NetworkInfo.Ipv4AddressesPerInterface, IPv6addr: NetworkInfo.Ipv6AddressesPerInterface}" --output table
------------------------------------------------
| DescribeInstanceTypes |
+----------+------------+---------+------------+
| IPv4addr | IPv6addr | MaxENI | Type |
+----------+------------+---------+------------+
| 10 | 10 | 3 | m5.large |
+----------+------------+---------+------------+
이러한 네트워크 인터페이스 설정으로 인해 kubelet[2]에 실행 가능한 최대 Pod 수를 나타내는 --max-pods
값에 적당한 값을 입력할 필요가 있습니다. 이를 위해 EKS AMI에서는 자동으로 —max-pods
를 계산하기 위한 max-pods-calculator.sh
[3] 및 각 인스턴스 타입별로 이미 계산된 결과인 eni-max-pods.txt
[4]를 제공하고 있습니다.
최대 실행 가능한 Pod 수를 계산하는 계산식은 다음과 같습니다.
# of ENI * (# of IPv4 per ENI - 1) + 2
인스턴스 타입별로 정의된 네트워크 인터페이스에서 사용 가능한 프라이빗 IP 주소에서 네트워크 인터페이스의 첫번째 IP를 제외한 갯수만큼 Pod를 사용할 수 있으며 각 Node에서 기본적으로 호스트 네트워크를 사용하는 Pod인 aws-node와 kube-proxy Pod를 위해 임의로 2를 더한 갯수로 계산해 최대 사용 가능한 Pod 수를 정의합니다.
위의 계산식을 통해 m5.large 타입의 인스턴스가 사용 가능한 Pod 수는 다음과 같이 계산할 수 있습니다.
3 * (10 - 1) + 2 = 29
샘플 Pod를 통해 배포 테스트를 진행해보았습니다.
-
m5.large 타입 인스턴스 노드를 1개 생성합니다.
$ kubectl get nodes -A NAME STATUS ROLES AGE VERSION ip-10-0-147-219.ec2.internal Ready <none> 98s v1.27.6-eks-a5df82a
-
클러스터에 노드가 1개만 존재하는 경우 호스트 네트워크를 사용하는 파드와 coredns 2개가 위치하게 됩니다.
$ kubectl get pods -A NAMESPACE NAME READY STATUS RESTARTS AGE kube-system aws-node-xn476 1/1 Running 0 3m22s kube-system coredns-79df7fff65-fspsl 1/1 Running 0 12m kube-system coredns-79df7fff65-psggs 1/1 Running 0 12m kube-system kube-proxy-628x7 1/1 Running 0 3m22s
-
샘플 Deployment의 replica를 25로 설정하여 배포합니다. [5]
$ kubectl get deployment -A NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE game-2048 deployment-2048 25/25 25 25 9m11s kube-system coredns 2/2 2 2 19m
-
Deployment Pod 25개, coredns Pod 2개, aws-node, kube-proxy까지 총 29개의 Pod가 정상 배포되었음을 확인할 수 있습니다.
$ kubectl get pods -A NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES game-2048 deployment-2048-8886b7b6b-2kr9j 1/1 Running 0 27s 10.0.147.98 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-2xfmd 1/1 Running 0 27s 10.0.151.226 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-446l4 1/1 Running 0 26s 10.0.149.12 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-5bpv8 1/1 Running 0 27s 10.0.153.85 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-7mgdp 1/1 Running 0 27s 10.0.145.12 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-7srh9 1/1 Running 0 27s 10.0.158.114 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-8hnkt 1/1 Running 0 82s 10.0.152.6 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-8vlmb 1/1 Running 0 26s 10.0.147.178 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-dz9rj 1/1 Running 0 26s 10.0.157.96 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-g9tbq 1/1 Running 0 27s 10.0.155.26 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-gmmbz 1/1 Running 0 82s 10.0.159.126 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-gnb84 1/1 Running 0 82s 10.0.148.62 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-hg2bp 1/1 Running 0 82s 10.0.152.145 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-jtx6k 1/1 Running 0 82s 10.0.147.52 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-kj8sh 1/1 Running 0 91s 10.0.157.169 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-ldmzd 1/1 Running 0 26s 10.0.159.74 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-lwsbb 1/1 Running 0 26s 10.0.152.77 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-mdsh9 1/1 Running 0 82s 10.0.159.157 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-pdslz 1/1 Running 0 26s 10.0.152.49 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-ptmm2 1/1 Running 0 26s 10.0.155.201 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-r86w4 1/1 Running 0 82s 10.0.145.3 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-tdzgd 1/1 Running 0 27s 10.0.145.24 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-tpjkf 1/1 Running 0 91s 10.0.158.178 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-v5r8w 1/1 Running 0 82s 10.0.157.250 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-xczrl 1/1 Running 0 26s 10.0.145.148 ip-10-0-147-219.ec2.internal <none> <none> kube-system aws-node-xn476 1/1 Running 0 10m 10.0.147.219 ip-10-0-147-219.ec2.internal <none> <none> kube-system coredns-79df7fff65-fspsl 1/1 Running 0 18m 10.0.156.64 ip-10-0-147-219.ec2.internal <none> <none> kube-system coredns-79df7fff65-psggs 1/1 Running 0 18m 10.0.154.128 ip-10-0-147-219.ec2.internal <none> <none> kube-system kube-proxy-628x7 1/1 Running 0 10m 10.0.147.219 ip-10-0-147-219.ec2.internal <none> <none>
-
Deployment Pod의 replica를 1개 증가시켜 26개로 증가하였습니다.
$ kubectl get deploymemt -A NAMESPACE NAME READY UP-TO-DATE AVAILABLE AGE game-2048 deployment-2048 25/26 26 25 10m kube-system coredns 2/2 2 2 20m
-
추가된 Pod 1개가
Too many Pod
메시지와 함께 Pending 상태로 유지됨을 확인할 수 있습니다.$ kubectl get pods -A -o wide ... game-2048 deployment-2048-8886b7b6b-j9h69 0/1 Pending 0 10s <none> <none> <none> <none>
$ kubectl describe pod deployment-2048-8886b7b6b-j9h69 -n game-2048 ... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 84s (x2 over 6m49s) default-scheduler 0/1 nodes are available: 1 Too many pods. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod..
DaemonSet이 있는 경우
DaemonSet을 배포한 경우에 어떤 영향이 있는지 살펴보겠습니다.
-
이전의 샘플에서 26개까지 증가시킨 Deployment를 0으로 초기화시키겠습니다.
$ kubectl scale -n game-2048 deploy/deployment-2048 --replicas 0 deployment.apps/deployment-2048 scaled
-
DaemonSet을 배포합니다.[6]
$ kubectl apply -f https://k8s.io/examples/controllers/daemonset.yaml
-
DaemonSet이 설치되었습니다.
$ kubectl get ds -A NAMESPACE NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE kube-system aws-node 1 1 1 1 1 <none> 32m kube-system fluentd-elasticsearch 1 1 1 1 1 <none> 8s kube-system kube-proxy 1 1 1 1 1 <none> 32m
$ kubectl get pods -A -o wide NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES kube-system aws-node-xn476 1/1 Running 0 24m 10.0.147.219 ip-10-0-147-219.ec2.internal <none> <none> kube-system coredns-79df7fff65-fspsl 1/1 Running 0 33m 10.0.156.64 ip-10-0-147-219.ec2.internal <none> <none> kube-system coredns-79df7fff65-psggs 1/1 Running 0 33m 10.0.154.128 ip-10-0-147-219.ec2.internal <none> <none> kube-system fluentd-elasticsearch-pmsf7 1/1 Running 0 69s 10.0.157.169 ip-10-0-147-219.ec2.internal <none> <none> kube-system kube-proxy-628x7 1/1 Running 0 24m 10.0.147.219 ip-10-0-147-219.ec2.internal <none> <none>
-
Deployment의 Replica를 다시 25로 증가시킵니다.
$ kubectl scale -n game-2048 deploy/deployment-2048 --replicas 25 deployment.apps/deployment-2048 scaled
$ kubectl get pods -A -o wide [23/11/5| 3:45PM] NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES game-2048 deployment-2048-8886b7b6b-6g4cv 0/1 Pending 0 56s <none> <none> <none> <none> game-2048 deployment-2048-8886b7b6b-6k7kt 1/1 Running 0 56s 10.0.157.96 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-7cfkn 1/1 Running 0 56s 10.0.159.126 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-7gd9r 1/1 Running 0 56s 10.0.159.74 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-8dg6s 1/1 Running 0 56s 10.0.145.3 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-9k9ss 1/1 Running 0 56s 10.0.155.15 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-9nzhc 1/1 Running 0 56s 10.0.145.148 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-fkj44 1/1 Running 0 56s 10.0.149.12 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-gpbqq 1/1 Running 0 56s 10.0.148.62 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-jlb5x 1/1 Running 0 56s 10.0.158.52 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-k7rn2 1/1 Running 0 56s 10.0.147.178 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-kjvp5 1/1 Running 0 56s 10.0.146.214 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-m2v8m 1/1 Running 0 56s 10.0.147.99 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-nnl5d 1/1 Running 0 56s 10.0.149.148 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-pqkl7 1/1 Running 0 56s 10.0.147.52 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-r87cf 1/1 Running 0 56s 10.0.145.222 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-rw8lr 1/1 Running 0 56s 10.0.159.157 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-s2cnk 1/1 Running 0 56s 10.0.155.201 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-t649h 1/1 Running 0 56s 10.0.152.49 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-thk4m 1/1 Running 0 56s 10.0.153.85 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-v2dsh 1/1 Running 0 56s 10.0.152.38 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-x6lv5 1/1 Running 0 56s 10.0.154.102 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-xwc5w 1/1 Running 0 56s 10.0.152.77 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-z7ql8 1/1 Running 0 56s 10.0.146.60 ip-10-0-147-219.ec2.internal <none> <none> game-2048 deployment-2048-8886b7b6b-z8vp9 1/1 Running 0 56s 10.0.158.178 ip-10-0-147-219.ec2.internal <none> <none> kube-system aws-node-xn476 1/1 Running 0 26m 10.0.147.219 ip-10-0-147-219.ec2.internal <none> <none> kube-system coredns-79df7fff65-fspsl 1/1 Running 0 35m 10.0.156.64 ip-10-0-147-219.ec2.internal <none> <none> kube-system coredns-79df7fff65-psggs 1/1 Running 0 35m 10.0.154.128 ip-10-0-147-219.ec2.internal <none> <none> kube-system fluentd-elasticsearch-pmsf7 1/1 Running 0 2m55s 10.0.157.169 ip-10-0-147-219.ec2.internal <none> <none> kube-system kube-proxy-628x7 1/1 Running 0 26m 10.0.147.219 ip-10-0-147-219.ec2.internal <none> <none>
$ kubectl describe pod deployment-2048-8886b7b6b-6g4cv -n game-2048 ... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 112s (x2 over 114s) default-scheduler 0/1 nodes are available: 1 Too many pods. preemption: 0/1 nodes are available: 1 No preemption victims found for incoming pod..
기존과 동일하게 25개로 Pod를 배포하였으나 Pod 1개가 Too many Pod
메시지와 함께 Pending 상태로 유지됨을 확인할 수 있습니다.
기존 25개 모두 배포되었을 때와 비교하면 현재는 인스턴스의 프라이빗 IP 중 하나가 사용중이지 않은 상태입니다.
이러한 예시를 통해 다음과 같은 내용을 확인할 수 있었습니다.
- --max-pods 에 호스트 네트워크를 사용하는 Pod의 갯수가 포함됩니다.
- 호스트 네트워크를 사용하는 Pod를 사용하는 경우, 사용 가능한 프라이빗 IP가 남아 있음에도 Pod를 배포할 수 없습니다.
이미 —max-pods 계산식에도 호스트 네트워크를 사용하는 aws-node, kube-proxy 파드에 대해 고려되어 +2가 포함되었으나 그 외에 사용자가 생성한 별도의 DaemonSet 혹은 Amazon EBS CSI 드라이버[7], Amazon EFS CSI 드라이버[8] 등을 사용하는 경우에는 프라이빗 IP를 사용하지 않는 Pod로 인해 프라이빗 IP는 남아있지만 더 이상 Pod를 배포할 수 없는 상황이 발생할 수 있습니다.
만일 클러스터 사용시 호스트 네트워크를 사용하는 Pod에 대해 예상할 수 있는 상황이신 경우에는, Pod 배치에 대해 사전에 고려하시어 —max-pods 값을 증가시켜 Node에 사용 가능한 프라이빗 IP 낭비 없이 Pod를 배치 하실 수 있습니다.
References:
[1] https://docs.aws.amazon.com/ko_kr/AWSEC2/latest/UserGuide/using-eni.html#AvailableIpPerENI
[2] https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/
[3] https://github.com/awslabs/amazon-eks-ami/blob/master/files/max-pods-calculator.sh
[4] https://github.com/awslabs/amazon-eks-ami/blob/master/files/eni-max-pods.txt
[6] https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/#create-a-daemonset
[7] https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/ebs-csi.html
[8] https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/efs-csi.html
관련 콘텐츠
- 질문됨 2년 전lg...
- AWS 공식업데이트됨 일 년 전
- AWS 공식업데이트됨 3년 전
- AWS 공식업데이트됨 2년 전
- AWS 공식업데이트됨 21일 전