- myeks-vpc에 3곳의 AZ를 사용하는 Public / Private Subnet을 구성합니다. 로드밸런서 배포를 위한 Public / Private Subnet에 Tag 설정하였습니다.
- operate-vpc에는 AZ1을 사용하는 Public / Private Subnet 구성합니다.
- VPC 간 내부 통신을 위한 VPC Peering을 구성합니다.
[operate-vpc 구성] - Cloudformation 사용
# cloudformation 배포
aws cloudformation deploy --template-file myeks-2week.yaml --stack-name mykops --parameter-overrides KeyName=imcr SgIngressSshCidr=218.148.84.104/32 --region ap-northeast-2
# CloudFormation 스택 배포 완료 후 운영서버 EC2 IP 출력
aws cloudformation describe-stacks --stack-name mykops --query 'Stacks[*].Outputs[*].OutputValue' --output text
# 운영서버 EC2 에 SSH 접속
예시) ssh ec2-user@3.35.137.31
ssh -i <ssh 키파일> ec2-user@$(aws cloudformation describe-stacks --stack-name mykops --query 'Stacks[*].Outputs[0].OutputValue' --output text)
[myeks-vpc 구성] - eksctl 사용
# yaml 파일 작성
export CLUSTER_NAME=myeks
# myeks-VPC/Subnet 정보 확인 및 변수 지정
export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" --query 'Vpcs[*].VpcId' --output text)
echo $VPCID
export PubSubnet1=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet1" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet2=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet2" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet3=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet3" --query "Subnets[0].[SubnetId]" --output text)
echo $PubSubnet1 $PubSubnet2 $PubSubnet3
# 출력된 내용 참고 : 아래 yaml 파일 참고해서 vpc/subnet id, ssh key 경로 수정
eksctl create cluster --name $CLUSTER_NAME --region=ap-northeast-2 --nodegroup-name=ng1 --node-type=t3.medium --nodes 3 --node-volume-size=30 --vpc-public-subnets "$PubSubnet1","$PubSubnet2","$PubSubnet3" --version 1.31 --with-oidc --external-dns-access --full-ecr-access --alb-ingress-access --node-ami-family AmazonLinux2023 --ssh-access --dry-run > myeks.yaml
-------------------------------------------
# 미사용
export PrivateSubnet1=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PrivateSubnet1" --query "Subnets[0].[SubnetId]" --output text)
export PrivateSubnet2=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PrivateSubnet2" --query "Subnets[0].[SubnetId]" --output text)
export PrivateSubnet3=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PrivateSubnet3" --query "Subnets[0].[SubnetId]" --output text)
echo $PrivateSubnet1 $PrivateSubnet2 $PrivateSubnet3
# ssh 퍼블릭 키 경로 지정
SshPublic=<각자 자신의 ssh 퍼블릭 키 경로>
SshPublic=~/.ssh/kp-gasida.pub
echo $SshPublic
eksctl create cluster --name $CLUSTER_NAME --region=ap-northeast-2 --nodegroup-name=ng1 --node-type=t3.medium --nodes 3 --node-volume-size=30 --vpc-public-subnets "$PubSubnet1","$PubSubnet2","$PubSubnet3" --version 1.31 --with-oidc --external-dns-access --full-ecr-access --alb-ingress-access --node-ami-family AmazonLinux2023 --ssh-access --ssh-public-key $SshPublic --dry-run > myeks.yaml
# yaml로 eks 배포
# kubeconfig 파일 경로 위치 지정 :
export KUBECONFIG=$HOME/kubeconfig
혹은 각자 편한 경로 위치에 파일 지정
export KUBECONFIG=~/Downloads/kubeconfig
# 배포
eksctl create cluster -f myeks.yaml --verbose 4
[실습에서 자주 사용하는 변수들]
#
export KUBECONFIG=~/Downloads/kubeconfig
export CLUSTER_NAME=myeks
# myeks-VPC/Subnet 정보 확인 및 변수 지정
export VPCID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$CLUSTER_NAME-VPC" --query 'Vpcs[*].VpcId' --output text)
echo $VPCID
export PubSubnet1=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet1" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet2=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet2" --query "Subnets[0].[SubnetId]" --output text)
export PubSubnet3=$(aws ec2 describe-subnets --filters Name=tag:Name,Values="$CLUSTER_NAME-Vpc1PublicSubnet3" --query "Subnets[0].[SubnetId]" --output text)
echo $PubSubnet1 $PubSubnet2 $PubSubnet3
# 인스턴스 IP 확인
aws ec2 describe-instances --query "Reservations[*].Instances[*].{InstanceID:InstanceId, PublicIPAdd:PublicIpAddress, PrivateIPAdd:PrivateIpAddress, InstanceName:Tags[?Key=='Name']|[0].Value, Status:State.Name}" --filters Name=instance-state-name,Values=running --output table
# 인스턴스 공인 IP 변수 지정
#export N1=<az1 배치된 EC2 공인 IP>
#export N2=<az2 배치된 EC2 공인 IP>
#export N3=<az3 배치된 EC2 공인 IP>
export N1=43.203.169.0
export N2=13.125.28.29
export N3=13.125.255.7
echo $N1, $N2, $N3
# 노드 정보 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i hostnamectl; 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
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo iptables -t nat -S; echo; done
# 파드 이름 변수 지정
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}')
# 파드 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}')
# 자신의 도메인 변수 지정 : 소유하고 있는 자신의 도메인을 입력하시면 됩니다
MyDomain=<자신의 도메인>
MyDomain=gasida.link
MyDnzHostedZoneId=`aws route53 list-hosted-zones-by-name --dns-name "${MyDomain}." --query "HostedZones[0].Id" --output text`
echo $MyDnzHostedZoneId
# A 레코드 값 반복 조회
while true; do aws route53 list-resource-record-sets --hosted-zone-id "${MyDnzHostedZoneId}" --query "ResourceRecordSets[?Type == 'A']" | jq ; date ; echo ; sleep 1; done
- Kubernetes CNI : 쿠버네티스 네트워크 환경을 마련해주는 요소로 pod에 ip를 할당하는 역할
- AWS VPC CNI : AWS EKS 클러스터에서 Pod의 네트워크 관리를 위해 만들어진 네트워크 인터페이스 플러그인. Pod의 IP를 할당해주고 이때 파드의 IP 네트워크 대역과 노드(워커)의 IP 대역이 같아서 직접 통신이 가능하다. (그 외 대부분 cni는 파드와 서버의 ip 대역이 다름), 각 워커 노드에 설치되며 Pod가 스케줄될 때 마다 ENI 할당, IP 할당, Pod와 ENI 연결 등의 관리를 한다.
> **VPC 와 통합** : VPC Flow logs , VPC 라우팅 정책, 보안 그룹(Security group) 을 사용 가능함
> VPC ENI 에 미리 할당된 IP(=Local-IPAM Warm IP Pool)를 파드에서 사용할 수 있음
* AWS VPC CNI와 타 CNI를 비교해보자면 ...
[Calico CNI]
다른 서버와 통신할 때 동작할때 Calico CNI는 오버레이 통신(캡슐화)을 한다. 패킷 캡슐화에 자원 소모가 들어간다.
1) 192.168.1.1 <> 192.168.1.2 서버 간 통신 가능
2) Pod1 (10.1.1.1) <> Pod2 (10.1.1.2) 간 통신 시 서버에서 다른 서버에 위치한 파드의 IP 정보는 알 수 없음 (internal 네트워크기 때문에)
3) 따라서 10.1.1.1에서 보내는 원본 패킷을 오버레이로 감싸서 192.168.1.2 서버로 보내고 서버에 패킷이 도착하면 오버 패킷 (outer 패킷)을 벗겨서 원본 패킷(iner 패킷)은 파드로 통신한다.
[AWS VPC CNI]
- aws vpc cni는 노드와 ip 대역이 동일하기 때문에 파드 간 직접 접근도 가능하다. (효율적)
[AWS VPC CNI의 2가지 컴포넌트]
- CNI binary : 포드간 통신을 활성화 (pod to pod 네트워크를 설정), CNI 바이너리는 노드 루트 파일 시스템에서 실행되며 새 pod에 추가되거나 노드에서 기존 pod가 제거될 때 kubelet에 의해 호출된다.
- ipamd (long-running node-local IP Address Management) : 장기 실행 노드 - 로컬 IP 주소 관리(IPAM) 데몬인 ipamd는 노드에서의 ENIs 관리, 사용 가능한 IP 주소 또는 접두사의 warm pool 유지 관리를 한다.
* warm pool이란 : CNI 플러그인은 노드에서 탄력적 네트워크 인터페이스 (ENI)를 관리한다. 노드가 프로비저닝되면 CNI 플러그인은 노드의 서브넷에서 기본 ENI로 slot pool (IPs 또는 접두사)을 자동으로 할당한다. 크기는 노드 인스턴스 유형에 따라 달라진다. (각 인스턴스 유형에는 연결 가능한 최대 ENI 수가 있다.)
즉, 노드가 적절한 수준의 IP POOL을 이미 가지고 있다. 가지고있어 야지만 파드가 새로 생성될 때 바로 ip 할당 가능
* L-IPAM : 각 노드에서 사용 가능한 보조 IP 주소의 warm-pool을 유지하기 위해 L-IPAM을 실행하며 kubelet에서 파드를 추가하라는 요청을 받을 때 마다 L-IPAM이 warm-pool에서 즉시 사용 가능한 보조 IP 주소를 하나 가져와 pod에 할당한다.
[워커 노드에 생성 가능한 최대 파드 갯수]
1) Secondary IPv4 Addresses : 인스턴스 유형에 최대 ENI 갯수와 할당 가능 IP 수를 조합하여 선정
2) IPv4 Prefix Delegation : IPv4 28bit (prefix)를 위임하여 할당 가능 IP 수와 인스턴스 유형에 권장하는 최대 갯수로 선정
3) AWS VPC CNI Custom Networking : 노드와 파드 대역 분리, 파드에 별도 서브넷 부여 후 사용
[실습]
## 네트워크 기본 정보 확인
# CNI 정보 확인
kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2
# kube-proxy config 확인 : 모드 iptables 사용 >> ipvs 모드로 변경 해보자!
kubectl 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 확인
kubectl get pod -n kube-system -o=custom-columns=NAME:.metadata.name,IP:.status.podIP,STATUS:.status.phase
# 파드 이름 확인
kubectl get pod -A -o name
# 파드 갯수 확인
kubectl get pod -A -o name | wc -l
## 노드의 네트워크 정보 확인
# CNI 정보 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i imrc.pem 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 -i imrc.pem ec2-user@$i sudo ip -br -c addr; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i imrc.pem ec2-user@$i sudo ip -c addr; echo; done
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i imrc.pem 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
- Net(work) Namespace는 호스트(Root)와 파드 별(Per Pod)로 구분
- 특정 Pod (aws-node, kube-proxy)는 호스트(Root)의 네트워크를 공유해서 사용하기 때문에 서버의 IP와 동일 (192.168.1.64, 포트를 다르게써서 충돌나지 않음)
- t3.medium 인스턴스는 ENI 마다 최대 6개의 IP를 소유
- ENI0, ENI1 2개의 ENI는 자신 IP 이외의 추가적으로 5개의 보조 IP를 가질 수 있다.
- coredns Pod는 veth로 호스트에는 eniY@ifN 인터페이스와 파드의 eth0과 연결
[실습]
# 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-86f5954566-dvxz2 1/1 Running 0 42m 192.168.2.4 ip-192-168-2-213.ap-northeast-2.compute.internal <none> <none>
coredns-86f5954566-v8brw 1/1 Running 0 42m 192.168.3.205 ip-192-168-3-157.ap-northeast-2.compute.internal <none> <none>
# 노드의 라우팅 정보 확인 >> EC2 네트워크 정보의 '보조 프라이빗 IPv4 주소'와 비교해보자
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done
# [추가 터미널1~3] 노드 모니터링을 위해 watch로 라우트 정보 띄워놓기
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"
# 테스트용 netshoot-pod 디플로이먼트 생성 : replicas 3
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: 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
# 노드의 라우팅 정보 확인
for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done
파드가 생성되면 워커 노드에 eni~~~가 추가되고 라우팅 테이블에도 정보가 추가되는 것을 확인할 수 있다.
* 테스트 용 Pod eni~~~ 정보 확인하기
1) 워커 노드 안에서 확인
# 노드3에서 네트워크 인터페이스 정보 확인
ssh ec2-user@$N3
----------------
ip -br -c addr show
ip -c link
ip -c addr
ip route # 혹은 route -n
# 네임스페이스 정보 출력 -t net(네트워크 타입)
sudo lsns -t net
# PID 정보로 파드 정보 확인
PID=<PID> # PID 높은 것 중 COMMAND가 pause 인것
sudo nsenter -t $PID -n ip -c addr
sudo nsenter -t $PID -n ip -c route
exit
----------------
2) pod 안에서 확인 (exec)
# 테스트용 파드 접속(exec) 후 Shell 실행
kubectl exec -it $PODNAME1 -- zsh
# 아래부터는 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
AWS VPC CNI의 경우 별도 오버레이 과정 없이 VPC Native하게 Pod 간 직접 통신이 가능하다.
[실습]
# 파드 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 확인
## 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
# 디폴트 네트워크 정보를 ens5 을 통해서 빠져나간다
ip route show table main
default via 192.168.1.1 dev ens5
...
파드가 외부 인터넷으로 통신할 때는 서버의 IP로 NAT되어 나간다. eth0(ens5) 등 서버의 사설 > 공인 IP를 가지고 외부로 통신, iptables에서 라우팅을 설정함.
[실습]
1) 파드에서 외부로 통신 테스트 및 확인 : 파드에서 외부로 ping 테스트, 워커 노드에서 tcpdump 및 iptables 정보
# 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
eni 인터페이스 : pod의 IP 확인
ens 인터페이스 : server의 IP 확인
1) pod (192.168.1.50)의 eni인터페이스에서 www.google.com(172.217.161.196)으로 ping 보냄
2) 패킷은 192.168.1.98 (워커 노드 ens5 인터페이스)로 치환되어 172.217.161.196으로 통신
3) 172.217.161.196에서 192.168.1.98 (워커 노드 IP)로 통신
또한 워커노드에서 ip route show table local 실행해보면 !
라우트 정보가 나오는데 "첫 줄 : default 룰 (아래 매칭되지 않는 네트워크 대역은) ens5로 가지고 나가라"는 의미이다.
워커 노드에서 sudo iptables -t nat -S | grep 'A AWS-SNAT-CHAIN' 실행해보면 !
192.168.0.0/16 대역하고 통신할 때(vpc 대역안에서의 통신)는 SNAT을 리턴해라 > pod 간 통신 시 ip가 바뀌지 않는 이유
하지만 저기에 매치되지 않는 (ex: 175.~~대역)은 SNAT으로 점프, 소스 아이피는 192.168.1.98 (서버의 ens5 IP) 로 바꿔라는 의미이다.
2) pod <> 운영서버 EC2 (Operate host 서) 간 통신 확인
2-1) 운영서버 EC2 > Pod IP 통신
# 운영서버 EC2 SSH 접속
ssh <운영서버 EC2 공인 IP>
-----------------------
POD1IP=<파드1 IP 지정>
POD1IP=192.168.1.50
ping -c 1 $POD1IP
exit
-----------------------
# 워커노드1 에서 tcpdump 확인 : NAT 동작 적용 여유 확인
sudo tcpdump -i any -nn icmp
별도 NAT 없이 pod ip로 통신
2-2) Pod > 운영서버 EC2 통신
# vpc cni env 정보 확인
kubectl get ds aws-node -n kube-system -o json | jq '.spec.template.spec.containers[0].env'
...
{
"name": "AWS_VPC_K8S_CNI_EXTERNALSNAT",
"value": "false"
},
...
# 운영서버 EC2 SSH 접속
kubectl exec -it $PODNAME1 -- ping 172.20.1.100
# 파드1 배치 워커노드에서 tcpdump 확인 : NAT 동작 적용 여유 확인
sudo tcpdump -i any -nn icmp
# 운영서버 EC2 에서 tcpdump 확인 : NAT 동작 적용 여유 확인
sudo tcpdump -i any -nn icmp
-----------------------------------------------------
# 파드1 배치 워커노드 : NAT 적용 정책 확인
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'
pod의 ip(192.168.1.50)가 172.27.1.100으로 통신할 때 서버의 ip (192.168.1.98)로 NAT되서 통신한다.
2-3) 사내 내부에 연결 확장된 네트워크 대역과 SNAT 없이 통신 가능하게 설정 해보기
# 파드 상태 모니터링
# kubectl set env 명령어는 내부적으로 kubectl patch를 실행하여 PodSpec을 변경 → 이로 인해 aws-node 데몬셋이 자동으로 롤링 업데이트
watch -d kubectl get pod -n kube-system
# 파드1 배치 워커노드 iptables rule 모니터링 : iptables rule 추가됨
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'
# 사내 내부에 연결 확장된 네트워크 대역과 SNAT 없이 통신 가능하게 설정
kubectl set env daemonset aws-node -n kube-system AWS_VPC_K8S_CNI_EXCLUDE_SNAT_CIDRS=172.20.0.0/16
#
kubectl get ds aws-node -n kube-system -o json | jq '.spec.template.spec.containers[0].env'
...
{
"name": "AWS_VPC_K8S_CNI_EXCLUDE_SNAT_CIDRS",
"value": "172.20.0.0/16"
}
# 운영서버 EC2 SSH 접속
kubectl exec -it $PODNAME1 -- ping 172.20.1.100
# 파드1 배치 워커노드 : NAT 적용 정책 확인
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'
Chain AWS-SNAT-CHAIN-0 (1 references)
pkts bytes target prot opt in out source destination
1 84 RETURN all -- * * 0.0.0.0/0 172.20.0.0/16 /* AWS SNAT CHAIN EXCLUSION */
730 45228 RETURN all -- * * 0.0.0.0/0 192.168.0.0/16 /* AWS SNAT CHAIN */
...
a) 노드 가시화를 위해 kube-ops-view를 설치
# 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 배율)
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"}'
b) 워커 노드의 인스턴스 타입 별 파드 생성 갯수는 제한되어 있다. (Secondary private IPv4 addresses)
> 인스턴스 타입 별 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
# t3 타입의 정보 확인
aws ec2 describe-instance-types --filters Name=instance-type,Values=t3.\* \
--query "InstanceTypes[].{Type: InstanceType, MaxENI: NetworkInfo.MaximumNetworkInterfaces, IPv4addr: NetworkInfo.Ipv4AddressesPerInterface}" \
--output table
# 워커노드 상세 정보 확인 : 노드 상세 정보의 Allocatable 에 pods 에 17개 정보 확인
kubectl describe node | grep Allocatable: -A6
# 파드 사용 가능 계산 예시 : aws-node 와 kube-proxy 파드는 host-networking 사용으로 IP 2개 남음
> ((MaxENI * (IPv4addr-1)) + 2) t3.medium 경우 : ((3 * (6 - 1) + 2 ) = 17개 > aws-node 와 kube-proxy 2개 제외 15개
c) 최대 파드 생성 및 확인
# 워커 노드 3대 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: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
EOF
- replicas 2개로 pod가 2개 생성되면서 pod가 할당되는 노드에는 eni***이 추가로 생성됨을 확인
- pod를 30개까지 증가 해보기
# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인 >> 어떤일이 벌어졌는가?
kubectl scale deployment nginx-deployment --replicas=30
ens7이라는 인터페이스가 새로 생성되었고 보조 프라이빗 IPv4 주소가 추가로 할당되었다.
- pod를 50개까지 증가 해보기
# 파드 증가 테스트 >> 파드 정상 생성 확인, 워커 노드에서 eth, eni 갯수 확인 >> 어떤일이 벌어졌는가?
kubectl scale deployment nginx-deployment --replicas=50
Pod에 할당할 IP가 없기 때문에 pending 상태의 pod가 발견된다.
노드에 배포할 수 있는 Pod의 최대 갯수는 지원하는 IP 주소가 주요 요인입니다. 다만 vCPU 30개 미만 EC2 인스턴스 유형은 노드에 최대 파드 110개가 제한, vCPU 30개 이상 EC2 인스턴스 유형은 노드에 최대 파드 250개 제한을 권고합니다.
d) 해결 방안 : Prefix Delegation, Warm & Min IP/Prefix Targets, Custom Network
[AEWS3기] 3주차 EKS Storage, Managed Node Groups (0) | 2025.02.19 |
---|---|
[AEWS3기] 2주차 - EKS Networking (2) (0) | 2025.02.14 |
[AEWS3기] 1주차 - Amazon EKS 설치 및 기본 사용 (4) (0) | 2025.02.08 |
[AEWS3기] 1주차 - Amazon EKS 설치 및 기본 사용 (3) (0) | 2025.02.05 |
[AEWS3기] 1주차 - Amazon EKS 설치 및 기본 사용 (2) (0) | 2025.02.05 |