Программно-определяемая маршрутизация строится на простом принципе: решение о пути пакета принимает централизованный контроллер, а пересылку выполняют узлы по готовым правилам. В терминологии SDN это разделение на control plane (плоскость управления) и data plane (плоскость данных). В Kubernetes такую схему собирают из Cilium в роли контроллера и eBPF в ядре Linux в роли быстрой плоскости данных.
Выгода измеримая: логика маршрутизации и политик живёт в одном месте и меняется через API, а пакеты обрабатываются на уровне ядра, без обхода длинных цепочек netfilter. Замена kube-proxy на eBPF-карты убирает десятки тысяч правил iptables в крупных кластерах и сокращает задержку на service-пути.
Дальше: архитектура SDN и смысл разделения плоскостей, точки подключения eBPF (XDP, TC, сокеты), установка Cilium с kubeProxyReplacement, политики L3/L4/L7 и чек-лист перехода в production.
Что такое программно-определяемая маршрутизация и зачем разделять control plane и data plane
Программно-определяемая маршрутизация (SDN) выносит принятие решений о путях трафика в отдельный централизованный компонент, а исполнителями оставляет устройства и ядро операционной системы. Классическая сеть работает иначе: каждый маршрутизатор сам участвует в OSPF или BGP и держит собственную таблицу маршрутов, поэтому изменение политики расползается по устройствам постепенно и непредсказуемо.
Практическая ценность разделения в том, что политику меняют в одном месте, а не на сотне узлов. В Kubernetes роль контроллера берёт на себя Cilium: его агенты читают объекты API-сервера, вычисляют маршруты и правила изоляции и записывают результат в eBPF-карты на каждой ноде. Сами карты и программы eBPF образуют data plane, который пересылает пакеты.
Control plane и data plane: в чём разница и почему это важно
Control plane решает, куда направить трафик. Он собирает топологию, знает, какие поды, сервисы и идентичности существуют в кластере, вычисляет пути и политики и рассылает их агентам. Data plane исполняет: ищет адрес назначения, делает DNAT, балансирует соединения, отбрасывает запрещённое, считает пакеты и байты.
Аналогия из авиации помогает удержать разницу: control plane это диспетчер, который строит маршрут и выдаёт полосы, data plane это пилоты, которые летят по его указаниям. Диспетчер не держит штурвал, но без его решений никто не знает, куда лететь.
Исторический протокол связи контроллера с исполнителями - OpenFlow: контроллер устанавливает правила в таблицы коммутатора, а коммутатор только выполняет совпадения и действия. Логика переезжает наверх, оборудование упрощается, а маршруты меняются программно, без перепрошивки железа.
Разделение плоскостей даёт три эффекта. Политику меняют в одном месте. Оборудование остаётся простым исполнителем. Логика маршрутизации обновляется без замены железа. В Kubernetes control plane это агенты Cilium на каждой ноде и оператор, читающий объекты API-сервера, а data plane это программы eBPF, прикреплённые к сетевым интерфейсам и сокетам.
Практические преимущества SDN при эксплуатации сетей
- Единая точка управления политиками: правила маршрутизации и изоляции задаются декларативно и применяются на всех нодах одинаково.
- Автоматизация: контроллер следит за API и сам добавляет маршруты и записи балансировки при появлении пода или сервиса.
- Меньше ручных ошибок: таблицы не правят на каждой ноде вручную, поэтому расхождение конфигураций между узлами исчезает как класс.
- Быстрая выдача сервисов: новый сервис становится доступным без перезапуска сетевых компонентов.
- Наблюдаемость: поток событий на уровнях L3, L4 и L7 видно без зеркалирования трафика.
- Независимость от вендора: логика живёт в контроллере, а не в прошивке конкретного коммутатора.
Разницу подходов удобно увидеть на одном примере. kube-proxy при каждом изменении сервиса переписывает цепочки iptables или таблицы IPVS и сбрасывает состояние. Cilium обновляет записи в eBPF-картах, не перестраивая цепочки правил, поэтому появление десятка подов не вызывает всплеск работы на всех нодах.
eBPF как основа высокопроизводительной маршрутизации в Linux
eBPF позволяет загружать в ядро Linux собственные программы и выполнять их без пересборки ядра и без модулей. Программу проверяет верификатор: он отсекает бесконечные циклы и небезопасные обращения к памяти. Затем JIT-компилятор переводит байт-код в машинные инструкции, и код выполняется с нативной скоростью внутри ядра.
Состояние хранят карты eBPF: хеш-таблицы, LRU-хеши, массивы, ring buffer. Cilium держит там таблицы сервисов, идентичности подов и правила политик. Поиск по хеш-карте даёт O(1), тогда как обход списка правил растёт линейно вместе с их числом.
Точки подключения eBPF: XDP, TC и сокеты
- XDP (eXpress Data Path): программа прикреплена к сетевому интерфейсу и запускается сразу после драйвера, до выделения структуры sk_buff. Доступны действия pass, drop, tx и redirect. Это самая быстрая точка, её берут для раннего отброса DDoS-трафика и перенаправления между интерфейсами.
- TC (traffic control) с фильтром cls_bpf: выполняется после выделения sk_buff, поэтому программа читает и меняет заголовки, переписывает адреса, ставит метки, работает с метаданными и перенаправляет пакет. Это основная точка для маршрутизации, NAT и проверки политик в Cilium.
- Сокетные программы и cgroup-хуки (sockops, sk_msg, cgroup/connect): подключают балансировку в момент установления соединения и сокращают путь пакета, а также фильтруют трафик на уровне приложения.
Cilium комбинирует точки подключения: XDP отвечает за ранний дроп и быстрый путь, TC за маршрутизацию, NAT и политики, сокетные хуки за локальную балансировку, когда клиент и сервер находятся на одной ноде.
Почему eBPF быстрее iptables и IPVS
iptables хранит правила в цепочках, и пакет проходит их последовательно. Число строк растёт вместе с числом сервисов и подов: в кластере на 5 000 сервисов счёт идёт на десятки тысяч правил. Каждое изменение сервиса перестраивает цепочки и берёт блокировки, а трафик дополнительно проходит через conntrack.
IPVS ускоряет выбор бэкенда: сервисы лежат в хеш-таблицах, и поиск получается почти мгновенным. Обработка при этом всё равно идёт через хуки netfilter и таблицу соединений.
Программа eBPF ищет запись в карте и работает без последовательного обхода, обновление карт не требует перезаписи цепочек, а проверка политик выполняется в ядре без прокси.
Публичные замеры проекта Cilium показывают, что замена kube-proxy на eBPF-путь убирает накладные расходы kube-proxy и даёт прирост пропускной способности порядка 5-8% относительно маршрутизации на базе iptables (Benchmark-CNI). Отдельные блоги и студенческие проекты приводят более крупные цифры (снижение задержки на 30-60% и CPU примерно на 50% при 1000+ сервисах) (Why I Chose Cilium Instead of kube-proxy), но это не официальные бенчмарки Cilium, поэтому относиться к ним стоит как к ориентиру, а не как к гарантии. Результат зависит от версии ядра, режима балансировки (DSR или SNAT), доли коротких соединений и версии Cilium. Свой стенд нужно измерять отдельно, методика описана ниже.
Отдельная оговорка из документации Cilium: комбинация eBPF kube-proxy replacement с WireGuard на момент публикации бенчмарков была немного медленнее, чем Cilium eBPF вместе с kube-proxy. Проблема была идентифицирована и планировалась к устранению в одном из следующих релизов, так что при выборе шифрования транспорта это стоит перепроверить на своей версии (CNI Performance Benchmark — Cilium documentation).
Cilium: замена kube-proxy и eBPF-маршрутизация в Kubernetes
Cilium это CNI-плагин для Kubernetes, который строит сеть на eBPF. Он выдаёт адреса подам, программирует маршруты между нодами (туннель VXLAN или Geneve, либо native routing с BGP), реализует Service, Ingress и сетевые политики CiliumNetworkPolicy, включая правила по доменным именам (FQDN). Как выглядят таблицы маршрутов внутри пода и на ноде, разобрано в статье Маршрутизация в Kubernetes: таблицы маршрутов между подами и нодами.
kube-proxy закрывает ту же задачу иначе: он читает объекты Service и EndpointSlice и превращает их в правила iptables или в записи IPVS. Каждый пакет к сервису проходит DNAT и таблицу соединений, а при изменении сервисов правила пересобираются.
Cilium заменяет kube-proxy целиком: Service реализуется через eBPF-карты, балансировка и DNAT происходят в ядре, а для трафика внутри ноды подключается socket-level балансировка, которая выводит соединение из обычного сетевого пути.
Пошаговая установка Cilium с заменой kube-proxy
Замена kube-proxy в Cilium опирается на функцию socket-LB, а для подключения BPF cgroup-программ нужна файловая система cgroup v2 (Kubernetes Without kube-proxy — Cilium documentation). Cilium по умолчанию сам монтирует cgroup v2 по пути /sys/fs/cgroup; при необходимости это делается вручную командой mount -t cgroup2 none /sys/fs/cgroup. Конкретные минимальные версии ядра и Kubernetes сверяйте с официальной документацией системных требований вашего релиза Cilium: в открытых материалах подтверждена зависимость от socket-LB и cgroup v2, но не фиксированный порог версии ядра.
- Подключите Helm-репозиторий Cilium.
- Создайте values-файл с параметрами замены kube-proxy:
kubeProxyReplacement: true k8sServiceHost: 10.0.0.10 k8sServicePort: 6443
- Установите Cilium:
helm install cilium cilium/cilium --namespace kube-system -f values.yaml
- Не разворачивайте kube-proxy. При установке через kubeadm используйте --skip-phases=addon/kube-proxy, в работающем кластере удалите DaemonSet kube-proxy из kube-system.
- Проверьте статус:
cilium status --wait cilium status | grep KubeProxyReplacement cilium service list
В выводе должна появиться строка KubeProxyReplacement: True.
В k8sServiceHost укажите адрес API-сервера, доступный с нод (в managed-кластерах это обычно endpoint балансировщика), в k8sServicePort его порт. Эти два параметра обязательны: без kube-proxy Cilium должен знать адрес kube-apiserver, и он задаётся глобально для всех компонентов Cilium (агентов и операторов). В этом режиме доступен только localhost apiserver loadbalancer, а bootstrap-поды Cilium не могут дойти до API-сервера через ClusterIP сервиса kubernetes, потому что маршрутизацию ещё никто не настроил (kubespray docs — cilium.md).
Учтите изменения в параметрах: в Cilium v1.16 и новее старый параметр замены kube-proxy больше не принимается напрямую, но инструменты вроде kubespray конвертируют его при работе с v1.16+ (kubespray docs — cilium.md).
Метрики и проверка производительности после замены
Cilium отдаёт метрики в формате Prometheus. Для оценки eBPF-маршрутизации и балансировки полезны четыре группы:
- cilium_forward_count_total: сколько пакетов переслано, с разбивкой по направлению;
- cilium_drop_count_total: отброшенные пакеты с указанием причины;
- cilium_policy_verdict_total: решения политик (allowed, denied, audit);
- cilium_endpoint_state: состояние эндпоинтов кластера.
Сравнение до и после делайте на одинаковой нагрузке. Официальные бенчмарки Cilium измеряют общее потребление CPU по всей системе и включают для прямого сравнения конфигурации Calico, а также варианты Cilium с eBPF host-routing и legacy host-routing с kube-proxy replacement (CNI Performance Benchmark — Cilium documentation). В бенчмарках разработчиков используется netperf с тестом TCP_CRR, который замеряет последовательность установления соединения (В чем силиум, брат? Обзор ключевых фишек Cilium). Воспроизводимые измерения на слайдах разработчиков Cilium показывают, что при росте числа сервисов задержка kube-proxy в режиме iptables растёт, потому что для каждого сервиса создаётся правило в цепочке iptables и пакет обходит их последовательно (сложность O(n)). Задержка Cilium и kube-proxy в режиме IPVS почти не меняется, так как оба используют поиск сложности O(1) (в Cilium это lookup хеш-таблицы), при этом Cilium остаётся чуть быстрее (В чем силиум, брат? Обзор ключевых фишек Cilium). При 10 000+ сервисов iptables становится серьёзным узким местом, а Cilium использует O(1) поиск вместо O(n) линейного сканирования (Why I Chose Cilium Instead of kube-proxy).
Конкретные абсолютные значения задержки в миллисекундах и проценты CPU сильно зависят от стенда, версии ядра и профиля трафика, поэтому фиксируйте свои замеры до переключения и не переносите чужие цифры напрямую.
Потоки удобно смотреть через Hubble: он показывает, кто с кем общался и какая политика разрешила или заблокировала трафик. Команда cilium monitor выводит события data plane в реальном времени, cilium monitor --type drop оставляет только отброшенные пакеты с причиной.
Политики L3/L4/L7 на базе eBPF: безопасность и производительность
Cilium применяет политики в ядре. Базовый формат совпадает с NetworkPolicy Kubernetes, а через CRD CiliumNetworkPolicy добавляются идентичности на основе меток, FQDN-правила и проверки на прикладном уровне.
Поведение по умолчанию важно держать в голове: пока ни одна политика не выбирает эндпоинт, трафик к нему разрешён целиком. Как только эндпоинт попадает под политику, для него включается запрет по умолчанию на входящий трафик, и разрешено становится только явно описанное.
Примеры CiliumNetworkPolicy для L3/L4
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-frontend-to-backend
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
Политика выбирает поды с меткой app=backend и разрешает входящие соединения только от подов с меткой app=frontend на TCP-порт 8080. Остальные источники получат дроп на уровне eBPF, без обращения к прокси.
Поле toPorts в CiliumNetworkPolicy описывается типом PortProtocol, где Port может быть номером L4-порта или именем в форме "http" или "http-8080". Поле EndPort игнорируется, если Port задан именованным портом. Политики уровня L4 задаются и на ingress, и на egress через поле toPorts и применяются к портам после маппинга сервисных портов (Layer 4 Policies — Cilium documentation). В Go-типах CiliumNetworkPolicy поле toPorts объявлено как ToPorts []PortInfo с json-тегом "toPorts,omitempty" и пометкой kubebuilder:validation:Optional (v2 package — Cilium API Go types). Утверждение, что порт обязательно должен быть строкой и что это частая причина ошибок валидации, в документации не подтверждается: допустимы и число, и имя.
Для микросегментации по модели Zero Trust такие политики применяют вместе с идентичностями и постепенным включением в режиме audit. Практические манифесты и порядок безопасного перехода собраны в статье Микросегментация Kubernetes: практическая реализация Zero Trust с Cilium и Calico.
L7-политики: HTTP, gRPC и Kafka
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: GET
path: /api/v1/users
Правило разрешает только GET /api/v1/users на порт 8080, остальные методы и пути к этому эндпоинту блокируются. Для разбора прикладного протокола Cilium поднимает Envoy и направляет трафик через него, поэтому появляются накладные расходы: единицы миллисекунд на запрос и дополнительная память на каждой ноде.
Поддерживаются и другие протоколы: в rules.grpc задаются service и method, в rules.kafka topic. Отдельный случай это политики по доменным именам: toFQDNs с matchName разрешают исходящие соединения на конкретные имена, для этого включают DNS-прокси Cilium.
Разумный компромисс: держать L3/L4 на всём кластере, а L7 включать для сервисов, где нужен контроль методов, путей или топиков.
Как внедрять Cilium в production: сценарии и подводные камни
Чек-лист перед миграцией на Cilium
- Версия ядра: uname -r. Точные минимальные требования сверяйте с официальной документацией Cilium; для замены kube-proxy критична поддержка socket-LB и cgroup v2 (Kubernetes Without kube-proxy — Cilium documentation).
- Версия Kubernetes совместима с выбранным релизом Cilium по официальной матрице.
- Есть права cluster-admin и возможность менять DaemonSet в kube-system.
- Другой CNI не установлен. Смена CNI требует пересоздания подов: адреса из старого podCIDR перестанут работать.
- Есть план отката: сохранённые манифесты предыдущего CNI, доступ к консоли нод, понимание, как вернуть kube-proxy.
- Managed-кластеры: в EKS, GKE и AKS параметр kubeProxyReplacement при установке не нужен, потому что kube-proxy всё ещё присутствует во время установки. Он обязателен, когда конфигурация явно задаёт kubeProxyMode: none. В таком режиме Cilium сам выполняет роль kube-proxy, его bootstrap-поды не могут достичь API-сервера через ClusterIP сервиса kubernetes, и нужно заранее передать реальный адрес API-сервера (How to Implement Zero-Trust Workload Identity in Kubernetes with SPIFFE, SPIRE and Cilium). Уточняйте у провайдера, можно ли отключить kube-proxy до начала работ.
Сравнить CNI-плагины по производительности и набору функций перед выбором помогает разбор Какой CNI-плагин для Kubernetes выбрать в 2026 году.
Переходите поэтапно: сначала поднимите Cilium без kubeProxyReplacement и убедитесь, что связность и DNS работают, затем включите замену kube-proxy отдельным изменением. Промежуточное рабочее состояние даёт точку возврата.
Мониторинг и отладка eBPF-маршрутизации
- Hubble: hubble observe --follow показывает поток событий, hubble observe --verdict DROPPED оставляет только заблокированные пакеты, Hubble UI рисует карту связей между сервисами.
- cilium monitor: cilium monitor --type drop выводит причины отброса, что ускоряет поиск ошибок в политиках.
- cilium bpf lb list и cilium service list: проверка, какие сервисы и бэкенды записаны в eBPF-карты.
- bpftool: bpftool prog show перечисляет загруженные программы, bpftool map dump показывает содержимое карт. На нагруженной ноде применяйте с осторожностью, операция затратная.
- Prometheus и Grafana: алерты на скорость роста cilium_drop_count_total и на долю denied в cilium_policy_verdict_total.
Общую схему трафика в кластере и типовые точки отказа разбирает руководство Управление и маршрутизация сетевого трафика в Kubernetes 2026.
Сравнение производительности: eBPF против iptables и IPVS
| Механизм | Поиск правила | Что происходит при 10 000 сервисов | Обновление |
|---|---|---|---|
| iptables | последовательный обход цепочек | десятки тысяч правил, задержка растёт вместе с их числом | перестройка цепочек и блокировки |
| IPVS | хеш-таблица бэкендов | поиск быстрый, но обработка идёт через netfilter и conntrack | правка таблиц без блокировок |
| eBPF в Cilium | хеш-карты eBPF | поиск O(1) плюс нативный код в ядре | обновление записей в картах, без перезаписи правил |
Качественная картина подтверждается бенчмарками разработчиков Cilium: при росте числа сервисов задержка kube-proxy в режиме iptables растёт линейно, тогда как Cilium и IPVS держат её примерно на одном уровне за счёт поиска O(1), причём Cilium остаётся чуть быстрее (В чем силиум, брат? Обзор ключевых фишек Cilium). При 10 000+ сервисов iptables становится серьёзным узким местом (Why I Chose Cilium Instead of kube-proxy).
Конкретные абсолютные значения задержки и процентов CPU из сторонних публикаций не подтверждаются официальными бенчмарками Cilium и сильно зависят от стенда. Любые внешние цифры нужно перепроверять на своей конфигурации: версия ядра, режим балансировки, MTU, тип нагрузки и версия Cilium меняют результат заметно.
Чтобы сравнение было честным, зафиксируйте условия: одинаковое ядро (например, 5.15), одна версия Kubernetes (например, 1.28), три ноды, постоянное число сервисов на весь прогон. Измеряйте p50 и p99 задержки (wrk, vegeta, fortio), расход CPU по нодам (node_cpu_seconds_total), число новых соединений в секунду и пропускную способность (iperf3). Меняйте за прогон только один сетевой механизм.
Итоги: когда стоит внедрять программно-определяемую маршрутизацию на eBPF
- SDN с разделением control plane и data plane даёт единую точку управления, автоматизацию и предсказуемые политики на всех нодах.
- eBPF переносит обработку пакетов в ядро, использует хеш-карты и JIT-компиляцию, поэтому не деградирует так, как iptables с ростом числа правил.
- Cilium заменяет kube-proxy, реализуя Service, балансировку и DNAT через eBPF-карты.
- Политики L3/L4/L7 в ядре усиливают изоляцию, при этом L7 стоит включать точечно, там где нужен контроль методов и путей.
Переход оправдан, когда в кластере тысячи сервисов, есть требования к задержке и нужна тонкая изоляция трафика. На небольших кластерах выигрыш будет скромнее, а сложность отладки выше. Начните с тестового окружения, измерьте задержку и CPU до и после, настройте Hubble и алерты на дропы, и только затем переносите изменение в production.