AWS EKS - VPC CNI
VPC CNI는 AWS EKS에서 VPC와의 통합 사용할수 있도록 하는 플러그인입니다.
AWS VPC CNI 구성요소
- CNI binary
- Pod-to-Pod 통신을 활성화하기 위해 Pod 네트워크를 설정
- node root filesystem에서 실행
- Node에서 pod가 신규로 생성되거나, 기존 pod가 삭제될 경우 kubelet에서 호출됨
- ipamd
- long-running node-local IP Address Management 의 daemon
- Node에서 ENI를 관리
- 사용 가능한 IP warm-pool 또는 prefix를 관리
※ EC2가 생성될 때 Primary subnet과 연결된 Primary ENI가 할당된다. ( Primary subnet = public or private ) Host Network mode에서 실행되는 POD는 Node Primary ENI에 할당된 주소를 사용하며, host와 같은 network namespace를 사용한다.
[ AWS CNI 가 지원하는 통신 ]
- 노드 내부에 있는 파드 통신
- 다른 노드에 위치하는 파드와의 통신
- 파드 ~ AWS 서비스 통신
- 파드 ~ 온프레미스 통신
- 파드 ~ 인터넷 통신
실습
정보 확인 및 POD 배포
기본 정보 확인
CNI 확인
k describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2
kube-proxy config 확인 : 모드 iptables 사용 >> ipvs 모드 사용하지 않는 이유???
k describe cm -n kube-system kube-proxy-config
...
mode: "iptables"
...
노드 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
파드 IP 확인
k get pod -n kube-system -o=custom-columns=NAME:.metadata.name,IP:.status.podIP,STATUS:.status.phase
파드 이름 확인
k get pod -A -o name
파드 갯수 확인
k get pod -A -o name | wc -l
--------------------------------------------------------------------------------------------------------------
노드 내 정보 확인
CNI 정보 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i tree /var/log/aws-routed-eni; echo; done
ssh ec2-user@$N1 sudo cat /var/log/aws-routed-eni/plugin.log | jq
ssh ec2-user@$N1 sudo cat /var/log/aws-routed-eni/ipamd.log | jq
ssh ec2-user@$N1 sudo cat /var/log/aws-routed-eni/egress-v6-plugin.log | jq
ssh ec2-user@$N1 sudo cat /var/log/aws-routed-eni/ebpf-sdk.log | jq
ssh ec2-user@$N1 sudo cat /var/log/aws-routed-eni/network-policy-agent.log | jq
네트워크 정보 확인 : 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
ssh ec2-user@$N1 sudo iptables -t nat -S
ssh ec2-user@$N1 sudo iptables -t nat -L -n -v
보조 IP 주소 사용 확인
k get pod -n kube-system -l k8s-app=kube-dns -owide
--------------------------------------------------------------------------------------------------------------
POD 배포 및 확인
[터미널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 | k 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: nicolaka/netshoot
command: ["tail"]
args: ["-f", "/dev/null"]
terminationGracePeriodSeconds: 0
EOF
파드 이름 변수 지정
PODNAME1=$(k get pod -l app=netshoot-pod -o jsonpath={.items[0].metadata.name})
PODNAME2=$(k get pod -l app=netshoot-pod -o jsonpath={.items[1].metadata.name})
PODNAME3=$(k get pod -l app=netshoot-pod -o jsonpath={.items[2].metadata.name})
파드 확인
k get pod -o wide
k 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 to POD
파드 IP 변수 지정
PODIP1=$(k get pod -l app=netshoot-pod -o jsonpath={.items[0].status.podIP})
PODIP2=$(k get pod -l app=netshoot-pod -o jsonpath={.items[1].status.podIP})
PODIP3=$(k get pod -l app=netshoot-pod -o jsonpath={.items[2].status.podIP})
파드1 Shell 에서 파드2로 ping 테스트
k exec -it $PODNAME1 -- ping -c 2 $PODIP2
파드2 Shell 에서 파드3로 ping 테스트
k exec -it $PODNAME2 -- ping -c 2 $PODIP3
파드3 Shell 에서 파드1로 ping 테스트
k exec -it $PODNAME3 -- ping -c 2 $PODIP1
워커 노드 TCPDUMP 확인
tcpdump -i any -nn icmp
tcpdump -i eth1 -nn icmp
tcpdump -i eth0 -nn icmp
tcpdump -i eniYYYYYYYY -nn icmp
[워커 노드1]
routing policy database management 확인
ip rule
routing table management 확인
ip route show table local
디폴트 네트워크 정보를 eth0 을 통해서 빠져나간다
ip route show table main
POD to Internet
작업용 EC2 : pod-1 Shell 에서 외부로 ping
k exec -it $PODNAME1 -- ping -c 1 www.google.com
k 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 확인
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 - 링크
for i in $PODNAME1 $PODNAME2 $PODNAME3; do echo ">> Pod : $i <<"; k exec -it $i -- curl -s ipinfo.io/ip; echo; echo; done
k exec -it $PODNAME1 -- curl -s wttr.in/seoul
k exec -it $PODNAME1 -- curl -s wttr.in/seoul?format=3
k exec -it $PODNAME1 -- curl -s wttr.in/Moon
k 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 링크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 RETURN
-A AWS-SNAT-CHAIN-0 ! -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에 매칭되어, 목적지가 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 확인
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
conntrack v1.4.5 (conntrack-tools):
POD의 노드 내 생성 제한
kube-ops-view
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
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
kube-ops-view 접속 URL 확인 (1.5 배율)
k 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"}'
--------------------------------------------------------------------------------------------------------------
워커 노드 EC2 - 모니터링
while true; do ip -br -c addr show && echo "--------------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done
작업용 EC2 - 터미널1
watch -d 'k get pods -o wide'
작업용 EC2 - 터미널2
디플로이먼트 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/2/nginx-dp.yaml
k apply -f nginx-dp.yaml
파드 확인
k get pod -o wide
k get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP
파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인
k scale deployment nginx-deployment --replicas=8
k scale deployment nginx-deployment --replicas=15
k scale deployment nginx-deployment --replicas=30
k scale deployment nginx-deployment --replicas=50
Service & AWS ALB
Helm Chart 설치
helm repo add eks https://aws.github.io/eks-charts
helm repo update
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=$CLUSTER_NAME
# 설치 확인
k get crd
k get deployment -n kube-system aws-load-balancer-controller
k describe deploy -n kube-system aws-load-balancer-controller
k describe deploy -n kube-system aws-load-balancer-controller | grep 'Service Account'
Service Account: aws-load-balancer-controller
클러스터롤, 롤 확인
k describe clusterrolebindings.rbac.authorization.k8s.io aws-load-balancer-controller-rolebinding
k describe clusterroles.rbac.authorization.k8s.io aws-load-balancer-controller-role
모니터링
watch -d k get pod,svc,ep
작업용 EC2 - 디플로이먼트 & 서비스 생성
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/2/echo-service-nlb.yaml
cat echo-service-nlb.yaml
k apply -f echo-service-nlb.yaml
확인
k get deploy,pod
k get svc,ep,ingressclassparams,targetgroupbindings
k get targetgroupbindings -o json | jq
aws elbv2 describe-load-balancers | jq
aws elbv2 describe-load-balancers --query 'LoadBalancers[*].State.Code' --output text
ALB_ARN=$(aws elbv2 describe-load-balancers --query 'LoadBalancers[?contains(LoadBalancerName, `k8s-default-svcnlbip`) == `true`].LoadBalancerArn' | jq -r '.[0]')
aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN | jq
TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN | jq -r '.TargetGroups[0].TargetGroupArn')
aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP_ARN | jq
웹 접속 주소 확인
k get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "Pod Web URL = http://"$1 }'
파드 로깅 모니터링
k logs -l app=deploy-websrv -f
분산 접속 확인
NLB=$(k get svc svc-nlb-ip-type -o jsonpath={.status.loadBalancer.ingress[0].hostname})
curl -s $NLB
for i in {1..100}; do curl -s $NLB | grep Hostname ; done | sort | uniq -c | sort -nr
while true; do curl -s --connect-timeout 1 $NLB | egrep 'Hostname|client_address'; echo "----------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done
--------------------------------------------------------------------------------------------------------------
(신규 터미널) 모니터링
while true; do aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP_ARN --output text; echo; done
작업용 EC2 - 파드 1개 설정
k scale deployment deploy-echo --replicas=1
확인
k get deploy,pod,svc,ep
curl -s $NLB
for i in {1..100}; do curl -s --connect-timeout 1 $NLB | grep Hostname ; done | sort | uniq -c | sort -nr
작업용 EC2 - 파드 3개 설정
k scale deployment deploy-echo --replicas=3
확인 : NLB 대상 타켓이 아직 initial 일 때 100번 반복 접속 시 어떻게 되는지 확인해보자!
k get deploy,pod,svc,ep
curl -s $NLB
for i in {1..100}; do curl -s --connect-timeout 1 $NLB | grep Hostname ; done | sort | uniq -c | sort -nr
k describe deploy -n kube-system aws-load-balancer-controller | grep -i 'Service Account'
Service Account: aws-load-balancer-controller
[AWS LB Ctrl] 클러스터 롤 바인딩 정보 확인
k describe clusterrolebindings.rbac.authorization.k8s.io aws-load-balancer-controller-rolebinding
[AWS LB Ctrl] 클러스터롤 확인
k describe clusterroles.rbac.authorization.k8s.io aws-load-balancer-controller-role
Ingress
게임 파드와 Service, Ingress 배포
curl -s -O https://raw.githubusercontent.com/gasida/PKOS/main/3/ingress1.yaml
cat ingress1.yaml
k apply -f ingress1.yaml
모니터링
watch -d k get pod,ingress,svc,ep -n game-2048
생성 확인
k get-all -n game-2048
k get ingress,svc,ep,pod -n game-2048
k get targetgroupbindings -n game-2048
ALB 생성 확인
aws elbv2 describe-load-balancers --query 'LoadBalancers[?contains(LoadBalancerName, `k8s-game2048`) == `true`]' | jq
ALB_ARN=$(aws elbv2 describe-load-balancers --query 'LoadBalancers[?contains(LoadBalancerName, `k8s-game2048`) == `true`].LoadBalancerArn' | jq -r '.[0]')
aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN
TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --load-balancer-arn $ALB_ARN | jq -r '.TargetGroups[0].TargetGroupArn')
aws elbv2 describe-target-health --target-group-arn $TARGET_GROUP_ARN | jq
Ingress 확인
k describe ingress -n game-2048 ingress-2048
k get ingress -n game-2048 ingress-2048 -o jsonpath="{.status.loadBalancer.ingress[*].hostname}{'\n'}"
게임 접속 : ALB 주소로 웹 접속
k get ingress -n game-2048 ingress-2048 -o jsonpath={.status.loadBalancer.ingress[0].hostname} | awk '{ print "Game URL = http://"$1 }'
파드 IP 확인
k get pod -n game-2048 -owide
