Зачем CNI
Когда kubelet запускает pod, ему надо: создать [[namespaces|netns]], присоединить pod к сети кластера, дать IP. Делать это руками для каждого CNI-implementation, тысячи строк в kubelet. Вместо этого есть Container Network Interface (CNI), простой контракт.
CNI plugin, бинарник, kubelet его вызывает с параметрами через stdin/JSON. Plugin делает что хочет (создаёт veth, навешивает routes, заводит overlay), возвращает выделенный IP в JSON.
Контракт:
ADD - подключить pod к сети, вернуть IP
DEL - отключить, освободить IP
CHECK - проверить состояние
VERSION - какие версии CNI поддерживаешь
Конфиг лежит в /etc/cni/net.d/*.conflist, бинарники в
/opt/cni/bin/. Это то, что DaemonSet CNI-плагина раскатывает
при старте.
Что должен обеспечить любой CNI в k8s
Помимо CNI-spec'и есть требования k8s:
- Каждому pod'у, уникальный IP в pod-CIDR
- Pod-to-pod без NAT, pod видит pod по реальному IP, не через proxy и не через NAT
- Pod-to-node работает в обе стороны
- NetworkPolicy, поддержка firewall'а между подами
- (Опционально) сервисы, encryption, observability
Некоторые CNI добавляют сверху: BGP-объявления, IPAM с reservations, bandwidth-shaping, encryption (WireGuard/IPsec), service-mesh.
Три подхода
| Подход | Как pod-to-pod трафик ходит | Pro | Con |
|---|---|---|---|
| L2 overlay (flannel/VXLAN) | encap пакета в UDP, decap на ноде | работает поверх любой сети | overhead 50 байт, MTU-сюрпризы |
| L3 routing (calico/BGP) | каждая нода объявляет pod-CIDR через BGP | нет encap, native MTU, fast | нужна BGP-friendly сеть (или IPIP fallback) |
| eBPF (cilium) | программируемые маршруты в kernel-space | обходят kube-proxy, observability, encryption | сложнее debug, нужен новый kernel |
flannel, простейший VXLAN-overlay
Архитектура:
- DaemonSet
flanneldна каждой ноде - Считывает Node CIDR из k8s API
- Создаёт
flannel.1VXLAN interface - Пакеты pod-to-pod cross-node инкапсулируются в UDP/8472
pod-A (10.244.1.5) ──► veth ──► flannel.1 ──► encap ──► UDP ──► node-B
│
decap ──► flannel.1 ──► veth ──► pod-B (10.244.2.7)
Минусы:
- MTU 1450 (вместо 1500) из-за VXLAN overhead. Если приложение шлёт 1500-байтные пакеты с DF=1, теряются. Сюда же проблема Path MTU Discovery в облаке (GCP/AWS режут ICMP).
- Нет NetworkPolicy в простой flannel (только с patch'ами).
- Нет BGP/encryption из коробки.
Плюсы: 5 минут до запуска, работает где угодно. Default для kind/minikube.
calico, BGP + native routing
Подход: вместо overlay'я анонсировать pod-CIDR'ы каждой ноды как маршруты по BGP. Тогда любой pod-to-pod пакет идёт нативно по сети, без encap.
Архитектура:
calico-nodeDaemonSet с BIRD (BGP-демон)- Каждая нода, BGP peer
- Анонсируют свои pod-CIDR'ы соседям (full mesh или через route reflector)
- Backend для NetworkPolicy, iptables (стандарт) или eBPF (опция)
Node-A (pod-CIDR 10.244.1.0/24) ◄──── BGP ────► Node-B (10.244.2.0/24)
│ │
└─► routing table: └─► routing table:
10.244.2.0/24 via Node-B 10.244.1.0/24 via Node-A
Если сеть не BGP-friendly (типичный AWS/GCP, где L3 не пропускает чужие src/dst), включают IP-in-IP или VXLAN encapsulation фактически фолбек на overlay.
Сильные стороны:
- NetworkPolicy реализована глубже, чем у flannel: помимо
стандартных k8s-policy есть
GlobalNetworkPolicy,HostEndpoint, DNS-based rules - Observability через
calicoctl, метрики - eBPF datapath (опция), заменяет kube-proxy, faster
Когда выбирать: production-кластеры, нужна гранулярная NetworkPolicy. Дефолт во многих managed k8s (GKE Dataplane v1, OpenShift).
cilium, eBPF first
Радикально другой подход: всё в eBPF в kernel. Нет iptables-цепочек для kube-proxy/policy, всё компилируется в BPF-программу.
Архитектура:
cilium-agentDaemonSet- Подключает eBPF-программы к XDP / tc-ingress / tc-egress / cgroup
- Identity-based policy (вместо src-IP/dst-IP, namespace+labels)
- Может полностью заменить kube-proxy (Service routing в eBPF)
Возможности (некоторые уникальные):
- Hubble, observability в eBPF, видит каждый flow
- Encryption, WireGuard или IPsec между нодами
- L7 NetworkPolicy, фильтр по HTTP method/path, DNS-name
- Cluster Mesh, несколько кластеров как один
- Service Mesh, sidecar-free через eBPF (cilium service mesh)
Минусы:
- Требует kernel 5.4+ (lots of eBPF features), реально 5.10+ для full feature set
- Сложнее debug,
bpftool prog listвместоiptables -t nat -L - Высокая потребность в RAM на больших policy-sets
Когда выбирать: новые кластеры на современном kernel, нужна observability и L7-policy, есть готовность к steeper learning curve. Backend в EKS Anywhere, GKE Dataplane v2, AKS Cilium.
NetworkPolicy
Стандартный k8s-объект для pod-уровня firewall'а:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: api-deny-egress, namespace: prod }spec:
podSelector:
matchLabels: { app: api }policyTypes: [Ingress, Egress]
ingress:
- from:
- podSelector:
matchLabels: { app: web }- namespaceSelector:
matchLabels: { name: monitoring }ports:
- { protocol: TCP, port: 8080 }egress:
- to:
- podSelector:
matchLabels: { app: postgres }ports:
- { protocol: TCP, port: 5432 }- to: # разрешить DNS
- namespaceSelector: {} podSelector: { matchLabels: { k8s-app: kube-dns } } ports: [{ protocol: UDP, port: 53 }]Семантика default-deny: если на pod есть хотя бы один
NetworkPolicy с типом Ingress, весь не-разрешённый Ingress
блокируется. Поэтому часто пишут pair: default-deny-all +
конкретные allow'ы.
Реализация, на CNI. flannel без patch'а игнорирует NetworkPolicy
(silently!). calico/cilium соблюдают. Проверить, пометить namespace
и попробовать kubectl exec curl.
IPAM, IP Address Management
Часть CNI: кто и как выделяет IP подам. Варианты:
| IPAM | Как работает | Где использовать |
|---|---|---|
| host-local | каждой ноде block IP, выдаёт из него | flannel, single-cluster |
| calico-ipam | централизованно через etcd, IP Pool с reservation | calico |
| cilium-ipam | как host-local, плюс multi-pool | cilium |
| AWS VPC CNI | каждый pod, настоящий VPC IP (ENI secondary IP) | EKS |
| Azure CNI | как AWS, в Azure VNET | AKS |
| whereabouts | cluster-wide IPAM из range'а, для multus | multi-net |
AWS VPC CNI и Azure CNI дают routable pod IP в облачной сети не нужен overlay, NLB балансирует прямо в pod. Минус: лимиты ENI на инстанс (m5.large = 30 IP), при больших pods-per-node нужны большие инстансы.
Multus, несколько сетей одному pod'у
Стандартная k8s-сеть, одна на pod. Если нужны две (management + data, RAN-functions, NFV), Multus CNI как мета-плагин:
metadata:
annotations:
k8s.v1.cni.cncf.io/networks: macvlan-conf, sriov-conf
Применяется в телеком, edge, ML-кластерах. Не для типичного web-prod.
Когда что-то пошло не так
- Pod в
ContainerCreatingбесконечно, NetworkReady=false CNI не работает.kubectl logs -n kube-system <cni-daemonset>,journalctl -u kubelet | grep -i cni. Часто CNI binary не установлен или конфиг неверный. - Pod-to-pod cross-node не работает, у flannel/VXLAN, UDP/8472
блокируется firewall'ом. У calico, BGP-сессии не установились,
calicoctl node status. - MTU блекхол, крупные пакеты теряются, мелкие проходят.
Симптом: SSH работает,
apt updateзависает на каком-то пакете. Лечится подгонкой MTU pod-сети под underlay (flannel: 1450 если underlay 1500). - NetworkPolicy не работает, CNI не реализует её
(флэннел!), либо есть baseline allow-all правило, либо
kube-systemnamespace в исключениях. Calico/IPIPрост encap latency, overlay включился сам потому что BGP не сошёлся. Проверьcalicoctl node status.- Pod IP exhaust в EKS, упёрлись в лимит ENI/IP per node,
kubectl get events | grep "no IP". Решение: больший instance type или prefix-delegation в VPC CNI. - cilium eBPF program не загружается, старый kernel, или
отключен
CONFIG_BPF_SYSCALL.cilium statusпокажет что падает.
Полезные команды
cat /etc/cni/net.d/*, конфиг текущего CNIls /opt/cni/bin/, какие плагины установленыcrictl exec <containerid> ip addr, pod-network изнутриcalicoctl node status(для calico), состояние BGPcilium status/cilium connectivity test, для ciliumkubectl get pods -n kube-system, здоровье CNI DaemonSet'а