본문 바로가기
  • lakescript
스터디 이야기/AWS EKS

[AEWS] 2-2. AWS EKS Networking - Node & Pod Network

by lakescript 2024. 3. 12.

사전 준비

더보기

 

위와 같이 사전 준비가 필요합니다.

저번 글에서 설명했듯이, EKS를 배포하기 위한 VPC를 생성하고, Public Subnet, Private Subnet을 생성합니다. 그 후 EKS Cluster에 접근하기 위한 bastion EC2를 미리 생성합니다.

Node의 네트워크 정보

 

위의 그림은 worker node 1의 네트워크 구성도입니다.

 

현재 배포되어 있는 Pod 확인

kubectl get po -o wide -A

 

해당 명령어로 현재 Cluster에 배포되어 있는 모든 Pod들을 보겠습니다.

kube-system 네임스페이스에 각 노드별로 aws-node, kube-proxy가 띄어져 있고 하나의 node에만 제외하고 coredns pod가 띄어져 있습니다.

 

node별 ENI 확인

for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -br -c addr; echo; done

 

위의 명령어를 통해 worker ndoe의 네트워크 인터페이스 정보를 볼 수 있습니다.

node 192.168.1.219는 eth0, eth1 로 2개의 ENI가 생성되어 있고, 

node 192.168.2.192는 eht0 으로 1개의 ENI,

node 192.168.3.61는 eth0, eth1로 2개의 ENI가 생성되어 있습니다.

 

그런데, 위에서 전체 pod를 출력해서 보았을 때 하나의 노드에만 coredns가 배포되지 않았는데 ENI의 수가 안맞는 것을 보실 수 있습니다. 이것은 coredns 파드는 veth 으로 호스트에는 eniY@ifN 인터페이스와 파드에 eth0 과 연결되어 있다는 것을 의미 합니다. 그렇기에 192.168.2.192 node에는 kube-proxy와 aws-node의 ENI가 하나만 생성되어 있음을 알 수 있습니다.

 

여기서 잠깐! 

EC2 Instance Type별로 최대로 생성할 수 있는 ENI수와 IP 수가 정해져 있다는 것을 알아야 합니다. (그렇기에 최대로 생성할 수 있는 Pod수도 정해져 있게 됩니다.)

현재 node group의 EC2 Instance type 확인

aws eks list-nodegroups --cluster-name $CLUSTER_NAME | jq -r '.nodegroups[]' | xargs -I {} aws eks describe-nodegroup --cluster-name $CLUSTER_NAME --nodegroup-name {} | jq -r '.nodegroup.instanceTypes[0]'

 

현재 EC2 Type의 ip 값 확인하기

aws ec2 describe-instance-types --instance-types $EC2_TYPE | jq -r .InstanceTypes[0].NetworkInfo

 

여기서 출력된 값을 자세히 살펴보겠습니다.

{
  "NetworkPerformance": "Up to 5 Gigabit", // 인스턴스의 최대 네트워크 성능
  "MaximumNetworkInterfaces": 3, // 인스턴스에 연결할 수 있는 최대 네트워크 인터페이스(ENI) 수
  "MaximumNetworkCards": 1, // 인스턴스에 추가할 수 있는 최대 네트워크 카드 수
  "DefaultNetworkCardIndex": 0, // 기본 네트워크 카드의 인덱스
  "NetworkCards": [ // 네트워크 카드에 대한 세부 정보 배열
    {
      "NetworkCardIndex": 0, // 네트워크 카드의 인덱스
      "NetworkPerformance": "Up to 5 Gigabit", // 해당 네트워크 카드의 네트워크 성능
      "MaximumNetworkInterfaces": 3, // 해당 네트워크 카드에 연결할 수 있는 최대 네트워크 인터페이스 수
      "BaselineBandwidthInGbps": 0.256, // 기본 대역폭(기가비트/초)
      "PeakBandwidthInGbps": 5 // 최대 대역폭(기가비트/초)
    }
  ],
  "Ipv4AddressesPerInterface": 6, // 각 네트워크 인터페이스당 할당할 수 있는 최대 IPv4 주소 수
  "Ipv6AddressesPerInterface": 6, // 각 네트워크 인터페이스당 할당할 수 있는 최대 IPv6 주소 수
  "Ipv6Supported": true, // 인스턴스가 IPv6를 지원하는지 여부
  "EnaSupport": "required", // Elastic Network Adapter(ENA) 지원 여부. "required"는 필수적으로 필요함을 의미
  "EfaSupported": false, // Elastic Fabric Adapter(EFA) 지원 여부
  "EncryptionInTransitSupported": false, // 데이터 전송 중 암호화 지원 여부
  "EnaSrdSupported": false // ENA의 Shared Receive Queue(SRQ) 기능 지원 여부
}

 

여기서 먼저 MaximumNetworkInterfaces를 보시면 이 인스턴스 타입엔 최대 3개의 ENI를 생성할 수 있고, Ipv4AddressesPerInterface를 통해 각 ENI별로 할당할 수 있는 최대 IP 주소 수를 확인하실 수 있습니다.

 

t3.medium인 경우 3개의 ENI를 생성할 수 있고, 최대 6개의 IP를 생성할 수 있는데 자기 자신의 IP를 제외하여 각 ENI당 5개의 IP를 할당할 수 있습니다.

 

 

그래서 현재 배포되어 있는 worker node별 ENI를 보겠습니다.

 

node 192.168.1.219는 eth0, eth1 로 2개의 ENI가 생성되어 있어 연결할 수 있는 ip 수는 10개,

node 192.168.2.192는 eh0 으로 1개의 ENI가 생성되어 있어 연결할 수 있는 ip 수는 5개,

node 192.168.3.61는 eth0, eth1로 2개의 ENI가 생성되어 있어 연결할 수 있는 IP수는 10개입니다.

 

 

t3.medium의 경우 3개의 ENI를 생성할 수 있고, 최대 15개의 IP를 부여가 가능합니다. (현재 node에 ENI가 없더라도 pod가 늘어나면 자동적으로 최대 3개까지 생성됩니다.)

 

이러한 EC2 Type별 최대 배포 가능 Pod수는 아래와 같이 계산하실 수 있습니다.

최대 파드 생성 갯수
: (Number of network interfaces for the instance type * (the number of IP addressess per network interface - 1)) + 2

 

 

최대로 생성가능한 Pod 수 확인하기

아래의 문서를 통해 EC2 Type별 최대 생성 가능한 Pod의 수를 확인해보겠습니다.

https://github.com/awslabs/amazon-eks-ami/blob/master/files/eni-max-pods.txt

 

 

 

 

t3.medium일 경우 17개의 Pod를 생성할 수 있는 것을 확인할 수 있습니다.

 

해당 값은 명령어로도 확인할 수 있습니다.

kubectl get nodes -o jsonpath="{range .items[*]}{.metadata.labels['beta\.kubernetes\.io\/instance-type']}{'\t'}{.status.capacity.pods}{'\n'}{end}"

 

그렇다면 t3.medium의 경우 17개의 Pod만 생성해야 하고, 그 이상의 Pod를 배포해야 한다면 Worker Node의 EC2 Type을 scale-up 해야 할 까요?

 

아닙니다!

https://aws.amazon.com/ko/about-aws/whats-new/2021/07/amazon-virtual-private-cloud-vpc-customers-can-assign-ip-prefixes-ec2-instances/

 

 

AWS VPC CNI의 IP prefixes 기능을 사용해 최대로 생성할 수 있는 파드 제한 보다 더 많은 pod를 생성할 수 있습니다.

 

ChatGPT가 알려주는 AWS VPC CNI의 IP Prefix
Amazon VPC CNI의 Prefix 모드는 Amazon EC2 네트워크 인터페이스에 네트워크 Prefix를 할당하여 노드에 사용 가능한 IP 주소 수를 늘리고 노드당 파드 밀도를 높이는 기능입니다. 이 모드는 Amazon VPC CNI 애드온 버전 1.9.0 이상에서 사용할 수 있으며, 네트워크 인터페이스에 개별 보조 IP 주소를 할당하는 대신 IPv4 및 IPv6 CIDR을 할당할 수 있습니다.
- IPv6 지원: IPv6 클러스터에서는 Prefix 모드만 지원되며 기본적으로 활성화됩니다. VPC CNI는 ENI의 슬롯에 /80 IPv6 Prefix를 할당합니다.
- IPv4 Prefix 할당: 인스턴스 유형당 최대 ENI 수는 동일하게 유지되지만, 네트워크 인터페이스의 슬롯에 개별 IPv4 주소를 할당하는 대신 /28(16개의 IP 주소) IPv4 주소 Prefix를 할당할 수 있습니다. ENABLE_PREFIX_DELEGATION이 true로 설정되면, CNI는 ENI에 할당된 Prefix에서 파드에 IP 주소를 할당합니다.

 

 

즉, 인스턴스 유형당 최대 ENI 수는 동일하게 유지되지만 네트워크 인터페이스의 슬롯에 개별 IPv4 주소를 할당하는 대신 /28(16개의 IP 주소) IPv$ 주소를 할당하여 기존에 비해 연결할 수 있는 IPv4의 갯수가 약 16배가 증가 되는 기능입니다.

 

Prefix 모드로 인한 최대 Pod 생성 갯수
: ( ENI * (ENI당 할당 가능한 IPv4 개수 -1) * 16

 

Prefix 모드를 통해 t3.medium일 경우 최대로 생성 가능한 Pod 수를 계산해보겠습니다.

 

( 3 * ( 6 - 1 ) * 16 ) = 240개가 됩니다.

 

최대로 생성가능한 Pod 수 늘리기

kubectl set env daemonset aws-node -n kube-system ENABLE_PREFIX_DELEGATION=true

 

위의 명령어를 통해 prefix mode를 활성화 시켜줍니다. (노드가 AWS Nitro를 기반이어야 합니다.)

 

curl -s -O https://github.com/awslabs/amazon-eks-ami/blob/master/files/max-pods-calculator.sh
chmod +x max-pods-calculator.sh
./max-pods-calculator.sh --instance-type $(aws eks list-nodegroups --cluster-name $CLUSTER_NAME | jq -r '.nodegroups[]' | xargs -I {} aws eks describe-nodegroup --cluster-name $CLUSTER_NAME --nodegroup-name {} | jq -r '.nodegroup.instanceTypes[0]' --cni-version ``1.9``.0 --cni-prefix-delegation-enabled

 

그 후 max-pods-calculator.sh 를 다운로드 받아와 실행 권한을 주고 prefix mode로 설정 시 생성할 수 있는 최대 Pod 수를 확인합니다. ( vCPU가 30개 미만인 인스턴스의 최대 Pod 수는 110이고, 그 외의 경우 최대 Pod 수는 250입니다.)

 

 

# WARM_PREFIX_TARGET
kubectl set env ds aws-node -n kube-system WARM_PREFIX_TARGET=1

# WARM_IP_TARGET
kubectl set env ds aws-node -n kube-system WARM_IP_TARGET=5

# MINIMUM_IP_TARGET 
kubectl set env ds aws-node -n kube-system MINIMUM_IP_TARGET=2

 

문서를 참고하여 WARM_PREFIX_TARGETWARM_IP_TARGETMINIMUM_IP_TARGET 값을 설정합니다. (단, WARM_IP_TARGET이나 MINIMUM_IP_TARGET 하나의 값이 설정되면 WARM_PREFIX_TARGET에 설정된 모든 값을 재정의합니다.)

 

ChatGPT가 알려주는 WARM_PREFIX_TARGET, WARM_IP_TARGET, MINIMUM_IP_TARGET
- ENABLE_PREFIX_DELEGATION 활성화 시: 이 옵션이 true로 설정되면, IPAMD는 ENI에 (/28) 프리픽스를 할당하기 시작합니다. WARM_PREFIX_TARGET 또는 WARM_IP_TARGET 및 MINIMUM_IP_TARGET 환경 변수 중 하나라도 0으로 설정하는 것은 프리픽스 위임이 활성화된 상태에서 지원되지 않습니다. 이러한 설정 중 하나를 0으로 설정하면, IPAMD가 웜 풀에 추가 프리픽스를 유지 관리하지 않아 파드에 IP를 할당하는 속도가 크게 느려집니다.
- 설정 우선순위: WARM_IP_TARGET 및 MINIMUM_IP_TARGET이 설정되면 WARM_PREFIX_TARGET을 오버라이드합니다. WARM_PREFIX_TARGET은 하나의 IP만 사용되더라도 전체 (/28) 프리픽스를 할당합니다. ENI에 프리픽스를 할당할 공간이 없으면 새 ENI가 생성됩니다. 따라서, 파드 밀도가 높은 경우에만 이 옵션을 사용하는 것이 좋습니다.
- ENI 및 프리픽스 할당: 새 ENI가 할당될 때, IPAMD는 WARM_PREFIX_TARGET 또는 WARM_IP_TARGET 및 MINIMUM_IP_TARGET 설정을 유지하기 위해 필요한 프리픽스 수를 결정하고, 해당 프리픽스를 할당합니다. 이는 EC2 호출을 최소화하고, ENI를 활성화할 때 추가 프리픽스를 나중에 해제하는 것을 피하기 위함입니다. 새 ENI는 기존 ENI에 할당된 모든 프리픽스를 소진한 후에만 할당됩니다.

 

 

EKS의 경우 Prefix 모드가 설정된 AWS VPC CNI를 가진 nodegroup을 새로 생성해야 합니다.

 

eksctl create nodegroup --cluster ${MyCluster} --max-pods-per-node 110

 

 

생성 후 아래의 명령어로 최대 생성가능한 Pod 수를 확인합니다.

kubectl describe node $NodeName | grep 'pods'

 

 

ENI 생성해보기

그렇다면 pod를 생성하면 ENI가 생성되는지 한번 실습해보겠습니다!

cat <<EOF | kubectl create -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: netshoot-pod
spec:
  replicas: 3
  selector:
    matchLabels:
      app: netshoot-pod
  template:
    metadata:
      labels:
        app: netshoot-pod
    spec:
      containers:
      - name: netshoot-pod
        image: nicolaka/netshoot
        command: ["tail"]
        args: ["-f", "/dev/null"]
      terminationGracePeriodSeconds: 0
EOF

 

위의 명령어를 통해 각 node별로 pod를 하나씩 생성해보겠습니다.

 

 

그 후 다시 ENI와 할당된 IP를 확인해보겠습니다.

 

 

 

2번째 EC2 worker node의 ENI가 하나 추가되고, 해당 IP 주소가 늘어난 것을 확인하실 수 있습니다!

 

즉, 파드가 생성되면, 워커 노드eniY@ifN 추가되고 라우팅 테이블에도 정보가 추가 됩니다.

 

Node간 Pod 통신

 

 

AWS VPC CNi를 사용하기 때문에  별도의 오버레이(터널링) 통신 기술 없이, 파드간 직접 통신이 가능합니다.

 

Pod에서 외부 통신

각 node별 public IP 확인

for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i curl -s ipinfo.io/ip; echo; echo; done

 

위의 명령어는 각 node에 접근하여 curl ipinfo.io/ip 명령어를 실행하는 명령어 입니다.

 

이 주소는 곧 node의 public IP 입니다.

 

 

 

 

Pod 내부에서 외부 통신 해보기

kubectl exec -it $PODNAME1 -- ping -i 0.1 www.google.com

 

왼쪽의 사진은 pod1 에서 www.google.com  으로 ICMP 요청을 보낼 때의 모습이고 오른쪽은 실제로 pod1이 띄어진 node에서 tcpdump를 확인한 모습입니다.

이를 통해, pod가 외부의 통신할 때는 iptable의 SNAT을 통하여 노드의 eth0 IP로 변경되어서 외부와 통신한다는 것을 확인 할 수 있습니다.

 

 


 

Reference

- https://github.com/aws/amazon-vpc-cni-k8s/blob/master/docs/cni-proposal.md

- https://github.com/aws/aws-eks-best-practices/blob/master/content/networking/prefix-mode/index_linux.ko.md

- https://github.com/awslabs/amazon-eks-ami/blob/master/files/eni-max-pods.txt

- https://aws.amazon.com/ko/about-aws/whats-new/2021/07/amazon-virtual-private-cloud-vpc-customers-can-assign-ip-prefixes-ec2-instances/

- https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-prefix-eni.html

- https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/cni-increase-ip-addresses.html