2주차 주제는 네트워킹이다.

AWS에서 쿠버네티스를 구축하면 CNI(Container Network Interface)는 Calico 같은 오픈소스를 사용하거나 AWS에서 지원하는 VPC CNI를 사용할 수 있다.

이번에 구축할 쿠버네티스에는 VPC CNI를 사용할 것이다.

VPC CNI란?

Untitled

VPC CNI의 가장 큰 특징은 노드와 파드의 IP 대역이 같다는 것이다. 즉, 쿠버네티스 네트워크 특징인 오버레이 네트워크가 없다는 것이다! 그래서 pod 간 통신을 하게 되는 경우 오버헤드가 없다는 장점이 있다!

VPC CNI는 노드마다 daemonset으로 aws-node라는 이름으로 배포되며, L-IPAM(Local IP Address Manager)CNI 플러그인으로 구성된다.

Untitled 1

L-IPAM은 사용 가능한 IP 주소들을 가지고 있는 warm-pool을 유지 관리하고 그 IP를 pod에 할당해 주는 역할을 한다. L-IPAM은 EC2 인스턴스의 메타데이터를 통해 사용 가능한 ENI와 해당 보조 IP를 알 수 있다. 그래서 kubelet이 생성할 pod에 대해 요청이 오면 L-IPAM은 warm-pool에서 보조 IP 하나를 해당 pod에게 할당해 줄 수 있다.

https://github.com/aws/amazon-vpc-cni-k8s

각 node에 접속해서 메타데이터를 검색해 보면 두 개의 MAC 주소를 확인할 수 있다. 이는 각 ENI의 주소이다.

Untitled 2

그래서 아래와 같은 주소에 curl을 날리면 현재 node가 가지고 있는 ENI IP를 알 수 있다.

http://169.254.169.254/latest/meta-data/network/interfaces/macs/02:5c:67:39:51:01/local-ipv4s

Untitled 3

Untitled 4

Untitled 5

인스턴스 메타데이터 검색 명령어는 아래 링크를 참조하였다.

인스턴스 메타데이터 검색 - Amazon Elastic Compute Cloud

node는 ENI를 인스턴스 사양에 따라 추가할 수 있다. 여기서 기본 ip와 보조 ip가 있는데 둘의 차이는 아래 사진과 같다. 보조ip는 한번 할당받아도 다른 서버에 재할당 받을 수 있다. 기본ip는 node가 가지게 되는 private ip이고 보조ip는 L-IPAM에서 할당받게 되는 pod의 ip이다.

Untitled 6

Amazon EC2 인스턴스 IP 주소 지정 - Amazon Elastic Compute Cloud

실습으로 VPC CNI 기본 네트워크 확인하기

가시다님이 제공해 주신 클포로 쿠버네티스 구축은 원클릭 배포했다.

현재 사용중인 CNI 이미지 정보는 아래와 같다.

# kubectl describe daemonsets.apps aws-node --namespace kube-system | grep Image
    Image:      602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon-k8s-cni-init:v1.16.4-eksbuild.2
    Image:      602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon-k8s-cni:v1.16.4-eksbuild.2
    Image:      602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon/aws-network-policy-agent:v1.0.8-eksbuild.1

VPC CNI는 daemonsets으로 각 노드에 하나씩 구성되어 있으며 aws-node라는 이름을 가지고 있다.

# kubectl describe cm -n kube-system kube-proxy-config | grep mode
mode: "iptables"

kubeproxy는 iptables를 사용하고 있다. ipvs 모드를 사용하지 않는 이유는 ipvs에서 동작하지 않는 기능이 있기 때문이라고 한다. 또 iptables는 오랜 기간 사용한 만큼 안정성이 있다고 한다.

Untitled 7

node와 pod의 ip를 확인해 보면 둘 다 같은 대역을 사용하고 있다는 것이 보인다

그리고 VPC CNI와 kube proxy는 node의 ip를 사용하고 있다. 이는 pod가 Host Network를 사용하기 때문이다.

# kubectl get daemonsets.apps kube-proxy --namespace kube-system -o yaml | grep hostNetwork
      hostNetwork: true

hostNetwork는 node의 ip, port를 사용하여 말 그대로 host 서버의 네크워크를 사용하는 것이다. VPC CNI와 kube proxy는 node와 같은 네트워크를 사용하면서 node 내 pod의 IP 관리와 트래픽 처리를 효율적으로 할 수 있다.

Untitled 8

그래서 hostNetwork: true로 설정하면 ports부분이 설정되어야 하는데, 아래 사진에서 hostIP는 node의 ip를 말하고, hostPort는 호스트 네트워크를 사용하기 때문에 containerPort와 동일한 값이어야 한다. containerPort만 지정하면 나머지 두 값은 필수 값은 아니다

Untitled 9

[Kubernetes] Pod 관련 Host Network 옵션과 동작원리

위 링크를 참고하였다.

반면 coredns는 192.168.1.0/24 대역의 node와 192.168.2.0/24 대역을 가진 node의 보조 ip를 할당받았다.

coredns는 hostnetwork를 사용하지 않고 각 node의 보조 ip를 할당받았다

Untitled 10

node 1,2에 있는 192.168.2.150, 192.168.1.88가 coredns의 ip인데 각 node 1,2의 라우팅테이블에 설정되어 있다.

이제 테스트 pod를 하나 띄워볼 것이다.

Untitled 11

이제 각 node에 테스트 pod가 할당되었고 라우팅테이블에도 설정되었다.

그리고 원래 한 node는 ENI가 하나밖에 없었는데 ENI1이 추가로 생겼다.

Untitled 12

그래서 만들어진 현재 node의 ENI 및 ip 정보들이다.

node 간 pod 통신 테스트

Untitled 13

테스트로 만든 pod3에서 pod1으로 ping 테스트를 할 것이다.

Untitled 14

tcpdump로 확인해 보면 패킷이 pod3에서 pod1로 향한 것을 알 수 있다.

pod에서 외부 통신

이제 외부 통신에 대해 확인해 볼 것이다.

Untitled 15

pod1에서 구글 주소(142.250.76.132)로 ping해 보았는데 tcpdump로는 pod에서 외부로 바로 나간 것만 보인다.

Untitled 16

그래서 iptables 룰을 확인해 보면 SNAT되어 외부와 통신했다는 것을 찾을 수 있었다.

VPC CNI의 SNAT 설정에 따라, 외부 통신 시 SNAT하거나 혹은 SNAT 없이 통신할 수 있다.

Untitled 17

현재는 AWS_VPC_K8S_CNI_EXTERNALSNAT 값이 기본적으로 false로 되어 있다. 이를 true로 바꾸게 되면 external NAT를 사용하겠다는 의미로 SNAT 설정이 없어지게 된다. 그래서 sudo iptables -t nat -S | grep 'A AWS-SNAT-CHAIN’를 입력하면 아무 것도 출력되지 않는다.

node에 pod 생성 개수 제한

인스턴스 사양에 따라 최대 pod 개수가 달라진다. ENI 개수와 보조 IP 할당 개수가 달라진다.

현재 인스턴스 사양은 t3.medium을 사용하고 있는데 이는 ENI는 3개까지, ENI 당 가질 수 있는 IP 개수는 6개이다 (기본IP를 빼면 보조IP는 5개)

Untitled 18

최대 pod 개수를 구하는 공식은 ENI 개수 X (IP 개수 - 1) 이다.

그럼 t3.medium 기준으로 3 X (6 - 1) = 15개이다. 15개는 aws-node와 kube-proxy를 제외한 개수이다.

또는 node를 확인해 보면 Allocatable에서 최대 몇 개까지 pod를 생성할 수 있는지 알 수 있다.

Untitled 19

17로 나와있는데 이는 15개에서 aws-node와 kube-proxy의 개수 2개를 더한 값이다.

그래서 pod 개수가 몇 개까지 만들어 지나 테스트를 해보았다.

Untitled 20

pod는 43개까지 생성되고 그 이후로는 Pending 상태가 되었다.

원인은 Too many pods였다.

ENI를 확인해 보면 3개의 node 모두 3개의 ENI를 가지게 되었다.

Untitled 21

ENI 3개와 그 ENI에 연결된 15개의 pod들이다.

계산식으로하면 3 X 3 X (6 - 1) = 45개까지 만들 수 있다.

여기서 나머지 두 개는 coredns가 있으니 43개 딱 맞다.

여기서 최대 개수를 늘리는 방법이 있다. IPv4 Prefix Delegation를 조정하는 방법이다.

prefix를 /28로 변경해서 16개씩 할당받는 방법이다.

계산 법은 ENI 개수 X (IP 개수 - 1) X 16 이다.

하지만 이는 Nitro 계열 인스턴스에만 해당된다. 또한 vCPU 30코어 미만은 110개로 제한되고 그 외는 250개가 최대값이다.

Untitled 22

처음에 기본값으로 ENABLE_PREFIX_DELEGATION 값이 false로 되어 있다. 그래서 이를 true로 변경하고 각 node를 재부팅해 주었다. 처음에 재부팅을 안하니 변경된 사항이 없어 당황했다. aws-node가 재생성되었는데도 node 자체의 prefix가 바뀌는 거라 그런지 node의 재부팅이 필요한 것 같다.

Untitled 23

원래 아무 것도 안뜨던 부분이 /28을 붙인 채로 변경되었다.

그래도 아직 최대 개수가 변경되지 않았다.

이번엔 kubectl cordon으로 node에 pod가 스케줄링되지 않게 disable 해 놓고 ASG를 0으로 줄였다가 다시 3으로 늘렸다.

하지만 분명 prefix는 28로 바꼈는데 아래 pods 수가 17에서 바뀌지 않는다….

Untitled 24

왜인지 좀 더 확인해 봐야 할 것 같다…..

마무리

이번 시간에는 VPC CNI를 중점으로 확인했던 것 같다. 쿠버네티스의 ingress, service, networkpolicy 부분은 좀 알았어도 VPC CNI는 생소했다.

근데 max pods가 실패해서 아쉬움이 많이 남는다…

내용 추가

가시다님이 주신 키워드에 따라 다시 내용 추가하였다

kubelet이 max-pods 값을 가지고 있고 이 값을 수정해 줘야 하는데 eks에서 수정하려면 노드그룹에 옵션 값으로 줄 수 있다.

기존 노드에서 max pods를 조절하려고 했지만 못찾고 새로운 노드그룹에 마이그레이션하는 방법으로 진행하였다

nodegroup 생성할 때 --max-pods-per-node에 최대 개수를 넣어주면 된다!

eksctl create nodegroup --cluster myeks --max-pods-per-node 110

image

image

prefix도 28로 바뀐걸 확인했고 max pods가 110으로 늘어난 것도 확인하였다. 그래서 테스트 파드를 생성해서 100개까지 생성되는 것을 확인하였다

image

https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/cni-increase-ip-addresses.html https://www.eksworkshop.com/docs/networking/vpc-cni/prefix/configure/

Categories:

Updated:

Leave a comment