[EKS] VPC CNI Prefix delegation

EKS VPC CNI의 Prefix Delegation 모드는 IP 관리 최적화와 높은 Pod 밀도를 통해 성능 향상을 제공합니다.

[EKS] VPC CNI Prefix delegation
Photo by Alina Grubnyak / Unsplash

개요 (EKS VPC CNI Prefix Delegation Mode)

1. 작동 방식

  • 일반 모드 (Secondary IP)
    • Pod 당 하나의 IP 주소 할당
    • 각 노드에서 IP 접두사의 웜풀을 유지 관리합니다.
  • Prefix 모드
    • /28 CIDR 블록 단위로 할당 (16개 IP)
    • 더 효율적인 IP 관리
    • ENI당 더 많은 Pod 실행 가능

또한 Pod가 ENI를 요청하는 방식은 다음과 같이 구성됩니다.

Notion Image

또한 접두사 모드를 사용하게 된다면 다음과 같은 값들을 사용하게 됩니다.

WARMPREFIXTARGET

  • 역할: 노드에서 항상 사용 가능한 상태로 유지할 IP 접두사(prefix)의 수를 지정
  • 기본값: 1
  • 작동방식:
    • 예를 들어 값이 1이면, 항상 하나의 사용 가능한 접두사를 유지
    • 새로운 파드가 생성될 때 즉시 IP 할당이 가능하도록 함
    • /28 접두사의 경우 16개의 IP 주소를 미리 확보

WARMIPTARGET

  • 역할: 노드에서 항상 사용 가능한 상태로 유지할 IP 주소의 수를 지정
  • 작동방식:
    • 지정된 수만큼의 사용 가능한 IP 주소를 항상 유지
    • 실제 IP 주소 단위로 관리
    • 접두사 모드가 아닌 개별 IP 모드에서 주로 사용

MINIMUMIPTARGET

  • 역할: 노드가 항상 유지해야 할 최소 IP 주소의 수를 지정
  • 작동방식:
    • 노드의 최소 IP 주소 풀 크기를 정의
    • WARMIPTARGET과 달리 이 수치 이하로 떨어지지 않도록 보장

기본적으로 WARMPREFIXTARGET을 사용하게 된다면 기본적으로 할당되는 IP 주소는 인스턴스 유형에 관계없이 /28 청크로 할당됩니다. 이로 인해 WARMPREFIXTARGET에서 권장되는 값은 “1”입니다. 단 하나의 파드에 사용되더라도 완전한 접두사를 할당하게 되므로, 인스턴스에 할당된 미사용된 IP 주소를 최소화하면서 빠른 파드 시작시간을 적절하게 조합하는 방식입니다.

하지만 노드 당 IPv4를 추가로 보존해야하는 경우, Pod 실행 시 약간의 성능 저하가 허용되는 경우 WARMIPTARGET 및 MINIMUMIPTARGET을 사용할 수 있습니다. 해당 값을 설정하게 되면 WARMPREFIXTARGET을 재정의하며 WARMIPTARGET을 16보다 작은 값으로 설정하면 IPAMD가 하나의 완전한 free 접두사를 할당한 상태로 유지하는 것을 방지할 수 있습니다. 또한 MINIMUMIPTARGET을 재정의하면 Pod에 할당된 IP 갯수를 유지할 수 있습니다.

시나리오로 정리하면 다음과 같습니다.

설정 값:

  • WARMPREFIXTARGET: 1
  • MINIMUMIPTARGET: 25
  • WARMIPTARGET: 5
  • 예상 파드 수: 25개
  • 인스턴스: m5.large (ENI당 9개 접두사 지원)

초기 상태:

  • 할당된 접두사: 2개 (/28 × 2 = 32 IP → 기본적으로 /28인 것의 주의)
  • 사용 중인 IP: 25개
  • 여유 IP: 7개 (WARMIPTARGET 5 만족)

트래픽 급증 과정 중

  • 총 접두사: 3개 (/28 × 3 = 48 IP)
  • 실행 중인 파드: 37개
  • 여유 IP: 11개

정리하면 첫 25개 파드는 기존 풀에서 MINIMUMIPTARGET으로 생성하였기 때문에 즉시 할당됩니다.

하지만 트래픽 증가로 인한 추가 12개 파드의 경우 기존의 여유분 7개는 즉시 할당 가능하지만 새로운 5개는 접두 사 생성 후 할당되므로 잠시 대기하게 됩니다.

  • 추가 12개 파드:
  • 7개: 즉시 할당 (기존 풀에서)
  • 5개: 잠시 지연 (새 접두사 할당 대기)

최종 상태

  • 사용 중인 ENI: 1개 (주 ENI만)
  • 총 접두사: 3개
  • 총 IP: 48개 (MINIMUMIPTARGET > 25)
  • 사용 중인 IP: 37개
  • 여유 IP: 11개 (WARMIPTARGET > 5)

하지만 이러한 IP 사용은 각 노드별 최대 상한이 존재하며 최대 Pod 개수를 구하는 공식은 다음과 같습니다.

  • m5.large의 경우 ⇒ ENI 3개 - 접두사 - 슬롯의 갯수 9개 - /28 prefix 값 16
(인스턴스 유형에 대한 네트워크 인터페이스 수 × (네트워크 인터페이스 당 슬롯 수 - 1)* 16)
curl -o max-pods-calculator.sh https://raw.githubusercontent.com/awslabs/amazon-eks-ami/master/files/max-pods-calculator.sh
chmod +x max-pods-calculator.sh
./max-pods-calculator.sh --instance-type m5.large --cni-version 1.9.0 --cni-prefix-delegation-enabled

최대 값을 계산하더라도, 최대 파드 수를 계산하는 스크립트는 쿠버네티스의 확장 한계와 권장되는 설정에 의해 110(m5.large)의 값을 반환하는 것으로 제한됩니다. 사용 중인 인스턴스 유형이 30개의 vCPUs 이상 되는 경우 제한은 내부 EKS 확장성 팀 테스트에 기반한 250개까지 증가가 가능하며 CNI 사용자 지정 네트워킹을 사용하면서 접두사 할당이 활성화된 m5.large는 여전히 288개의 IPv4 주소를 할당할 수 있습니다.

2. 사용이 권장되는 경우

  1. 높은 Pod 밀도가 필요한 경우```plain text 예시 (m5.large):
    • 노드당 더 많은 Pod 실행 필요
    • 일반 모드: 최대 29개 Pod
    • Prefix 모드: 최대 110개 Pod ```
  2. 서브넷 상태가 양호한 경우
    • 연속된 /28 CIDR 블록 사용 가능
    • IP 단편화가 적은 새로운 서브넷
    • 충분한 가용 IP 주소 보유
  3. CNI 커스텀 네트워킹 사용 시
    • Primary ENI를 Pod에 미사용
    • Nitro 기반 인스턴스 사용

3. 사용을 피해야 하는 경우

  1. 서브넷 문제
    • IP 단편화가 심한 경우
    • 연속된 /28 블록 확보 불가
    • 가용 IP 부족
  2. 보안 요구사항
    • Pod별 다른 보안그룹 필요
    • 엄격한 네트워크 정책 필요
    • 컴플라이언스 요구사항 존재

4. 제한사항

  1. 노드 제한
    • ENI 수 제한 존재
    • 인스턴스 타입별 최대 IP 제한
    • 서브넷 가용 IP 범위 제한
  2. CIDR 할당```plain text /24 서브넷 예시:
    • 전체 256개 IP
    • /28 블록 = 16개 IP
    • 최대 16개 프리픽스 블록 ```

EKS VPC CNI 구성하기

AWS Nitro 기반 EC2 인스턴스 유형에서 지원되며, VPC_CNI 가 사전에 설치되고 구성되어있어야 합니다.

kubectl describe daemonset aws-node --namespace kube-system | grep Image | cut -d "/" -f 2

amazon-k8s-cni-init:v1.19.0-eksbuild.1
amazon-k8s-cni:v1.19.0-eksbuild.1
amazon
kubectl get ds aws-node -o yaml -n kube-system | yq '.spec.template.spec.containers[].env'

...
- name: ENABLE_PREFIX_DELEGATION
  value: "false"
...
kubectl set env daemonset aws-node -n kube-system ENABLE_PREFIX_DELEGATION=true

실제 동작을 확인해보기 위해 애플리케이션을 80개까지 실행시킵니다.

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 80
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: public.ecr.aws/nginx/nginx:1.21
        ports:
        - containerPort: 80
EOF

kubectl get pods -l app=nginx

Capacity가 제한되어 있는 경우 새로운 노드 그룹으로 이동이 필요합니다.

kubectl describe nodes <node-name> 

...
Capacity:
  cpu:                2
  ephemeral-storage:  20134592Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             7922592Ki

  pods:               29


 ...
# 1. 새로운 노드 그룹 생성
eksctl create nodegroup \
--cluster=<EKS_CLUSTERL_NAME> \
--name=<NEW_NODEGROUP_NAME> \  # 새로운 이름
--node-type=m5.large \
--max-pods-per-node=110 \
--nodes=1

# 2. 파드들이 새 노드로 이동하도록 기존 노드 drain
kubectl drain <old-node-name> --ignore-daemonsets --delete-emptydir-data

# 3. 기존 노드 그룹 삭제
eksctl delete nodegroup \
--cluster=<EKS_CLUSTERL_NAME> \
--name=<NODE_GROUP_NAME>

# 4. Pod가 정상적으로 동작하는 것을 확인할 수 있습니다.
k get pods -A | grep Running | wc -l

93

접두사는 7개로 확장 된 것을 확인할 수 있습니다.

Notion Image

이를 통해 하나의 노드에 할당되는 노드 갯수를 늘릴 수 있다는 것을 확인하였습니다.

고려 사항

서브넷 단편화

ENI에서 할당되는 /28 접두사는 연속된 블럭으로 구성되어 있어야합니다. 접두사가 생성된 서브넷이 세분화 된 경우(보조 IP 주소가 분산되어 많이 사용되어지는 서브넷) 접두사 할당은 실패할 수 있으며 이를 방지하기 위해 VPC Subnet CIDR 예약이 필요할 수 있습니다.

업그레이드/다운그레이드 동작

VPN CNI 버전 1.9.0 이상에서만 동작하므로 접두사 모드가 활성화되는 경우 다운그레이드하지 않아야 합니다.

다운그레이드해야한다면 위 과정에서처럼 노드를 지우고 재생성이 필요합니다. 모든 기존 노드를 cordon과 drain으로 기존 모든 파드들을 안전하게 제거해야하며, 이후 기존 노드와 노드 그룹을 제거해야합니다.

리소스 요청

접두사 할당이 활성화 된 상태로 스케쥴 될 수 있는 파드가 증가하면 노드에서 리소스 경합이 발생할 가능성이 높아지므로 예상치 못한 서비스 다운을 방지하기 위해선 Request를 적절히 구성해야하며 Limit을 충분하게 할당해야합니다.

보안 그룹 설정

사용 사례와 요구사항에 대한 네트워크 모드나 전략을 선택할 필요가 있습니다. 더 작은 인스턴스 유형의 파드 실행 시간과 파드 밀도가 중요하고, 노드 수준의 보안 그룹으로 작업할 수 있는 경우 접두사 할당을 사용할 수 있습니다.

하나의 파드는 브랜치 인터페이스(파드별 독립적인 네트워크)의 주 IP 주소에 연결되며, 현재 보조 IP 주소나 접두사는 브랜치 인터페이스에 할당되지 않습니다.

Reference