Сетевая архитектура
Обзор
Cozystack использует многослойный сетевой стек, разработанный для bare-metal-кластеров Kubernetes. Архитектура объединяет несколько компонентов, каждый из которых отвечает за свой уровень сети:
| Уровень | Компонент | Назначение |
|---|---|---|
| Внешняя балансировка нагрузки | MetalLB | Публикация сервисов во внешние сети |
| Балансировка нагрузки сервисов | Cilium eBPF | Замена kube-proxy, DNAT внутри ядра |
| Сетевые политики | Cilium eBPF | Изоляция тенантов и обеспечение безопасности |
| Сеть подов (CNI) | Kube-OVN | Централизованный IPAM, оверлейная сеть |
| Проброс IP в ВМ | cozy-proxy | Проброс внешних IP-адресов внутрь виртуальных машин |
| Вторичные интерфейсы ВМ | Multus CNI | Подключение вторичных L2-интерфейсов к виртуальным машинам |
| Наблюдаемость | Hubble (опционально) | Видимость сетевого трафика (по умолчанию отключено) |
flowchart TD
EXT["External Clients"]
RTR["Upstream Router / Gateway"]
MLB["MetalLB<br/>(L2 ARP / BGP)"]
CIL["Cilium eBPF<br/>(Service Load Balancing + Network Policies)"]
OVN["Kube-OVN<br/>(Pod Networking + IPAM)"]
PODS["Pods"]
EXT --> RTR
RTR --> MLB
MLB --> CIL
CIL --> OVN
OVN --> PODSСетевая конфигурация кластера
| Параметр | Значение по умолчанию |
|---|---|
| Pod CIDR | 10.244.0.0/16 |
| Service CIDR | 10.96.0.0/16 |
| Join CIDR | 100.64.0.0/16 |
| Домен кластера | cozy.local |
| Тип оверлея | GENEVE |
| CNI | Kube-OVN |
| Замена kube-proxy | Cilium eBPF |
Варианты сетевого стека
Cozystack поддерживает несколько вариантов сетевого стека для разных
типов кластеров. Вариант выбирается через bundles.system.variant в
конфигурации платформы.
| Вариант | Компоненты | Целевая платформа |
|---|---|---|
kubeovn-cilium | Kube-OVN + Cilium (по умолчанию) | Talos Linux |
kubeovn-cilium-generic | Kube-OVN + Cilium | kubeadm, k3s, RKE2 |
cilium | Только Cilium | Talos Linux |
cilium-generic | Только Cilium | kubeadm, k3s, RKE2 |
cilium-kilo | Cilium + Kilo | Talos Linux |
noop | Нет (используйте собственный CNI) | Любая |
В вариантах с Kube-OVN Cilium работает как цепочечный CNI (режим generic-veth):
Kube-OVN отвечает за сеть подов и IPAM, а Cilium обеспечивает балансировку
нагрузки сервисов, применение сетевых политик и опциональную наблюдаемость через Hubble.
В вариантах только с Cilium он выступает одновременно и как CNI, и как балансировщик нагрузки сервисов.
kubeovn-cilium.Выделение Pod CIDR (Kube-OVN)
Kube-OVN использует модель общего Pod CIDR:
- Все поды получают адреса из единого общего пула IP-адресов (10.244.0.0/16)
- IP-адреса выделяются централизованно через IPAM Kube-OVN
- Нет разбиения CIDR по узлам (в отличие от Calico или Flannel)
- Поскольку IP-адреса не привязаны к CIDR-блокам конкретных узлов, поды можно переносить на другие узлы с сохранением адресов
- Взаимодействие подов между узлами использует туннели GENEVE (Join CIDR: 100.64.0.0/16)
Приём внешнего трафика через MetalLB
MetalLB - реализация балансировщика нагрузки для bare-metal-кластеров Kubernetes. Он назначает внешние IP-адреса сервисам типа LoadBalancer, позволяя внешнему трафику достигать кластера.
flowchart TD
CLIENT["External Client"]
RTR["Upstream Router"]
subgraph CLUSTER["Kubernetes Cluster"]
S1["Node 1<br/>MetalLB Speaker"]
S2["Node 2<br/>MetalLB Speaker"]
S3["Node 3<br/>MetalLB Speaker"]
CIL["Cilium (eBPF)<br/>Service Load Balancing<br/>DNAT to Pod IP"]
POD["Target Pod<br/>(Pod CIDR)"]
end
CLIENT -->|"Traffic to external IP<br/>(e.g. 10.x.x.20)"| RTR
RTR -->|"L2 (ARP) or BGP"| S1
RTR -->|"L2 (ARP) or BGP"| S2
RTR -->|"L2 (ARP) or BGP"| S3
S1 --> CIL
S2 --> CIL
S3 --> CIL
CIL --> PODРежим Layer 2 (ARP)
В режиме L2 MetalLB отвечает на ARP-запросы для внешнего IP-адреса сервиса. Один узел становится «лидером» для этого IP и принимает весь трафик.
Как это работает:
- Спикер MetalLB на одном из узлов забирает внешний IP себе
- Спикер отвечает на ARP-запросы: «IP X находится по MAC-адресу aa:bb:cc:dd:ee:ff»
- Весь трафик для этого IP идёт на узел-лидер
- Cilium на узле выполняет DNAT к нужному поду
sequenceDiagram
participant C as Client
participant L as Node (MetalLB Leader)
participant CIL as Cilium (eBPF)
participant P as Pod
C->>L: ARP: Who has 10.x.x.20?
L-->>C: ARP Reply: 10.x.x.20 is at aa:bb:cc:dd:ee:ff
C->>L: Send traffic to 10.x.x.20
L->>CIL: Packet enters kernel
CIL->>P: DNAT → Pod 10.244.x.x:8080Режим BGP
В режиме BGP MetalLB устанавливает BGP-сессии с вышестоящими маршрутизаторами и анонсирует маршруты /32 для IP-адресов сервисов. Это обеспечивает настоящую балансировку нагрузки ECMP между узлами.
Как это работает:
- Спикеры MetalLB устанавливают BGP-сессии с вышестоящими маршрутизаторами
- Каждый спикер анонсирует IP сервиса как маршрут /32
- У маршрутизатора появляется несколько next-hop для одного префикса
- ECMP распределяет трафик между узлами
- Cilium на принимающем узле выполняет DNAT к нужному поду
sequenceDiagram
participant S1 as Node 1 (Speaker)
participant S2 as Node 2 (Speaker)
participant S3 as Node 3 (Speaker)
participant R as Upstream Router
participant CIL as Cilium (eBPF)
participant P as Pod
S1->>R: BGP UPDATE: 10.x.x.20/32 via Node 1
S2->>R: BGP UPDATE: 10.x.x.20/32 via Node 2
S3->>R: BGP UPDATE: 10.x.x.20/32 via Node 3
Note over R: ECMP: 3 next-hops for 10.x.x.20/32
R->>S1: Traffic (1/3)
R->>S2: Traffic (1/3)
R->>S3: Traffic (1/3)
S1->>CIL: Packet enters kernel
CIL->>P: DNAT → PodИнтеграция VLAN для внешнего трафика
Внешний трафик может доставляться в кластер через дополнительные VLAN (клиентские VLAN, DMZ, публичные сети и т.п.), откуда он направляется к сервисам через MetalLB и Cilium.
flowchart TD
EXT["External Traffic"]
subgraph VLANs["Additional VLANs<br/>(Client, DMZ, Public, etc.)"]
V1["VLAN A"]
V2["VLAN B"]
end
subgraph LB["MetalLB"]
L2["L2 Mode → Service → Pod"]
BGP["BGP Mode → Service → Pod"]
end
EXT --> VLANs
V1 --> L2
V2 --> BGPCilium как замена kube-proxy
Cilium заменяет kube-proxy, подключая программы eBPF непосредственно в ядре Linux. Это обеспечивает более эффективную обработку пакетов и расширенные возможности.
Традиционный kube-proxy (iptables) против Cilium eBPF
flowchart LR
subgraph IPTABLES["kube-proxy (iptables)"]
direction LR
P1["Packet"] --> IPT["iptables<br/>PREROUTING"]
IPT --> NAT["NAT chains<br/>O(n) rule traversal"]
NAT --> DNAT1["DNAT to Pod"]
DNAT1 --> POD1["Pod"]
end
subgraph EBPF["Cilium (eBPF)"]
direction LR
P2["Packet"] --> BPF["eBPF program<br/>(TC/XDP)"]
BPF --> MAP["eBPF map lookup<br/>O(1) hash"]
MAP --> DNAT2["DNAT"]
DNAT2 --> POD2["Pod"]
endКлючевые отличия:
| Аспект | kube-proxy (iptables) | Cilium (eBPF) |
|---|---|---|
| Сложность поиска | Обход правил за O(n) | Поиск по хешу за O(1) |
| Контекст выполнения | Накладные расходы в пользовательском пространстве | Нативно в ядре |
| Переключения контекста | Требуются | Отсутствуют |
| Масштабируемость | Деградирует с ростом числа сервисов | Постоянная производительность |
Архитектура eBPF
flowchart TD
subgraph KERNEL["Kernel Space"]
subgraph BPF["eBPF Programs"]
TC["TC<br/>(ingress/egress)"]
XDP["XDP<br/>(fastest path)"]
SOCK["Socket-level<br/>(connect, sendmsg)"]
end
subgraph MAPS["eBPF Maps"]
SVC["Service Tables"]
EP["Endpoint Maps"]
POL["Policy Maps"]
end
TC --> MAPS
XDP --> MAPS
SOCK --> MAPS
endИзоляция тенантов с Kube-OVN и Cilium
В мультитенантном кластере Cozystack все тенанты используют общий Pod CIDR. Это безопасно, потому что изоляция обеспечивается политиками Cilium eBPF на уровне ядра, а не сегментацией сети. Тенанты не могут взаимодействовать друг с другом, хотя используют общий пул IP-адресов. Kube-OVN выделяет IP-адреса из этого общего пула централизованно, без разбиения CIDR по узлам.
Архитектура CNI
flowchart TD
subgraph KO["Kube-OVN"]
IPAM["Centralized IPAM — Shared pool 10.244.0.0/16"]
OVN["OVN/OVS Overlay Network (GENEVE)"]
SUBNET["Subnet management per namespace/tenant"]
end
subgraph CIL["Cilium"]
POLICY["eBPF Network Policies"]
SVCBAL["Service Load Balancing (kube-proxy replacement)"]
IDENT["Identity-based Security"]
HUB["Observability via Hubble"]
end
KO --> CILKube-OVN является основным CNI-плагином для сети подов и IPAM. Собственный
механизм сетевых политик Kube-OVN отключён (ENABLE_NP: false), и всё
применение политик делегировано Cilium. Cilium работает как цепочечный CNI-компонент
(режим generic-veth), который применяет сетевые политики через eBPF и заменяет
kube-proxy для балансировки нагрузки сервисов.
Модель изоляции тенантов
flowchart TD
TA["Tenant A — Namespace app-a<br/>Pods: 10.244.0.10, 10.244.0.11"]
TB["Tenant B — Namespace app-b<br/>Pods: 10.244.1.20, 10.244.1.21"]
TC["Tenant C — Namespace app-c<br/>Pods: 10.244.2.30, 10.244.2.31"]
ENGINE{"Cilium eBPF Policy Engine"}
TA --> ENGINE
TB --> ENGINE
TC --> ENGINE
ENGINE -->|"A ↔ A — ALLOWED"| ALLOW["Same-tenant traffic passes"]
ENGINE -->|"A ↔ B — DENIED"| DENY["Cross-tenant traffic dropped"]Безопасность на основе идентичностей
Cilium присваивает каждой конечной точке (поду) идентичность безопасности на основе её меток. Политики применяются с использованием этих идентичностей, а не IP-адресов.
flowchart LR
POD["Pod: frontend-abc123<br/>Labels: app=frontend,<br/>tenant=acme, env=prod"]
AGENT["Cilium Agent<br/>Hash(labels) → Identity: 12345"]
BPFMAP["eBPF Map<br/>10.244.0.10 → Identity 12345"]
POD --> AGENT
AGENT --> BPFMAPПрименение политик в ядре
Когда пакет передаётся между подами, Cilium применяет политики полностью в пространстве ядра:
flowchart TD
PKT["Packet: 10.244.0.10 → 10.244.1.20"]
STEP1["1. Lookup source identity:<br/>10.244.0.10 → ID 12345 (tenant-a)"]
STEP2["2. Lookup destination identity:<br/>10.244.1.20 → ID 67890 (tenant-b)"]
STEP3["3. Check policy map:<br/>(12345, 67890, TCP, 80) → DENY"]
DROP["4. DROP packet"]
PKT --> STEP1 --> STEP2 --> STEP3 --> DROPВсё это происходит в пространстве ядра примерно за 100 наносекунд.
Почему применение политик через eBPF безопасно
| Свойство | Описание |
|---|---|
| Верификатор | Программы eBPF проверяются перед загрузкой - без сбоев и бесконечных циклов |
| Изоляция | Программы выполняются в ограниченном контексте ядра |
| Нет обхода из пользовательского пространства | Весь сетевой трафик обязан проходить через хуки eBPF |
| Атомарные обновления | Изменения политик атомарны - без состояний гонки |
| Внутри ядра | Не нужны переключения контекста, быстрее, чем в пользовательском пространстве |
Применение на уровне ядра
flowchart TD
subgraph US["User Space"]
PODA["Pod A<br/>(Tenant A)"]
PODB["Pod B<br/>(Tenant B)"]
NOTE["Cannot bypass policy —<br/>traffic MUST go through kernel"]
end
subgraph KS["Kernel Space"]
EBPF["eBPF Programs<br/>• Attached to network interfaces<br/>• Run in privileged kernel context<br/>• Verified by kernel<br/>• Cannot be bypassed by userspace<br/>• Atomic policy updates"]
end
US -->|"all traffic"| KSЗапрет по умолчанию и изоляция пространств имён
Для изоляции Cozystack использует иерархические метки тенантов. Политики сопоставляются
по меткам пространств имён tenant.cozystack.io/*, что позволяет родительским тенантам
включать пространства имён дочерних тенантов. Пример:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: allow-internal-communication
namespace: tenant-example
spec:
endpointSelector: {}
ingress:
- fromEndpoints:
- matchLabels:
k8s:io.cilium.k8s.namespace.labels.tenant.cozystack.io/tenant-example: ""
egress:
- toEndpoints:
- matchLabels:
k8s:io.cilium.k8s.namespace.labels.tenant.cozystack.io/tenant-example: ""
- toEntities:
- kube-apiserver
- cluster
Host Firewall and Node IPv6
Cozystack enables Cilium’s host firewall (hostFirewall.enabled: true) to enforce the system-port restrictions described above on the nodes themselves. The Cilium IPv6 datapath stays disabled (ipv6.enabled: false), since pod networking is provided by Kube-OVN.
In upstream Cilium this combination drops all IPv6 traffic on the node’s network devices before any policy evaluation. This breaks IPv6 Neighbor Discovery and, with it, all node-level IPv6 connectivity — for example, BGP unnumbered peering over link-local addresses on L3 fabrics. A CiliumClusterwideNetworkPolicy cannot allow this traffic back, because the drop happens before policy enforcement.
The Cilium image shipped with Cozystack carries a BPF patch that passes IPv6 to the kernel stack instead, matching the behavior when the host firewall is disabled. The patch is carried until an equivalent fix is available upstream. See cozystack/cozystack#2871 for the implementation. Practical consequences:
- Node IPv6 (Neighbor Discovery, BGP over link-local addresses, and any other node-level IPv6 traffic) keeps working with the host firewall enabled.
- Cilium host policies apply to IPv4 only. Node IPv6 is not filtered by Cilium; if nodes exposed over IPv6 need filtering, it must be done by other means.
To opt out of the host firewall entirely, set cilium.hostFirewall.enabled: false in the values of the cozystack.networking Package. Note that this also disables all IPv4 host policies, including the system-port restrictions.
Наблюдаемость с Hubble
Hubble обеспечивает видимость сетевого трафика для плоскости данных Cilium. Он входит в сетевой стек Cozystack, но по умолчанию отключён, чтобы минимизировать потребление ресурсов.
Во включённом состоянии Hubble предоставляет:
- Журналы потоков в реальном времени для всего трафика между подами и внешнего трафика
- Видимость DNS-запросов
- Метрики уровня запросов HTTP/gRPC
- Интеграцию с метриками Prometheus
- Веб-интерфейс для визуализации трафика
Чтобы включить Hubble, задайте следующее в конфигурации Cilium:
cilium:
hubble:
enabled: true
relay:
enabled: true
ui:
enabled: true
Полные сведения о настройке см. в разделе Enabling Hubble.
Сводка потоков трафика
Внешний доступ
flowchart LR
C["Client"] --> R["Router"]
R --> M["MetalLB<br/>(L2/BGP)"]
M --> N["Node"]
N --> E["Cilium eBPF"]
E --> P["Pod"]Изоляция тенантов
flowchart LR
A["Pod A"] --> CHECK{"eBPF<br/>Policy Check"}
CHECK -->|"Cross-tenant"| DENY["DENY"]
CHECK -->|"Same tenant"| ALLOW["ALLOW → Pod A'"]