AEWS 스터디에서는 AWS의 관리형 Kubernetes인 Elastic Kubernetes의 다양한 기능들을 실습해보면서 익혀본다. 이 글은 스터디를 참여하면서 학습한 내용을 정리하는 연재 글이다. 스터디 진도에 맞춰 글을 작성한다.
이 글에서는 EKS Networking을 실습하면서 특징과 사용법에 대해서 알아본다.
1. AWS VPC CNI 란?
CNI (Container Network Interface)는 Kubernetes의 네트워크를 구성해준다. AWS에서는 AWS VPC CNI를 사용하고 있다. EKS 에 생성되는 Pod는 AWS VPC CNI에서 IP주소를 할당해준다.
AWS VPC CNI의 가장 큰 장점은 파드의 IP 네트워크 대역과 노드(워커)의 IP 대역이 같아서 직접 통신이 가능하다는 점이다. 아래 그림을 보면 Calico CNI는 파드와 노드의 네트워크 대역이 다르지만 AWS VPC CNI 노드와 파드의 네트워크 대역이 같다.
파드간의 통신을 할때를 보면 K8S CNI (아래 그림에서 왼쪽)는 오버레이(VXLAN, IP-IP 등) 통신을 하고, AWS VPC CNI (아래그림 오른쪽)는 동일 대역으로 직접 통신을 한다.
2. 노드에서 기본 네트워크 정보 확인
노드 한개의 구성을 확인해보자. 네트워크 인터페이스 2개이고, 호스트 네트워크인 aws-node, kube-proxy의 네트워크 인터페이스와 같다.
- Network 네임스페이스는 호스트(Root)와 파드 별(Per Pod)로 구분된다
- 특정한 파드(kube-proxy, aws-node)는 호스트(Root)의 IP를 그대로 사용한다
- t3.medium 의 경우 ENI 마다 최대 6개의 IP를 가질 수 있다
- ENI0, ENI1 으로 2개의 ENI는 자신의 IP 이외에 추가적으로 5개의 보조 프라이빗 IP를 가질수 있다
- coredns 파드는 veth 으로 호스트에는 eniY@ifN 인터페이스와 파드에 eth0 과 연결되어 있다
그럼 coredns 파드를 파고 들어가 보자.
1
2
3
4
5
6
7
8
9
10
|
# coredns 파드 IP 정보 확인
kubectl get pod -n kube-system -l k8s-app=kube-dns -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
coredns-6777fcd775-5hbmw 1/1 Running 0 8h 192.168.2.170 ip-192-168-2-109.ap-northeast-2.compute.internal <none> <none>
coredns-6777fcd775-jrrx4 1/1 Running 0 8h 192.168.1.202 ip-192-168-1-38.ap-northeast-2.compute.internal <none> <none>
# 노드의 라우팅 정보 확인 >> EC2 네트워크 정보의 '보조 프라이빗 IPv4 주소'와 비교해보자
ssh ec2-user@$N1 sudo ip -c route
ssh ec2-user@$N2 sudo ip -c route
ssh ec2-user@$N3 sudo ip -c route
|
cs |
하나의 coredns 파드 IP 정보 확인하여 노드 ip를 찾아 AWS의 노드를 확인해본다.
1) 인스턴스를 AWS콘솔에서 찾아본다.
2) 인스턴스의 보조 프라이빗 IP 중에 coredns 파드의 IP를 찾아본다.
눈으로 확인 해보니 모식도에 설명되어 있는 내용들이 더 확실해 지는 느낌이다. ENI당 가질 수 있는 개수가 5개 이므로 ENI는 2개, 보조 프라이빗 주소는 10개 이다.
노드의 라우팅 정보 확인해보면 Coredns의 Pod ip를 확인 할 수 있다.
테스트용 파드 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
# [터미널1~3] 노드 모니터링
ssh ec2-user@$N1
watch -d "ip link | egrep 'eth|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"
ssh ec2-user@$N2
watch -d "ip link | egrep 'eth|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"
ssh ec2-user@$N3
watch -d "ip link | egrep 'eth|eni' ;echo;echo "[ROUTE TABLE]"; route -n | grep eni"
# 테스트용 파드 netshoot-pod 생성
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
# 파드 이름 변수 지정
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})
# 파드 확인
kubectl get pod -o wide
kubectl get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP
|
cs |
생성된 Pod를 모니터링에서 확인해본다.
3. 노드 간 파드 통신
노드 간 파드 통신을 알아보기 위해, 워커노드1번에 뜬 파드에서 워커노드2번으로 핑을 한다. ENI의 Tcp dump를 캡처를 떠본다. 오버레이 통신없이 직접 통신이 되는지 확인해본다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
# 파드 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})
# 파드1 Shell 에서 파드2로 ping 테스트
kubectl exec -it $PODNAME1 -- ping -c 2 $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 확인
sudo tcpdump -i any -nn icmp
sudo tcpdump -i eth1 -nn icmp
sudo tcpdump -i eth0 -nn icmp
[워커 노드1]
# routing policy database management 확인
ip rule
# routing table management 확인
ip route show table local
# 디폴트 네트워크 정보를 eth0 을 통해서 빠져나간다
ip route show table main
default via 192.168.1.1 dev eth0
...
|
cs |
TCP 덤프를 보면 노드의 IP가 보이지 않는다. 즉 Pod의 IP가 직접 통신을 한다. 스터디에서는 eth1 dump도 확인이 돼서 확인이 필요하다고 했는데, 내가 실습할 때는 위 네트워크 통신 모식도 처럼 eth0 tcp dump만 확인이 됐다.
4. 파드에서 외부 통신
파드에서 외부 (인터넷)으로 통신을 할때의 흐름을 알아본다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
# 작업용 EC2 : 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
# 워커 노드 EC2 : TCPDUMP 확인
sudo tcpdump -i any -nn icmp
sudo tcpdump -i eth0 -nn icmp
# 워커 노드 EC2 : 퍼블릭IP 확인
curl -s ipinfo.io/ip ; echo
13.209.40.224
# 작업용 EC2 : pod-1 Shell 에서 외부 접속 확인 - 공인IP는 어떤 주소인가?
## The right way to check the weather - 링크
kubectl exec -it $PODNAME1 -- curl -s ipinfo.io/ip ; echo
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, AWS-SNAT-CHAIN-1' 룰(rule)에 의해서 SNAT 되어서 외부와 통신!
# 참고로 뒤 IP는 eth0(ENI 첫번째)의 IP 주소이다
# --random-fully 동작 - 링크1 링크2
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 AWS-SNAT-CHAIN-1
-A AWS-SNAT-CHAIN-1 ! -o vlan+ -m comment --comment "AWS, SNAT" -m addrtype ! --dst-type LOCAL -j SNAT --to-source 192.168.1.251 --random-fully
## 아래 'mark 0x4000/0x4000' 매칭되지 않아서 RETURN 됨!
-A KUBE-POSTROUTING -m mark ! --mark 0x4000/0x4000 -j RETURN
-A KUBE-POSTROUTING -j MARK --set-xmark 0x4000/0x0
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -j MASQUERADE --random-fully
...
# 카운트 확인 시 AWS-SNAT-CHAIN-0, AWS-SNAT-CHAIN-1 에 매칭되어, 목적지가 192.168.0.0/16 아니고 외부 빠져나갈때 SNAT 192.168.1.251 변경되어 나간다!
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 AWS-SNAT-CHAIN-1; echo ; sudo iptables -v --numeric --table nat --list KUBE-POSTROUTING'
# conntrack 확인
sudo conntrack -L -n |grep -v '169.254.169'
conntrack v1.4.5 (conntrack-tools):
icmp 1 28 src=172.30.66.58 dst=8.8.8.8 type=8 code=0 id=34392 src=8.8.8.8 dst=172.30.85.242 type=0 code=0 id=50705 mark=128 use=1
tcp 6 23 TIME_WAIT src=172.30.66.58 dst=34.117.59.81 sport=58144 dport=80 src=34.117.59.81 dst=172.30.85.242 sport=80 dport=44768 [ASSURED] mark=128 use=1
|
cs |
외부로 핑을 해보면 tcp 덤프 (any)가 잘 확인이 된다. 그렇다면 pod의 ip 주소는 무엇으로 확인 될까?
노드의 퍼블릭 IP주소가 Pod 내부에서 확인해본 외부 IP와 같다.
AWS-SNAT-CHAIN-0 를 보면 192.168.0.0/16 대역만 SNAT 하지 않고 예외처리 하여 직접통신 하도록 되어 있다. 이 대역이 아니라면 AWS-SNAT-CHAIN-1로 SNAT하여 외부로 나간다.
5. 노드에 파드 생성 갯수 제한
- 인스턴스 타입 별 ENI 최대 갯수와 할당 가능한 최대 IP 갯수에 따라서 파드 배치 갯수가 결정됨
- 단, aws-node 와 kube-proxy 파드는 호스트의 IP를 사용함으로 최대 갯수에서 제외함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
# 워커 노드 EC2 - 모니터링
while true; do ip -br -c addr show && echo "--------------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done
# 작업용 EC2 - 터미널1
watch -d 'kubectl get pods -o wide'
# 작업용 EC2 - 터미널2
# 디플로이먼트 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/2/nginx-dp.yaml
kubectl apply -f nginx-dp.yaml
# 파드 확인
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=8
# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인 >> 어떤일이 벌어졌는가?
kubectl scale deployment nginx-deployment --replicas=15
# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인 >> 어떤일이 벌어졌는가?
kubectl scale deployment nginx-deployment --replicas=30
# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인 >> 어떤일이 벌어졌는가?
kubectl scale deployment nginx-deployment --replicas=50
# 파드 생성 실패!
kubectl get pods | grep Pending
nginx-deployment-6fb79bc456-28g4j 0/1 Pending 0 41s
nginx-deployment-6fb79bc456-4b99h 0/1 Pending 0 41s
...
kubectl describe pod <Pending 파드> | grep Events: -A5
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 45s default-scheduler 0/3 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }, 2 Too many pods. preemption: 0/3 nodes are available: 1 Preemption is not helpful for scheduling, 2 No preemption victims found for incoming pod.
# 디플로이먼트 삭제
kubectl delete deploy nginx-deployment
|
cs |
모니터링을 걸고 Pod 스케일을 50개까지 늘려본다. Pending 상태인 Pod들이 확인 된다.
Pod의 Events를 확인해보면 너무 많은 Pod (Too many pods) 문구를 확인 할 수있다.
인스턴스가 가질 수 있는 최대 개수인 ENI 3와 IP주소 15개를 확인 할 수 있다.
'스터디 > Kubernetes' 카테고리의 다른 글
[AWES] EKS Storage 1/5 - 임시 파일시스템과 local-path 스토리지 클래스 (0) | 2023.05.14 |
---|---|
[AWES] EKS Networking - AWS LB Controller, Ingress (0) | 2023.05.07 |
[AEWS] Amzaon EKS - Cluster Endpoint 구성 톱아보기 (0) | 2023.04.30 |
[AEWS] Amzaon EKS (0) | 2023.04.30 |
[PKOS] Kubernetes 보안 - kubescape, polaris, RBAC (2/2) (0) | 2023.04.10 |