Сетевая архитектура

Обзор сетевой архитектуры кластера Cozystack: балансировка нагрузки MetalLB, сеть Cilium на eBPF и изоляция тенантов с Kube-OVN.

Обзор

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 CIDR10.244.0.0/16
Service CIDR10.96.0.0/16
Join CIDR100.64.0.0/16
Домен кластераcozy.local
Тип оверлеяGENEVE
CNIKube-OVN
Замена kube-proxyCilium eBPF

Варианты сетевого стека

Cozystack поддерживает несколько вариантов сетевого стека для разных типов кластеров. Вариант выбирается через bundles.system.variant в конфигурации платформы.

ВариантКомпонентыЦелевая платформа
kubeovn-ciliumKube-OVN + Cilium (по умолчанию)Talos Linux
kubeovn-cilium-genericKube-OVN + Ciliumkubeadm, k3s, RKE2
ciliumТолько CiliumTalos Linux
cilium-genericТолько Ciliumkubeadm, k3s, RKE2
cilium-kiloCilium + KiloTalos Linux
noopНет (используйте собственный CNI)Любая

В вариантах с Kube-OVN Cilium работает как цепочечный CNI (режим generic-veth): Kube-OVN отвечает за сеть подов и IPAM, а Cilium обеспечивает балансировку нагрузки сервисов, применение сетевых политик и опциональную наблюдаемость через Hubble.

В вариантах только с Cilium он выступает одновременно и как CNI, и как балансировщик нагрузки сервисов.

Выделение 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 и принимает весь трафик.

Как это работает:

  1. Спикер MetalLB на одном из узлов забирает внешний IP себе
  2. Спикер отвечает на ARP-запросы: «IP X находится по MAC-адресу aa:bb:cc:dd:ee:ff»
  3. Весь трафик для этого IP идёт на узел-лидер
  4. 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 между узлами.

Как это работает:

  1. Спикеры MetalLB устанавливают BGP-сессии с вышестоящими маршрутизаторами
  2. Каждый спикер анонсирует IP сервиса как маршрут /32
  3. У маршрутизатора появляется несколько next-hop для одного префикса
  4. ECMP распределяет трафик между узлами
  5. 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 --> BGP

Cilium как замена 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 --> CIL

Kube-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'"]
Last modified 2026-07-23: translate 1.6 (c21c9f4)