Программно-определяемая маршрутизация: SDN, eBPF и Cilium на практике | AdminWiki

Программно-определяемая маршрутизация: SDN, eBPF и Cilium на практике

20 сентября 2026 14 мин. чтения

Программно-определяемая маршрутизация строится на простом принципе: решение о пути пакета принимает централизованный контроллер, а пересылку выполняют узлы по готовым правилам. В терминологии 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, но не фиксированный порог версии ядра.

  1. Подключите Helm-репозиторий Cilium.
  2. Создайте values-файл с параметрами замены kube-proxy:
    kubeProxyReplacement: true
    k8sServiceHost: 10.0.0.10
    k8sServicePort: 6443
  3. Установите Cilium:
    helm install cilium cilium/cilium --namespace kube-system -f values.yaml
  4. Не разворачивайте kube-proxy. При установке через kubeadm используйте --skip-phases=addon/kube-proxy, в работающем кластере удалите DaemonSet kube-proxy из kube-system.
  5. Проверьте статус:
    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.

Поделиться:
Сохранить гайд? В закладки браузера