Ssoon

[9주차] AWS EKS : VPC CNI : 노드 간 파드 통신 / 파드에서 외부 통신 / 노드에 파드 생성 갯수 제한 본문

쿠버네티스 네트워크 스터디 3기

[9주차] AWS EKS : VPC CNI : 노드 간 파드 통신 / 파드에서 외부 통신 / 노드에 파드 생성 갯수 제한

구구달스 2024. 10. 28. 22:02
CloudNet@ 가시다님이 진행하는 쿠버네티스 네트워크 스터디 3기

✅노드 간 파드 통신

  • AWS VPC CNI를 사용하면 각 파드에 직접적으로 VPC의 IP를 할당받기 때문에, 파드가 AWS 네트워크의 일부처럼 동작하게 됩니다.
  • 이렇게 파드들이 동일한 VPC 네트워크 내에서 연결되기 때문에 별도의 오버레이가 필요하지 않으며, 네트워크 계층을 추가할 필요가 없어 성능 손실이 줄어들고 네트워크 지연이 최소화됩니다.

🧿 netshoot-pod 각각의 IP 주소를 PODIP1, PODIP2, PODIP3 변수에 저장

(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# PODIP1=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[0].status.podIP})
(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# PODIP2=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[1].status.podIP})
(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# PODIP3=$(kubectl get pod -l app=netshoot-pod -o jsonpath={.items[2].status.podIP})

🧿 PODNAME1 에서 PODNAME2 로 ping 테스트

  • 송신 Pod (PODNAME1): 192.168.3.104 주소에서 192.168.2.172로 ICMP echo request 패킷이 송신되고, ICMP echo reply가 수신되는 것을 볼 수 있습니다.
  • 수신 Pod (PODNAME2): 192.168.2.172 주소에서 192.168.3.104로 ICMP 요청을 받고, 이에 대한 응답을 반환하는 패킷이 확인되었습니다.
(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# kubectl exec -it $PODNAME1 -- ping -c 2 $PODIP2
PING 192.168.2.172 (192.168.2.172) 56(84) bytes of data.
64 bytes from 192.168.2.172: icmp_seq=1 ttl=125 time=1.74 ms
64 bytes from 192.168.2.172: icmp_seq=2 ttl=125 time=1.32 ms

--- 192.168.2.172 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.322/1.533/1.744/0.211 ms

< PODNAME1 >
[ec2-user@ip-192-168-3-215 ~]$ sudo tcpdump -i any -nn icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
13:16:02.116002 IP 192.168.3.104 > 192.168.2.172: ICMP echo request, id 135, seq 1, length 64
13:16:02.116046 IP 192.168.3.104 > 192.168.2.172: ICMP echo request, id 135, seq 1, length 64
13:16:02.117463 IP 192.168.2.172 > 192.168.3.104: ICMP echo reply, id 135, seq 1, length 64
13:16:02.117570 IP 192.168.2.172 > 192.168.3.104: ICMP echo reply, id 135, seq 1, length 64
13:16:03.117695 IP 192.168.3.104 > 192.168.2.172: ICMP echo request, id 135, seq 2, length 64
13:16:03.117732 IP 192.168.3.104 > 192.168.2.172: ICMP echo request, id 135, seq 2, length 64
13:16:03.119014 IP 192.168.2.172 > 192.168.3.104: ICMP echo reply, id 135, seq 2, length 64
13:16:03.119031 IP 192.168.2.172 > 192.168.3.104: ICMP echo reply, id 135, seq 2, length 64

< PODNAME2 >
[ec2-user@ip-192-168-2-59 ~]$ sudo tcpdump -i any -nn icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
13:16:02.116699 IP 192.168.3.104 > 192.168.2.172: ICMP echo request, id 135, seq 1, length 64
13:16:02.116869 IP 192.168.3.104 > 192.168.2.172: ICMP echo request, id 135, seq 1, length 64
13:16:02.116884 IP 192.168.2.172 > 192.168.3.104: ICMP echo reply, id 135, seq 1, length 64
13:16:02.116896 IP 192.168.2.172 > 192.168.3.104: ICMP echo reply, id 135, seq 1, length 64
13:16:03.118377 IP 192.168.3.104 > 192.168.2.172: ICMP echo request, id 135, seq 2, length 64
13:16:03.118408 IP 192.168.3.104 > 192.168.2.172: ICMP echo request, id 135, seq 2, length 64
13:16:03.118431 IP 192.168.2.172 > 192.168.3.104: ICMP echo reply, id 135, seq 2, length 64
13:16:03.118439 IP 192.168.2.172 > 192.168.3.104: ICMP echo reply, id 135, seq 2, length 64

🧿 aws-node-gqkth (192.168.1.111) 노드의 라우팅 설정을 확인

목적지 게이트웨이 서브넷 마스크 인터페이스  
0.0.0.0 192.168.1.1 0.0.0.0 eth0 기본 경로. 명시된 목적지가 없는 트래픽은 eth0을 통해 192.168.1.1로 전달됩니다.
169.254.169.254 0.0.0.0 255.255.255.255 eth0 AWS 메타데이터 서버로의 경로. eth0을 통해 접근합니다.
192.168.1.0/24 0.0.0.0 255.255.255.0 eth0 192.168.1.0/24 서브넷은 eth0 인터페이스를 통해 직접 연결됩니다.
192.168.1.153 0.0.0.0 255.255.255.255 enif1e092ddbf8 192.168.1.153 IP는 ENI enif1e092ddbf8 인터페이스를 통해 연결됩니다.
192.168.1.187 0.0.0.0 255.255.255.255 eni89edfeecc39 192.168.1.187 IP는 ENI eni89edfeecc39 인터페이스를 통해 연결됩니다.
[ec2-user@ip-192-168-1-111 ~]$ ip route show table main
default via 192.168.1.1 dev eth0
169.254.169.254 dev eth0
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.111
192.168.1.153 dev enif1e092ddbf8 scope link
192.168.1.187 dev eni89edfeecc39 scope link

✅ 파드에서 외부 통신

  • 파드의 외부 통신 시, iptables의 SNAT 규칙이 파드의 프라이빗 IP를 노드의 eth0 IP로 변환하여 외부와 통신할 수 있게 하고, 외부에서는 노드 IP를 통해 파드의 요청에 응답할 수 있습니다. 

🧿 PODNAME1 (192.168.3.104) 가 외부 인터넷(구글)에 연결을 시도하고, 그와 동시에tcpdump로 네트워크 트래픽을 캡처하여 확인

  • netshoot-pod-74b7555dc7-4jzzz (192.168.3.104) 과 aws-node-mqz2q  (192.168.3.215) 에서 142.250.207.100으로 ICMP echo 요청(ping 요청)이 발생했습니다. 이 주소들은 각각 Pod와 Bastion 서버의 IP
  • ICMP echo request: 이 부분은 구글 서버로 향한 ping 요청입니다.
  • ICMP echo reply: 구글 서버에서 응답이 왔으며, 원래의 요청을 보낸 192.168.3.104와 192.168.3.215로 각각 돌아왔습니다.
(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# kubectl exec -it $PODNAME1 -- ping -c 1 www.google.com
PING www.google.com (142.250.207.100) 56(84) bytes of data.
64 bytes from kix06s11-in-f4.1e100.net (142.250.207.100): icmp_seq=1 ttl=47 time=38.8 ms

--- www.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 38.779/38.779/38.779/0.000 ms


[ec2-user@ip-192-168-3-215 ~]$ sudo tcpdump -i any -nn icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
13:19:30.442365 IP 192.168.3.104 > 142.250.207.100: ICMP echo request, id 160, seq 1, length 64
13:19:30.442386 IP 192.168.3.215 > 142.250.207.100: ICMP echo request, id 47011, seq 1, length 64
13:19:30.481071 IP 142.250.207.100 > 192.168.3.215: ICMP echo reply, id 47011, seq 1, length 64
13:19:30.481104 IP 142.250.207.100 > 192.168.3.104: ICMP echo reply, id 160, seq 1, length 64

🧿 $N1, $N2, $N3에 해당하는 각 노드에 SSH를 통해 접속한 뒤, 외부 IP 주소를 확인

(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i curl -s ipinfo.io/ip; echo; echo; done
>> node 192.168.1.111 <<
13.125.157.40

>> node 192.168.2.59 <<
3.36.12.174

>> node 192.168.3.215 <<
13.124.252.15

🧿 $PODNAME1, $PODNAME2, $PODNAME3에 해당하는 각 Pod에서 외부 IP 주소를 조회

(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# for i in $PODNAME1 $PODNAME2 $PODNAME3; do echo ">> Pod : $i <<"; kubectl exec -it $i -- curl -s ipinfo.io/ip; echo; echo; done
>> Pod : netshoot-pod-74b7555dc7-4jzzz <<
13.124.252.15

>> Pod : netshoot-pod-74b7555dc7-7xcn5 <<
3.36.12.174

>> Pod : netshoot-pod-74b7555dc7-n9tjg <<
13.125.157.40

🧿 aws-node-mqz2q (192.168.3.215) 네트워크 테이블의 라우팅 정보

목적지 게이트웨이 서브넷 마스크 인터페이스  
default 192.168.3.1 eth0 - 외부 네트워크로 가는 기본 경로
169.254.169.254 - eth0 - AWS 메타데이터 서버와의 통신 경로
192.168.3.0/24 - eth0 link 동일 서브넷(192.168.3.x 대역)과의 통신
192.168.3.40 - enicb023416899 link 192.168.3.40 IP와의 직접 통신 경로
192.168.3.104 - eni3ef89bef356 link 192.168.3.104 IP와의 직접 통신 경로
[ec2-user@ip-192-168-3-215 ~]$ ip route show table main
default via 192.168.3.1 dev eth0
169.254.169.254 dev eth0
192.168.3.0/24 dev eth0 proto kernel scope link src 192.168.3.215
192.168.3.40 dev enicb023416899 scope link
192.168.3.104 dev eni3ef89bef356 scope link

🧿 AWS EC2 인스턴스의 NAT(Network Address Translation) 테이블에서 설정된 규칙을 확인

  1. -A AWS-SNAT-CHAIN-0 -d 192.168.0.0/16 -m comment --comment "AWS SNAT CHAIN" -j RETURN:
    • -A AWS-SNAT-CHAIN-0: AWS-SNAT-CHAIN-0이라는 체인에 규칙을 추가하는 것을 의미합니다.
    • -d 192.168.0.0/16: 목적지 IP가 192.168.0.0부터 192.168.255.255까지인 경우를 지정합니다.
    • -m comment --comment "AWS SNAT CHAIN": 이 규칙에 대한 설명(코멘트)을 추가합니다.
    • -j RETURN: 이 규칙이 적용되면 처리를 종료하고 다음 규칙으로 넘어갑니다.
  2. -A AWS-SNAT-CHAIN-0 ! -o vlan+ -m comment --comment "AWS, SNAT" -m addrtype ! --dst-type LOCAL -j SNAT --to-source 192.168.3.215 --random-fully:
    • ! -o vlan+: VLAN 인터페이스가 아닌 경우를 의미합니다.
    • -m addrtype ! --dst-type LOCAL: 목적지가 로컬이 아닌 경우를 의미합니다.
    • -j SNAT --to-source 192.168.3.215 --random-fully: 이 규칙이 적용되면 출발지 IP를 192.168.3.215로 변경하고, 랜덤하게 출발지 포트를 선택합니다.

첫 번째 규칙은 특정 서브넷의 트래픽을 그대로 통과시키고, 두 번째 규칙은 로컬이 아닌 목적지로 나가는 트래픽의 출발지 IP를 192.168.3.215로 변경하는 역할을 합니다

[ec2-user@ip-192-168-3-215 ~]$ sudo iptables -t nat -S | grep 'A AWS-SNAT-CHAIN'
-A AWS-SNAT-CHAIN-0 -d 192.168.0.0/16 -m comment --comment "AWS SNAT CHAIN" -j RETURN
-A AWS-SNAT-CHAIN-0 ! -o vlan+ -m comment --comment "AWS, SNAT" -m addrtype ! --dst-type LOCAL -j SNAT --to-source 192.168.3.215 --random-fully

🧿 AWS EC2 인스턴스에서 conntrack을 사용하여 네트워크 연결의 상태를 모니터링

  • ICMP:
    • src=192.168.3.104: 출발지 IP 주소. PODNAME1 (192.168.3.104)
    • dst=142.250.206.228: 목적지 IP 주소. 
    • type=8: ICMP 요청의 종류.
  • TCP:
    • src=192.168.3.215 dst=52.95.195.109: 연결의 출발지와 목적지 IP. aws-node-mqz2q (192.168.3.215)
    • sport=53210 dport=443: 출발지 포트와 목적지 포트 (여기서 443은 HTTPS를 의미).
    • [ASSURED]: 이 연결이 활성 상태임을 나타내는 상태 표시입니다.
    • mark=128: 연결에 대한 마킹 정보입니다.
    • use=2: 이 연결이 사용된 횟수입니다.
(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# 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
...
>> node 192.168.3.215 <<
conntrack v1.4.4 (conntrack-tools): 61 flow entries have been shown.
icmp     1 25 src=192.168.3.104 dst=142.250.206.228 type=8 code=0 id=186 src=142.250.206.228 dst=192.168.3.215 type=0 code=0 id=17466 mark=128 use=1
tcp      6 86393 ESTABLISHED src=192.168.3.215 dst=52.95.195.109 sport=53210 dport=443 src=52.95.195.109 dst=192.168.3.215 sport=443 dport=65330 [ASSURED] mark=128 use=2

🧿 파드 삭제

(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# kubectl delete deploy netshoot-pod
deployment.apps "netshoot-pod" deleted

✅ 노드에 파드 생성 갯수 제한

  • 인스턴스 타입 별 ENI 최대 갯수와 할당 가능한 최대 IP 갯수에 따라서 파드 배치 갯수가 결정됨
  • 단, aws-node 와 kube-proxy 파드는 호스트의 IP를 사용함으로 최대 갯수에서 제외함

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

 


🧿  Helm을 사용하여 Kubernetes 클러스터에 kube-ops-view 애플리케이션을 설치하고, 해당 애플리케이션에 대한 접근 URL을 출력

(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
"geek-cookbook" has been added to your repositories
(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=LoadBalancer --set env.TZ="Asia/Seoul" --namespace kube-system
NAME: kube-ops-view
LAST DEPLOYED: Mon Oct 28 22:29:33 2024
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
     NOTE: It may take a few minutes for the LoadBalancer IP to be available.
           You can watch the status of by running 'kubectl get svc -w kube-ops-view'
  export SERVICE_IP=$(kubectl get svc --namespace kube-system kube-ops-view -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
  echo http://$SERVICE_IP:8080
(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# kubectl get svc -n kube-system kube-ops-view -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "KUBE-OPS-VIEW URL = http://"$1":8080/#scale=1.5"}'
KUBE-OPS-VIEW URL = http://ad5fdfedb98624e9091580087a38aed7-1265835324.ap-northeast-2.elb.amazonaws.com:8080/#scale=1.5

 


🧿 EC2 인스턴스의 t3 계열 인스턴스 타입에 대한 정보를 조회

  • MaxENI: 최대 네트워크 인터페이스 수
  • IPv4addr: 각 인터페이스당 사용할 수 있는 IPv4 주소 수
(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# aws ec2 describe-instance-types --filters Name=instance-type,Values=t3.* \
>  --query "InstanceTypes[].{Type: InstanceType, MaxENI: NetworkInfo.MaximumNetworkInterfaces, IPv4addr: NetworkInfo.Ipv4AddressesPerInterface}" \
>  --output table
--------------------------------------
|        DescribeInstanceTypes       |
+----------+----------+--------------+
| IPv4addr | MaxENI   |    Type      |
+----------+----------+--------------+
|  12      |  3       |  t3.large    |
|  15      |  4       |  t3.2xlarge  |
|  15      |  4       |  t3.xlarge   |
|  6       |  3       |  t3.medium   |
|  2       |  2       |  t3.micro    |
|  2       |  2       |  t3.nano     |
|  4       |  3       |  t3.small    |
+----------+----------+--------------+

🧿 노드의 리소스 할당 가능량 (Allocatable) 정보

  • cpu: 1930m:
    • 이 노드에서 사용 가능한 CPU 자원의 양입니다. 1930m은 1930 milliCPU를 의미하며, 이는 1.93개의 CPU 코어에 해당합니다.
  • ephemeral-storage: 27905944324:
    • 이 노드에서 사용할 수 있는 일시적 스토리지의 양입니다. 이 경우 27,905,944,324 바이트(약 25.9GB)의 일시적 스토리지가 있습니다.
  • hugepages-1Gi: 0:
    • 1GiB 크기의 HugePage가 할당 가능하지 않음을 나타냅니다. HugePage는 대용량 메모리 페이지를 의미하며, 메모리 관리 성능을 향상시킬 수 있습니다.
  • hugepages-2Mi: 0:
    • 2MiB 크기의 HugePage가 할당 가능하지 않음을 나타냅니다.
  • memory: 3388304Ki:
    • 사용 가능한 메모리의 양입니다. 3388304Ki는 약 3.2GB에 해당합니다.
  • pods: 17:
    • 이 노드에서 실행할 수 있는 최대 파드 수입니다. 현재 17개의 파드가 이 노드에서 실행될 수 있습니다.
(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# kubectl describe node | grep Allocatable: -A6
Allocatable:
  cpu:                1930m
  ephemeral-storage:  27905944324
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             3388304Ki
  pods:               17
--
Allocatable:
  cpu:                1930m
  ephemeral-storage:  27905944324
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             3388304Ki
  pods:               17
--
Allocatable:
  cpu:                1930m
  ephemeral-storage:  27905944324
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             3388304Ki
  pods:               17

🧿 Kubernetes 클러스터에 NGINX를 배포하기 위한 YAML 파일을 다운로드하고, 이를 클러스터에 적용

(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/2/nginx-dp.yaml
(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# kubectl apply -f nginx-dp.yaml
deployment.apps/nginx-deployment created

🧿 Kubernetes 클러스터에서 실행 중인 파드(pod)의 상태를 조회

  • 첫 번째 파드는 192.168.3.88, 두 번째 파드는 192.168.1.226의 IP를 가지고 있습니다.
  • 첫 번째 파드는 ip-192-168-3-215.ap-northeast-2.compute.internal에서 실행되고, 두 번째 파드는 ip-192-168-1-111.ap-northeast-2.compute.internal에서 실행되고 있습니다.
(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# kubectl get pod -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP              NODE                                               NOMINATED NODE   READINESS GATES
nginx-deployment-6f999cfffb-9bs27   1/1     Running   0          28s   192.168.3.88    ip-192-168-3-215.ap-northeast-2.compute.internal   <none>           <none>
nginx-deployment-6f999cfffb-vpmdk   1/1     Running   0          28s   192.168.1.226   ip-192-168-1-111.ap-northeast-2.compute.internal   <none>           <none>


🧿 kubectl scale 명령어를 사용하여 NGINX 배포의 파드 수를 8개로 조정

(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# kubectl scale deployment nginx-deployment --replicas=8
deployment.apps/nginx-deployment scaled


🧿 kubectl scale 명령어를 사용하여 NGINX 배포의 파드 수를 15개로 조정

(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# kubectl scale deployment nginx-deployment --replicas=15
deployment.apps/nginx-deployment scaled

 


🧿 kubectl scale 명령어를 사용하여 NGINX 배포의 파드 수를 30개로 조정

(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# kubectl scale deployment nginx-deployment --replicas=30
deployment.apps/nginx-deployment scaled


🧿 kubectl scale 명령어를 사용하여 NGINX 배포의 파드 수를 50개로 조정 > 파드 생성 실패!

각 노드에서 최대  17개의 파드까지 생성 가능 > 시스템 파드제외 14개까지 생성 가능 > 14 x 3 = 42 개까지 생성 가능 > 총 8개 생성 실패!

(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# kubectl scale deployment nginx-deployment --replicas=50
deployment.apps/nginx-deployment scaled


  • NGINX 배포의 파드 중에서 현재 Pending 상태인 파드
(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# kubectl get pods | grep Pending
nginx-deployment-6f999cfffb-5f7d7   0/1     Pending   0          33s
nginx-deployment-6f999cfffb-68krg   0/1     Pending   0          33s
nginx-deployment-6f999cfffb-7mxst   0/1     Pending   0          33s
nginx-deployment-6f999cfffb-hswt4   0/1     Pending   0          33s
nginx-deployment-6f999cfffb-lt58h   0/1     Pending   0          33s
nginx-deployment-6f999cfffb-qvbcc   0/1     Pending   0          33s
nginx-deployment-6f999cfffb-sqjd7   0/1     Pending   0          33s
nginx-deployment-6f999cfffb-vcqhv   0/1     Pending   0          33s

🧿 디플로이먼트 삭제

(iam-root-account@myeks:N/A) [root@myeks-bastion ~]# kubectl describe pod nginx-deployment-6f999cfffb-5f7d7 | grep Events: -A5
Events:
  Type     Reason            Age   From               Message
  ----     ------            ----  ----               -------
  Warning  FailedScheduling  71s   default-scheduler  0/3 nodes are available: 3 Too many pods. preemption: 0/3 nodes are available: 3 No preemption victims found for incoming pod.
Comments