AEWS 4기 2주차(1) - AWS VPC CNI(feat. K8s Networking)

2026. 3. 20. 22:20AWS 4기

Before Start..

본 글은 공식 문서 및 서종호(가시다)님의 AWS EKS 워크샵 내용을 기반으로 참고하여 학습 목적으로 작성하였습니다.
주관적인 해석이 포함되어 있어 사실과 다르거나 오류가 있을 수 있으니 참고용으로만 읽어주시기 바랍니다.

 

실습 환경 준비

Terraform 코드 준비

항목 설명
eks.tf EKS 워커 노드 보안 그룹 생성 및 설정, EKS 클러스터 생성 및 노드그룹 설정(userdata 작성), 애드온 설정(Warm Pool 설정 등)
outputs.tf 배포 완료된 EKS cluster 설정 업데이트
var.tf 변수 설정 - SSH Keypair, 접근 허용 IP(자택 IP), Cluster 정보, Kubernetes 버전, 워커노드 인스턴스(타입, 수량, 기본 스토리지 용량), 리전, vpc 및 subnet CIDR
vpc.tf VPC, Subnet 생성, DNS 설정, nat gateway 사용 여부(싱글,멀티 중). NACL 사용 여부, pub/priv CIDR 대역

배포 후 EKS 정보 확인

# EKS cluster 정보 확인
kubectl cluster-info

# 네임스페이스 default 변경
kubens default

# 노드 정보 확인(루트 디스크 용량, 노드 조회 시 API 엔드포인트 디버깅, label 조회)
kubectl get node --label-columns=node.kubernetes.io/instance-type,eks.amazonaws.com/capacityType,topology.kubernetes.io/zone
kubectl get node -v=6
kubectl get node --show-labels

# 파드 정보 확인
kubectl get pod -A
kubectl get pdb -n kube-system

# 관리형 노드 그룹 확인
aws eks describe-nodegroup --cluster-name myeks --nodegroup-name myeks-1nd-node-group | jq

# eks addon 확인
aws eks list-addons --cluster-name myeks | jq

Control plane(API 서버) URL 주소 확인

CoreDNS는 내부 Endpoint URL 주소를 디스커버리 하는 용도로 kube-system 네임스페이스에 등록되어 사용합니다. 만약, 외부 서비스를 호출하는 경우 노드 DNS로 전달해주는 역할도 같이 수행하는 점 참고부탁드립니다.

Node조회(API 서버 호출 이후 응답 값 확인)
tier=primary 라벨을 부여 받은 워커 노드 조회
myeks 클러스터에 적용된 Addon 확인

쿠버네티스 Label이란 ? 노드, 파드 , 서비스 등 리소스에 부여되는 Key-Value 한 쌍에 메타데이터입니다. 해당 Label을 통해 특정 리소스를 매핑하거나 그룹화할 수 있는 필터링이 가능합니다.

Kubernetes 네트워크 - 참고

공식 문서에서도 나와있지만 쿠버네티스 네트워킹 환경은 중요한 부분이고 4가지 형태로 구분할 수 있습니다.

  • 컨테이너 간 통신(Pod 안에 통신)
  • Pod to Pod 통신
  • Pod와 Service 통신
  • 외부와 Service 통신

Pod 내 컨테이너 간 통신

통신흐름 : 같은 Pod안에 있는 컨테이너들은 외부로 나가지 않고(별도의 NAT) 내부의 Loopback 인터페이스인 localhost 네트워크를 통해 다른 Port로 통신하게 됩니다.

같은 노드 내 Pod to Pod 통신

[ 통신흐름 ]

같은 노드에 있는 Pod끼리는 노드 간에 네트워크 통신이 필요 없이 Pod에 eth0과 가상의 연결선 veth0으로 통신하여 Virtual Bridge에서 같은 네트워크인지 확인 이후 노드 내에서 Pod간 통신이 가능

 

 

다른 노드에서 Pod to Pod 통신(overlay 통신)

 

[ 통신 흐름 ]

  • 파드 A에서(10.244.1.7) 트래픽이 발생하면 파드 내부에 있는 이더넷(pod eth0)에서 나와 노드와 연결된 가상의 연결선인 veth0와 cbr0라는 노드의 가상브릿지 네트워크로 들어옵니다.
  • 노드 A의 iptables를 확인하여 목적지 IP(Pod B의 IP)가 다른 노드에 있다는 것을 확인하고 패킷을 노드의 실제 물리 네트워크 인터페이스(eth0)로 내보냅니다.
  • CNI[Container Network Interface] 오버레이 네트워크를 타고 노드 B의 node eth0로 도착합니다.
  • 노드 B의 cbr0 및 veth0을 거쳐 Pod B와 연결된 pod eth0을 타고 최종적으로 파드 B 내부 컨테이너로 통신합니다.
Bridge : Virtual Bridge로 노드 내에 생성된 모든 Pod들은 해당 브릿지를 거쳐 같은 내에 Pod끼리 통신 시 분기시켜주는 스위치 역할을 합니다. 다른 노드에 Pod에 있는 경우 노드의 네트워크 인터페이스(node eth0)로 전달 역할
CNI Overlay : 다른 노드간 Pod to Pod 통신을 위한 라우팅(L3) 역할 및 패킷을 캡슐화하여 다른 노드로 통신시키는 터널 역할

(주의)Pod to Pod 연결 시 문제점
애플리케이션 버전을 업데이트하거나, 노드에 장애가 발생할 경우 삭제되고 생성되는 일이 빈번하게 발생합니다. 파드는 새로 생성될 때마다 새로운 IP 주소를 부여 받는데 소스 코드단에서 하드코딩으로 IP가 설정되어 있을 경우 서비스 장애로 이어질 수 있습니다.


이러한 이유로 Pod끼리도 통신이 필요한 경우 서비스를 통해 통신하는 것이 안전한 방법입니다.

외부(External)에서 Service 통신

 

[ 통신 흐름 ]

  • 1. 클라이언트가 Service로 접속하기 위해 Service(Nodeport, LoadBalancer) IP로 접근합니다.
  • 2. Master Node는 Service와 연결된 Pod가 변경될 수 있어 주기적으로 감시하고, Pod가 재생성되었을 경우 변경된 IP 정보를 kube-proxy에 전달합니다.
  • 3. kube-proxy는 변경된 내용을 각 노드에 iptables 정책을 업데이트하여 진입구간을 수정합니다.
  • 4. iptables 정책을 통해 목적지 POD로 통신하기 위해 DNAT로 변환합니다.
  • 5. Pod까지 진입 후 서비스가 정상적으로 실행되는 것을 확인할 수 있습니다.
결국, 쿠버네티스에 모든 통신은 Pod에 진입점을 만들기 위한 설정으로 Pod통신을 어떤식으로 효율적으로 할 것인지 확인이 필요하다

네트워크 기본 정보 확인

iptables 설정 확인
노드 IP 및 Pod IP 확인

Kubernetes 애드온(Add-on)

쿠버네티스에서는 여러가지 애드온을 설치하여 기능을 확장할 수 있어 아래 URL에서 수많은 애드온을 확인할 수 있습니다. 해당 애드온을 통해서 CSP에서 제공하는 여러가지 서비스를 호출하여 사용할 수 있습니다.

[ Add-on 종류 및 설치 ]

 

애드온 설치

참고: 이 섹션은 쿠버네티스에 필요한 기능을 제공하는 써드파티 프로젝트와 관련이 있다. 쿠버네티스 프로젝트 작성자는 써드파티 프로젝트에 책임이 없다. 이 페이지는 CNCF 웹사이트 가이드

kubernetes.io

 

오늘 작성할 내용은 쿠버네티스 Add-on 중에 AWS VPC CNI에 대해서 배운 내용을 정리하였습니다.

AWS VPC CNI(Amazon Virtual Private Cloud Container Network Interface)

AWS VPC CNI는 AWS EKS에서 기본적으로 사용하는 환경으로 Pod to Pod 통신, Pod to 다른 AWS 서비스(RDS 등) 간의 통신을 원활하게 만들어주는 기본 네트워크 플러그인입니다.

위 플러그인을 통하여 가상의 오버레이 네트워크를 거치지 않고 AWS의 ENI(Elastic Network Interface)를 직접 제어하기 때문에 네트워크 지연(Latency)이 매우 낮고, VPC Flow Logs나 Security Group 같은 AWS의 강력한 네트워크 및 보안 기능을 Pod 단위로 그대로 사용할 수 있다는 것이 가장 큰 장점입니다.

중요! Overlay network를 쓰지 않는 이유는 앞에서 설명하겠지만 Pod에 AWS ENI(Elastic Network Interface) Secondary IP를 Pod에 직접 할당하여 veth와 route table 정책만으로 통신이 가능하도록 설정합니다.

 

AWS VPC CNI - 핵심 구성 요소 - 참고

CNI 바이너리 (CNI Plugin)

  • 역할: 노드 내에서 Pod의 네트워킹을 실제로 구성하는 역할을 담당합니다.
  • 작동 방식: Kubelet이 Pod를 생성하거나 삭제할 때 이 바이너리를 호출합니다. 호스트 노드와 Pod 사이의 가상 네트워크 인터페이스(veth pair)를 연결하고 IP 주소를 설정합니다.
워커 노드의 CNI 관련 파일 경로 : cat /etc/cni/net.d/10-aws.conflist

L-IPAMD (Local IP Address Manager Daemon) , aws-node DaemonSet

  • 역할: 노드 레벨에서 AWS ENI(Elastic Network Interface)와 그에 속한 IP 주소들을 지속적으로 관리하는 aws-node 쿠버네티스 DaemonSet입니다. 
  • 작동 방식: AWS EC2 API와 통신하여 노드에 필요한 ENI를 추가/제거합니다. 또한, Pod가 배포될 때 IP를 즉시 할당받을 수 있도록 사용 가능한 'Warm Pool'을 미리 확보하고 유지합니다.
Warm Pool에 ENIs 및 IP 주소 수는 아래 환경 변수를 통해 구성됩니다.
  • WARM_ENI_TARGET (기본값: 1) - 미리 붙여둘 ENI 개수
  • WARM_IP_TARGET - 남겨둘 여유 IP 개수
  • MINIMUM_IP_TARGET - 최소 확보해야 할 IP 총량
항목 WARM_ENI_TARGET WARM_IP_TARGET MINIMUM_IP_TARGET
제어 단위 ENI IP IP
용도 단순 / 공격적 정밀 제어 초기 확보
권장 X (0 설정으로 관리 Off) O O
스케일 대응 매우 빠름 빠름 초기만 빠름
리소스 효율 낮음 높음 중간

AWS VPC CNI - Pod IP 추가 동작 방식

WS VPC CNI의 기본 동작 흐름 (보조 IP 할당)

  • 1. 관리자가 Pod 생성을 요청(yaml 배포)하면 Master Node의 API Server에 정보가 등록되고 스케줄링이 완료됩니다. 이후 해당 Worker Node의 kubelet이 이를 감지하고 Pod 생성 준비를 시작합니다.
  • 2. Worker Node에서는 신규 Pod에 대한 네트워크 환경 설정을 위해 VPC CNI를 호출합니다.
  • 3. VPC CNI는 L-IPAMD에게 Pod IP를 요청합니다.
  • 4. L-IPAMD는 Warm pool이 있는지 확인 후 Pod IP를 다시 VPC CNI에게 IP 값을 반환해줍니다.
  • 5. Pod IP를 반환 받은 VPC CNI는 System Kernel에게 반환 받은 IP에 대한 네임스페이스와 트래픽이 흐를 수 있도록 iptables 및 route table 정보에 대해서 수정 요청을 합니다.
  • 6. 위 설정이 끝난 이후 VPC CNI는 Worker Node에 kubelet에게 Pod IP 정보를 전달해줍니다.
  • 7. 최종적으로 kubelet은 컨테이너 런타임(Container Runtime)을 통해 할당받은 IP와 네트워크 환경이 적용된 신규 Pod(컨테이너)를 실행합니다.
접두사 모드(Prefix Mode) : 보조 IP 모드의 '파드 수 제한' 단점을 극복하고자 도입된 모드로 IP를 낱개로 가져 오는 대신 /28 크기의 IP 블록을 통째로 Warm pool로 사용할 수 있어 노드 내에서 Pod 수가 늘어날 수 있지만 IP 고갈 속도가 높아지는 단점 발생.

AWS VPC CNI - K8s CNI 비교

구분 K8s CNI AWS VPC CNI
네트워크 방식 Overlay - 물리 네트워크(NIC)에서 가상 네트워크를 만들어서 통신 Underlay - 물리 네트워크(ENI)를 Pod와 Node가 모두 같이 사용
Pod IP 주소 가상의 IP CIDR 사용 AWS VPC 서브넷의 IP CIDR 사용
통신 오버헤드 데이터 캡슐화로 인한 부하 높음 직접 통신(node to pod, pod to pod)으로 부하 적음
IP 제한성 가상 IP를 사용하여 고갈 문제 낮음 서브넷 IP CIDR을 사용하여 고갈 문제 높음
(노드, Pod 모두 같이 사용함)

 

aws-node 데몬셋 정보 확인

AWS VPC CNI는 작업자 노드에 aws-node라는 Kubernetes Daemonset으로 배포되어 작업자 노드가 프로비저닝되면 기본 ENI가 노드에 연결됩니다. 이때, CNI는 노드의 기본 ENI에 연결된 서브넷에서 ENI Pool과 Secondary IP 주소를 할당하려고 시도합니다.

 

aws-node Daemonset은 주기적으로 충분한 수의 ENI가 연결되어 있는지 확인합니다.

kubectl get ds aws-node -n kube-system -o json | jq '.spec.template.spec.containers[0].env'

# Warm Pool 설정 확인
kubectl describe ds aws-node -n kube-system | grep -E "WARM_ENI_TARGET|WARM_IP_TARGET|MINIMUM_IP_TARGET"

aws-node 환경설정 확인

노드 및 파드 정보 확인

노드 정보

# 워커 노드  공인 IP 확인
aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAdd:PublicIpAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table

# 워커 노드 ssh 접속 테스트
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -o StrictHostKeyChecking=no ec2-user@$i hostname; echo; done

# cni log 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i tree /var/log/aws-routed-eni ; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo cat /var/log/aws-routed-eni/plugin.log | jq ; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo cat /var/log/aws-routed-eni/ipamd.log | jq ; echo; done

# 네트워크 정보 확인 : eniY는 pod network 네임스페이스와 veth pair
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -br -c addr; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c addr; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done

# NAT 정보 확인
ssh ec2-user@$N1 sudo iptables -t nat -S
ssh ec2-user@$N1 sudo iptables -t nat -L -n -v

 

노드 내 인터페이스 간단 정보 확인
네트워크 인터페이스 자세한 정보 확인
node 라우트 정보 확인
항목 수집 로그
ebpf-sdk.log 최근 AWS CNI는 성능과 보안을 위해 eBPF 기술을 많이 사용합니다. 이 SDK가 커널 수준에서 네트워크 트래픽을 모니터링하거나 제어하면서 발생하는 로그
egress-v6-plugin.log IPv6 환경에서 외부로 나가는 트래픽(Egress)을 처리할 때 사용하는 플러그인의 기록
ipamd.log 워커 노드에 보조 IP가 몇 개나 남았는지와 새로운 파드가 생성될 때 어떤 IP를 할당했는지, 혹은 ENI(네트워크 인터페이스)를 새로 추가했는지 등의 IP 자원 관리 기록
network-policy-agent.log 쿠버네티스 파드에 네트워크를 연결이 필요한 경우 NetworkPolicy 리소스를 AWS VPC 수준에서 적용에 대한 에이전트 기록
plugin.log 워커 노드에 Kubelet이 파드에 네트워크를 연결하는 과정 중 네트워크 네임스페이스를 생성하고 인터페이스를 설정하는 작업 과정을 기록

plugin.log 확인 및 분석

  • 1. Constructed new logger instance 및 Received CNI add request - 로그 인스턴스 생성 및 요청 수신
  • 2. Prev Result & MTU value set is 9001 - 네트워크 기본 설정 확인 (MTU 및 NIC)
  • 3. pod requires multi-nic attachment - 다중 네트워크 인터페이스 필요 여부 : false
  • 4. Received add network response from ipamd for container 7dd29f~ - IP 주소 할당 성공 (ipamd 응답)
  • 4. SetupPodNetwork - 가상 인터페이스(veth) 쌍 생성
  • 5. Successfully set IPv6 ~ -IPv6 설정 및 라우팅 구성
  • 6. Using dummy interface - 더미 인터페이스(dummyf0431a65403) 및 네트워크 정책 적용

CoreDNS 정보 확인

# coredns 파드 IP 정보 확인
kubectl get pod -n kube-system -l k8s-app=kube-dns -owide

# 노드의 라우팅 정보 확인 >> EC2 네트워크 정보의 '보조 프라이빗 IPv4 주소'와 비교해보자
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done

# IpamD debugging commands
# https://github.com/aws/amazon-vpc-cni-k8s/blob/master/docs/troubleshooting.md
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i curl -s http://localhost:61679/v1/enis | jq; echo; done
# Ipamd 디버깅 확인
for i in $N1; do echo ">> node $i <<"; ssh ec2-user@$i curl -s http://localhost:61679/v1/enis | jq; echo; done
>> node 43.202.164.14 <<
{
  "0": {
    "TotalIPs": 10, # 해당 노드에 총 IP 갯수
    "AssignedIPs": 1,  # 실제 파드에 할당된 IP 갯수 
    "ENIs": {
      "eni-07d1575d2276ade7d": { # 1번째 ENI 확인
        "ID": "eni-07d1575d2276ade7d",
        "IsPrimary": false,
        "IsTrunk": false,
        "IsEFA": false,
        "DeviceNumber": 1,
        "AvailableIPv4Cidrs": {
          "192.168.4.132/32": {
            "Cidr": {
              "IP": "192.168.4.132",
              "Mask": "/////w=="
            },
            "IPAddresses": {},
            "IsPrefix": false,
            "AddressFamily": ""
          },
          "192.168.5.240/32": {
            "Cidr": {
              "IP": "192.168.5.240",
              "Mask": "/////w=="
            },
            "IPAddresses": {},
            "IsPrefix": false,
            "AddressFamily": ""
          },
          "192.168.5.5/32": {
            "Cidr": {
              "IP": "192.168.5.5",
              "Mask": "/////w=="
            },
            "IPAddresses": {},
            "IsPrefix": false,
            "AddressFamily": ""
          },
          "192.168.5.6/32": {
            "Cidr": {
              "IP": "192.168.5.6",
              "Mask": "/////w=="
            },
            "IPAddresses": {},
            "IsPrefix": false,
            "AddressFamily": ""
          },
          "192.168.7.212/32": {
            "Cidr": {
              "IP": "192.168.7.212",
              "Mask": "/////w=="
            },
            "IPAddresses": {},
            "IsPrefix": false,
            "AddressFamily": ""
          }
        },
        "IPv6Cidrs": {},
        "RouteTableID": 2
      },
      "eni-0d570f66604b18a08": { # 2번째 ENI 확인
        "ID": "eni-0d570f66604b18a08",
        "IsPrimary": true,
        "IsTrunk": false,
        "IsEFA": false,
        "DeviceNumber": 0,
        "AvailableIPv4Cidrs": {
          "192.168.4.203/32": {
            "Cidr": {
              "IP": "192.168.4.203",
              "Mask": "/////w=="
            },
            "IPAddresses": {},
            "IsPrefix": false,
            "AddressFamily": ""
          },
          "192.168.5.165/32": {
            "Cidr": {
              "IP": "192.168.5.165",
              "Mask": "/////w=="
            },
            "IPAddresses": { # CoreDNS가 사용 중인 IP 확인
              "192.168.5.165": {
                "Address": "192.168.5.165",
                "IPAMKey": {
                  "networkName": "aws-cni",
                  "containerID": "7dd29fa53366536d134277e325c2a67fe6f3fbc08c3d96ce4eeefbd46eb5d5d1",
                  "ifName": "eth0"
                },
                "IPAMMetadata": {
                  "k8sPodNamespace": "kube-system",
                  "k8sPodName": "coredns-d487b6fcb-k2mdk",
                  "interfacesCount": 1
                },
                "AssignedTime": "2026-03-25T11:47:05.818729623Z",
                "UnassignedTime": "0001-01-01T00:00:00Z"
              }
            },
            "IsPrefix": false,
            "AddressFamily": ""
          },
          "192.168.5.224/32": {
            "Cidr": {
              "IP": "192.168.5.224",
              "Mask": "/////w=="
            },
            "IPAddresses": {},
            "IsPrefix": false,
            "AddressFamily": ""
          },
          "192.168.7.118/32": {
            "Cidr": {
              "IP": "192.168.7.118",
              "Mask": "/////w=="
            },
            "IPAddresses": {},
            "IsPrefix": false,
            "AddressFamily": ""
          },
          "192.168.7.183/32": {
            "Cidr": {
              "IP": "192.168.7.183",
              "Mask": "/////w=="
            },
            "IPAddresses": {},
            "IsPrefix": false,
            "AddressFamily": ""
          }
        },
        "IPv6Cidrs": {},
        "RouteTableID": 254
      }
    }
  }
}

Network-Multitool 디플로이먼트 생성 - 참고

Network-Multitool은 컨테이너 네트워킹에 대해서 테스트와 트러블슈팅을 할 수 있는 Container Image입니다. 해당 Pod를 배포하여 어떤 IP를 사용 중인지와 어떤식으로 통신하는지 확인해보았습니다.

# 노드 모니터링
ssh ec2-user@$N1
watch -d "ip link | egrep 'ens|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"

ssh ec2-user@$N2
watch -d "ip link | egrep 'ens|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"

ssh ec2-user@$N3
watch -d "ip link | egrep 'ens|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"

 

# Network-Multitool 디플로이먼트 생성
cat <<EOF | kubectl apply -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: praqma/network-multitool
        ports:
        - containerPort: 80
        - containerPort: 443
        env:
        - name: HTTP_PORT
          value: "80"
        - name: HTTPS_PORT
          value: "443"
      terminationGracePeriodSeconds: 0
EOF

 

# 파드 이름 변수 지정
PODNAME1=$(kubectl get pod -l app=netshoot-pod -o jsonpath='{.items[0].metadata.name}')
PODNAME2=$(kubectl get pod -l app=netshoot-pod -o jsonpath='{.items[1].metadata.name}')
PODNAME3=$(kubectl get pod -l app=netshoot-pod -o jsonpath='{.items[2].metadata.name}')
echo $PODNAME1 $PODNAME2 $PODNAME3

# 파드 확인
kubectl get pod -o wide
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP

# 노드에 라우팅 정보 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done

Pod가 생성되면 IP가 할당되면서 라우트 정보에서 가상 인터페이스 목적지로 보낼 수 있도록 경로가 설정됩니다.

 

# 네트워크 인터페이스 정보 확인
ssh ec2-user@$N1

# ip 정보 확인
ip -br -c addr show
ip -c link
ip -c addr
ip route # 혹은 route -n

# 네임스페이스 정보 출력 -t net(네트워크 타입)
sudo lsns -t net

exit

워커 노드1에서 해당 네트워크 인터페이스를 확인할 수 있으며, 192.168.7.118 IP로 트래픽이 발생되는 경우 노드에서는 enif27945b3124 가상 인터페이스로 라우팅 되는 것을 볼수 있다.

 

Pod 내부 네트워크 확인

# 테스트용 파드 접속(exec) 후 Shell 실행
kubectl exec -it $PODNAME1 -- bash

# pod-1 Shell 에서 실행 : 네트워크 정보 확인

ip -c addr
ip -c route
route -n
ping -c 1 <pod-2 IP>
ps
cat /etc/resolv.conf
exit


# 파드2 Shell 실행
kubectl exec -it $PODNAME2 -- ip -c addr

# 파드3 Shell 실행
kubectl exec -it $PODNAME3 -- ip -br -c addr

  • 위 파드에서 보이는 169.254.1.1은 가상 게이트웨이 주소로 파드가 외부로 패킷을 보낼 때 정해진 경로로 트래픽을 보내기 위한 임시 경로 역할입니다.
  • 가상 게이트웨이에서 트래픽을 받으면 노드에 있는 veth인터페이스가 감지 후 실제 목적지로 배송하도록 유도합니다.
  • 다만 Pod는 실제 AWS VPC에서 사용중인 실제 IP로 노드 사이를 이동할 때는 별도의 캡슐화(Overlay network)를 하지 않고 전달이 가능합니다.
  • /etc/resolv.conf에 search 도메인을 통해서 서비스 이름만으로도 통신이 가능하도록 설정되어 있습니다.

 

AWS VPC CNI - 노드 간 파드 통신

앞서 쿠버네티스에서 다른 노드에서의 Pod 통신에 경우 Overlay 통신 기술을 사용하여 네트워크 패킷을 캡슐화하여 Pod끼리 안전하게 통신하게 할 수 있다고 설명하였습니다.

[본문 내용-다른 노드에서 Pod to Pod 통신(overlay 통신)]

 

일반적인 쿠버네티스 환경과 다르게 AWS VPC CNI는 다른 노드에서 Pod 통신이 필요한 경우 Overlay를 사용하지 않고 직접 통신하는지 확인하기 위해 아래 실습 내용을 진행하였습니다.

 

# 파드 IP 변수 지정
PODIP1=$(kubectl get pod -l app=netshoot-pod -o jsonpath='{.items[0].status.podIP}')
PODIP2=$(kubectl get pod -l app=netshoot-pod -o jsonpath='{.items[1].status.podIP}')
PODIP3=$(kubectl get pod -l app=netshoot-pod -o jsonpath='{.items[2].status.podIP}')
echo $PODIP1 $PODIP2 $PODIP3

# 파드1 Shell 에서 파드2로 ping 테스트
kubectl exec -it $PODNAME1 -- ping -c 2 $PODIP2
kubectl exec -it $PODNAME1 -- curl -s http://$PODIP2
kubectl exec -it $PODNAME1 -- curl -sk https://$PODIP2

# 파드2 Shell 에서 파드3로 ping 테스트
kubectl exec -it $PODNAME2 -- ping -c 2 $PODIP3

# 파드3 Shell 에서 파드1로 ping 테스트
kubectl exec -it $PODNAME3 -- ping -c 2 $PODIP1


# 워커 노드 EC2 : TCPDUMP 확인
## For Pod to external (outside VPC) traffic, we will program iptables to SNAT using Primary IP address on the Primary ENI.
sudo tcpdump -i any -nn icmp
sudo tcpdump -i ens5 -nn icmp
sudo tcpdump -i ens6 -nn icmp
sudo tcpdump -i eniYYYYYYYY -nn icmp

# 워커 노드1
# routing policy database management 확인
ip rule

# routing table management 확인
ip route show table local
ip route show table main
ip route show table 2

워커 노드3에 파드(192.168.7.118)가 워커 노드1의 Pod(192.168.10.185)로 핑(Ping)을 날리고 응답을 받은 상황

  1. enif27945b3124 In: 파드가 던진 패킷이 노드의 가상 인터페이스(enif)로 들어왔습니다. (파드 → 노드)
  2. ens5 Out: 노드가 이 패킷을 받고, 실제 물리 인터페이스(ens5)를 통해 외부 네트워크로 내보냈습니다. (노드 → 외부)
  3. ens5 In: 외부에서 보낸 응답 패킷이 노드의 물리 인터페이스(ens5)로 들어왔습니다. (외부 → 노드)
  4. enif27945b3124 Out: 노드가 이 응답을 확인하고, 목적지인 파드에게 전달하기 위해 가상 인터페이스(enif...)로 내보냈습니다. (노드 → 파드)

 

AWS VPC CNI - Pod → External 통신 - 참고

IPtables에 SNAT를 통하여 노드의 eth0(ens5) IP로 변경되어 외부와 통신합니다. 예를 들어 프라이빗 서브넷에 있는 EC2 서버가 NAT Gateway에 IP를 타고 외부 인터넷망으로 아웃바운드 통신하는 원리와 비슷합니다.

# pod-1 Shell 에서 외부로 ping
kubectl exec -it $PODNAME1 -- ping -c 1 www.google.com
kubectl exec -it $PODNAME1 -- ping -i 0.1 www.google.com
kubectl exec -it $PODNAME1 -- ping -i 0.1 8.8.8.8

# 워커 노드 EC2 : TCPDUMP 확인
sudo tcpdump -i any -nn icmp
sudo tcpdump -i ens5 -nn icmp

# 퍼블릭IP 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i curl -s ipinfo.io/ip; echo; echo; done

# 작업용 EC2 : pod-1 Shell 에서 외부 접속 확인 - 공인IP는 어떤 주소인가?
## The right way to check the weather - [링크](https://github.com/chubin/wttr.in)
for i in $PODNAME1 $PODNAME2 $PODNAME3; do echo ">> Pod : $i <<"; kubectl exec -it $i -- curl -s ipinfo.io/ip; echo; echo; done
kubectl exec -it $PODNAME1 -- curl -s wttr.in/seoul
kubectl exec -it $PODNAME1 -- curl -s wttr.in/seoul?format=3
kubectl exec -it $PODNAME1 -- curl -s wttr.in/Moon
kubectl exec -it $PODNAME1 -- curl -s wttr.in/:help

# 워커 노드 EC2
ip rule
ip route show table main
sudo iptables -L -n -v -t nat
sudo iptables -t nat -S

# 파드가 외부와 통신시에는 아래 처럼 'AWS-SNAT-CHAIN-0' 룰(rule)에 의해서 SNAT 되어서 외부와 통신!
# 참고로 뒤 IP는 eth0(ENI 첫번째)의 IP 주소이다
# --random-fully 동작 - [링크1](https://ssup2.github.io/issue/Linux_TCP_SYN_Packet_Drop_SNAT_Port_Race_Condition/)  [링크2](https://ssup2.github.io/issue/Kubernetes_TCP_Connection_Delay_VXLAN_CNI_Plugin/)
sudo iptables -t nat -S | grep 'A AWS-SNAT-CHAIN'

# 카운트 확인 시 AWS-SNAT-CHAIN-0에 매칭되어, 목적지가 192.168.0.0/16 아니고 외부 빠져나갈때 SNAT 192.168.1.251(EC2 노드1 IP) 변경되어 나간다!
sudo iptables -t filter --zero; sudo iptables -t nat --zero; sudo iptables -t mangle --zero; sudo iptables -t raw --zero
watch -d 'sudo iptables -v --numeric --table nat --list AWS-SNAT-CHAIN-0; echo ; sudo iptables -v --numeric --table nat --list KUBE-POSTROUTING; echo ; sudo iptables -v --numeric --table nat --list POSTROUTING'

# conntrack 확인 : EC2 메타데이터 주소(169.254.169.254) 제외 출력
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo conntrack -L -n |grep -v '169.254.169'; echo; done
외부 통신 가능 여부 확인
파드가 외부에 있는 서비스를 호출할 때 실제 노드에 할당된 ENI카드를 SNAT시켜서 외부와 통신한다. 단, 192.168.0.0/16(AWS VPC) 대역에 대해서는 통과(Return)시켜 내부 통신 시에는 외부로 나가는게 아닌 점 참고 부탁드립니다.

실습 Pod 삭제

kubectl delete deploy netshoot-pod

 

AWS VPC CNI - 설정 변경(Warm Pool 설정)

기존 설정 확인

# 모니터링
watch -d kubectl get pod -n kube-system -l k8s-app=aws-node # aws-node 데몬셋 파드 확인
watch -d eksctl get addon --cluster myeks # addon 확인

AWS console 워커 노드 2번 ENI 확인

 

우리가 terraform 코드를 배포할때 1개 ENI와 5개의 IP를 Warp Pool로 가져가도록 설정했습니다. 1,3번 노드에서는 coreDNS에 IP를 할당하면서 IP가 4개로 줄어들면서 ENI를 1개 더 추가하였다. 다만, 2번 노드에는 현재 파드가 없어서 ENI 1개로도 충분하기에 ENI 수가 다른 노드와 다른 점 참고 부탁드립니다.

Terraform 코드 수정

# add-on
  addons = {
    coredns = {
      most_recent = true
    }
    kube-proxy = {
      most_recent = true
    }
    vpc-cni = {
      most_recent = true
      before_compute = true
      configuration_values = jsonencode({
        env = {
          #WARM_ENI_TARGET = "1" # 현재 ENI 외에 여유 ENI 1개를 항상 확보
          WARM_IP_TARGET  = "5" # 현재 사용 중인 IP 외에 여유 IP 5개를 항상 유지, 설정 시 WARM_ENI_TARGET 무시됨
          MINIMUM_IP_TARGET   = "10" # 노드 시작 시 최소 확보해야 할 IP 총량 10개
          #ENABLE_PREFIX_DELEGATION = "true" 
          #WARM_PREFIX_TARGET = "1" # PREFIX_DELEGATION 사용 시, 1개의 여유 대역(/28) 유지
        }
      })
    }
  }

 

Terraform 코드 적용

terraform plan
terraform apply -auto-approve

워커 노드 2번 ENI 추가 확인

적용 확인

# 파드 재생성 확인
kubectl get pod -n kube-system -l k8s-app=aws-node

# addon 확인
eksctl get addon --cluster myeks
NAME            VERSION                 STATUS  ISSUES  IAMROLE UPDATE AVAILABLE        CONFIGURATION VALUES             NAMESPACE        POD IDENTITY ASSOCIATION ROLES
coredns         v1.13.2-eksbuild.3      ACTIVE  0                                                                        kube-system
kube-proxy      v1.34.5-eksbuild.2      ACTIVE  0                                                                        kube-system
vpc-cni         v1.21.1-eksbuild.5      ACTIVE  0                                       {"env":{"MINIMUM_IP_TARGET":"10","WARM_IP_TARGET":"5"}}   kube-system

# aws-node DaemonSet의 env 확인
kubectl get ds aws-node -n kube-system -o json | jq '.spec.template.spec.containers[0].env'
kubectl describe ds aws-node -n kube-system | grep -E "WARM_IP_TARGET|MINIMUM_IP_TARGET"

# 노드 정보 확인 : (hostNetwork 제외) 파드가 없는 노드에도 ENI 추가 확인!
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c addr; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done


# cni log 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i tree /var/log/aws-routed-eni ; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo cat /var/log/aws-routed-eni/plugin.log | jq ; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo cat /var/log/aws-routed-eni/ipamd.log | jq ; echo; done

# IpamD debugging commands  https://github.com/aws/amazon-vpc-cni-k8s/blob/master/docs/troubleshooting.md
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i curl -s http://localhost:61679/v1/enis | jq; echo; done
더보기

ipamd 로그에서도 여전히 워커 노드2에서는 별도의 Pod가 배포되지 않았지만 ENI가 1개가 추가되는 것을 알 수 있습니다.

>> node 13.125.199.102 <<
{
"0": {
"TotalIPs": 10,
"AssignedIPs": 0,
"ENIs": {
"eni-00177aee324650d78": {
"ID": "eni-00177aee324650d78",
"IsPrimary": true,
"IsTrunk": false,
"IsEFA": false,
"DeviceNumber": 0,
"AvailableIPv4Cidrs": {
"192.168.0.122/32": {
"Cidr": {
"IP": "192.168.0.122",
"Mask": "/////w=="
},
"IPAddresses": {},
"IsPrefix": false,
"AddressFamily": ""
},
"192.168.0.247/32": {
"Cidr": {
"IP": "192.168.0.247",
"Mask": "/////w=="
},
"IPAddresses": {},
"IsPrefix": false,
"AddressFamily": ""
},
"192.168.1.233/32": {
"Cidr": {
"IP": "192.168.1.233",
"Mask": "/////w=="
},
"IPAddresses": {},
"IsPrefix": false,
"AddressFamily": ""
},
"192.168.1.6/32": {
"Cidr": {
"IP": "192.168.1.6",
"Mask": "/////w=="
},
"IPAddresses": {},
"IsPrefix": false,
"AddressFamily": ""
},
"192.168.3.170/32": {
"Cidr": {
"IP": "192.168.3.170",
"Mask": "/////w=="
},
"IPAddresses": {},
"IsPrefix": false,
"AddressFamily": ""
}
},
"IPv6Cidrs": {},
"RouteTableID": 254
},
"eni-00cfe4bac414f8c11": {
"ID": "eni-00cfe4bac414f8c11",
"IsPrimary": false,
"IsTrunk": false,
"IsEFA": false,
"DeviceNumber": 1,
"AvailableIPv4Cidrs": {
"192.168.1.220/32": {
"Cidr": {
"IP": "192.168.1.220",
"Mask": "/////w=="
},
"IPAddresses": {},
"IsPrefix": false,
"AddressFamily": ""
},
"192.168.2.154/32": {
"Cidr": {
"IP": "192.168.2.154",
"Mask": "/////w=="
},
"IPAddresses": {},
"IsPrefix": false,
"AddressFamily": ""
},
"192.168.2.156/32": {
"Cidr": {
"IP": "192.168.2.156",
"Mask": "/////w=="
},
"IPAddresses": {},
"IsPrefix": false,
"AddressFamily": ""
},
"192.168.2.164/32": {
"Cidr": {
"IP": "192.168.2.164",
"Mask": "/////w=="
},
"IPAddresses": {},
"IsPrefix": false,
"AddressFamily": ""
},
"192.168.2.238/32": {
"Cidr": {
"IP": "192.168.2.238",
"Mask": "/////w=="
},
"IPAddresses": {},
"IsPrefix": false,
"AddressFamily": ""
}
},
"IPv6Cidrs": {},
"RouteTableID": 2
}
}
}
}

AWS EKS - 워커 노드 maxPods

쿠버네티스 공식 문서에서는 노드당 파드의 개수를 110개로 제한하고 있습니다. 다만, 1개 노드의 리소스가 얼마인지에 따라서 이보다 적게 생성될 수도 있고, 많이 생성될 수도 있습니다. 노드 내 설정된 maxPods에 설정을 변경할 수 있습니다.

 

EKS에 경우 노드에 적용되는 최종 maxPods 값은 특정 우선순위로 상호 작용하는 여러 구성 요소에 따라 달라집니다.

 

EKS(AWS VPC CNI) - maxPods 결정 방법(참고)

우선 순위
(위에서 아래로 낮아짐)
설명
관리형 노드 그룹 적용 사용자 지정 AMI 없이 관리형 노드 그룹을 사용하는 경우 Amazon EKS는 노드 사용자 데이터의 maxPods에 최대 한도를 적용합니다. vCPU가 30개 미만인 인스턴스의 경우 최대 한도는 110 입니다. vCPU가 30개를 초과하는 인스턴스의 경우 최대 한도는 250입니다. 이 값은 maxPodsExpression을 포함하여 다른 maxPods 구성보다 우선합니다.
kubelet maxPods 구성 kubelet 구성에서 직접 maxPods를 설정하는 경우(예: 사용자 지정 AMI를 사용하는 시작 템플릿을 통해) 이 값이 maxPodsExpression보다 우선
nodeadm NodeConfig에서 maxPodExpression을 사용하는 경우 nodeadm은 표현식을 평가하여 maxPods를 계산합니다. 이 방법은 우선순위가 더 높은 소스에 의해 값이 아직 설정되지 않은 경우에만 유효합니다.
기본 ENI 기반 계산 다른 값이 설정되지 않은 경우 AMI는 인스턴스 유형에서 지원하는 탄력적 네트워크 인터페이스 및 IP 주소 수를 기반으로 maxPods를 계산합니다. 이는 (number of ENIs × (IPs per ENI − 1)) + 2 공식과 동일합니다. + 2는 포드 IP 주소를 소비하지 않는 모든 노드에서 실행되는 Amazon VPC CNI 및 kube-proxy를 고려합니다.
maxPodExpressions(최대 Pod 생성 표현식) 정의 - 특정 상황에서 특정 노드 또는 인스턴스 유형에 대해 원하는 최대 Pod 값은 기본 계산과 다를 수 있습니다. nodeadm의 입력 소스로 정적 NodeConfig를 사용하는 것이 권장되므로, nodeadm은 kubelet이 전달되는 최종 maxPods 값을 결정하기 위해 maxPodsExpression을 허용합니다. 이 문자열은 환경 변수로 설정된 세 가지 변수를 포함하는 CEL(Common Expression Language) 표현식으로 해석됩니다. - 아래 링크 참고
 

Examples - Amazon EKS AMI

Examples Merging multiple configuration objects When using the IMDS configuration source (--config-source=imds://user-data), nodeadm will merge any configuration objects it discovers before configuring your node. With the following user data: MIME-Version:

awslabs.github.io

일반적인 쿠버네티스와 달리, AWS VPC CNI 환경에서는 EC2 인스턴스의 크기(타입)에 따라 물리적으로 부착할 수 있는 가상 랜카드(ENI)와 IP 개수가 정해져 있습니다. 따라서 노드에 파드를 몇 개까지 띄울 수 있는지 정확한 수식으로 계산해야 하며, 이 수식은 블로그 독자들의 이해를 돕는 아주 훌륭한 핵심 포인트가 됩니다.

모드별 계산 수식(Expression)과 그 의미를 명확하게 정리해 드리겠습니다.


1. 보조 IP 모드의 수식

가장 기본적으로 작동하는 상태에서의 노드당 최대 파드 개수 계산 공식입니다.

  • 수식: 최대 파드 수 = (ENI 개수 * (ENI당 IP(Primary + Secondary) 개수 - 1)) + 2

수식의 각 요소가 의미하는 부분

  • - 1이 들어가는 이유: 각 ENI가 호스트와 통신하기 위해 자체적으로 사용하는 기본 ENI(Primary) IP 1개를 제외하고, 순수하게 파드에게 나누어 줄 수 있는 '보조 IP'의 개수만 계산하기 위함입니다.
  • + 2가 들어가는 이유: K8s 클러스터를 구동하기 위한 필수 시스템 파드인 aws-node와 kube-proxy를 위한 공간입니다. 이 두 파드는 VPC CNI가 주는 보조 IP를 쓰지 않고 노드의 네트워크(HostNetwork)를 직접 사용하므로 계산식 끝에 항상 2를 더해줍니다.

2. 접두사 위임(Prefix Delegation) 모드의 수식

앞서 단점을 극복하기 위해 소개했던 접두사 모드를 켜면 수식이 이렇게 확장됩니다.

  • 수식: 최대 파드 수 = (ENI 개수 * ((ENI당 IP(Primary + Secondary) 개수 - 1) * 16)) + 2

바뀐 부분:

  • * 16이 추가된 이유: IP를 낱개(1개)로 가져오지 않고, 16개의 IP가 묶인 /28 블록(접두사) 단위로 꽂아 넣기 때문에 파드를 띄울 수 있는 공간이 16배로 뻥튀기됩니다.

실습 - MaxPod 확인

해당 실습을 통해 각 노드마다 몇개의 Pod가 할당될 수 있는지 확인해 보았습니다.

eks.tf 수정 - 모드변경( Secondary IP Mode → Prefix Mode ) 

  # add-on
  addons = {
    coredns = {
      most_recent = true
    }
    kube-proxy = {
      most_recent = true
    }
    vpc-cni = {
      most_recent = true
      before_compute = true
      configuration_values = jsonencode({
        env = {
          #WARM_ENI_TARGET = "1" # 현재 ENI 외에 여유 ENI 1개를 항상 확보
          #WARM_IP_TARGET  = "5" # 현재 사용 중인 IP 외에 여유 IP 5개를 항상 유지, 설정 시 WARM_ENI_TARGET 무시됨
          #MINIMUM_IP_TARGET   = "10" # 노드 시작 시 최소 확보해야 할 IP 총량 10개
          ENABLE_PREFIX_DELEGATION = "true" 
          #WARM_PREFIX_TARGET = "1" # PREFIX_DELEGATION 사용 시, 1개의 여유 대역(/28) 유지
        }
      })
    }
  }

 

모니터링(전·후 설정 변경) 및 Terraform 적용

# 모니터링
watch -d kubectl get pod -n kube-system -l k8s-app=aws-node # aws-node 데몬셋 파드 확인
watch -d eksctl get addon --cluster myeks # addon 확인

# 적용
terraform plan
terraform apply -auto-approve

# 기존 파드 재기동
kubectl rollout restart -n kube-system deployment coredns
kubectl rollout restart -n kube-system deployment kube-ops-view

# 파드 재생성 확인
kubectl get pod -n kube-system -l k8s-app=aws-node

# addon 확인
eksctl get addon --cluster myeks

# aws-node DaemonSet의 env 확인
kubectl get ds aws-node -n kube-system -o json | jq '.spec.template.spec.containers[0].env'

# IPv4 접두사 위임 확인
aws ec2 describe-instances --filters "Name=tag-key,Values=eks:cluster-name" "Name=tag-value,Values=myeks" \
  --query 'Reservations[*].Instances[].{InstanceId: InstanceId, Prefixes: NetworkInterfaces[].Ipv4Prefixes[]}' | jq

 

CNI 로그 확인 및 분석

# cni log 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i tree /var/log/aws-routed-eni; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo cat /var/log/aws-routed-eni/plugin.log | jq ; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo cat /var/log/aws-routed-eni/ipamd.log | jq ; echo; done

# IpamD debugging commands  https://github.com/aws/amazon-vpc-cni-k8s/blob/master/docs/troubleshooting.md
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i curl -s http://localhost:61679/v1/enis | jq; echo; done

 

최대 Pod 생성 및 모니터링

# 워커 노드 EC2 - 모니터링
while true; do ip -br -c addr show && echo "--------------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done

# 터미널1
watch -d 'kubectl get pods -o wide'

# 터미널2
## 디플로이먼트 생성
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 15
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:alpine
        ports:
        - containerPort: 80
EOF

# 파드 확인
kubectl get pod -o wide
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP

# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인
kubectl scale deployment nginx-deployment --replicas=30

# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인
kubectl scale deployment nginx-deployment --replicas=50
kubectl events

# cni log 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i tree /var/log/aws-routed-eni; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo cat /var/log/aws-routed-eni/plugin.log | jq ; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo cat /var/log/aws-routed-eni/ipamd.log | jq ; echo; done
  "msg": "IP stats for Network Card 0 - total IPs: 32, assigned IPs: 15, cooldown IPs: 0"

# IpamD debugging : IP 할당 가능하지만, maxPods 에서 제한됨!
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i curl -s http://localhost:61679/v1/enis | jq; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i curl -s http://localhost:61679/v1/enis | jq; echo; done | grep -E 'node|TotalIPs|AssignedIPs'
>> node 13.124.203.19 <<
    "TotalIPs": 32,
    "AssignedIPs": 15,
>> node 43.203.146.201 <<
    "TotalIPs": 33,
    "AssignedIPs": 15,
>> node 3.34.197.113 <<
    "TotalIPs": 33,
    "AssignedIPs": 15,
# 모니터링
while true; do kubectl describe node -l tier=primary | grep pods | uniq ; sleep 1; done
while true; do kubectl get pod | grep Pending | wc -l ; sleep 1; done

# 기본 정보 확인
cat /etc/kubernetes/kubelet/config.json | grep maxPods
cat /etc/kubernetes/kubelet/config.json.d/40-nodeadm.conf | grep maxPods

# sed 로 변경 : 기존 17 -> 변경 40
sudo sed -i 's/"maxPods": 17/"maxPods": 50/g' /etc/kubernetes/kubelet/config.json
sudo sed -i 's/"maxPods": 17/"maxPods": 50/g' /etc/kubernetes/kubelet/config.json.d/40-nodeadm.conf 

# 적용
sudo systemctl restart kubelet

 

# 현재 파드 갯수
kubectl get pod -l app=nginx --no-headers=true | wc -l

# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인
kubectl scale deployment nginx-deployment --replicas=60

# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인
kubectl scale deployment nginx-deployment --replicas=90

# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인
kubectl scale deployment nginx-deployment --replicas=110

# 파드 확인
kubectl events
kubectl get pod -o wide
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP

 

# cni log 확인 : maxPods 는 가능하지만, IP 할당 실패!
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i tree /var/log/aws-routed-eni; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo cat /var/log/aws-routed-eni/plugin.log | jq ; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo cat /var/log/aws-routed-eni/ipamd.log | jq ; echo; done

# IpamD debugging commands  https://github.com/aws/amazon-vpc-cni-k8s/blob/master/docs/troubleshooting.md
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i curl -s http://localhost:61679/v1/enis | jq; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i curl -s http://localhost:61679/v1/enis | jq; echo; done | grep -E 'node|TotalIPs|AssignedIPs'

# 노드 정보 확인 : maxPods 가 50으로 이번에는 Prefix mode 에서 최대 IP 할당 후에도 IP 부족!
kubectl describe node -l tier=primary | grep pods
kubectl describe node -l tier=primary

 

용어 정리

  • 1. ENI(Elastic Network Interface) - AWS에서 제공하는 네트워크 인터페이스
  • 2. CNI(Container Network Interface) - 컨테이너 네트워크 인터페이스 
  • 3. Warm Pool - Pod가 배포될 때 필요한 Secondary IP를 미리 확보되는 용어
  • 4. aws-node - 쿠버네티스 데몬셋으로 배포되어 노드 수준에서 IP주소 관리를 담당하는 프로세스입니다.