1. 실습 환경
- VPC 2ea
- EC2 1ea
- EKS 1ea (Worker node 3ea)
EKS를 1개의 VPC 내 여러 AZ로 분리 배치
- 로드밸런서 배포를 위한 Pub/Priv 서브넷 태그 설정
operator-vpc 에 AZ1를 사용하는 Pub/Priv 서브넷 배치
내부 통신을 위한 VPC Peering 배치
실습 환경 배포
## Cloud Formation Script
$ curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/K8S/myeks-2week.yaml
## 환경 배포
$ aws cloudformation deploy --template-file ./myeks-2week.yaml --stack-name myeks --parameter-overrides KeyName={keyname} SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2
## 생성된 EC2 IP 출력
$ aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[*].OutputValue' --output text
## 운영 서버 SSH 접속
$ ssh -i {key file} ec2-user@$(aws cloudformation describe-stacks --stack-name myeks --query 'Stacks[*].Outputs[0].OutputValue' --output test)
콘솔에서 Cloud Formation 완료 확인
운영 VM 접속 확인
EKS 배포 사전 준비
## Cluster Name 변수 설정
$ export CLUSTER_NAME=myeks
## VPC ID / 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
$ echo $VPCID $PubSubnet1 $PubSubnet2 $PubSubnet3
vpc-0ece9237619f5bf51 subnet-052e98aa79656fce3 subnet-028ca31d7feaf482f subnet-0ae9a0ef9ba865757
EKSCTL을 사용하여 클러스터 배포
## 배포할 EKS 검증 및 yaml 파일 추출
$ 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
yaml 파일 수정
managedNodeGroups:
- amiFamily: AmazonLinux2023
desiredCapacity: 3
disableIMDSv1: true
disablePodIMDS: false
iam:
withAddonPolicies:
albIngress: false # Disable ALB Ingress Controller
appMesh: false
appMeshPreview: false
autoScaler: false
awsLoadBalancerController: true # Enable AWS Load Balancer Controller
certManager: true # Enable cert-manager
cloudWatch: false
ebs: false
efs: false
externalDNS: true # Enable ExternalDNS
fsx: false
imageBuilder: true
xRay: false
instanceSelector: {}
instanceType: t3.medium
preBootstrapCommands:
# install additional packages
- "dnf install nvme-cli links tree tcpdump sysstat ipvsadm ipset bind-utils htop -y"
# disable hyperthreading
- "for n in $(cat /sys/devices/system/cpu/cpu*/topology/thread_siblings_list | cut -s -d, -f2- | tr ',' '\n' | sort -un); do echo 0 > /sys/devices/system/cpu/cpu${n}/online; done"
labels:
alpha.eksctl.io/cluster-name: myeks
alpha.eksctl.io/nodegroup-name: ng1
maxSize: 3
minSize: 3
name: ng1
privateNetworking: false
releaseVersion: ""
securityGroups:
withLocal: null
withShared: null
ssh:
allow: true
publicKeyName: {KEY NAME} # 각자 환경 정보로 수정
tags:
alpha.eksctl.io/nodegroup-name: ng1
alpha.eksctl.io/nodegroup-type: managed
volumeIOPS: 3000
volumeSize: 30
volumeThroughput: 125
volumeType: gp3
Cluster 생성 확인
- Console
- Kubectl
## node, Cluster 정보, 네임스페이스 변경, Context 확인
$ k get node && k cluster-info && kns default && kctx
NAME STATUS ROLES AGE VERSION
ip-192-168-1-172.ap-northeast-2.compute.internal Ready <none> 20m v1.31.5-eks-5d632ec
ip-192-168-2-38.ap-northeast-2.compute.internal Ready <none> 20m v1.31.5-eks-5d632ec
ip-192-168-3-98.ap-northeast-2.compute.internal Ready <none> 20m v1.31.5-eks-5d632ec
Kubernetes control plane is running at https://DF590354A9C668BC646B909F612B7662.gr7.ap-northeast-2.eks.amazonaws.com
CoreDNS is running at https://DF590354A9C668BC646B909F612B7662.gr7.ap-northeast-2.eks.amazonaws.com/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
Context "hoontest1@myeks.ap-northeast-2.eksctl.io" modified.
Active namespace is "default".
hoontest1@myeks.ap-northeast-2.eksctl.io
- eksctl
## 설치된 cluster addon 확인
$ eksctl get addon --cluster $CLUSTER_NAME
2025-02-15 13:47:42 [ℹ] Kubernetes version "1.31" in use by cluster "myeks"
2025-02-15 13:47:42 [ℹ] getting all addons
2025-02-15 13:47:43 [ℹ] to see issues for an addon run `eksctl get addon --name <addon-name> --cluster <cluster-name>`
NAME VERSION STATUS ISSUES IAMROLE UPDATE AVAILABLE CONFIGURATION VALUES POD IDENTITY ASSOCIATION ROLES
coredns v1.11.4-eksbuild.2 ACTIVE 0
kube-proxy v1.31.3-eksbuild.2 ACTIVE 0
metrics-server v0.7.2-eksbuild.2 ACTIVE 0
vpc-cni v1.19.2-eksbuild.5 ACTIVE 0 arn:aws:iam::632128666759:role/eksctl-myeks-addon-vpc-cni-Role1-JI7OzbhNOBcq enableNetworkPolicy: "true"
- Worker Node 접속 설정
-----------------------------------------------------------------------------------------
| DescribeInstances |
+----------------------+-----------------+----------------+-----------------+-----------+
| InstanceID | InstanceName | PrivateIPAdd | PublicIPAdd | Status |
+----------------------+-----------------+----------------+-----------------+-----------+
| i-05c4ca36c708bb9a1 | myeks-ng1-Node | 192.168.3.98 | 15.164.98.61 | running |
| i-0b17c7d5fca02aeaf | myeks-ng1-Node | 192.168.1.172 | 3.38.178.42 | running |
| i-09724014ae59e9955 | operator-host | 172.20.1.100 | 43.202.56.173 | running |
| i-0ee742bdfcae8b5cf | myeks-ng1-Node | 192.168.2.38 | 15.164.179.186 | running |
+----------------------+-----------------+----------------+-----------------+-----------+
$ export N1=15.164.98.61
$ export N2=3.38.178.42
$ export N3=15.164.179.186
$ echo $N1 $N2 $N3
15.164.98.61 3.38.178.42 15.164.179.186
## SG ID를 변수 지정
$ export MYSG={SG ID}
$ aws ec2 authorize-security-group-ingress --group-id $MYSG --protocol '-1' --cidr $(curl -s ipinfo.io/ip)/32
$ aws ec2 authorize-security-group-ingress --group-id $MYSG --protocol '-1' --cidr 172.20.1.100/32
## 방화벽 정책 적용 확인
$ ping -c 2 $N1
PING 15.164.98.61 (15.164.98.61): 56 data bytes
64 bytes from 15.164.98.61: icmp_seq=0 ttl=115 time=13.188 ms
64 bytes from 15.164.98.61: icmp_seq=1 ttl=115 time=13.141 ms
$ ping -c 2 $N2
PING 3.38.178.42 (3.38.178.42): 56 data bytes
64 bytes from 3.38.178.42: icmp_seq=0 ttl=115 time=28.152 ms
64 bytes from 3.38.178.42: icmp_seq=1 ttl=115 time=13.724 ms
## worker Node SSH 접속 확인
$ for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh -i ~/.ssh/my-ec2-keypair.pem -o StrictHostKeyChecking=no ec2-user@$i hostname; echo; done
>> node 15.164.98.61 <<
ip-192-168-3-98.ap-northeast-2.compute.internal
>> node 3.38.178.42 <<
ip-192-168-1-172.ap-northeast-2.compute.internal
>> node 15.164.179.186 <<
ip-192-168-2-38.ap-northeast-2.compute.internal
## 운영 서버에서 접속 시 사설 IP로 worker 에 접근
ping -c 2 $N1
PING 192.168.3.98 (192.168.3.98) 56(84) bytes of data.
64 bytes from 192.168.3.98: icmp_seq=1 ttl=127 time=2.24 ms
64 bytes from 192.168.3.98: icmp_seq=2 ttl=127 time=1.36 ms
--- 192.168.3.98 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.368/1.806/2.244/0.438 ms
[root@operator-host ~]# ping -c 2 $N2
PING 192.168.1.172 (192.168.1.172) 56(84) bytes of data.
64 bytes from 192.168.1.172: icmp_seq=1 ttl=127 time=1.40 ms
64 bytes from 192.168.1.172: icmp_seq=2 ttl=127 time=2.33 ms
--- 192.168.1.172 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.405/1.869/2.334/0.466 ms
1. AWS VPC CNI
AWS VPC CNI는 kubernetes에서 사용되는 Container Network Interface 플로그인 중
하나로 Pod에 AWS VPC의 네이티브 IP 주소를 할당하는 방식으로 작동합니다.
이를 통해 EKS 클러스터의 POD들이 AWS VPC 내에서 직접 통신할 수 있습니다.
Pod 수가 증가하면 VPC CNI는 자동으로 ENI를 추가로 생성하고 IP 주소를 동적으로 할당하여 확장성을 제공하며,
AWS Controller 내에서 동작하기 때문에 AWS VPC 내의 SG, Routing을 사용할 수 있기 때문에 보안 설정에 편리함을 갖고 있습니다.
아래와 같이 네이티브 IP를 할당하기 때문에 Pod에 할당되는 IP는 워커 노드의 IP 대역과 동일하며, 직접 통신이 가능합니다.
기능 비교 | AWS VPC CNI | Calico |
Pod 간 직접 통신 | ✅ | ❌ (IP-inIP or BGP 필요) |
네이티브 VPC 보안 그룹 사용 | ✅ | ❌ |
성능 | 매우 높음 (VPC 네이티브) | 중간 |
확장성 | AWS ENI 기반 | 높음 |
BGP 지원 | ❌ | ✅ |
eBPF 지원 | ✅ | ❌ |
- Pod 간 통신 (동일 노드)
- AWS VPC CNI
- 같은 노드의 Pod끼리는 서로 다른 ENI의Secondary IP를 직접 사용하여 통신
- 리눅스 커널의 veth pair와 L3 라우팅을 이용하여 트래픽 전달 - Calico CNI
- 같은 node의 Pod끼리는 veth pair를 통해 통신
- Calico는 kubernetes의 기본 L3 라우팅을 사용하거나, eBPF 기반으로 패킷을 전달할 수 있음.
- AWS VPC CNI
- Pod 간 통신 (다른 노드)
- AWS VPC CNI
- Pod A가 다른 node에 있는 Pod B에게 요청을 보냄
- AWS 네트워크에서 Pod의 ENI IP를 직접 사용하여 VPC 내에서 L2/L3 네트워크를 통해 바로 전달
- 트래픽이 AWS 네트워크에서 처리되므로 추가적인 NAT, Overlay가 필요 없음 - Calico CNI
- Pod A가 다른 node에 있는 Pod B에게 요청을 보냄
- 기본적으로 IP-in-IP 또는 VXLAN을 통해 패킷을 캡슐화하여 전달
- AWS VPC CNI
AWS VPC CNI 두가지 컴포넌트
- CNI Binary : Pod 간의 통신 활성화를 위해 Pod 네트워크를 설정, 바이너리는 노드 루트 파일 시시스템에서 실행되고 신규 POD가 추가되거나, 기존 POD가 삭제될 때 kubelet에서 호출
- ipamd : ENI의 secondary ip 기능을 이용해 eks pod가 사용할 ip pool을 관리하고, 이 ip pool을 warm pool이라고 부르며, kubelet 요청에 따라 pool내의 IP 개수를 자동으로 추가하거나 삭제합니다.
IPMD를 통한 ip addr 관리는 여러 제한 사항이 있습니다.
- EC2 인스턴스의 타입에 따라 연결 가능한 ENI 개수의 제한
- 하나의 ENI가 가질 수 있는 Secondary IP 개수가 정해져 있음
- IPAMD가 ENI의 추가, 삭제를 통해 Warm Pool 내의 ip addr 개수를 조절
Kubelet이 신규 파드 생성 요청을 받으면, CNI 바이너리는 ipamd에 사용 가능한 IP주소를 쿼리하고, ipamd는 이를 파드에 제공합니다.
CNI 바이너리는 호스트와 파드 네트워크를 연결합니다.
노드에 배포된 파드는 기본적으로 기본 ENI와 동일한 보안 그룹에 할당되며, 파드를 다른 보안 그룹으로 구성할 수도 있습니다.
IP 주소 풀이 고갈되면 플러그인은 자동으로 다른 Elastic Network Interface를 인스턴스에 연결하고 해당 인터페이스에 다른 보조 IP 주소 세트를 할당합니다. 이 프로세스는 노드가 더 이상 추가 Elastic Network Interface를 지원할 수 없을 때까지 지원합니다.
파드가 삭제되면 VPC CNI는 파드의 IP 주소를 30초 쿨다운 캐시에 저장합니다. 쿨 다운 캐시의 IP는 신규 파드에 할당되지 않습니다.
쿨링오프 주기가 끝나면 VPC CNI는 파드 IP를 웜 풀로 다시 옮깁니다. 쿨링 오프 주기는 파드 IP주소가 너무 이르게 재활용되는 것을 방지하고 모든 클러스터 노드의 Kube-proxy가 IPtables규칙 업데이트를 완료할 수 있도록 합니다.
IP 또는 ENI의 수가 웜 풀 설정 수를 초과하면 IPamd 플러그인은 VPC에 IP와 ENI를 반환합니다.
워커 노드에 생성 가능한 최대 파드 갯수
- K8s CNI는 Container Network Interface로 Kubernetes 환경의 네트워크를 위한 구성이다.
이를 통해 다양한 플러그인, 오브젝트간의 통신 같은 네트워크 통신이 이뤄진다.
AWS VPC CNI는 VPC Container Network Interface로 Kubernetes의 오브젝트들이 VPC 대역의 IP를 갖을 수 있도록 해주는,
플러그인입니다. 파드 내부는 네트워크 네임스페이스를 통해 통신이 이뤄집니다.
Secondary IPv4 Addr : 인스턴스 유형에 최대 ENI 갯수와 할당 가능 IP 수를 조합하여 선정
IPv4 Prefix Delegation : IPv4 28bit 서브넷(prefix)를 위임하여 할당 가능 IP 수와 인스턴스 유형에 권장하는 최대 갯수로 선정
## CNI 정보 확인
$ k describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2
amazon-k8s-cni-init:v1.19.2-eksbuild.5
amazon-k8s-cni:v1.19.2-eksbuild.5
amazon
$ k get ds -n kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
aws-node 3 3 3 3 3 <none> 10h
## kube-proxy config 확인 : 모드 iptables 사용 >> ipvs 모드로 변경
$ k describe cm -n kube-system kube-proxy-config
kind: KubeProxyConfiguration
metricsBindAddress: 0.0.0.0:10249
mode: "iptables"
nodePortAddresses: null
oomScoreAdj: -998
portRange: ""
## node ip 확인
$ aws ec2 describe-instances --query "Reservations[*].Instances[*].{PublicIPAddress,PrivateIPAdd:PrivateIpAddress,InstanceName:Tags[?Key=='Name']|[0].Value,Status:State.Name}" --filters Name=instance-state-name,Values=running --output table
------------------------------------------------------------------
| DescribeInstances |
+----------------+-----------------+------------------+----------+
| InstanceName | PrivateIPAdd | PublicIPAdd | Status |
+----------------+-----------------+------------------+----------+
| myeks-ng1-Node| 192.168.3.98 | 15.164.98.61 | running |
| myeks-ng1-Node| 192.168.1.172 | 3.38.178.42 | running |
| operator-host | 172.20.1.100 | 43.202.56.173 | running |
| myeks-ng1-Node| 192.168.2.38 | 15.164.179.186 | running |
+----------------+-----------------+------------------+----------+
## pod ip 확인
$ k get pod -n kube-system -o=custom-columns=NAME:.metadata.name,IP:.status.podIP,STATUS:.status.phase
NAME IP STATUS
aws-node-522wh 192.168.2.38 Running
aws-node-krz9t 192.168.1.172 Running
aws-node-pnq8s 192.168.3.98 Running
coredns-86f5954566-9gzcg 192.168.3.73 Running
coredns-86f5954566-nsh8k 192.168.1.104 Running
kube-proxy-495jt 192.168.3.98 Running
kube-proxy-gtv4c 192.168.1.172 Running
kube-proxy-x2sgq 192.168.2.38 Running
metrics-server-6bf5998d9c-4llv7 192.168.1.108 Running
metrics-server-6bf5998d9c-rhv6n 192.168.2.68 Running
nvidia-device-plugin-daemonset-84v9z 192.168.3.185 Running
nvidia-device-plugin-daemonset-qqq2v 192.168.1.28 Running
nvidia-device-plugin-daemonset-vg5d6 192.168.2.73 Running
- -o=custom-columns
- 출력 형식을 사용자 정의 컬럼 형식으로 지정하는 옵션입니다.
- 원하는 필드를 .(dot notation)으로 접근하여 출력할 수 있습니다.
- NAME:.metadata.name
- .metadata.name: 각 Pod의 이름을 가져옴
- NAME은 사용자 정의 컬럼 이름
- IP:.status.podIP
- .status.podIP: 각 Pod의 할당된 IP 주소를 출력
- IP는 컬럼 이름
- STATUS:.status.phase
- .status.phase: Pod의 현재 상태를 출력 (Running, Pending, Succeeded, Failed, Unknown)
- STATUS는 컬럼 이름
노드의 네트워크 정보 확인 (CNI)
$ for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i tree /var/log/aws-routed-eni; echo; done
>> node 15.164.98.61 <<
/var/log/aws-routed-eni
├── ebpf-sdk.log
├── egress-v6-plugin.log
├── ipamd.log
├── network-policy-agent.log
└── plugin.log
0 directories, 5 files
>> node 3.38.178.42 <<
/var/log/aws-routed-eni
├── ebpf-sdk.log
├── egress-v6-plugin.log
├── ipamd.log
├── network-policy-agent.log
└── plugin.log
0 directories, 5 files
>> node 15.164.179.186 <<
/var/log/aws-routed-eni
├── ebpf-sdk.log
├── egress-v6-plugin.log
├── ipamd.log
├── network-policy-agent.log
└── plugin.log
0 directories, 5 files
## Secondary IP를 직접 사용하도록 설정할 때 발생하는 로그가 기록된 파일
$ ssh ec2-user@$N1 sudo cat /var/log/aws-routed-eni/plugin.log | jq
"level": "info",
"ts": "2025-02-15T04:26:21.047Z",
"caller": "routed-eni-cni-plugin/cni.go:128",
"msg": "Received add network response from ipamd for container b3c65d5aa0f11e0c2dbdbd7968b847029954030b5d97b091bc6914147c45a874 interface eth0: Success:true IPv4Addr:\"192.168.3.185\" VPCv4CIDRs:\"192.168.0.0/16\" NetworkPolicyMode:\"standard\""
## ipamd에서 생성하는 IP주소 관리 관련 로그 파일
$ ssh -i ~/.ssh/my-ec2-keypair.pem ec2-user@$N1 sudo cat /var/log/aws-routed-eni/ipamd.log | jq
{
"level": "debug",
"ts": "2025-02-15T15:24:29.604Z",
"caller": "ipamd/ipamd.go:661",
"msg": "IP stats - total IPs: 10, assigned IPs: 2, cooldown IPs: 0"
}
## IPv6에 대한 Egress-Only Internet Gateway(EIGW)를 통해 인바운드 트래픽을 차단하고 아웃바운드 트래픽만 허용하는 설정과 관련된 로그
$ ssh -i ~/.ssh/my-ec2-keypair.pem ec2-user@$N1 sudo cat /var/log/aws-routed-eni/egress-v6-plugin.log | jq
{
"level": "info",
"ts": "2025-02-15T04:25:26.425Z",
"caller": "egress-cni-plugin/netconf.go:74",
"msg": "Constructed new logger instance"
}
{
"level": "debug",
"ts": "2025-02-15T04:25:26.425Z",
"caller": "egress-cni-plugin/main.go:43",
"msg": "Received an Add request: nsPath: /var/run/netns/cni-647ee23b-ef7e-c87f-5f6e-e7e6110044a8 conf={NetConf:{CNIVersion:0.4.0 Name:aws-cni Type:egress-cni Capabilities:map[] IPAM:{Type:host-local} DNS:{Nameservers:[] Domain: Search:[] Options:[]} RawPrevResult:map[] PrevResult:0xc00009c370 ValidAttachments:[]} IfName: MTU:9001 Enabled:false RandomizeSNAT:prng NodeIP:<nil> PluginLogFile:/var/log/aws-routed-eni/egress-v6-plugin.log PluginLogLevel:DEBUG}"
}
## eBPF 프로그램이 커널에 로드되거나 특정 이벤트에 연결될 때의 상태를 기록하는 로그
$ ssh -i ~/.ssh/my-ec2-keypair.pem ec2-user@$N1 sudo cat /var/log/aws-routed-eni/ebpf-sdk.log | jq
{
"level": "info",
"ts": "2025-02-15T04:25:20.433Z",
"caller": "elfparser/elf.go:197",
"msg": "Calling BPFsys for name policy_events mapType 27 keysize 0 valuesize 0 max entries 262144 and flags 0"
}
## 네트워크 정책에 의해 허용되거나 거부된 트래픽에 대한 로그
$ ssh -i ~/.ssh/my-ec2-keypair.pem ec2-user@$N1 sudo cat /var/log/aws-routed-eni/network-policy-agent.log | jq
{
"level": "info",
"ts": "2025-02-15T15:22:50.774Z",
"logger": "ebpf-client",
"caller": "conntrack/conntrack_client.go:54",
"msg": "Check for any stale entries in the conntrack map"
}
노드의 네트워크 정보 확인 (네트워크)
## eniY는 pod network 네임스페이스와 veth pair
$ for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c addr; echo; done
>> node 15.164.98.61 <<
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 0a:60:8c:62:29:e1 brd ff:ff:ff:ff:ff:ff
altname enp0s5
inet 192.168.3.98/24 metric 1024 brd 192.168.3.255 scope global dynamic ens5
valid_lft 3371sec preferred_lft 3371sec
inet6 fe80::860:8cff:fe62:29e1/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
3: eni8f158163158@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
link/ether 2a:7d:c7:61:8c:89 brd ff:ff:ff:ff:ff:ff link-netns cni-647ee23b-ef7e-c87f-5f6e-e7e6110044a8
inet6 fe80::287d:c7ff:fe61:8c89/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
4: ens6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
link/ether 0a:bf:e1:0d:66:87 brd ff:ff:ff:ff:ff:ff
altname enp0s6
inet 192.168.3.221/24 brd 192.168.3.255 scope global ens6
valid_lft forever preferred_lft forever
inet6 fe80::8bf:e1ff:fe0d:6687/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
5: enif6638a9b400@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc noqueue state UP group default
link/ether 36:f1:44:a8:d3:a0 brd ff:ff:ff:ff:ff:ff link-netns cni-7fb81439-b297-1bbc-7139-062293f7de9d
inet6 fe80::34f1:44ff:fea8:d3a0/64 scope link proto kernel_ll
valid_lft forever preferred_lft forever
$ ssh ec2-user@$N1 sudo iptables -t nat -S
$ ssh ec2-user@$N1 sudo iptables -t nat -L -n -v
노드에서 기본 네트워크 정보 확인
- Network 네임스페이스는 호스트(root)와 파드 별 (Per Pod)로 구분
- 특정한 파드 (kube-proxy, aws-node)는 호스트(root)의 IP를 그대로 사용한다 > 파드의 Host Network 옵션
- t3.mediumd의 경우 ENI 마다 최대 6개 IP를 가질 수 있다.
- ENI0, ENI1으로 2개의 ENI는 자신의 IP 이외에 추가적으로 5개의 보조 프라이빗 IP를 가질 수 있다.
- coredns POD는 veth으로 호스트에는 eniY@ifN 인터페이스와 파드에 eth0 과 연결되어 있다.
보조 IPv4 주소를 파드가 사용하는지 확인
$ k get pod -n kube-system -l k8s-app=kube-dns -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
coredns-86f5954566-9gzcg 1/1 Running 0 11h 192.168.3.73 ip-192-168-3-98.ap-northeast-2.compute.internal <none> <none>
coredns-86f5954566-nsh8k 1/1 Running 0 11h 192.168.1.104 ip-192-168-1-172.ap-northeast-2.compute.internal <none> <none>
$ for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done
>> node 15.164.98.61 <<
default via 192.168.3.1 dev ens5 proto dhcp src 192.168.3.98 metric 1024
192.168.0.2 via 192.168.3.1 dev ens5 proto dhcp src 192.168.3.98 metric 1024
192.168.3.0/24 dev ens5 proto kernel scope link src 192.168.3.98 metric 1024
192.168.3.1 dev ens5 proto dhcp scope link src 192.168.3.98 metric 1024
192.168.3.73 dev eni8f158163158 scope link
192.168.3.185 dev enif6638a9b400 scope link
>> node 3.38.178.42 <<
default via 192.168.1.1 dev ens5 proto dhcp src 192.168.1.172 metric 1024
192.168.0.2 via 192.168.1.1 dev ens5 proto dhcp src 192.168.1.172 metric 1024
192.168.1.0/24 dev ens5 proto kernel scope link src 192.168.1.172 metric 1024
192.168.1.1 dev ens5 proto dhcp scope link src 192.168.1.172 metric 1024
192.168.1.28 dev enib580f8ce7f1 scope link
##192.168.1.104 dev enibb7e08a26fa scope link##
192.168.1.108 dev enif095b7a11bc scope link
>> node 15.164.179.186 <<
default via 192.168.2.1 dev ens5 proto dhcp src 192.168.2.38 metric 1024
192.168.0.2 via 192.168.2.1 dev ens5 proto dhcp src 192.168.2.38 metric 1024
192.168.2.0/24 dev ens5 proto kernel scope link src 192.168.2.38 metric 1024
192.168.2.1 dev ens5 proto dhcp scope link src 192.168.2.38 metric 1024
192.168.2.68 dev eni19b9b1ead18 scope link
##192.168.2.73 dev enib815175c1e4 scope link##
테스트용 netshoot-pod 생성
# [터미널1~3] 노드 모니터링
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 deploy 생성
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
[ Deploy 배포 전]
[ Deploy 배포 전]
## 파드 이름 변수 지정
$ 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}')
## POD 조회
$ k get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
netshoot-pod-744bd84b46-5fprf 1/1 Running 0 49s 192.168.1.214 ip-192-168-1-172.ap-northeast-2.compute.internal <none> <none>
netshoot-pod-744bd84b46-8xdr7 1/1 Running 0 49s 192.168.2.150 ip-192-168-2-38.ap-northeast-2.compute.internal <none> <none>
netshoot-pod-744bd84b46-dmj2b 1/1 Running 0 49s 192.168.3.161 ip
## POD 이름과 IP 정보 확인
$ k get pod -o=custom-columns=NAME:.metadata.name,IP:.status.podIP
NAME IP
netshoot-pod-744bd84b46-5fprf 192.168.1.214
netshoot-pod-744bd84b46-8xdr7 192.168.2.150
netshoot-pod-744bd84b46-dmj2b 192.168.3.161
## 노드의 라우팅 정보 확인
$ for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i sudo ip -c route; echo; done
>> node 15.164.98.61 <<
default via 192.168.3.1 dev ens5 proto dhcp src 192.168.3.98 metric 1024
192.168.0.2 via 192.168.3.1 dev ens5 proto dhcp src 192.168.3.98 metric 1024
192.168.3.0/24 dev ens5 proto kernel scope link src 192.168.3.98 metric 1024
192.168.3.1 dev ens5 proto dhcp scope link src 192.168.3.98 metric 1024
192.168.3.73 dev eni8f158163158 scope link
192.168.3.161 dev eni0e870bc57e3 scope link
192.168.3.185 dev enif6638a9b400 scope link
>> node 3.38.178.42 <<
default via 192.168.1.1 dev ens5 proto dhcp src 192.168.1.172 metric 1024
192.168.0.2 via 192.168.1.1 dev ens5 proto dhcp src 192.168.1.172 metric 1024
192.168.1.0/24 dev ens5 proto kernel scope link src 192.168.1.172 metric 1024
192.168.1.1 dev ens5 proto dhcp scope link src 192.168.1.172 metric 1024
192.168.1.28 dev enib580f8ce7f1 scope link
192.168.1.104 dev enibb7e08a26fa scope link
192.168.1.108 dev enif095b7a11bc scope link
192.168.1.214 dev enicb941457d62 scope link
>> node 15.164.179.186 <<
default via 192.168.2.1 dev ens5 proto dhcp src 192.168.2.38 metric 1024
192.168.0.2 via 192.168.2.1 dev ens5 proto dhcp src 192.168.2.38 metric 1024
192.168.2.0/24 dev ens5 proto kernel scope link src 192.168.2.38 metric 1024
192.168.2.1 dev ens5 proto dhcp scope link src 192.168.2.38 metric 1024
192.168.2.68 dev eni19b9b1ead18 scope link
192.168.2.73 dev enib815175c1e4 scope link
192.168.2.150 dev eni253e2c3c007 scope link
- 파드가 생성되면, 워커 노드에 eniY@ifN 추가되고 라우팅 테이블에도 정보가 추가되는 것을 확인할 수 있습니다.
노드 간 파드 통신
파드간 통신 흐름 : AWS VPC CNI 경우 별도의 오버레이 과정을 거치지 않고, VPC Native 하게 Pod간 직접 통신이 가능하다.
Pod간 통신 테스트 : 별도의 NAT 없이 통신 가능
## 각 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}')
## 각 파드에서 PING 테스트 진행
$ k exec -it $PODNAME1 -- ping -c 2 $PODIP1
PING 192.168.1.214 (192.168.1.214) 56(84) bytes of data.
64 bytes from 192.168.1.214: icmp_seq=1 ttl=127 time=0.021 ms
64 bytes from 192.168.1.214: icmp_seq=2 ttl=127 time=0.030 ms
--- 192.168.1.214 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1024ms
rtt min/avg/max/mdev = 0.021/0.025/0.030/0.004 ms
$ k exec -it $PODNAME2 -- ping -c 2 $PODIP2
PING 192.168.2.150 (192.168.2.150) 56(84) bytes of data.
64 bytes from 192.168.2.150: icmp_seq=1 ttl=127 time=0.019 ms
64 bytes from 192.168.2.150: icmp_seq=2 ttl=127 time=0.030 ms
--- 192.168.2.150 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1006ms
rtt min/avg/max/mdev = 0.019/0.024/0.030/0.005 ms
$ k exec -it $PODNAME3 -- ping -c 2 $PODIP3
PING 192.168.3.161 (192.168.3.161) 56(84) bytes of data.
64 bytes from 192.168.3.161: icmp_seq=1 ttl=127 time=0.026 ms
64 bytes from 192.168.3.161: icmp_seq=2 ttl=127 time=0.039 ms
--- 192.168.3.161 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1044ms
rtt min/avg/max/mdev = 0.026/0.032/0.039/0.006 ms
$ sudo tcpdump -i any -nn icmp
$ sudo tcpdump -i ens5 -nn icmp
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ens5, link-type EN10MB (Ethernet), snapshot length 262144 bytes
16:16:25.171580 IP 192.168.3.161 > 192.168.1.214: ICMP echo request, id 18, seq 1, length 64
16:16:25.175700 IP 192.168.1.214 > 192.168.3.161: ICMP echo reply, id 18, seq 1, length 64
16:16:26.172857 IP 192.168.3.161 > 192.168.1.214: ICMP echo request, id 18, seq 2, length 64
16:16:26.173918 IP 192.168.1.214 > 192.168.3.161: ICMP echo reply, id 18, seq 2, length 64
$ sudo tcpdump -i ens6 -nn icmp
$ sudo tcpdump -i eniYYYYYYYY -nn icmp
## ip rule 확인
$ ip rule
0: from all lookup local
512: from all to 192.168.3.73 lookup main
512: from all to 192.168.3.185 lookup main
512: from all to 192.168.3.161 lookup main
1024: from all fwmark 0x80/0x80 lookup main
32766: from all lookup main
32767: from all lookup default
## route local table 확인
$ ip route show table local
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
local 192.168.3.98 dev ens5 proto kernel scope host src 192.168.3.98
local 192.168.3.221 dev ens6 proto kernel scope host src 192.168.3.221
broadcast 192.168.3.255 dev ens5 proto kernel scope link src 192.168.3.98
broadcast 192.168.3.255 dev ens6 proto kernel scope link src 192.168.3.221
## route main table 확인
$ ip route show table main
default via 192.168.3.1 dev ens5 proto dhcp src 192.168.3.98 metric 1024
192.168.0.2 via 192.168.3.1 dev ens5 proto dhcp src 192.168.3.98 metric 1024
192.168.3.0/24 dev ens5 proto kernel scope link src 192.168.3.98 metric 1024
192.168.3.1 dev ens5 proto dhcp scope link src 192.168.3.98 metric 1024
192.168.3.73 dev eni8f158163158 scope link
192.168.3.161 dev eni0e870bc57e3 scope link
192.168.3.185 dev enif6638a9b400 scope link
## NODE 간 통신 테스트 (노드 1번 > 노드 3번)
$ k exec -it $PODNAME1 -- ping -c 2 $PODIP2
파드에서 외부 통신
파드에서 외부 통신 흐름 : iptable에 SNAT를 통하여 노드의 eth0(ens5) IP로 변경되어서 외부와 통신
아웃바운드에서는 host ip로 nat되며, 라우팅 설정은 iptables에서 관리
파드 -> 외부 통신 테스트 및 확인
## 외부 통신 테스트
$ k exec -it $PODNAME1 -- ping -c 1 www.google.com
$ k exec -it $PODNAME1 -- ping -i 0.1 www.google.com
$ k exec -it $PODNAME1 -- ping -i 0.1 8.8.8.8
## 워커 노드에서 패킷 캡쳐
$ sudo tcpdump -i any -nn icmp
$ sudo tcpdump -i ens5 -nn icmp
## 192.168.1.214 - POD IP , 192.168.1.172 - 노드 IP (ens5) , 172 대역 - 구글 IP
16:48:55.129850 IP 192.168.1.172 > 172.217.25.164: ICMP echo request, id 24356, seq 5, length 64
16:48:55.158610 IP 172.217.25.164 > 192.168.1.172: ICMP echo reply, id 24356, seq 5, length 64
16:48:55.230987 IP 192.168.1.172 > 172.217.25.164: ICMP echo request, id 24356, seq 6, length 64
16:48:55.259527 IP 172.217.25.164 > 192.168.1.172: ICMP echo reply, id 24356, seq 6, length 64
16:48:55.332120 IP 192.168.1.172 > 172.217.25.164: ICMP echo request, id 24356, seq 7, length 64
16:48:55.360648 IP 172.217.25.164 > 192.168.1.172: ICMP echo reply, id 24356, seq 7, length 64
16:48:55.433047 IP 192.168.1.172 > 172.217.25.164: ICMP echo request, id 24356, seq 8, length 64
16:48:55.461587 IP 172.217.25.164 > 192.168.1.172: ICMP echo reply, id 24356, seq 8, length 64
16:48:55.533844 IP 192.168.1.172 > 172.217.25.164: ICMP echo request, id 24356, seq 9, length 64
16:48:55.562470 IP 172.217.25.164 > 192.168.1.172: ICMP echo reply, id 24356, seq 9, length 64
16:48:55.634829 IP 192.168.1.172 > 172.217.25.164: ICMP echo request, id 24356, seq 10, length 64
16:48:55.663364 IP 172.217.25.164 > 192.168.1.172: ICMP echo reply, id 24356, seq 10, length 64
16:48:55.735804 IP 192.168.1.172 > 172.217.25.164: ICMP echo request, id 24356, seq 11, length 64
16:48:55.764331 IP 172.217.25.164 > 192.168.1.172: ICMP echo reply, id 24356, seq 11, length 64
16:48:55.836651 IP 192.168.1.172 > 172.217.25.164: ICMP echo request, id 24356, seq 12, length 64
16:48:55.865190 IP 172.217.25.164 > 192.168.1.172: ICMP echo reply, id 24356, seq 12, length 64
16:48:55.937473 IP 192.168.1.172 > 172.217.25.164: ICMP echo request, id 24356, seq 13, length 64
16:48:55.966058 IP 172.217.25.164 > 192.168.1.172: ICMP echo reply, id 24356, seq 13, length 64
## 각 node 공인 IP와 POD가 Outbound 했을 때의 공인 IP 비교
## Node IP
$ for i in $N1 $N2 $N3; do echo ">> node $i <<"; ssh ec2-user@$i curl -s ipinfo.io/ip; echo; echo; done
>> node 15.164.98.61 <<
15.164.98.61
>> node 3.38.178.42 <<
3.38.178.42
>> node 15.164.179.186 <<
15.164.179.186
## POD IP
$ 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-744bd84b46-5fprf <<
3.38.178.42
>> Pod : netshoot-pod-744bd84b46-8xdr7 <<
15.164.179.186
>> Pod : netshoot-pod-744bd84b46-dmj2b <<
15.164.98.61
통신 흐름은 POD > NODE > Google 로 통신이 되는 것을 tcpdump에서 확인할 수 있고,
route main table에서도 172 ip를 ens5 로 전달하는 라우트 설정이 default로 적용되어 있는 것을 확인할 수 있습니다.
$ ip route show table main
default via 192.168.1.1 dev ens5 proto dhcp src 192.168.1.172 metric 1024
아래 명령어를 통해 VPC CNI가 SNAT를 적용하는 정책을 확인 할 수 있습니다.
첫 번째 규칙: 대상 IP가 192.168.0.0/16인 경우 SNAT를 적용하지 않고 반환(RETURN)
두 번째 규칙: vlan 인터페이스가 아닌 경우, SNAT를 적용하여 출발지 IP를 192.168.1.172(노드 ENI)로 변경 (Pod가 외부로 나갈 때 ENI의 IP를 사용하도록 함)
$ 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.172 --random-fully
파드 <> 운영 EC2 간 통신 확인
## 운영서버 EC2 SSH 접속
$ ssh <운영서버 EC2 공인 IP>
## 운영서버 내부
POD1IP=<파드1 IP 지정>
POD1IP=192.168.1.214
ping -c 1 $POD1IP
exit
-----------------------
파드1 > 운영서버로 통신이 가능한 이유는?
false (기본값): AWS VPC CNI가 내부적으로 SNAT를 적용함.
Pod가 AWS VPC 외부로 나갈 때, 노드의 ENI IP로 출발지 IP를 변경.
iptables -t nat -S | grep 'A AWS-SNAT-CHAIN'의 정책과 같이 SNAT 규칙이 적용
$ k get ds aws-node -n kube-system -o json | jq '.spec.template.spec.containers[0].env'
{
"name": "AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG",
"value": "false"
},
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
55 3358 RETURN all -- * * 0.0.0.0/0 192.168.0.0/16 /* AWS SNAT CHAIN */
50 3112 SNAT all -- * !vlan+ 0.0.0.0/0 0.0.0.0/0 /* AWS, SNAT */ ADDRTYPE match dst-type !LOCAL to
:192.168.1.172 random-fully
Chain KUBE-POSTROUTING (1 references)
pkts bytes target prot opt in out source destination
128 7850 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 mark match ! 0x4000/0x4000
0 0 MARK all -- * * 0.0.0.0/0 0.0.0.0/0 MARK xor 0x4000
0 0 MASQUERADE all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service traffic requiring SNAT */
random-fully
Chain POSTROUTING (policy ACCEPT 78 packets, 4738 bytes)
pkts bytes target prot opt in out source destination
사내 내부에 연결 확장된 네트워크 대역과 SNAT 없이 통신 가능하게 설정 해보기
## [터미널 1]
$ watch -d kubectl get pod -n kube-system
## [워커노드]
$ 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 없이 통신 가능하도록 정책 업데이트
$ k set env daemonset aws-node -n kube-system AWS_VPC_K8S_CNI_EXCLUDE_SNAT_CIDRS=172.20.0.0/16
## 변경된 Daemonset 설정 확인
$ k 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"
}
## 172 대역으로 ping 테스트
$ k exec -it $PODNAME1 -- ping 172.20.1.100
## 패킷 카운트 확인
$ 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
0 0 RETURN all -- * * 0.0.0.0/0 172.20.0.0/
16 /* AWS SNAT CHAIN EXCLUSION */
43 2672 RETURN all -- * * 0.0.0.0/0 192.168.0.0
/16 /* AWS SNAT CHAIN */
36 2256 SNAT all -- * !vlan+ 0.0.0.0/0 0.0.0.0/0
/* AWS, SNAT */ ADDRTYPE match dst-type !LOCAL to:192.168.1.172 rando
m-fully
env에 변경 설정한 값을 영구 유지하려면?
## ConfigMap 생성
apiVersion: v1
kind: ConfigMap
metadata:
name: aws-cni-config
namespace: kube-system
data:
AWS_VPC_K8S_CNI_EXCLUDE_SNAT_CIDRS: "172.20.0.0/16"
## Configmap 배포
$ k apply -f aws-cni-config.yaml
## DaemonSet에 참조 설정
spec:
containers:
- name: aws-node
env:
- name: AWS_VPC_K8S_CNI_EXCLUDE_SNAT_CIDRS
valueFrom:
configMapKeyRef:
name: aws-cni-config
key: AWS_VPC_K8S_CNI_EXCLUDE_SNAT_CIDRS
## Daemonset 재배포
$ k rollout restart daemonset aws-node -n kube-system
노드에 파드 생성 갯수 제한
- 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 배율)
$ 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"}'
- 인스턴스 타입 별 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
- 워커 노드의 최대 파드 갯수 확인
$ 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 |
+----------+----------+--------------+
| 15 | 4 | t3.xlarge |
| 12 | 3 | t3.large |
| 15 | 4 | t3.2xlarge |
| 6 | 3 | t3.medium |
| 2 | 2 | t3.nano |
| 2 | 2 | t3.micro |
| 4 | 3 | t3.small |
+----------+----------+--------------+
$ k describe node | grep Allocatable: -A6
Allocatable:
cpu: 1930m
ephemeral-storage: 27845546346
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3364528Ki
pods: 17
--
Allocatable:
cpu: 1930m
ephemeral-storage: 27845546346
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3364528Ki
pods: 17
--
Allocatable:
cpu: 1930m
ephemeral-storage: 27845546346
hugepages-1Gi: 0
hugepages-2Mi: 0
memory: 3364528Ki
pods: 17
실제 최대 개수까지 배포해보기
## 각 노드 접속 후 모니터링
$ while true; do ip -br -c addr show && echo "--------------" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; done
## POD 모니터링
$ watch -d 'kubectl get pod -o wide'
## Deploy 배포
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
- deploy replicas로 pod 증설하기
(pod 8개) - 정상
$ k scale deployment nginx-deployment --replicas=8
$ watch -d 'kubectl get pods -o name | wc -l
8
(pod 15개) - 정상
$ k scale deployment nginx-deployment --replicas=15
$ watch -d 'kubectl get pods -o name | wc -l
15
(pod 30개) - 정상
$ k scale deployment nginx-deployment --replicas=30
$ watch -d 'kubectl get pods -o name | wc -l
30
(pod 50개) - 비정상
$ k scale deployment nginx-deployment --replicas=30
$ watch -d 'kubectl get pods -o name | wc -l
50
$ k get pod -o wide | grep Pending
nginx-deployment-6c8cb99bb9-4t52k 0/1 Pending 0 62s <none> <none> <none> <none>
nginx-deployment-6c8cb99bb9-5lfjb 0/1 Pending 0 62s <none> <none> <none> <none>
nginx-deployment-6c8cb99bb9-8hmlp 0/1 Pending 0 62s <none> <none> <none> <none>
nginx-deployment-6c8cb99bb9-8wd2z 0/1 Pending 0 62s <none> <none> <none> <none>
nginx-deployment-6c8cb99bb9-966cm 0/1 Pending 0 62s <none> <none> <none> <none>
nginx-deployment-6c8cb99bb9-9xfkw 0/1 Pending 0 62s <none> <none> <none> <none>
nginx-deployment-6c8cb99bb9-cwwxc 0/1 Pending 0 62s <none> <none> <none> <none>
nginx-deployment-6c8cb99bb9-lx75p 0/1 Pending 0 62s <none> <none> <none> <none>
nginx-deployment-6c8cb99bb9-nnkqn 0/1 Pending 0 62s <none> <none> <none> <none>
nginx-deployment-6c8cb99bb9-rdddl 0/1 Pending 0 62s <none> <none> <none> <none>
nginx-deployment-6c8cb99bb9-s4gz5 0/1 Pending 0 62s <none> <none> <none> <none>
nginx-deployment-6c8cb99bb9-wdnkr 0/1 Pending 0 62s <none> <none> <none> <none>
nginx-deployment-6c8cb99bb9-xsnh5 0/1 Pending 0 62s <none> <none> <none> <none>
이렇게 위에서 node를 describe했던 것과 같이 50대가 넘어가며, 더이상 Pod가 생성되지 않고 pending 상태에 빠지는 것을 확인할 수 있고 이벤트 로그 확인 시 Too many pods로 생성되지 않는 로그를 확인할 수 있습니다.
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 2m47s 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.
해결 방안 : Prefix Delegation, WARM & MIN IP/Prefix Targets, Custom Network
'Cloud > AWS' 카테고리의 다른 글
[AWS] EKS Networkings (2) (0) | 2025.02.16 |
---|---|
[AWS] EKS 설치 및 기본 사용 (3) (0) | 2025.02.08 |
[AWS] EKS 설치 및 기본 사용 (2) (0) | 2025.02.07 |
[AWS] EKS 설치 및 기본 사용 (1) (0) | 2025.02.06 |
[AWS] EKS에 대하여 (0) | 2025.02.05 |