Обзор

Сеть Kubernetes  —  это сложная, увлекательная тема со множеством подвижных частей. Kube-Proxy и CNI  —  два важных компонента сети Kubernetes, которыми совместно обеспечивается превосходное взаимодействие различных компонентов.

Kube-Proxy  —  сетевой посредник, запускаемый на каждом узле кластера Kubernetes. За счет преобразования определений служб в действующие сетевые правила им поддерживается сетевая связность служб и подов.

CNI или Container Network Interface  —  это спецификация для настройки сетевых ресурсов в Kubernetes. В CNI имеется динамическая платформа для выделения IP-адресов, установки межхостового подключения и настройки оверлейных или андерлейных сетей.

Рассмотрим внутренние механизмы Kube-Proxy и CNI, их интегрирование с Kubernetes, а также различные сетевые плагины Kubernetes.

Kube-Proxy  —  сетевой посредник Kubernetes

В Kubernetes временным характером подов обусловливается изменяемость их IP-адресов, поэтому установление устойчивых соединений затруднительно. Здесь приходятся кстати объекты Service с единым IP-адресом для доступа к подам. Они связаны с группой подов. При поступлении в Service трафик направляется в соответственные поды бэкенда.

Но как это сопоставление Service с подом осуществляется на уровне сети? Через Kube-Proxy.

Kube-Proxy  —  важный агент Kubernetes, который есть в каждом узле кластера. Основная его роль  —  отслеживание изменений в объектах Service и соответственных им конечных точках. Затем эти изменения преобразуются им в значимые сетевые правила внутри узла.

Обычно Kube-Proxy находится в кластере в роли DaemonSet. Но в зависимости от типа установки кластера он устанавливается непосредственно и как процесс Linux в узле. Какие бы ни были настройки, с Kube-Proxy сетевой трафик неизменно добирается до нужных адресатов в кластере Kubernetes.

Функционирование Kube-Proxy

После того как Kube-Proxy установлен, организуется аутентификация с сервером API. Когда появляются или удаляются новые Services или конечные точки, эти изменения сразу передаются сервером API в Kube-Proxy, где они принимаются и преобразуются в правила преобразования сетевых адресов NAT внутри узла. Правила NAT  —  это сопоставления IP-адресов этих Service с IP-адресами подов.

Направляемый к Service трафик придерживается этих правил и приводится к соответственному поду бэкенда.

Проиллюстрируем этот процесс на примере.

Когда создается Service SVC01 с типом ClusterIP, сервером API проверяется  —  сопоставлением меток с селектором меток Service,  —  какие поды должны относиться к этому Service. Назовем эти поды Pod01 и Pod02. Впоследствии сервером API создается абстракция  —  конечная точка с IP-адресом каждого из этих подов, так что SVC01 увязывается с двумя конечными точками  —  EP01 и EP02.

На заключительном этапе IP-адрес SVC01 сопоставляется сервером API с IP-адресами EP01 и EP02, и соединение между Service и связанными с ним конечными точками закрепляется.

Вся эта конфигурация сейчас лишь часть плоскости управления. Это сопоставление нужно реализовать в сети фактически. Тогда при поступлении в IP-адрес SVC01 трафик перенаправится в EP01 или EP02 и встретится здесь с Kube-Proxy, который сервером API оповещается об этих обновлениях на каждом узле. В узле они применяются как внутренние правила.

Теперь предназначенный для IP-адреса SVC01 трафик, следуя этому правилу DNAT, перенаправляется в поды. Не забываем, что EP01 и EP02  —  это IP-адреса подов.

Сценарий упрощен ради важной части Kube-Proxy.

Здесь стоит упомянуть вот о чем:

  1.  Этими Services и конечными точками  —  сопоставлениями IP-адресов и портов  —  в Kubernetes обеспечивается корректная маршрутизация трафика к определенным сочетаниям IP-адресов и портов внутри кластера.
  2. ClusterIP и преобразование DNAT: службами ClusterIP в исходном узле применяется преобразование сетевых адресов назначения DNAT, IP-адреса скрыты от внешнего доступа. Это внутренние правила NAT, доступные исключительно внутри кластера.
  3. Типы служб и правила узлов: различные типы служб чреваты установкой неодинаковых правил внутри узлов. Эти правила организованы в цепочки, конкретные наборы правил с определенным порядком в пути трафика.
  4. Случайный выбор подов: для обработки входящего трафика правилами NAT по умолчанию выбирается случайный под. Однако это поведение варьируется в зависимости от выбранного режима Kube-Proxy, которым определяется стратегия балансировки нагрузки для маршрутизации трафика в поды.

Режимы Kube-Proxy

У Kube-Proxy имеются разные режимы, каждый со своим подходом к реализации правил NAT. Разберем нюансы этих режимов:

Режим IPtables

Это популярный режим по умолчанию, функционал Linux для обработки и фильтрации пакетов. Здесь правила NAT Service-под вставляются в IPtables, трафик перенаправляется с IP-адреса Service на IP-адрес пода.

Но при большом количестве правил режим IPtables менее эффективен, производительность его последовательного алгоритма становится O(n). Отсутствуют в нем и специальные алгоритмы балансировки нагрузки, где применяется случайное распределение с равной стоимостью.

Роль Kube-Proxy описывается теперь, скорее, как «установщик» правил.

Режим IPVS

IPVS или IP Virtual Server предназначен для балансировки нагрузки с эффективным поиском со сложностью O(1), им обеспечивается стабильная производительность независимо от количества правил. Правила вставляются в этом режиме Kube-Proxy не в IPtables, а в IPVS.

В IPVS поддерживаются различные алгоритмы балансировки нагрузки, такие как циклический перебор round robin и наименьшие соединения. Внимание: в отличие от IPtables, IPVS доступен не во всех системах Linux.

Режим KernelSpace

Этот режим доступен только для Windows nodes. В Kube-Proxy правила фильтрации пакетов вставляются с помощью платформы виртуальной фильтрации Windows.

Аналогичная IPtables на Linux, она ответственна за инкапсуляцию пакетов и замену IP-адреса назначения. Знакомы с виртуальными машинами на платформе Windows? Тогда для вас это расширение коммутатора Hyper-V.

Режим Userspace

Этот режим применялся в Kube-Proxy с самого начала, когда соединения управлялись проксированием их через процесс на уровне пользователя. Несмотря на простоту, этот режим менее эффективен, чем другие: это как ездить на детском велосипеде с поддерживающими колесиками.

У каждого режима Kube-Proxy имеются преимущества и ограничения, при его выборе учитываются задачи и инфраструктура конкретного кластера.

Проверка режима Kube-Proxy

Режим, в котором запущен Kube-Proxy, определяется конечной точкой /proxyMode для запроса информации:

  1. Подключаемся по SSH к Cluster Node, то есть к одному из узлов кластера Kubernetes.
  2. Подключившись, запрашиваем режим Kube-Proxy такой командой curl:
curl -v localhost:10249/proxyMode

Так извлекается информация о Kube-Proxy и проверяется, в каком он сейчас режиме:

Здесь в последней строке обозначен применяемый Kube-Proxy режим  —  iptables.

Кратко подытожим: Service и Kube-Proxy в Kubernetes

  • Service в Kubernetes аналогичен посреднику: им предоставляется стабильный IP-адрес для клиентских подключений, полученный по этому IP-адресу трафик направляется на соответственный IP-адрес пода бэкенда, чем фактически решается проблема изменений динамических IP-адресов подов.
  • Балансировка нагрузки зависит от того, какой рассматривается аспект Kube-Proxy. Самим агентом Kube-Proxy трафик не обрабатывается, не выполняется им и балансировка нагрузки; это прерогатива исключительно плоскости управления, ответственной за создание правил Service. Однако в Kube-Proxy посредством этих правил балансировка нагрузки упрощается: трафик распределяется между несколькими идентичными подами, связанными с конкретным Service. Входящие запросы обрабатываются всеми этими подами-репликами.

Что такое Container Network Interface?

Это фреймворк для динамической настройки сетевых ресурсов в кластерах Kubernetes. Благодаря архитектуре плагинов здесь можно подобрать оптимальные для конкретных задач плагины.

CNI применяется для настройки оверлейных и андерлейных сетей:

  • Оверлейными сетями сетевой трафик инкапсулируется с помощью виртуального интерфейса, такого как виртуальная расширяемая локальная сеть VXLAN.
  • Андерлейные сети работают на физическом уровне, состоят из коммутаторов и маршрутизаторов.

Как только выбран тип сетевой конфигурации, средой запуска контейнеров определяется сеть, к которой присоединяются контейнеры. В среде запуска интерфейс добавляется к пространству имен контейнера вызовом плагина CNI, подключенные маршруты подсети распределяются вызовом плагина службы управления IP-адресами IPAM.

CNI применяется с Kubernetes и другими платформами контейнерной оркестрации на основе Kubernetes, такими как OpenShift. Взаимодействие контейнеров во всех кластерах унифицируется здесь с помощью технологии программно-определяемых сетей.

Сеть Kubernetes

В Kubernetes имеется сеть для контейнерных приложений с плоской сетевой структурой. С этой сетью не нужно сопоставлять порты хоста и контейнера, а работа распределенной системы возможна без динамического выделения портов.

Сетевая архитектура в Kubernetes основана на спецификации плагина CNI  —  единого интерфейса среды запуска Kubernetes и базовой сети с поддержкой различных сетевых решений: Flannel, Calico, Weave Net и Cilium.

Работа сети Kubernetes

Когда создается под Kubernetes, плагином CNI настраивается сетевой интерфейс с присвоением поду IP-адреса. Этим облегчается взаимодействие внутри сети подов Kubernetes  —  плоской сети, доступной всем подам кластера. Кроме того, поды взаимодействуют с внешними приложениями через виртуальную сеть Service Kubernetes с предоставлением подов внешним сущностям.

Преимущества сети Kubernetes:

  • Переносимость: кластеры Kubernetes разворачиваются в различных облачных и необлачных средах.
  • Масштабируемость: с поддержкой большого количества подов в кластере Kubernetes.
  • Надежность: в кластере Kubernetes обеспечивается стабильное подключение подов.

Linux-контейнер и технология контейнерных сетей неуклонно развиваются, чтобы соответствовать задачам запускаемых в различных средах приложений. CNI  —  это инициатива Cloud-Native Computing Foundation, которым определяется конфигурация сетевых интерфейсов контейнеров Linux.

CNI призван сделать сетевые решения интегрируемыми с разными системами оркестрации и средами запуска контейнеров. Вместо того чтобы делать сетевые решения подключаемыми, им определяется стандарт единого интерфейса как для сетевого уровня, так и для уровня запуска контейнера.

CNI  —  неродной для Kubernetes фреймворк. Для взаимодействия с различными средами запуска контейнеров разработчики, которыми применяется стандарт CNI, создают сетевые плагины. В сетях CNI используется инкапсулированная сетевая модель, такая как виртуальная расширяемая локальная сеть VXLAN, или неинкапсулированная  —  называемая также декапсулированной  —  сетевая модель, такая как протокол граничного шлюза BGP.

Работа CNI

В CNI, чтобы настроить сеть кластера Kubernetes, применяется архитектура плагинов. Плагин CNI занимается созданием и настройкой сетевого интерфейса каждого контейнера. Помимо этого, вызываемым с помощью kubelet плагином CNI при создании контейнера присваивается и добавляется в сеть Kubernetes IP-адрес.

Кроме того, для выделения IP-адресов контейнерам плагин CNI взаимодействует с плагином службы управления IP-адресами IPAM. Сюда относится при необходимости управление пулом доступных IP-адресов и их присвоение. Как только сетевой интерфейс установлен, контейнер с помощью kubelet запускается и может взаимодействовать с другими контейнерами в сети Kubernetes.

CNI фокусируется на связности сети контейнеров и удалении выделенных ресурсов после завершения работы контейнеров. Таким фокусом спецификации CNI упрощаются, их применяемость увеличивается.

Подробнее о спецификациях CNI, сторонних плагинах и средах запуска  —  в проекте на GitHub.

Необходимость в разных сетевых плагинах Kubernetes

В Kubernetes имеется богатая экосистема сетевых плагинов, многие из которых включены и активно применяются основными платформами оркестрации контейнеров.

Все эти плагины соответствуют строгим стандартам спецификации CNI и важны в обеспечении различных функций сети контейнеров. Учитывая свойственную сетям сложность и широкий диапазон требований пользователей, в CNI предусмотрительно выделили спецификации для нескольких плагинов.

В CNI сети инстанцируются с двумя основными моделями: инкапсулированной и неинкапсулированной.

Инкапсулированная, оверлейная сетевая модель

Эта модель представлена технологиями вроде VXLAN и IPsec, накладывается логической сетью уровня 2 поверх имеющейся топологии сети уровня 3. Таким подходом упрощается маршрутизация, минимизируются затраты.

Данные инкапсуляции распространяются через UDP-порты среди воркеров Kubernetes, для соединения которых с подами создается мост. Дальнейшее взаимодействие внутри подов контролируется при помощи Docker или других контейнерных движков.

Эта инкапсулированная сетевая модель оказывается вполне пригодной в сценариях с мостом уровня 2, особенно чувствительных к временны́м задержкам воркеров Kubernetes в среде уровня 3. В распределенных ЦОД, которыми охватываются различные географические регионы, минимизация задержки важна для предотвращения сегментации сети.

Неинкапсулированная, андерлейная сетевая модель

Этой моделью расширяется сеть уровня 3, облегчается маршрутизация пакетов между контейнерами. В отличие от инкапсулированной модели, здесь не вводится изолированная сеть уровня 2 или связанные с ней затраты. Однако бремя управления необходимым распределением маршрутов полностью возлагается на плечи воркеров Kubernetes.

Чтобы это сработало и информация о маршрутизации эффективно передавалась в поды, вводится сетевой протокол соединения воркеров Kubernetes и применения протокола граничного шлюза BGP. Внутри этих подов контроль взаимодействия с рабочими нагрузками осуществляется с помощью Docker или аналогичного контейнерного движка. Неинкапсулированная сетевая модель идеальна для сценариев с маршрутизируемой сетью уровня 3.

Здесь маршруты для воркеров Kubernetes подвергаются динамическим обновлениям на уровне операционной системы, чем фактически нивелируются проблемы с задержкой. По сути, сеть Kubernetes разворачивается как многоаспектный ландшафт со множеством вариантов плагинов и разнообразными сетевыми моделями, адаптированными к конкретным задачам контейнерных рабочих нагрузок.

CNI с Cilium

Cilium  —  проект с открытым исходным кодом для сетевого взаимодействия, безопасности, наблюдаемости кластеров Kubernetes и других контейнеризированных сред.

Он основан на технологии eBPF внедрения прямо в ядро Linux логики сетевого управления, средств контроля безопасности и функционала наблюдаемости.

CNI Cilium  —  мощный сетевой плагин для Kubernetes, которым обеспечивается повышенная безопасность и сетевые возможности контейнерных приложений. Для сквозной сетевой безопасности и мониторинга трафика им задействуется мощь высокоэффективной программируемой технологии уровня ядра eBPF, сокр. от extended Berkeley Packet Filter.

Для формирования оверлейной сети в Cilium используется VXLAN, для управления сетевой связностью и правилами приложений  —  этот расширенный пакетный фильтр Беркли, для неинкапсулированной маршрутизации  —  BGP. А еще поддерживаются адресация по IPv4 и IPv6, несколько кластеров Kubernetes и, подобно Multus, предоставляются возможности нескольких CNI.

В Cilium работают над аспектами сетевого управления, такими как сетевые политики, через фильтры HTTP-запросов. Политики записываются в файлы YAML или JSON, которыми обеспечивается контроль сетевого трафика  —  входящего и исходящего.

Cilium запускается с VXLAN как оверлейной сетью для инкапсуляции и маршрутизации, а с маршрутизацией BGP  —  как андерлейной. Раньше для функционала BGP в Cilium применялся metallb, но с версией Cilium 1.3 появилась собственная реализация на GoBGP.

Cilium  —  единственный CNI с политиками седьмого уровня L7, то есть с возможностью написать сетевые политики Kubernetes для DNS, HTTP и даже Kafka.

Например, напишем сетевую политику DNS L7 для:

Ограничения DNS-разрешения подмножеством

apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: dns-allow-list
spec:
endpointSelector: {}
egress:
- toEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP
rules:
dns:
- matchPattern: "*.abc.xyz"

Разрешения HTTP-запросов методом «POST» на abc.xyz

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: http-post-abc-xyz
spec:
endpointSelector: {}
egress:
- toPorts:
- ports:
- port: "443"
protocol: TCP
rules:
http:
- method: POST
- toFQDNs:
- matchPattern: "*.abc.xyz"

Ограничения доступа к теме Kafka такими подами

Обычно сетевые политики пишутся так: “Allow any application with the label kafka-consumer” to speak to Kafka («Разрешить взаимодействовать с Kafka любому приложению с меткой kafka-consumer»). Так сеть раскидывается широковато.

А с политиками L7 доступ к отдельным темам ограничивается в зависимости от меток. Так уже только пивовар beer-brewer публикует в теме хмеля hops:

apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: beer-brewers
spec:
ingress:
- fromEndpoints:
- matchLabels:
role: beer-brewer
toPorts:
- ports:
- port: 9092
protocol: TCP
rules:
kafka:
- role: produce
topic: hops

Глазами разработчика

Ресурсы выглядят сложновато? В Cilium учли и это. Все эти сетевые политики визуализируются, мидифицируются и даже конструируются простейшим визуальным построителем.

Убедитесь сами.

Установка Cilium

Cilium устанавливается в виде чарта Helm, сначала делаем репозиторий доступным:

helm repo add cilium https://helm.cilium.io/

Затем настраиваем значения по умолчанию для требуемой установки:

Режим IPAM

В Cilium для управления IPAM имеются разные режимы:

--set global.ipam.mode=cluster-pool
--set global.ipam.operator.clusterPoolIPv4PodCIDR=192.168.0.0/16
--set global.ipam.operator.clusterPoolIPv4MaskSize=23

А еще предварительный просмотр, где для динамического выделения узлов CIDR на основе использования ресурсов режим IPAM устанавливается на cluster-pool-v2beta.

eBPF и XDP

eBPF  —  относительно новая технология ядра Linux для выполнения программ eBPF, запускаемых в «песочнице». Этими программами код в пространстве пользователя запускается в ядре с беспрецедентной производительностью, возможности ядра расширяются.

XDP для высокопроизводительного конвейера обработки пакетов, запускаемого после получения сетевым драйвером пакета, задействуется eBPF. Что это означает? В Cilium с помощью XDP проще предотвратить последствия DDOS-атак: пакеты отбрасываются еще до их попадания в традиционный сетевой стек.

Kube Proxy

С eBPF и XDP в Cilium мы не зависим от iptables, поэтому можем отключить kube-proxy с помощью kubeadm и через развертывание Cilium:

--set kubeProxyReplacement=probe

Нативная маршрутизация

Включим ее, ведь для работы с маршрутизацией пакетов внутри кластера инкапсуляция в Cilium не нужна:

--nativeRoutingCIDR=192.168.0.0/16

Hubble

Наблюдаемость Hubble и Cilium очень важна, включаем ее:

--set global.hubble.relay.enabled=true
--set global.hubble.enabled=true
--set global.hubble.listenAddress=":4244"
--set global.hubble.ui.enabled=true

Завершаем установку

helm repo add cilium https://helm.cilium.io/
helm upgrade --install cilium/cilium cilium \
--version 1.13.4 \
--namespace kube-system \
--set image.repository=quay.io/cilium/cilium \
--set global.ipam.mode=cluster-pool \
--set global.ipam.operator.clusterPoolIPv4PodCIDR=192.168.0.0/16 \
--set global.ipam.operator.clusterPoolIPv4MaskSize=23 \
--set global.nativeRoutingCIDR=192.168.0.0/16 \
--set global.endpointRoutes.enabled=true \
--set global.hubble.relay.enabled=true \
--set global.hubble.enabled=true \
--set global.hubble.listenAddress=":4244" \
--set global.hubble.ui.enabled=true \
--set kubeProxyReplacement=probe \
--set k8sServiceHost=${PUBLIC_IPv4} \
--set k8sServicePort=6443

Хотя Cilium в ландшафте CNI без году неделю, за короткое время он стал золотым стандартом для сетей Kubernetes. Calico тоже отличный вариант, но за счет внедрения eBPF и XDP, лучшего из доступных инструмента отладки Hubble, простоты и скорости разработки его редактора Cilium становится перспективным решением.

Заключение

Сеть Kubernetes состоит из Kube-Proxy и CNI: в Kube-Proxy поддерживаются сетевые правила, трафик перенаправляется в поды; в CNI для базовой сети обеспечивается единый интерфейс. Эти компоненты необходимы для запуска контейнерных приложений в Kubernetes.

Читайте также:

Читайте нас в Telegram, VK и Дзен


Перевод статьи Seifeddine Rajhi: Kube-Proxy and CNI: The Hidden Components of Kubernetes Networking

Предыдущая статья10 способов повысить эффективность RAG-системы
Следующая статьяТоп-12 расширений VSCode