Skip to content

Amazon VPC CNI ​

검토 기준: VPC CNI / Helm 차트 1.23.0, network policy agent 1.4.1. 최종 검토: 2026년 10월 12일.

현재 EKS 애드온 표기: AWS는 Kubernetes 1.31–1.37에 v1.23.1-eksbuild.1을 표시합니다.

예제 버전 고정값: 아래 설정 예제는 업스트림/Helm 1.23.0을 사용합니다. 업스트림·Helm·EKS eksbuild 버전은 서로 다른 식별자입니다. 실제 클러스터 버전과 리전의 이용 가능성을 확인하세요.

목차 ​

VPC CNI 개요 ​

Amazon VPC CNI는 표준 EKS EC2 노드에 VPC 기반 Pod 네트워킹을 제공합니다. 이 문서의 aws-node DaemonSet 명령도 해당 설치를 대상으로 합니다. Auto Mode는 노드 서비스로 관리형 네트워킹을 실행하고 Fargate·Windows는 관리 경로가 다릅니다. EC2/Linux 절차를 다른 컴퓨팅 모드에 적용하기 전에 개요를 확인합니다.

Pod는 overlay 캡슐화 없이 VPC에서 라우팅 가능한 주소를 사용합니다. 실제 연결·성능은 라우팅, 보안 그룹, 네트워크 정책, ENI/IP 한계, 애플리케이션에도 달려 있습니다. EKS는 생성 시 선택한 IPv4 또는 IPv6 Pod/Service 주소를 지원하며 dual-stack Pod·Service는 지원하지 않습니다. dual-stack VPC나 IPv6 Pod의 IPv4 egress 보조 인터페이스는 다른 개념입니다.

아키텍처 ​

구성 요소역할
컨테이너 런타임 / CNI 바이너리런타임이 Pod sandbox의 CNI를 호출하고 AWS 플러그인이 주소 요청·네트워크 네임스페이스 설정 수행
IPAMDLinux EC2 노드의 주소 pool과 필요한 일반 ENI·IP 관리
EKS 네트워크 정책 컨트롤러 / 노드 에이전트관리형 컨트롤러가 정책 엔드포인트를 해석하고, 활성화된 aws-eks-nodeagent가 지원 정책을 eBPF로 강제
VPC resource controller별도 전제에 따라 branch/trunk 인터페이스, Windows 주소 할당 등 관리

이전 그림은 kubelet이 CNI 바이너리를 직접 호출한다고 표시했습니다. 현재 Kubernetes의 CNI 관리는 컨테이너 런타임이 담당합니다.

IP 할당 방식 ​

특성보조 IPv4 주소Prefix delegation
할당 단위ENI의 개별 보조 주소IPv4는 16개 주소의 /28, IPv6는 /80 prefix
용량인터페이스·주소 슬롯 및 kubelet 설정에 제한슬롯당 주소를 늘리지만 지원 하드웨어·여유 prefix·kubelet/리소스 한계 적용
할당 절충주소 단위로 세밀하게 할당블록을 한 번에 할당하므로 warm target에 따라 미사용 주소 예약
선택 기준호환성과 측정한 수요Nitro 지원, 서브넷 단편화, 워크로드 변경, 기능 조합 확인

Prefix delegation이 모든 대규모 클러스터의 필수 조건은 아니며 고갈된 서브넷에 새 주소 공간을 만들어 주지도 않습니다.

네트워킹 모델 ​

ENI 아키텍처 ​

일반 secondary-IPv4 모드에서는 각 ENI에 primary 주소와 CNI가 사용할 추가 주소가 있습니다. Primary ENI는 노드의 primary 주소도 전달합니다. 추가 ENI에서 더 많은 보조 Pod 주소를 공급할 수 있습니다. Custom networking은 Pod 주소를 공급하는 인터페이스·서브넷을 바꾸고, prefix·branch-ENI 모드는 할당 규칙이 다릅니다.

text
Linux EC2 노드 — 일반 secondary-IPv4 예제
├── Primary ENI: 노드 primary IP + Pod용 secondary IP
├── 추가 ENI: 해당 ENI의 primary IP + Pod용 secondary IP
└── 추가 ENI: 해당 ENI의 primary IP + Pod용 secondary IP

인스턴스 유형별 ENI/IP 제한 ​

인스턴스 유형최대 ENI 수ENI당 IPv4 슬롯과거 secondary-IP bootstrap maxPods
t3.medium3617
t3.large31235
m5.large31029
m5.xlarge41558
m5.2xlarge41558
c5.4xlarge830234
m5.8xlarge830234

과거 공식은 **ENI 수 × (ENI당 IPv4 슬롯 − 1) + 2**입니다. +2는 해당 bootstrap 계산의 host-network 시스템 Pod 2개를 반영하므로 m5.large는 3 × 9 + 2 = 29입니다. 현재 모든 배포에 host-network Pod가 정확히 2개라는 뜻은 아닙니다.

이 값은 과거 bootstrap 값이며 현재 모든 환경의 Pod 밀도 권장값이 아닙니다. Prefix delegation, custom networking, branch 인터페이스, 다중 네트워크 카드, CPU·메모리, kubelet maxPods를 함께 고려합니다. EKS 관리형 노드 그룹은 vCPU 30개 미만에서 maxPods 110, 그 외에는 250을 상한으로 적용합니다. 실제 노드의 allocatable 용량을 확인합니다.

Prefix Delegation ​

Linux IPv4 prefix 모드의 EKS 관리형 add-on 설정 조각입니다.

json
{
  "env": {
    "ENABLE_PREFIX_DELEGATION": "true",
    "WARM_PREFIX_TARGET": "1"
  }
}

아래 관리 절차로 의도한 add-on 설정과 합칩니다. Helm 관리 설치에서는 같은 env 매핑을 Helm values에 둡니다. 직접 kubectl set env로 바꾼 값은 관리 주체가 다시 조정할 수 있습니다.

IPv4 할당에는 단순히 양수 AvailableIpAddressCount가 아니라 적절한 연속 /28 블록이 필요합니다. 서브넷 예약·단편화와 Nitro 지원을 확인합니다. Prefix를 켜도 기존 kubelet Pod 상한이나 branch-ENI Pod 상한이 모두 자동으로 증가하지는 않습니다.

설치 및 구성 ​

관리 주체와 호환성 확인 ​

대상 클러스터의 AWS CLI 자격 증명과 Kubernetes context를 준비하고 읽기부터 시작합니다.

bash
EKS_REGION=ap-northeast-2
CLUSTER_NAME=my-cluster
KUBERNETES_MINOR="$(aws eks describe-cluster --region "$EKS_REGION" \
  --name "$CLUSTER_NAME" --query cluster.version --output text)"
aws eks describe-addon-versions --region "$EKS_REGION" \
  --addon-name vpc-cni --kubernetes-version "$KUBERNETES_MINOR"
aws eks describe-addon --region "$EKS_REGION" \
  --cluster-name "$CLUSTER_NAME" --addon-name vpc-cni
kubectl -n kube-system get daemonset aws-node -o yaml

EKS describe-addon의 ResourceNotFoundException만으로 CNI가 없다고 판단하지 않습니다. 자체 관리 설치일 수 있습니다. 기존 DaemonSet, ServiceAccount, Helm release, 설정, IAM을 확인하고 관리 주체를 하나 선택합니다. Auto Mode 네트워킹에는 이 설치 절차를 적용하지 않습니다.

기존 EKS 관리형 Add-on ​

기존 설정을 내보내고 선택한 호환 build의 스키마를 확인합니다.

bash
umask 077
aws eks describe-addon --region "$EKS_REGION" \
  --cluster-name "$CLUSTER_NAME" --addon-name vpc-cni > vpc-cni-before.json
jq -r '.addon.configurationValues // "{}"' vpc-cni-before.json > vpc-cni-config.json
: "${VPC_CNI_ADDON_VERSION:?Select a compatible EKS add-on build from the metadata}"
aws eks describe-addon-configuration --region "$EKS_REGION" \
  --addon-name vpc-cni --addon-version "$VPC_CNI_ADDON_VERSION"

필요한 중간 업그레이드 버전과 릴리스 변경을 검토합니다. vpc-cni-config.json에 의도한 기존 설정을 보존하면서 선택한 변경만 반영합니다. 일부 payload나 충돌 플래그가 모든 값을 자동 보존한다고 가정하지 않습니다. CNI IAM 권한과 설정한 IRSA/Pod Identity 역할을 확인하고 IPv6에 맞는 권한도 준비합니다.

이미 관리형인 add-on의 검토된 업데이트는 다음 형태로 실행할 수 있습니다.

bash
set -eu
VPC_CNI_UPDATE_ID="$(aws eks update-addon --region "$EKS_REGION" \
  --cluster-name "$CLUSTER_NAME" --addon-name vpc-cni \
  --addon-version "$VPC_CNI_ADDON_VERSION" \
  --configuration-values file://vpc-cni-config.json --resolve-conflicts PRESERVE \
  --query update.id --output text)"
aws eks describe-update --region "$EKS_REGION" --name "$CLUSTER_NAME" \
  --addon-name vpc-cni --update-id "$VPC_CNI_UPDATE_ID"

PRESERVE는 명시적인 충돌 처리 선택이며 결과 환경 변수·이미지·동작을 확인해야 합니다. 기록한 update ID의 상태가 Successful이 될 때까지 조회합니다. Failed·Cancelled이면 오류를 확인하고 진행을 중단합니다. 성공한 뒤 결과 add-on·DaemonSet을 확인합니다.

bash
aws eks describe-addon --region "$EKS_REGION" \
  --cluster-name "$CLUSTER_NAME" --addon-name vpc-cni
kubectl -n kube-system rollout status daemonset/aws-node --timeout=10m

API 요청 수락이나 이전 DaemonSet의 준비 상태만으로 이번 업데이트·네트워크 검증이 끝났다고 판단하지 않습니다.

관리형 add-on이 없고 설치·소유 관계 준비를 마친 경우의 생성은 별도 작업입니다.

bash
aws eks create-addon --region "$EKS_REGION" \
  --cluster-name "$CLUSTER_NAME" --addon-name vpc-cni \
  --addon-version "$VPC_CNI_ADDON_VERSION" \
  --configuration-values file://vpc-cni-config.json

개별 기능을 켤 때마다 create-addon을 반복하거나, 기존 사용자 설정에 마이그레이션 계획 없이 OVERWRITE를 강제하지 않습니다.

Helm 관리 설치 ​

Init·정책 에이전트 구성 요소도 함께 선택되도록 전체 차트를 고정합니다. 이미지 태그 두 개만 바꿔서는 차트의 나머지가 업데이트되지 않습니다.

bash
helm repo add eks https://aws.github.io/eks-charts
helm repo update eks
helm show values eks/aws-vpc-cni --version 1.23.0 > chart-defaults.yaml
helm template aws-vpc-cni eks/aws-vpc-cni --namespace kube-system \
  --version 1.23.0 -f helm-values.yaml > rendered-cni.yaml

실제 IP family, CNI ServiceAccount/IAM, 선택한 기능에 맞게 helm-values.yaml을 준비합니다. 이 문서의 기본 Linux 예제는 IPv4입니다. 렌더링한 리소스를 검토한 뒤 적용합니다.

bash
helm upgrade --install aws-vpc-cni eks/aws-vpc-cni --namespace kube-system \
  --version 1.23.0 -f helm-values.yaml --wait --timeout 10m

명령은 새 설치나 Helm 관리 설치를 전제로 합니다. 기존 EKS add-on·bootstrap 관리 객체는 소유 전환 계획이 필요합니다. EKS 파티션·레지스트리 접근과 이미지 pull 전제도 환경에 맞춰야 합니다.

주요 설정 값 ​

설정의미기준·기본값 구분
WARM_IP_TARGET새 일반 Pod 할당에 대비한 여유 주소 목표기본 미설정, 최대 한계가 아님
MINIMUM_IP_TARGET전체 할당 주소의 하한기본 미설정, 사용 시 양수 warm-IP 목표와 조합
WARM_ENI_TARGETWarm ENI 용량 목표릴리스 기본 1, IP target이 우선
WARM_PREFIX_TARGET여유 IPv4 prefix 목표릴리스 차트·매니페스트는 1, bare daemon 문서는 미설정
ENABLE_PREFIX_DELEGATIONPrefix 할당 선택Linux 차트 기본 "false"
AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFGCustom networking 선택기본 "false"
ENI_CONFIG_LABEL_DEFENIConfig를 선택하는 노드 레이블 키Daemon 기본 k8s.amazonaws.com/eniConfig, zone 예제는 재정의
ENABLE_POD_ENIEC2 Pod-ENI 연동 활성화기본 "false", 다른 SGPP 전제도 필요
POD_SECURITY_GROUP_ENFORCING_MODESGPP 라우팅·SNAT·보안 그룹 동작기본 strict
NETWORK_POLICY_ENFORCING_MODE새 Pod의 규칙을 설정하는 동안 네트워크 정책 동작기본 standard

두 enforcing-mode 설정은 다른 시스템을 제어합니다. EKS 설정 payload의 환경 변수 값은 문자열입니다.

Custom Networking (ENIConfig) ​

의도한 VPC·AZ에 실제 Pod 서브넷·보안 그룹을 생성하고 ID를 참조합니다.

yaml
apiVersion: crd.k8s.amazonaws.com/v1alpha1
kind: ENIConfig
metadata:
  name: ap-northeast-2a
spec:
  subnet: subnet-0123456789abcdef0
  securityGroups:
  - sg-0123456789abcdef0
---
apiVersion: crd.k8s.amazonaws.com/v1alpha1
kind: ENIConfig
metadata:
  name: ap-northeast-2b
spec:
  subnet: subnet-0abcdef0123456789
  securityGroups:
  - sg-0123456789abcdef0

Custom networking을 켜고 노드의 실제 zone 레이블을 사용합니다.

json
{
  "env": {
    "AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG": "true",
    "ENI_CONFIG_LABEL_DEF": "topology.kubernetes.io/zone"
  }
}

명시적인 ENIConfig 노드 어노테이션이 레이블보다 우선합니다. ENIConfig 객체만으로 custom networking이 활성화되지는 않습니다. 라우팅, DNS·보안 규칙, 주소 용량, 워크로드·노드 전환을 계획합니다. 새 CIDR·ENIConfig만 생성해도 현재 Pod가 다른 서브넷으로 이동하지는 않습니다.

IP 주소 관리 ​

Warm Pool 튜닝 ​

측정한 Pod 수요·변경 빈도, 주소 공간, EC2 API 한계로 정합니다. 다음은 클러스터 크기만으로 정한 권장값이 아닌 서로 다른 목표 예제입니다.

json
{
  "env": {
    "WARM_IP_TARGET": "2",
    "MINIMUM_IP_TARGET": "4"
  }
}
json
{
  "env": {
    "WARM_IP_TARGET": "5",
    "MINIMUM_IP_TARGET": "10"
  }
}

MINIMUM_IP_TARGET은 전체 할당 하한, WARM_IP_TARGET은 여유 주소 목표이며 ENI/prefix warm 전략보다 우선합니다. Prefix delegation에서는 실제로 prefix 단위 할당이 이루어집니다. Warm 용량은 할당 대기를 줄일 수 있지만 주소를 사용하고, 잦은 변경은 API 호출을 늘릴 수 있습니다.

Secondary CIDR 추가 ​

먼저 기존 연결, 연결된 네트워크와의 중복, VPC CIDR 제한, 서브넷·라우팅 전제를 검토합니다.

bash
VPC_ID=vpc-0123456789abcdef0
aws ec2 describe-vpcs --region "$EKS_REGION" --vpc-ids "$VPC_ID" \
  --query 'Vpcs[0].CidrBlockAssociationSet'

다음 ID·주소 공간은 예제이며 검토한 VPC 계획의 일부로만 수행합니다.

bash
aws ec2 associate-vpc-cidr-block --region "$EKS_REGION" \
  --vpc-id "$VPC_ID" --cidr-block 100.64.0.0/16
aws ec2 describe-vpcs --region "$EKS_REGION" --vpc-ids "$VPC_ID" \
  --query 'Vpcs[0].CidrBlockAssociationSet'

새 CIDR의 상태가 associating이 아니라 associated인지 확인한 뒤 그 범위에 서브넷을 생성합니다.

bash
aws ec2 create-subnet --region "$EKS_REGION" --vpc-id "$VPC_ID" \
  --cidr-block 100.64.0.0/19 --availability-zone ap-northeast-2a

서브넷의 라우팅 테이블·보안 규칙·CNI 선택도 필요합니다. 기존 Pod는 계획한 전환 전까지 현재 네트워킹을 유지합니다. RFC 6598 100.64.0.0/10은 shared address space이지 전 세계에서 고유한 사설 용량이 아니므로 연결된 모든 환경과 중복을 확인합니다.

IPv6 클러스터 구성 ​

IP family는 생성 시 선택하고 이후 변경할 수 없습니다. 공식 eksctl 인터페이스는 --ip-family 플래그가 아닌 설정 파일입니다. 스키마 예제는 다음과 같습니다.

yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: ipv6-example
  region: ap-northeast-2
  version: '1.35'
kubernetesNetworkConfig:
  ipFamily: IPv6
iam:
  withOIDC: true
addons:
- name: vpc-cni
- name: coredns
- name: kube-proxy
managedNodeGroups:
- name: linux-nitro
  amiFamily: AmazonLinux2023
  instanceType: m5.large
  desiredCapacity: 2
  privateNetworking: true

예제 버전·리전을 바꾸고 지원되는 노드 이미지를 선택하며 생성 전 VPC endpoint·접근·IAM을 검토합니다. 여기서 생략한 add-on 버전은 EKS/eksctl의 지원 경로로 선택되므로, 통제된 배포에는 필요한 호환 build를 확인·고정합니다. iam.withOIDC와 관리형 add-on·node group은 공식 eksctl IPv6 전제를 반영합니다.

bash
eksctl create cluster --config-file ipv6-cluster.yaml

이 감사에서 클러스터를 생성하지 않았습니다. 로컬 CLI help·schema 검사는 eksctl 0.229.0으로 했으며 네트워킹·IAM·리전별 build의 실제 배포 검증은 아닙니다.

IPv6에는 지원되는 Linux Nitro/Fargate 경로와 prefix 할당이 필요하고 Windows는 지원되지 않습니다. IPv6 Pod에 egress-only IPv4 보조 인터페이스가 있을 수 있습니다. 정책 에이전트 문서는 주 인터페이스의 IPv6 정책이 보조 인터페이스의 IPv4 트래픽을 보호하지 않는다고 명시합니다. 이 경로를 제거해야 한다면 ENABLE_V4_EGRESS, 의존성, Pod 롤아웃을 검토하고 IPv6 정책만으로 차단된다고 가정하지 않습니다.

Network Policy 지원 ​

네이티브 정책 강제 ​

표준 네이티브 eBPF 정책은 VPC CNI 1.14에서 처음 도입되었습니다. 현재 EKS 문서는 표준 NetworkPolicy와 Admin ClusterNetworkPolicy 모두에 VPC CNI 1.21.0 이상을 요구하며(EKS 네트워크 정책 요구 사항), 검토한 1.23도 클러스터·플랫폼에 맞춰야 합니다.

json
{
  "enableNetworkPolicy": "true"
}

"enableNetworkPolicy": "true"는 공식 문자열 설정입니다. 지원되는 EC2 Linux 노드에서 사용할 수 있고 Fargate·Windows에는 이 강제를 적용하지 않습니다. Auto Mode는 자체 관리형 구현을 사용합니다. EKS ClusterNetworkPolicy Admin/Baseline 제어와 Auto Mode DNS ApplicationNetworkPolicy는 표준 NetworkPolicy의 다른 이름이 아닌 확장입니다.

Standard 모드의 새 Pod는 규칙이 해석되는 동안 처음에 트래픽을 허용합니다. 더 엄격한 시작 동작을 의도적으로 선택할 수 있습니다.

json
{
  "enableNetworkPolicy": "true",
  "env": {
    "NETWORK_POLICY_ENFORCING_MODE": "strict"
  }
}

Strict 모드에서는 시작 전 DNS 등 필수 트래픽 정책을 올바르게 준비해야 합니다. SGPP의 별도 POD_SECURITY_GROUP_ENFORCING_MODE를 설정하는 기능은 아닙니다.

NetworkPolicy 예제 ​

전제는 app 네임스페이스의 컨트롤러 관리 frontend/backend 워크로드와 TCP 8080을 수신하는 backend Pod입니다. 현재 AWS는 안정적인 강제에 metadata.ownerReferences가 중요하며 Service·컨테이너 포트 번호(이름 있는 포트는 이름도)가 일치해야 한다고 문서화합니다.

yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: app
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080

출발 Pod 선택자는 같은 네임스페이스로 제한됩니다. 선택한 backend ingress를 격리하고 해당 frontend의 8080 접근을 허용하지만 backend egress를 거부하거나 애플리케이션 사용자를 인증하지는 않습니다. 다른 일치 정책과 Admin tier 동작도 고려합니다. 독립 진단 Pod 대신 Deployment/Job 관리 Pod로 허용·거부 흐름을 모두 확인합니다.

검증과 진단 ​

bash
kubectl -n kube-system logs -l k8s-app=aws-node -c aws-eks-nodeagent --tail=200
kubectl get networkpolicy -A
kubectl get policyendpoints.networking.k8s.aws -A

이는 노드 에이전트 로그이며 정책 컨트롤러는 EKS 관리 제어 평면에서 실행됩니다. PolicyEndpoint는 생성된 상태이므로 임의로 편집·삭제하지 않습니다.

접근 권한이 있고 정책 CLI가 설치된 Linux 노드에서는 다음을 사용합니다.

bash
sudo /opt/cni/bin/aws-eks-na-cli ebpf progs
sudo /opt/cni/bin/aws-eks-na-cli ebpf maps

도구는 ebpf-sdk list-maps가 아닌 aws-eks-na-cli입니다. 영향받은 노드를 확인하고 프로세스 로그, 설정한 정책 이벤트 로그, CloudWatch 전달을 구분합니다. 외부 로그 전달에는 해당 IAM·설정도 필요합니다.

고급 기능 ​

Pod별 보안 그룹 ​

해당 보안 그룹이 DNS·API·애플리케이션·반환 경로에 맞는 규칙을 가지고 이미 존재해야 합니다.

yaml
apiVersion: vpcresources.k8s.aws/v1beta1
kind: SecurityGroupPolicy
metadata:
  name: my-security-group-policy
  namespace: app
spec:
  podSelector:
    matchLabels:
      app: database
  securityGroups:
    groupIds:
    - sg-0123456789abcdef0
    - sg-0abcdef0123456789

Standard SGPP 동작을 선택한 EC2 설정 예제입니다.

json
{
  "env": {
    "ENABLE_POD_ENI": "true",
    "POD_SECURITY_GROUP_ENFORCING_MODE": "standard"
  }
}

완전한 SGPP 설치가 아닙니다. Trunking 지원 인스턴스, 클러스터 역할의 VPC resource-controller 권한, CNI 권한, 서브넷 용량, EKS 전제를 확인합니다. T 계열이 Nitro라고 해서 trunking을 지원하지는 않습니다. 새로 생성·재생성한 선택 Pod에 의도한 설정이 적용됩니다.

관리형 resource controller는 추가 trunk ENI를 붙이고 branch ENI를 연결합니다. Trunk는 노드의 primary eth0 ENI가 아닙니다. 선택 Pod는 보안 그룹이 지정된 branch 인터페이스를 사용하며 prefix delegation이 branch Pod 상한을 늘리지 않습니다. Fargate 보안 그룹은 별도 관리 경로를 따릅니다.

모드·기능확인할 영향
SGPP strictBranch 보안 그룹 동작과 Pod SNAT 비활성화, NodeLocal DNSCache 및 instance 대상 LoadBalancer/NodePort의 externalTrafficPolicy: Local 제약
SGPP standard문서화된 정책·DNS 조합 지원. 기본 external-SNAT 동작에서는 VPC 밖 트래픽이 노드 primary 주소·보안 그룹 사용
Custom networking + SGPPPod 보안 그룹이 ENIConfig 보안 그룹보다 우선
IPv6EKS 문서의 버전·플랫폼 조건에 따라 지원(EC2 CNI 1.16+ 포함). 오래된 README 기능 표의 “No”가 상세 안내를 무효화하지 않음
Windows / Auto Mode이 SGPP 방식은 미지원, Auto Mode는 별도 NodeClass 네트워킹 제어 사용

모드 변경은 새 Pod에 적용되므로 재생성과 트래픽 경로를 검증합니다. SNAT 뒤에도 모든 패킷에 같은 보안 그룹이 적용된다고 가정하지 않습니다.

다중 인터페이스와 Multus ​

VPC CNI 1.20+에는 다중 네트워크 카드를 지원하는 인스턴스용 native multi-NIC 기능이 있습니다. ENABLE_MULTI_NIC와 Pod NIC 설정은 Multus와 다르며 추가 인터페이스의 이점을 얻으려면 애플리케이션이 실제로 사용해야 합니다.

Multus는 meta-plugin입니다. AWS 지원 구성은 VPC CNI를 주 delegate로 사용하며 VPC CNI를 추가 인터페이스의 플러그인으로 쓰는 것은 지원하지 않습니다. 추가 인터페이스에는 호환 플러그인, 주소 할당, 수명 주기 관리가 필요합니다.

추가 인터페이스 전제이유
식별한 전용 인터페이스고정 eth1이 IPAMD 관리 인터페이스일 수 있음
추가 ENI의 node.k8s.amazonaws.com/no_manage=trueVPC CNI가 Multus 인터페이스를 관리하지 않도록 함
AWS에 할당·라우팅된 주소와 올바른 서브넷·SG·경로임의의 192.168.1.0/24 할당이 EC2 ENI에서 자동으로 유효해지지 않음
조정된 IPAM공유 host-local 범위는 여러 노드에서 중복 주소를 할당할 수 있음
인터페이스별 정책 시험추가 인터페이스·IPv4 보조 경로가 모든 주 인터페이스 정책에 자동 포함되지는 않음

NetworkAttachmentDefinition의 spec.config는 지원 버전, 플러그인, 실제 parent 인터페이스, IPAM을 포함한 CNI JSON입니다. 이전 범용 ipvlan/eth1/host-local 매니페스트는 위 전제를 누락하여 구현 요구사항으로 대체했습니다. 이 문서는 실제 배포한 Multus/IPAM 솔루션이라고 주장하지 않습니다.

Windows ​

Windows는 VPC resource-controller IPAM을 사용합니다. 클러스터 역할 권한, Windows 노드 역할의 인증·access entry(해당 시 EC2_WINDOWS), CoreDNS용 Linux/Fargate 용량을 준비합니다. Windows Fargate, Auto Mode, Hybrid Nodes, IPv6, custom networking, SGPP, 네이티브 VPC-CNI 네트워크 정책에는 문서화된 제약이 있습니다.

컨트롤러의 결과 ConfigMap에는 다음 Windows IPAM 항목이 필요합니다. 필요한 데이터를 나타내며 관리 주체가 소유한 ConfigMap 전체를 덮어쓰라는 명령은 아닙니다.

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: amazon-vpc-cni
  namespace: kube-system
data:
  enable-windows-ipam: 'true'

Helm 관리 설치의 Windows prefix 목표는 Linux와 다른 키를 사용합니다.

yaml
enableWindowsIpam: 'true'
enableWindowsPrefixDelegation: 'true'
warmWindowsPrefixTarget: 1
warmWindowsIPTarget: 0
minimumWindowsIPTarget: 0

해당 build 스키마, 필드 소유, 결과 ConfigMap을 확인합니다. Helm 값이 EKS add-on 설정 속성으로 그대로 수락된다고 가정하지 않습니다. Windows 차트 플래그는 enable-windows-ipam, enable-windows-prefix-delegation으로 변환되며 여기의 warm-target 필드도 Windows용입니다.

전제와 AMI·버전을 확인한 후 node-group 명령은 다음 형태로 사용할 수 있습니다.

bash
eksctl create nodegroup --region "$EKS_REGION" --cluster "$CLUSTER_NAME" \
  --name windows-example --managed --node-type m5.large --nodes 2 \
  --node-ami-family WindowsServer2022FullContainer

Windows secondary-IP 모드는 일반적으로 ENI 1개와 그 주소 슬롯 상한을 사용하며 Linux의 다중 ENI 공식과 다릅니다. Prefix delegation과 실제 kubelet 상한도 별도로 계산합니다.

트러블슈팅 ​

IP 할당과 스케줄링 ​

Pod 이벤트로 스케줄링 실패와 sandbox/CNI 주소 할당 실패를 구분합니다.

bash
kubectl -n kube-system logs -l k8s-app=aws-node -c aws-node --tail=300
kubectl get nodes -o json | jq '.items[] | {name: .metadata.name, allocatablePods: .status.allocatable.pods}'
SUBNET_ID=subnet-0123456789abcdef0
aws ec2 describe-subnets --region "$EKS_REGION" --subnet-ids "$SUBNET_ID" \
  --query 'Subnets[].{SubnetId:SubnetId,AvailableIPs:AvailableIpAddressCount}'

allocatablePods는 kubelet 스케줄링 용량이지 현재 IP 사용률이 아닙니다. 서브넷의 여유 주소 수만으로 연속 /28 존재를 알 수 없습니다. 대응을 정하기 전에 IPAMD 로그, 모드, warm target, ENI 한계, API 오류, 영향받은 노드를 확인합니다.

ENI 수 ​

bash
INSTANCE_ID=i-0123456789abcdef0
aws ec2 describe-instances --region "$EKS_REGION" --instance-ids "$INSTANCE_ID" \
  --query 'Reservations[].Instances[].{InstanceId:InstanceId,AttachedENIs:length(NetworkInterfaces)}'
aws ec2 describe-instance-types --region "$EKS_REGION" --instance-types m5.large \
  --query 'InstanceTypes[].NetworkInfo.{MaxENI:MaximumNetworkInterfaces,IPv4PerENI:Ipv4AddressesPerInterface}'

이전 쿼리는 인터페이스 자체가 아닌 중첩된 목록 수를 세었습니다. 수정 쿼리는 인스턴스별 개수를 반환합니다. 다중 카드, unmanaged/trunk 인터페이스, 인스턴스별 한계는 추가 해석이 필요합니다.

Introspection과 메트릭 ​

해당 노드의 실제 aws-node Pod를 선택하고 forwarding 세션을 유지합니다.

bash
kubectl -n kube-system get pods -l k8s-app=aws-node -o wide
AWS_NODE_POD=aws-node-example
kubectl -n kube-system port-forward "pod/$AWS_NODE_POD" 61678:61678 61679:61679

다른 로컬 터미널에서 조회합니다.

bash
curl --fail http://127.0.0.1:61679/v1/enis
curl --fail http://127.0.0.1:61678/metrics

IPAMD introspection 기본값은 loopback 61679, Prometheus 메트릭은 61678입니다. /v1/enis는 메트릭 엔드포인트가 아닙니다. Kubernetes forwarding 경로의 로컬 curl을 사용하므로 CNI 이미지 안의 curl에 의존하지 않습니다.

변경 전 오류 분류 ​

관측조치 전 확인
InsufficientFreeAddressesInSubnet실제 여유 주소, warm 할당, 선택 서브넷, 계획한 용량 확장
InsufficientCidrBlocks연속 prefix·단편화·서브넷 예약
ENI/SG 한계 오류해당 quota, 인스턴스·인터페이스 유형, 사용 중 객체. 무관한 SG를 제거하지 않음
ENI 생성 실패상세 AWS 오류, CNI 자격 증명 역할, 권한·조건, quota, API 연결
Pod IP 대기IPAMD 상태, 컨트롤러·API 지연, 스로틀링, sandbox 이벤트, 주소 준비

IPAMD 재시작, 인스턴스 확대, 노드 역할 권한 추가는 범용 해결책이 아닙니다. 근거를 먼저 수집하고 실제 실패 작업의 관리 주체에 검토한 변경을 적용합니다.

모범 사례 ​

예상 Pod, warm pool, 증가량, 장애·교체 중복을 고려해 서브넷을 계획합니다. /19나 RFC 6598 범위는 설계 예제이지 필수 조건이 아닙니다. 새 CIDR 연결, 필요한 서브넷·경로, CNI·워크로드 전환을 함께 계획합니다.

Warm 전략을 하나 선택합니다. 여유 IP와 전체 IP 목표를 사용하는 IPv4 prefix 예제입니다.

json
{
  "env": {
    "ENABLE_PREFIX_DELEGATION": "true",
    "WARM_IP_TARGET": "5",
    "MINIMUM_IP_TARGET": "10"
  }
}

IP target이 설정된 상태의 추가 WARM_PREFIX_TARGET을 독립적으로 유효한 목표라고 해석하지 않습니다. 클러스터 크기만으로 안전성을 추정하지 말고 할당 실패·실제 제약을 감시합니다.

메트릭과 알림 ​

릴리스 IPAMD 코드는 awscni_total_ip_addresses, awscni_assigned_ip_addresses, counter awscni_no_available_ip_addresses를 제공합니다. Total/assigned gauge는 전체 VPC 서브넷이 아닌 IPAMD 할당 pool을 나타냅니다. 작은 warm target에서는 높은 assigned/total 비율이 정상일 수 있으며 cooldown, branch 인터페이스, IP family, kubelet 용량을 함께 봐야 합니다.

Prometheus Operator CRD와 선택자가 먼저 구성되어 있어야 합니다. Helm 관리 CNI에서는 다음 IPv4 클러스터 예제로 차트의 PodMonitor를 생성할 수 있습니다.

yaml
podMonitor:
  create: true
  labels:
    release: prometheus
  interval: 30s
  relabelings:
  - sourceLabels:
    - __meta_kubernetes_pod_node_name
    targetLabel: node
  - targetLabel: cluster
    replacement: example-cluster
  - targetLabel: ip_family
    replacement: ipv4
  - targetLabel: job
    replacement: aws-vpc-cni

클러스터 레이블을 교체하고 release 선택자를 맞추며 IP family를 사실대로 지정합니다. 레이블이 실제 family를 탐지하지는 않습니다. 차트는 Agent의 이름 있는 metrics 포트와 활성화한 정책 에이전트의 agentmetrics를 수집합니다. EKS 관리형 add-on에는 두 번째 CNI Helm release를 설치하지 말고 독립된 scraper/PodMonitor를 구성합니다.

yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: vpc-cni-signals
  namespace: monitoring
  labels:
    release: prometheus
spec:
  groups:
  - name: vpc-cni
    rules:
    - record: vpc_cni:allocated_ipv4_pool_utilization:ratio
      expr: (awscni_assigned_ip_addresses{job="aws-vpc-cni",ip_family="ipv4"} / awscni_total_ip_addresses{job="aws-vpc-cni",ip_family="ipv4"})
        and (awscni_total_ip_addresses{job="aws-vpc-cni",ip_family="ipv4"} > 0)
    - alert: CniHighAllocatedIPv4PoolUtilization
      expr: vpc_cni:allocated_ipv4_pool_utilization:ratio > 0.9
      for: 5m
      labels:
        severity: info
      annotations:
        summary: Most currently allocated IPAMD IPv4 addresses are assigned
        description: This is allocated-pool utilization, not subnet exhaustion. Check
          warm targets, assignment failures and available subnet space.
    - alert: CniIPAssignmentFailures
      expr: increase(awscni_no_available_ip_addresses{job="aws-vpc-cni"}[5m]) > 0
      for: 1m
      labels:
        severity: warning
      annotations:
        summary: IPAMD could not assign an available IP address
    - alert: CniMetricsScrapeFailed
      expr: up{job="aws-vpc-cni"} == 0
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: A known CNI metrics endpoint cannot be scraped

Pool 비율은 IPv4와 관측된 양수 분모로 한정합니다. 서브넷 고갈 증거가 아닌 정보성 튜닝 신호입니다. 할당 실패 counter는 실제 실패를 나타냅니다. 수집 실패와 대상 삭제는 다르므로 예상 노드·구성 요소 목록도 비교합니다. 데이터 없음은 정상 0이 아닙니다. 실제 관측 환경에 맞게 임계값, 레이블, 알림 전달을 조정합니다.

참고 자료 ​

퀴즈 ​

VPC CNI 퀴즈로 이해를 확인합니다.