Исследование режима Amazon VPC CNI Custom Networking / ENIConfig в Amazon EKS
Цель работы
Целью данной работы является экспериментальная проверка режима Custom Networking / ENIConfig в кластере Amazon EKS, использующем Amazon VPC CNI. Основная задача эксперимента - подтвердить возможность назначения IP-адресов Pod из заранее выделенных subnet, отличных от subnet, в которых размещены worker nodes, а также оценить применимость данного механизма для сетевой сегментации workloads.
В стандартной конфигурации Amazon VPC CNI Pod получают IP-адреса из subnet, связанных с primary ENI worker node. Иными словами, адресное пространство Pod и адресное пространство нод фактически используют один и тот же subnet-level ресурс. Режим Custom Networking изменяет это поведение: primary ENI ноды остаётся в node subnet, тогда как secondary ENI создаются в subnet, указанных в объекте ENIConfig. В результате Pod получают IP-адреса из subnet, определённых в ENIConfig, а не из subnet primary ENI ноды.
Следует подчеркнуть, что данный механизм не является overlay-сетью. Amazon VPC CNI назначает Pod реальные VPC IP-адреса, маршрутизируемые внутри VPC. Таким образом, Pod становятся полноценными участниками сетевой модели VPC, а их трафик подчиняется правилам маршрутизации, security groups и другим механизмам AWS networking.
Теоретическая основа
Primary и secondary ENI
Elastic Network Interface, или ENI, представляет собой виртуальный сетевой интерфейс EC2-инстанса. При создании worker node EC2-инстанс получает primary ENI, связанный с subnet, в котором размещена нода. Этот интерфейс используется самой нодой и host-network Pod.
Secondary ENI - это дополнительные сетевые интерфейсы, которые могут быть подключены к тому же EC2-инстансу. Amazon VPC CNI управляет такими интерфейсами через компонент ipamd, который отвечает за выделение ENI, IP-адресов и prefix slots для последующего назначения Pod.
В режиме Custom Networking secondary ENI создаются не в subnet primary ENI, а в subnet, определённых в соответствующем ENIConfig. Именно с этих secondary ENI Pod получают свои IP-адреса. При включённом Custom Networking IP-адреса primary network interface не назначаются Pod; для Pod используются только IP-адреса secondary network interfaces.
Ограничения режима Custom Networking
Custom Networking применим только для IPv4-кластеров. Для IPv6-кластеров этот режим не поддерживается. Если основная проблема заключается в исчерпании IPv4-адресов, то при проектировании новой платформы также следует рассматривать возможность использования IPv6-кластера, если архитектура приложений и инфраструктурные ограничения позволяют такую миграцию.
Кроме того, subnet и security groups, указанные в ENIConfig, должны находиться в той же VPC, что и worker node. Это означает, что Custom Networking не является механизмом меж-VPC адресного размещения Pod; он работает в рамках одной VPC и расширяет контроль над тем, из каких subnet Pod получают адреса.
Практическая постановка задачи
В рассматриваемой модели требуется добиться следующего результата:
Pod в Amazon EKS должны получать IP-адреса из заранее созданных pod subnet, а не из subnet, где размещены worker nodes. Для каждой Availability Zone желательно иметь отдельный pod subnet с корректно настроенной маршрутизацией. Такой подход позволяет:
- разгрузить node subnet и снизить риск исчерпания IPv4-адресов;
- отделить адресное пространство Pod от адресного пространства worker nodes;
- назначить secondary ENI отдельный набор security groups;
- использовать subnet-level и security-group-level сегментацию для workloads.
В обобщённой реализации подготавливаются отдельные subnet для Pod. Их CIDR-диапазоны должны быть выбраны из адресного пространства VPC и не должны пересекаться с node subnet, service CIDR, peering CIDR, transit gateway routes и другими уже используемыми диапазонами.
Пример обезличенного описания pod subnet:
pod_subnet_name_prefix = "pod-subnet"
pod_subnet_cidrs = [
"10.0.10.0/24",
"10.0.20.0/24",
"10.0.30.0/24"
]
Каждый pod subnet должен соответствовать отдельной Availability Zone:
10.0.10.0/24 -> availability-zone-a
10.0.20.0/24 -> availability-zone-b
10.0.30.0/24 -> availability-zone-c
В production-среде вместо приведённых выше демонстрационных диапазонов должны использоваться реальные внутренние CIDR, выделенные в рамках утверждённой IPAM-модели организации.
Включение Custom Networking и Prefix Delegation
Для включения Custom Networking в Amazon VPC CNI используется переменная окружения:
AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG=true
В Terraform или другом IaC-инструменте это может быть выражено через соответствующий параметр модуля или конфигурации add-on:
vpc_cni_enable_custom_networking = true
Дополнительно рекомендуется включить Prefix Delegation:
vpc_cni_enable_prefix_delegation = true
Prefix Delegation необходим для повышения доступной pod density. В стандартном secondary IP mode CNI выделяет отдельный secondary IPv4 address для каждого Pod. В prefix mode CNI вместо отдельных IP-адресов запрашивает IPv4 prefix /28, после чего адреса внутри prefix используются для назначения Pod. Один IPv4 prefix /28 содержит 16 адресов.
Это особенно важно при Custom Networking, поскольку primary ENI не используется для назначения Pod IP. Соответственно, доступная плотность Pod на ноде может снизиться. Поэтому при использовании Custom Networking следует отдельно проверять значение max-pods для выбранных instance types и учитывать сетевые лимиты EC2-инстансов.
Требования к security group для secondary ENI
Для secondary ENI, используемых Pod, должна быть назначена отдельная security group или набор security groups. Конкретная реализация зависит от принятой сетевой модели, однако такая security group должна выполнять следующие функции:
- разрешать исходящий трафик Pod к необходимым внутренним и внешним сервисам;
- разрешать доступ Pod к Kubernetes API, если доступ к API endpoint ограничен security group rules;
- разрешать DNS-трафик к используемому DNS-резолверу;
- разрешать доступ к container registry, artifact repository, observability endpoints и другим обязательным инфраструктурным сервисам;
- ограничивать входящий трафик к Pod в соответствии с принятой моделью безопасности;
- не предоставлять избыточный доступ к административным, инфраструктурным или межсегментным ресурсам.
В общем виде security group для secondary ENI можно описать следующим образом:
resource "aws_security_group" "pod_eni" {
name = "example-pod-eni-sg"
description = "Security group for secondary ENI used by Kubernetes Pods"
vpc_id = var.vpc_id
tags = {
Purpose = "pod-secondary-eni"
}
}
Пример минимальной логики правил:
resource "aws_security_group_rule" "pod_eni_egress_internal" {
type = "egress"
security_group_id = aws_security_group.pod_eni.id
protocol = "-1"
from_port = 0
to_port = 0
cidr_blocks = ["10.0.0.0/8"]
description = "Allow Pod egress to approved internal network ranges"
}
resource "aws_security_group_rule" "pod_eni_egress_https" {
type = "egress"
security_group_id = aws_security_group.pod_eni.id
protocol = "tcp"
from_port = 443
to_port = 443
cidr_blocks = ["0.0.0.0/0"]
description = "Allow Pod egress to approved HTTPS endpoints"
}
resource "aws_security_group_rule" "pod_eni_dns_udp" {
type = "egress"
security_group_id = aws_security_group.pod_eni.id
protocol = "udp"
from_port = 53
to_port = 53
cidr_blocks = ["10.0.0.0/8"]
description = "Allow DNS queries to internal DNS resolvers"
}
resource "aws_security_group_rule" "pod_eni_dns_tcp" {
type = "egress"
security_group_id = aws_security_group.pod_eni.id
protocol = "tcp"
from_port = 53
to_port = 53
cidr_blocks = ["10.0.0.0/8"]
description = "Allow DNS queries over TCP to internal DNS resolvers"
}
Если Kubernetes API endpoint является private only или ограничен security group rules, необходимо отдельно разрешить доступ от pod ENI security group к security group, связанной с EKS control plane endpoint. Логически это правило можно описать так:
Source: security group assigned to Pod secondary ENI
Destination: security group associated with Kubernetes API private endpoint
Protocol: TCP
Port: 443
Purpose: Allow Pods to communicate with Kubernetes API when required
Детали inbound- и egress-правил должны определяться не по принципу максимального разрешения, а исходя из фактических зависимостей workloads.
Проверка параметров Amazon VPC CNI
После применения конфигурации необходимо проверить, что DaemonSet aws-node получил требуемые переменные окружения:
kubectl -n kube-system describe ds aws-node | \
egrep -n "AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG|ENI_CONFIG_LABEL_DEF|AWS_VPC_K8S_CNI_EXTERNALSNAT"
Ожидаемый результат:
AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG: true
AWS_VPC_K8S_CNI_EXTERNALSNAT: false
ENI_CONFIG_LABEL_DEF: topology.kubernetes.io/zone
Параметр ENI_CONFIG_LABEL_DEF=topology.kubernetes.io/zone означает, что выбор ENIConfig выполняется на основании Availability Zone, в которой размещена нода. При такой модели имя объекта ENIConfig должно соответствовать значению zone label на ноде.
Также необходимо убедиться, что DaemonSet был успешно перезапущен:
kubectl -n kube-system rollout status ds/aws-node
kubectl -n kube-system get pods -l k8s-app=aws-node -o wide
Создание ENIConfig
Для каждой Availability Zone создаётся отдельный объект ENIConfig. Имя объекта должно соответствовать значению label, используемого для выбора конфигурации. В данном случае это условные имена зон.
apiVersion: crd.k8s.amazonaws.com/v1alpha1
kind: ENIConfig
metadata:
name: availability-zone-a
spec:
subnet: subnet-placeholder-a
securityGroups:
- sg-placeholder-pod-eni
---
apiVersion: crd.k8s.amazonaws.com/v1alpha1
kind: ENIConfig
metadata:
name: availability-zone-b
spec:
subnet: subnet-placeholder-b
securityGroups:
- sg-placeholder-pod-eni
---
apiVersion: crd.k8s.amazonaws.com/v1alpha1
kind: ENIConfig
metadata:
name: availability-zone-c
spec:
subnet: subnet-placeholder-c
securityGroups:
- sg-placeholder-pod-eni
В реальной конфигурации вместо subnet-placeholder-* должны использоваться идентификаторы pod subnet, расположенных в соответствующих Availability Zone. Вместо sg-placeholder-pod-eni должна использоваться security group, предназначенная для secondary ENI, через которые Pod получают IP-адреса.
Проверка созданных объектов:
kubectl get eniconfig
Пример обезличенного результата:
NAME AGE
availability-zone-a 1m
availability-zone-b 1m
availability-zone-c 1m
Для детальной проверки можно запросить конкретный объект:
kubectl get eniconfig availability-zone-a -o yaml
После создания ENIConfig необходимо пересоздать worker nodes, поскольку существующие ноды и уже запущенные Pod не будут автоматически переведены на новую сетевую конфигурацию. На практике это означает, что следует создать или обновить node group, дождаться появления новых нод с корректной конфигурацией, затем выполнить drain старых нод.
Проверка состояния worker nodes
После пересоздания нод необходимо убедиться, что они находятся в состоянии Ready и имеют корректную топологическую разметку:
kubectl get nodes -L topology.kubernetes.io/zone -o wide
kubectl wait --for=condition=Ready nodes --all --timeout=300s
Это важно, поскольку при текущей схеме выбор ENIConfig зависит от label topology.kubernetes.io/zone.
Тестовое размещение Pod по Availability Zone
Для проверки создаются тестовые Deployment, каждый из которых размещается в отдельной Availability Zone через nodeSelector.
apiVersion: apps/v1
kind: Deployment
metadata:
name: eni-test-zone-a
labels:
app: eni-test
zone: availability-zone-a
spec:
replicas: 1
selector:
matchLabels:
app: eni-test
zone: availability-zone-a
template:
metadata:
labels:
app: eni-test
zone: availability-zone-a
spec:
nodeSelector:
topology.kubernetes.io/zone: availability-zone-a
containers:
- name: test
image: alpine:3.20
command: ["sh", "-c", "sleep 365d"]
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: eni-test-zone-b
labels:
app: eni-test
zone: availability-zone-b
spec:
replicas: 1
selector:
matchLabels:
app: eni-test
zone: availability-zone-b
template:
metadata:
labels:
app: eni-test
zone: availability-zone-b
spec:
nodeSelector:
topology.kubernetes.io/zone: availability-zone-b
containers:
- name: test
image: alpine:3.20
command: ["sh", "-c", "sleep 365d"]
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: eni-test-zone-c
labels:
app: eni-test
zone: availability-zone-c
spec:
replicas: 1
selector:
matchLabels:
app: eni-test
zone: availability-zone-c
template:
metadata:
labels:
app: eni-test
zone: availability-zone-c
spec:
nodeSelector:
topology.kubernetes.io/zone: availability-zone-c
containers:
- name: test
image: alpine:3.20
command: ["sh", "-c", "sleep 365d"]
Применение манифеста:
kubectl apply -f deployment-eni-test.yaml
Проверка назначения Pod IP из pod subnet
Для подтверждения результата необходимо сопоставить IP-адрес каждого Pod с subnet соответствующего ENI в AWS EC2:
for pod_name in $(kubectl get pods -l app=eni-test -o jsonpath='{.items[*].metadata.name}'); do
pod_ip=$(kubectl get pod "${pod_name}" -o jsonpath='{.status.podIP}')
node_name=$(kubectl get pod "${pod_name}" -o jsonpath='{.spec.nodeName}')
subnet_id=$(aws ec2 describe-network-interfaces \
--region "${AWS_REGION}" \
--filters Name=addresses.private-ip-address,Values="${pod_ip}" \
--query 'NetworkInterfaces[0].SubnetId' \
--output text)
echo "${pod_name} ip=${pod_ip} node=${node_name} subnet=${subnet_id}"
done
Пример полностью обезличенного результата:
eni-test-zone-a-xxxxxxxxxx-aaaaa ip=10.0.10.25 node=worker-node-a subnet=subnet-placeholder-a
eni-test-zone-b-xxxxxxxxxx-bbbbb ip=10.0.20.25 node=worker-node-b subnet=subnet-placeholder-b
eni-test-zone-c-xxxxxxxxxx-ccccc ip=10.0.30.25 node=worker-node-c subnet=subnet-placeholder-c
Полученные IP-адреса должны находиться внутри ожидаемых pod subnet:
10.0.10.0/24 -> пример Pod IP 10.0.10.25 находится внутри pod subnet зоны A
10.0.20.0/24 -> пример Pod IP 10.0.20.25 находится внутри pod subnet зоны B
10.0.30.0/24 -> пример Pod IP 10.0.30.25 находится внутри pod subnet зоны C
Следовательно, если фактический вывод показывает соответствие Pod IP ожидаемым pod subnet, эксперимент подтверждает, что Pod действительно получают IP-адреса из subnet, заданных в ENIConfig, а не из subnet primary ENI worker nodes.
Выводы
Проведённая проверка подтверждает, что Amazon VPC CNI Custom Networking / ENIConfig позволяет отделить адресное пространство Pod от адресного пространства worker nodes. В рассматриваемой конфигурации Pod получают IP-адреса из специально выделенных pod subnet, при этом secondary ENI создаются в subnet, указанных в соответствующих объектах ENIConfig.
Данный механизм может использоваться для грубой сетевой сегментации. Он позволяет назначить Pod subnet и security groups, отличные от subnet и security groups primary ENI ноды.
Однако ENIConfig не следует рассматривать как полноценную pod-level security model. В текущей реализации выбор ENIConfig выполняется через label topology.kubernetes.io/zone, поэтому сегментация фактически выполняется на уровне Availability Zone. Все Pod, запущенные на нодах одной зоны и использующие один и тот же ENIConfig, получают адреса из одного pod subnet и используют один и тот же набор security groups для secondary ENI.
Следовательно, механизм ENIConfig не позволяет выборочно назначать разные security groups отдельным Pod внутри одной и той же node group, если эти Pod используют одну и ту же node-level сетевую конфигурацию. Для более точечной модели контроля доступа следует рассматривать альтернативные или дополнительные механизмы, например Security Groups for Pods, Kubernetes NetworkPolicy или отдельные node pools с независимыми ENIConfig.
Если требуется сегментация разных типов workloads, Custom Networking может быть применён как механизм coarse-grained сегментации для выделенных node pools. В такой модели разные группы нод могут использовать разные pod subnet и разные security groups для secondary ENI. При этом выбор ENIConfig может выполняться не только по Availability Zone, но и по специализированному label или annotation ноды, если такая модель поддерживается выбранной конфигурацией Amazon VPC CNI.
Также необходимо учитывать влияние Custom Networking на pod density. Поскольку primary ENI не используется для назначения Pod IP, максимальное количество Pod на ноде может снизиться. Поэтому для production-конфигурации рекомендуется использовать Prefix Delegation и отдельно проверять корректность параметра max-pods для выбранных instance types. Особенно это важно для малых instance types, где сетевые лимиты EC2-инстанса быстро становятся ограничивающим фактором.
Отдельного внимания требует egress-трафик. При AWS_VPC_K8S_CNI_EXTERNALSNAT=false, то есть в конфигурации по умолчанию, VPC CNI применяет SNAT-правила на ноде. Это означает, что даже при Custom Networking часть поведения исходящего трафика остаётся связанной с node-side networking. Поэтому ENIConfig следует рассматривать как механизм subnet/ENI-level сегментации, а не как абсолютную изоляцию сетевого поведения каждого Pod.
Итоговый вывод: Custom Networking / ENIConfig является корректным и применимым решением для выделения отдельного адресного пространства Pod, снижения давления на node subnet и реализации coarse-grained сетевой сегментации. При этом для production-использования необходимо учитывать ограничения IPv4-only режима, снижение pod density, необходимость Prefix Delegation, корректный расчёт max-pods, соответствие pod subnet Availability Zone ноды, а также тот факт, что ENIConfig не заменяет pod-level security controls.
Комментарии