AEWS 2주차 - Networking(CNI, 최대 파드 개수)
본 글은 가시다님이 진행하시는 AEWS(AWS EKS Workshop Study)를 참여하여 정리한 글입니다. 모르는 부분이 많아서 틀린 내용이 있다면 말씀 부탁드리겠습니다! |
CNI
CNI란?
CNI는 Container Network Interface의 줄임말입니다. 즉 컨테이너 오케스트레이션 환경에서 네트워크를 설정하고 관리하는 표준 인터페이스입니다.
CNI의 핵심 기능
✔️ IP 주소 할당:
- 각 컨테이너(Pod)에 고유한 IP를 할당합니다.
- AWS에서는 AWS VPC CNI를 사용하면 AWS VPC 내부에서 직접 IP를 할당할 수 있음.
✔️ 네트워크 라우팅:
- 컨테이너 간 트래픽이 올바르게 전달되도록 라우팅을 설정합니다.
- Kubernetes에서는 kube-proxy와 함께 동작하여 트래픽을 관리.
✔️ 네트워크 정책 적용:
- 특정 Pod 간의 통신을 허용하거나 차단하는 **네트워크 정책(Network Policy)**을 적용할 수 있음.
- Calico, Cilium 등의 CNI는 네트워크 정책을 세밀하게 관리하는 기능을 제공.
✔️ 확장 가능성:
- 플러그인 기반이므로 특정 환경에 맞춰 CNI 플러그인을 교체할 수 있음.
- AWS, GCP, Azure 등 클라우드 환경에 최적화된 CNI 플러그인이 존재.
대표적인 CNI 플러그인으로는 Calico, Clium, AWS VPC CMI, Flannel, 등이 있습니다.
AWS VPC CNI
위에서 언급한 대표적인 CNI 플러그인중 AWS에 최적화되어있는 AW VPC CNI에 대해서 알아보겠습니다.
AWS VPC CNI란
EKS 클러스터를 생성하면 자동으로 설치되는 플러그인으로써 각 노드에 설치되는 Daemonset입니다. 각 노드에 설치되어 있는 VPC CNI는 노드의 ENI 관리 및 사용 가능한 VPC Secondary IP를 미리 확보해 (warm pool) 파드가 생성될 때 IP를 할당해줍니다. 또한 CNI 바이너리는 Pod-to-Pod 통신을 활성화하기 위해 Pod 네트워크를 설정합니다. CNI 바이너리는 노드 루트 파일 시스템에서 실행되며 새 Pod가 노드에 추가되거나 기존 Pod가 노드에서 제거될 때 kubelet에서 호출됩니다.
그러면 기능들도 보았지만 AWS VPC CNI를 왜 사용할까요?
-> 기능만으로도 보이듯이 AWS 환경에서 너무 뛰어난 확장성과 유연함을 보여줍니다.
ENI는 파드에게 할당해줄 IP가 부족하면 새로운 물리 네트워크 인터페이스 (ENI)를 할당하고 IP를 다시 파드에게 할당해줍니다. 또한 VPC와 통합하여 VPC Flow logs, VPC 라우팅 정책, 보안 그룹을 사용 가능해서 보다 보안적이며 분석이 가능해집니다.
이러한 순서를 도식화한 그림입니다.
여기서 L-IPAM과 Warm IP Pool이 있습니다.
L-IPAM은 뭘까요?
L-IPAM은 기존의 중앙 집중식(centralized) IP 관리 방식과 달리, 각 노드가 독립적으로 IP를 관리하도록 하는 방식입니다. 기존 방식에서는 노드가 새로운 IP가 필요할 때마다 VPC에서 IP를 요청해야 했지만, L-IPAM은 노드 자체적으로 IP 풀을 유지하면서 필요할 때 즉시 할당할 수 있도록 합니다. 이를 통해 IP 할당 속도가 빨라지고, VPC API 호출이 줄어들어 AWS API rate limit 문제를 완화할 수 있습니다. |
Warmpool은 뭘까요?
Warmpool은 EKS 노드가 필요할 때 즉시 사용할 수 있도록 미리 IP 주소를 예약해 두는 기능입니다. 기본적으로 VPC CNI는 IP를 필요할 때마다 요청하는 방식이지만, Warmpool을 사용하면 특정 개수의 IP를 미리 확보해 두어 Pod가 빠르게 시작될 수 있도록 최적화합니다. Warmpool은 WARM_IP_TARGET 과 MINIMUM_IP_TARGET 같은 설정을 통해 원하는 만큼의 IP를 미리 확보할 수 있습니다. |
이제 CNI가 L-IPAM을 통해 IP를관리하고 Pod에게 IP를 할당해주는 것을 알았습니다.
이때 2가지 경우를 예시로 들 수 있을 것 같습니다.
첫번째는 위에서 설명한 것과 같이 Pod가 생성될 때 IP를 할당해주는 프로세스입니다.
순서는
1. 컨트롤 플레인에서 스케쥴러가 노드에게 파드를 스케쥴링합니다.
2. 노드의 kubelet이 vpc cni에 이를 Add합니다.
3. L-IPAM에서 사용 가능한 Pod IP를 요청합니다.
4. 사용 가능한 Pod IP를 반환받습니다.
5. Network NS를 셋업합니다.
6. Pod IP를 Kubelet에게 알려줍니다.
7.새로운 파드에게 IP를 할당해줍니다.
두번째는 ENI가 할당할 수 있는 최대 IP를 모두 할당하여 새로운 ENI를 노드에 부착하고 Pod에게 IP를 할당해주는 프로세스입니다.
순서는
1. 컨트롤 플레인에서 스케줄러가 노드에게 파드를 스케줄링합니다.
2. 노드의 kubelet이 vpc cni에 이를 Add합니다.
3. L-IPAM에서 사용 가능한 Pod IP를 요청합니다.
4. 사용 가능한 IP가 없음을 알고 AWS에게 새로운 ENI를 요청합니다.
5. 새로운 ENI를 생성하여 노드에 부착합니다.
6. 이후에는 Pod에게 IP를 할당하는 프로세스를 반복합니다.
ENI를 생성하고 부착하는 과정에서는 지연이 발생할 수 있습니다.
기본적으로 스케줄러가 IP 가용성을 미리 고려하도록 하는 방법에 대해서는 대표적으로 2가지 방법이 있습니다.
1. Topology Spread Constraints
2. Custom Scheduler
이에 대해서는 GPT에게 물었습니다.
질문 : 기본 Kubernetes 스케줄러는 IP 가용성을 고려하지 않는다고 알고 있어 이에 자세하게 알려줘
답변 :
EKS에서 스케줄러가 IP 가용성을 고려하지 않는 이유와 개선 방법
기본적으로 Kubernetes 스케줄러는 노드의 CPU, 메모리, GPU 등의 리소스를 고려하여 Pod를 배치하지만, 네트워크 리소스(ENI, IP 가용성 등)는 고려하지 않습니다.
즉, EKS의 기본 스케줄러는 노드에 할당 가능한 IP가 남아있는지 확인하지 않고 Pod를 배치합니다.
그 결과, 노드에 IP가 부족한 상태에서도 스케줄러가 해당 노드로 Pod를 배치할 수 있고, 이 경우 VPC CNI가 뒤늦게 ENI를 추가하려고 AWS API를 호출하는 방식이 됩니다.
이로 인해 몇 초 이상의 지연(latency)이 발생할 수 있습니다.
EKS에서 IP 가용성을 고려하도록 개선하는 방법
EKS 기본 설정에서는 스케줄러가 IP 가용성을 고려하지 않지만, 몇 가지 방법을 사용하여 이를 개선할 수 있습니다.
1. Topology Spread Constraints 활용
Topology Spread Constraints는 특정 노드나 영역(zone) 간에 Pod를 균등하게 분산하도록 도와주는 Kubernetes 기능입니다.
이를 이용해 IP가 충분한 노드로 우선적으로 스케줄링되도록 유도할 수 있습니다.
✅ 설정 방법
Topology Spread Constraints를 활용해 IP가 많은 노드로 우선 스케줄링하는 전략을 사용할 수 있습니다.
2. Custom Scheduler 활용
Topology Spread Constraints는 Kubernetes 기본 스케줄러의 설정을 조정하는 방식이라 한계가 있습니다.
더 강력하게 IP 가용성을 고려하려면 Custom Scheduler를 만들어 적용하는 것도 방법입니다.
✅ Custom Scheduler 개념
- Custom Scheduler를 만들면 스케줄링할 때 노드의 IP 가용성을 직접 확인한 후 Pod를 배치할 수 있음.
- Kubernetes에서 기본 스케줄러 대신 IP 가용성을 고려한 스케줄러를 별도로 배포하여 Pod를 특정 노드로 유도할 수 있음.
✅ 설정 방법
- 노드의 남은 ENI 및 사용 가능한 IP 개수를 모니터링하는 서비스 작성
- Kubernetes API를 통해 노드의 ENI 및 사용 가능한 IP 개수를 주기적으로 확인하는 스크립트 또는 컨트롤러 작성
- Custom Scheduler를 만들고 Pod를 배포할 때 특정 조건을 만족하는 노드만 선택
- Custom Scheduler를 개발하여 kubectl을 통해 사용할 수 있도록 설정
- Pod 배포 시 schedulerName: custom-scheduler 를 지정하여 특정 스케줄러를 사용
💡 현실적으로는 "Warmpool"을 적극적으로 활용하는 것이 가장 쉬운 해결책
- WARM_IP_TARGET 값을 충분히 설정하면 ENI 추가 지연을 최소화할 수 있음.
- MINIMUM_IP_TARGET을 설정하면 항상 특정 개수 이상의 IP가 확보되도록 할 수 있음.
Calico와 VPC CNI의 차이
* VPC CNI는 네트워크 통신의 최적화(성능, 지연)를 위해서 노드와 파드의 네트워크 대역을 동일하게 설정합니다.
* Calico는 노드와 파드의 네트워크 대역이 달라서 오버레이 통신을 하지만 VPC CNI는 직접(네 통신이 가능합니다.
오버레이 통신을하게 되면 기본적으로 원본패킷에서 NAT가 발생하여 Outer패킷이 캡슐화하는 과정에서 지연이 더 발생할 수 밖에 없습니다. (단 Calico BGP 모드는 AWS ENI를 직접 활용합니다.)
최대 파드 개수 제한
인스턴스 타입에 따라 최대 부착할 수 있는 ENI 개수와 인터페이스다 할당할 수 있는 IP수의 차이가 있습니다.
이를 고려하여 사이징을 정하면 좋을 것 같습니다.
근데 만약 노드의 스펙에 비해 경량 Pod가 많이 떠야할 경우에는 Prefix위임을 고려할 수 있습니다.
Secondary 할당의 경우 ENI가 가질 수 있는 IP를 슬롯에 /32로 미리 확보해두는 것입니다.
Prefix 위임의 경우 슬롯에 /28 대역으로 정의합니다. 이러면 한 대역당 16개의 IP를 확보할 수 있게되는 것입니다.
그러면 모든 사람들이 Prefix를 사용하면 될까요??
그건 아닙니다. 기본적으로 Prefix 위임은 Nitro Type의 인스턴스에서만 적용이 가능하며
30vCPU미만 인스턴스는 110개의 파드가 최대입니다. (이외는 250이 max)
Nitro 구축된 인스턴스는 아래에서 확인이 가능합니다.
https://docs.aws.amazon.com/ko_kr/ec2/latest/instancetypes/ec2-nitro-instances.html
AWS Nitro 시스템에 구축된 인스턴스 - Amazon EC2
ENA FreeBSD v2.3.1 이전의 드라이버 버전은 지원되지 않으므로 탄력적 네트워크 인터페이스 연결 실패가 발생합니다.
docs.aws.amazon.com
네트워크 기본 정보 확인
CNI 정보를 확인해봅니다.
kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2
또한 노드들에서는 CNI관련 로그들을 /var/log/aws-routed-eni아래에 볼 수 있습니다.
여기서 ipamd란 위에서 언급한 L-IPAM의 데몬 서비스입니다.
* kube-proxy는 기본적으로 iptables 모드로 작동합니다. 이는 이전에 사용되던 기술과의 호환성을 유지하기 위해서입니다. 또한 ipvs모드도 있습니다.
Chatgpt에게 각 모드의 차이점을 물어보았습니다.
특징 | iptables 모드 | IPVS 모드 |
기본 동작 방식 | iptables 규칙을 사용하여 패킷을 포워딩 | IPVS(IP Virtual Server)를 사용하여 커널 공간에서 로드 밸런싱 |
성능 | 작은 규모에서는 적절하지만, 규칙이 많아지면 성능 저하 | 커널 공간에서 동작하므로 높은 성능 및 확장성 제공 |
확장성 | iptables 규칙이 많아지면 느려질 수 있음 | 많은 서비스와 엔드포인트를 효율적으로 처리 가능 |
로드 밸런싱 알고리즘 | 라운드 로빈(iptables의 기본 NAT) | RR, LC, DH, SH, WRR 등 다양한 알고리즘 지원 |
서비스 상태 감지 | endpoints를 기반으로 kube-proxy가 감지 | ipvsadm을 통해 실시간 상태 감지 가능 |
커널 모듈 필요 여부 | 별도 모듈 불필요 (iptables 기본 포함) | ip_vs, ip_vs_rr, ip_vs_wrr, ip_vs_sh 커널 모듈 필요 |
빠른 업데이트 지원 | iptables 규칙 재적용 시 모든 규칙을 다시 로드 (비효율적) | 개별 엔트리 업데이트 가능하여 빠른 변경 지원 |
환경 지원 | 대부분의 Linux 배포판에서 기본 지원 | 특정 커널 모듈이 필요하므로 설정이 필요 |
적용 방법 | kube-proxy --proxy-mode=iptables | kube-proxy --proxy-mode=ipvs 및 ipvsadm 필요 |
장점 | 별도 커널 모듈 없이 간단한 설정으로 사용 가능 | 성능 우수, 다양한 로드 밸런싱 알고리즘 지원, 빠른 업데이트 가능 |
단점 | 규칙이 많아지면 성능 저하, 로드 밸런싱 방식이 제한적 | IPVS 커널 모듈 필요, 추가 설정이 필요 |
노드에서 기본 네트워크 정보 확인
위에서 언급했던 것과 같이 인스턴스 타입별로 네트워크 인터페이스가 가질 수 있는 IP는 다릅니다.
t3.medium의 경우 1개의 인터페이스당 6개의 IP를 가질 수 있으며 3개의 ENI까지 부착이 가능합니다.
그러면 18개의 파드를 생성할 수 있을까요??
그림에서 보면 aws-node와 kube-proxy는 노드의 ENI0(eth0)과 동일한 것을 볼 수 있습니다.
aws-node와 kube-proxy는 호스트 노드의 네트워크 네임스페이스를 공유하여 노드의 프라이머리 IP를 사용하게 됩니다
* 여기서 잠깐
aws-node : VPC CNI
호스트 네트워크란?
호스트 네트워크(Host Network)는 Pod가 노드의 네트워크 네임스페이스를 직접 사용하는 방식을 의미합니다.
즉, Pod 내부의 네트워크 인터페이스가 노드의 네트워크 인터페이스와 동일하게 설정되며, 노드와 같은 IP 주소를 사용합니다
📌 Kubernetes에서 hostNetwork를 사용하면:
|
돌아와서 말씀드리면 결론적으로 최대 18개의 파드가 생성되지 않습니다.
일반 공식은
- MAX PODS = (Number of network interfaces × [the number of IP addresses per network interface – 1]) + 2
으로 설명되어지는데요
인터페이스당 1개의 IP를 빼는 이유는 노드가 이 IP를 점유하기 때문(Primary IP)이며
2개를 더하는 이유는 설명드렸듯이 aws-node와 kube-proxy는 별도의 IP를 점유하지 않고 노드의 IP를 공유하기 떄문입니다.
그러면 t3.medium의 경우
Max Pod = ( 3ea x (6-1) + 2 ) = 17입니다.
자 이제 콘솔에서 ENI가 보유하고 있는 IP를 실제 파드가 지니고 있는지 확인해봅시다.
2번 노드에 떠있는 파드들을 보는데요 aws-node와 kube-proxy는 192.168.2.229로 같습니다.
coredns pod IP : 192.168.2.138
metric server IP : 192.168.2.183
콘솔 화면에서도 확인하다시피 노드의 IP는 CNI와 kube-proxy와 동일한 192.168.2.229이며
해당 ENI에서 지닌 보조 프라이빗 IP에 coredns와 metric server IP가 있음을 화인할 수 있습니다.
이글은 계속 prefix 위임 방법도 추가할 예정입니다.