CNI
- CNI란, Container Network Interface의 약자로 컨테이너 간의 네트워크를 정의하는 인터페이스를 말한다.
- 기본적으로 Kubernetes에서 사용되는 CNI는 Calico, Flannel 등이 유명하고, AWS에서는 AWS VPC CNI라는 자체 CNI를 제공한다.
- 현재까지는 KOPS에서는 POD 단위의 보안그룹 적용이 되지 않는다. (EKS는 가능)
AWS VPC CNI
우리가 EKS 환경에서 실습을 하기 때문에 AWS VPC CNI에 대해서 조금 더 알아보자.
AWS VPC CNI의 경우 파드의 IP 네트워크 대역이 해당 노드의 IP 대역과 같게 설정되어 직접 통신이 가능하다는 장점이 있다.
아래 그림에서 확인할 수 있듯 Calico에서는 node와 pod의 ip 대역이 다르지만 AWS VPC CNI의 경우 같다.
Pod간 통신으로 넘어가 보면 pod1에서 pod2로 패킷이 전달될 때 Calico는 원본 패킷이 IP대역대의 변화로 인해 오버레이 통신을 진행하고, AWS VPC CNI의 경우 IP 대역대가 동일하기 때문에 원본 패킷을 그대로 전달 할 수 있다.
노드 간 파드 통신
앞서 보았듯 AWS VPC CNI의 경우 별도 오버레이 통신 기술 없이 VPC Native 통신이 가능하다.
# 파드 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})
# 파드1 Shell 에서 파드2로 ping 테스트
kubectl exec -it $PODNAME1 -- ping -c 2 $PODIP2
# 파드2 Shell 에서 파드1로 ping 테스트
kubectl exec -it $PODNAME2 -- ping -c 2 $PODIP1
# 워커 노드 EC2 : TCPDUMP 확인
sudo tcpdump -i any -nn icmp
sudo tcpdump -i ens5 -nn icmp
[워커 노드1]
# routing policy database management 확인
ip rule
# routing table management 확인
ip route show table local
파드에서 외부로의 통신
AWS VPC CNI에서 파드가 외부 통신하는 경우 다음과 같다. iptable에 SNAT를 통해 노드의 eth0 IP로 변경되 외부와 통신할 수 있다.
# 각 워커 노드에서 tcpdump 실행
sudo tcpdump -i any -nn icmp
# ping 테스트
kubectl exec -it $PODNAME1 -- ping -c 1 www.google.com
kubectl exec -it $PODNAME2 -- ping -c 1 www.google.com
# 파드가 외부와 통신시에는 아래 처럼 '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 172.30.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 172.30.85.242 --random-fully
max-pod 설정
기본적으로 AWS 워커 노드에서 생성 가능한 최대 파드 갯수 제한이 있다. 네트워크 인터페이스는 3개, Secondary IPv4는 5개씩 할당 가능하다.
- secondary IPv4 : 인스턴스 유형에 따라 최대 ENI 갯수와 할당 가능 IP 수를 조합해 선정
기본적으로 aws-node와 kube-proxy 파드는 제외하고 최대 갯수는 다음 공식대로 구할 수 있다. (뒤의 +2가 제외되는 2개이다)
최대 파드 생성 갯수 ( 인스턴스의 타입 별 최대 ENI 갯수 * ( ENI에 할당 가능한 최대 IP 갯수 - 1 ) ) + 2
aws ec2 describe-instance-types --filters Name=instance-type,Values=c5d.* \
--query "InstanceTypes[].{Type: InstanceType, MaxENI: NetworkInfo.MaximumNetworkInterfaces, IPv4addr: NetworkInfo.Ipv4AddressesPerInterface}" \
--output table
--------------------------------------
| DescribeInstanceTypes |
+----------+----------+--------------+
| IPv4addr | MaxENI | Type |
+----------+----------+--------------+
| 15 | 4 | t3.2xlarge |
| 6 | 3 | t3.medium |
| 12 | 3 | t3.large |
| 15 | 4 | t3.xlarge |
| 2 | 2 | t3.micro |
| 2 | 2 | t3.nano |
| 4 | 3 | t3.small |
+----------+----------+--------------+
만약 위의 표처럼 t3.medium을 예로 들면 MaxENI 3 * (IPv4addr 6 - 1 ) + 2 이므로 총 17개가 생성 가능하다.
이를 해결하기 위해서는 LimitRange 설정인 kubelet의 max-pods 태그를 수정하거나
AWS VPC CNI애서 (ENABLE_PREFIX_DELEGATION, WARM_PREFIX_TARGET)을 수정하면 설정 값을 늘릴 수 있다.
LimitRange를 확인하는 방법은 다음과 같은데, 기본적으로 컨테이너에게 0.1cpu를 보장하기 때문에 해당 설정 값을 제거해서 제한을 푸는 것이다.
# LimitRanges 기본 정책 확인 : 컨테이너는 기본적으로 0.1CPU(=100m vcpu)를 최소 보장(개런티)
kubectl describe limitranges
Name: limits
Namespace: default
Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio
---- -------- --- --- --------------- ------------- -----------------------
Container cpu - - 100m - -
# LimitRanges 기본 정책 삭제
kubectl delete limitranges limits
kubectl get limitranges
KOPS를 통한 VPC CNI 수정
# 수정 전 env 정보 확인 : WARM_PREFIX_TARGET은 기본값이 1로 이미 되어 있음
kubectl describe ds -n kube-system aws-node | grep ADDITIONAL_ENI_TAGS: -A22
ADDITIONAL_ENI_TAGS: {"KubernetesCluster":"gasida.link","kubernetes.io/cluster/gasida.link":"owned"}
AWS_VPC_CNI_NODE_PORT_SUPPORT: true
AWS_VPC_ENI_MTU: 9001
AWS_VPC_K8S_CNI_CONFIGURE_RPFILTER: false
AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG: false
AWS_VPC_K8S_CNI_EXTERNALSNAT: false
AWS_VPC_K8S_CNI_LOGLEVEL: DEBUG
AWS_VPC_K8S_CNI_LOG_FILE: /host/var/log/aws-routed-eni/ipamd.log
AWS_VPC_K8S_CNI_RANDOMIZESNAT: prng
AWS_VPC_K8S_CNI_VETHPREFIX: eni
AWS_VPC_K8S_PLUGIN_LOG_FILE: /var/log/aws-routed-eni/plugin.log
AWS_VPC_K8S_PLUGIN_LOG_LEVEL: DEBUG
DISABLE_INTROSPECTION: false
DISABLE_METRICS: false
DISABLE_NETWORK_RESOURCE_PROVISIONING: false
ENABLE_IPv4: true
ENABLE_IPv6: false
ENABLE_POD_ENI: false
ENABLE_PREFIX_DELEGATION: false
WARM_ENI_TARGET: 1
WARM_PREFIX_TARGET: 1
# kops 클러스터와 인스턴스 그룹에 파라미터 수정 : 노드에 kubelet 에 maxPods 110개로 수정, Prefix Assign 활성화
kops edit cluster
...
kubelet:
anonymousAuth: false
maxPods: 110
...
networking:
amazonvpc:
env:
- name: ENABLE_PREFIX_DELEGATION
value: "true"
...
이상으로 AWS VPC CNI를 기반으로 한 EKS의 네트워킹에 대해서 확인해보았다.
쿠버네티스를 온프레미스 환경에서 운영하는 것 보다는 확실히 EKS에서도 많은 최적화가 되어있고
오히려 AWS 서비스를 이용하는 사람들이라면 쿠버네티스 운영에 EKS가 더 효율적이라고 생각해볼 수 있는 구간이였다.