# CNI plugins - сеть Kubernetes (calico, cilium, flannel) _Контейнеры (бонус) · LinuxLab Knowledge Base_ **TL;DR:** CNI - спека плагина: дай pod IP и сеть. Реализации: flannel (VXLAN L2-overlay), calico (BGP routing), cilium (eBPF в kernel). Каждая даёт NetworkPolicy для firewall'а между подами. IPAM - часть CNI, выделяет адреса. ## Зачем 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: 1. Каждому pod'у, **уникальный IP в pod-CIDR** 2. **Pod-to-pod без NAT**, pod видит pod по реальному IP, не через proxy и не через NAT 3. **Pod-to-node** работает в обе стороны 4. **NetworkPolicy**, поддержка firewall'а между подами 5. (Опционально) сервисы, 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.1` VXLAN 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-node` DaemonSet с 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-agent` DaemonSet - Подключает 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'а: ```yaml 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** как мета-плагин: ```yaml 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 `, `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-system` namespace в исключениях. - **`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/*`, конфиг текущего CNI - `ls /opt/cni/bin/`, какие плагины установлены - `crictl exec ip addr`, pod-network изнутри - `calicoctl node status` (для calico), состояние BGP - `cilium status` / `cilium connectivity test`, для cilium - `kubectl get pods -n kube-system`, здоровье CNI DaemonSet'а ## Команды ```bash kubectl get pods -n kube-system -l k8s-app=calico-node ``` Проверить, что CNI-DaemonSet запущен на всех нодах ```bash calicoctl node status ``` Состояние BGP-сессий между нодами в calico-кластере ```bash cilium status --verbose ``` Полный статус cilium: eBPF datapath, kube-proxy replacement, encryption ```bash kubectl get networkpolicy -A ``` Все NetworkPolicy в кластере - смотреть до подозрений на неправильную блокировку ```bash kubectl exec -it pod-a -- nc -zv pod-b 8080 ``` Проверить L4-связность между подами - быстрый тест NetworkPolicy ```bash ip route show table all | grep -E 'cali|flannel|cilium' ``` Маршруты на ноде, добавленные CNI - debug при проблемах с pod-to-pod ```bash kubectl get pods -A -o wide | awk '{print $7}' | sort -u | wc -l ``` Сколько уникальных IP занято подами - на лимит per-node влияет IPAM ## См. также - [Kubernetes Service и Ingress - сетевая публикация подов](/kb/kubernetes-services-and-ingress.md) - [Kubernetes pod lifecycle - от Pending до Terminated](/kb/kubernetes-pod-lifecycle.md) - [veth pair](/kb/veth-pair.md) - [Linux bridge - программный свитч](/kb/linux-bridge.md) - [VXLAN - L2 overlay поверх L3-сети](/kb/vxlan-overlay.md) - [eBPF XDP - kernel data-plane](/kb/ebpf-xdp.md) - [eBPF - программируемый kernel](/kb/ebpf-basics.md) - [Policy routing - rule-based маршрутизация](/kb/policy-routing.md) - [Service discovery в Prometheus: k8s, Consul, file_sd, relabel](/kb/service-discovery-prometheus.md)