Kubernetes Networking: CNI, Cilium z eBPF i polityką sieciową
Kubernetes po mistrzowsku abstrahuje sieć: każdy Pod otrzymuje routowalny adres IP, kontenery w tym samym Podu współdzielą przestrzeń nazw sieci, a Pody mogą to robić komunikują się ze sobą na dowolnym węźle bez NAT. Jednak pozorna prostota się kryje prawdziwa złożoność: jak ten model faktycznie działa? Kto ma do czynienia przypisywać adresy IP, kierować ruch między węzłami, wdrażać zasady sieciowe?
Odpowiedź kryje się w Interfejs sieciowy kontenera (CNI), to standard definiuje, w jaki sposób wtyczki sieciowe powinny integrować się z Kubernetesem. W tym artykule przyjrzymy się modelowi sieci Kubernetes od środka: jak działa kube-proxy, dlaczego Rzęska z eBPF czy i w jaki sposób zastępuje tradycyjne rozwiązania wdrożyć zasady sieciowe, aby odizolować obciążenia w klastrach produkcyjnych.
Czego się nauczysz
- Model sieci Kubernetes: IP-per-Pod, sieć płaska, bez NAT
- Jak działa interfejs sieci kontenerowej (CNI) i jego główne wtyczki
- Różnica między kube-proxy (iptables/IPVS) a Cilium z eBPF
- Ponieważ eBPF zapewnia -30% opóźnienia i +60% przepustowości w porównaniu do iptables
- Jak zainstalować i skonfigurować Cilium jako CNI
- Polityka sieciowa: składnia, przykłady praktyczne, domyślna odmowa i izolacja przestrzeni nazw
- CiliumNetworkPolicy dla reguł warstwy 7 (HTTP, gRPC, Kafka)
Model sieci Kubernetes
Kubernetes narzuca model sieciowy z czterema podstawowymi wymaganiami Wdrożenie CNI musi być zgodne z:
- Wszystkie Pody mogą komunikować się ze wszystkimi innymi Podami bez NAT
- Wszystkie węzły mogą komunikować się ze wszystkimi Podami bez NAT
- Adres IP, który Pod postrzega jako swój własny i ten sam adres IP, którego używają inni, aby się z nim połączyć
- Kontenery w ramach kapsuły udostępniają sieciowe przestrzenie nazw i adresy IP
Ten „płaski model sieci” znacznie upraszcza rozumowanie aplikacji: a mikrousługa nie musi wiedzieć, czy wywoływana przez nią usługa znajduje się w tym samym węźle, czy w węźle zdalny. Złożoność zostaje przeniesiona do warstwy sieci klastrowej.
Jak działa komunikacja między kapsułami
Kiedy Pod A chce komunikować się z Podem B w różnych węzłach, typowy przepływ to: poniżej z rozwiązaniem opartym na sieci nakładkowej (np. VXLAN):
- Pakiet opuszcza kontener Pod A poprzez interfejs
eth0 - Wprowadź przestrzeń nazw węzła poprzez a
veth pair - Wtyczka CNI przechwytuje go i hermetyzuje w VXLAN (lub GRE, Geneve...)
- Pakiet przechodzi przez sieć fizyczną do węzła Pod B
- CNI w węźle docelowym dekapsuluje pakiet
- Pakiet dociera do Poda B poprzez parę V
Dzięki rozwiązaniom opartym na BGP lub routingu natywnym (np. Cilium w trybie routingu natywnego), hermetyzacja nie jest konieczna, a wydajność znacznie się poprawia.
Interfejs sieciowy kontenera (CNI)
CNI to specyfikacja CNCF, która definiuje, w jaki sposób środowiska wykonawcze kontenerów powinny wywoływać wtyczki sieciowe. Kubernetes używa CNI do delegowania zarządzania siecią do wtyczek: kiedy kubelet tworzy Poda, wywołuje skonfigurowaną wtyczkę CNI w celu przydzielenia adresu IP i skonfiguruj interfejs sieciowy.
Główne wtyczki CNI
| Wtyczki | Technologia | Polityka sieciowa | Polityka L7 | Przypadek użycia |
|---|---|---|---|---|
| Flanela | Nakładka VXLAN | Nie (natywny) | No | Rozwój, proste klastry |
| Perkal | BGP/nakładka | Si | No | Lokalnie, wydajność |
| Migawka | eBPF | Si | Tak (HTTP, gRPC) | Siatka produkcyjna, usługowa |
| AWS VPC CNI | Natywny ENI | Tak (SG) | No | EKS |
| Azure CNI | Natywna sieć wirtualna | Tak (NSG) | No | AK |
kube-proxy: Stare podejście
kube-proxy to komponent Kubernetes odpowiedzialny za implementację Usług: kiedy klient wywołuje usługę, kube-proxy zapewnia, że ruch dociera do jednego z Zaplecze kapsuły. Tradycyjnie wykorzystuje iptables w tym celu.
Problem z iptables polega na tym, że złożoność skaluje się liniowo wraz z liczbą reguł. W klastrze obejmującym 10 000 usług i 100 000 punktów końcowych iptables zarządza milionami reguł. Każdy pakiet musi przejść przez ten łańcuch reguł ze znaczącym skutkiem na opóźnienie i procesor węzła.
Alternatywa w drzewie, np IPVS (IP Virtual Server), który korzysta z tablicy mieszającej dla wyszukiwania O(1) zamiast skanowania liniowego iptables. Ale IPVS ma również ograniczenia: Nie obsługuje zaawansowanych polityk i nadal wymaga zarządzania dodatkowymi regułami iptables.
Problem ze skalowaniem iptables
Dzięki 10 000 usług kube-proxy z iptables tworzy około 40 000 reguł dla samych usług. Czas aktualizacji tych reguł rośnie z milisekund do minut. Zgrupowane duże, prowadzi to do dużych opóźnień podczas zdarzeń skalowania i aktualizacji wdrażania. Jest to jeden z głównych powodów, dla których Cilium zastępuje kube-proxy.
Cilium i eBPF: przyszłość sieci Kubernetes
eBPF (rozszerzony filtr pakietów Berkeley) to technologia jądra Linuksa co pozwala na uruchamianie programów w trybie sandbox bezpośrednio w jądrze, bez jego modyfikowania, np bez modułów jądra. Cilium wykorzystuje eBPF do przechwytywania i przetwarzania ruchu sieciowego na poziomie jądra, całkowicie omijając iptables i kube-proxy.
Korzyści z Cilium z eBPF
- Wydajność: Opóźnienie zmniejszone nawet o 30%, przepustowość zwiększona o 60% w porównaniu do iptables
- Skalowalność: Wyszukiwanie O(1) za pomocą map BPF zamiast skanowania liniowego iptables
- Obserwowalność: Hubble zapewnia widoczność ruchu L3/L4/L7 w czasie rzeczywistym
- Polityka warstwy 7: Polityki oparte na ścieżce HTTP, metodzie, nagłówku, metodzie gRPC, temacie Kafki
- Service Mesh bez przyczepy bocznej: mTLS i równoważenie obciążenia zaimplementowane w jądrze, zerowe obciążenie dodatkowe
- Wymiana serwera proxy Kube: Cilium może całkowicie zastąpić kube-proxy
Instalacja rzęsek
Instalujemy Cilium w klastrze Kubernetes przy użyciu Helma, konfigurując go do zastąpienia kube-proxy i włącz Hubble'a, aby był widoczny:
# Aggiungi il repo Helm di Cilium
helm repo add cilium https://helm.cilium.io/
helm repo update
# Installa Cilium con kube-proxy replacement e Hubble abilitati
helm install cilium cilium/cilium \
--version 1.16.0 \
--namespace kube-system \
--set kubeProxyReplacement=true \
--set k8sServiceHost=API_SERVER_HOST \
--set k8sServicePort=API_SERVER_PORT \
--set hubble.relay.enabled=true \
--set hubble.ui.enabled=true \
--set ipam.mode=kubernetes
# Verifica installazione
cilium status --wait
# Verifica connettivita
cilium connectivity test
Cilium w produkcji: zaawansowana konfiguracja
W przypadku klastra produkcyjnego poniżej znajduje się pełna konfiguracja wartości Helm natywny routing (bez enkapsulacji VXLAN) w celu maksymalizacji wydajności:
# cilium-values-production.yaml
kubeProxyReplacement: true
k8sServiceHost: "10.0.0.1" # indirizzo API server
k8sServicePort: "6443"
# Native routing mode (senza overlay VXLAN)
# Richiede che la rete sottostante supporti il routing dei pod CIDR
tunnel: disabled
autoDirectNodeRoutes: true
ipv4NativeRoutingCIDR: "10.244.0.0/16"
# IPAM
ipam:
mode: kubernetes
# BGP per annunciare i pod CIDR ai router
bgp:
enabled: true
announce:
podCIDR: true
lbIP: true
# Hubble - osservabilita Layer 7
hubble:
enabled: true
metrics:
enabled:
- dns:query;ignoreAAAA
- drop
- tcp
- flow
- icmp
- http
relay:
enabled: true
replicas: 2
ui:
enabled: true
# Monitoring con Prometheus
prometheus:
enabled: true
serviceMonitor:
enabled: true
# Encryption (WireGuard)
encryption:
enabled: true
type: wireguard
# Load Balancing con DSR (Direct Server Return)
loadBalancer:
mode: dsr # elimina un hop di rete nel path di ritorno
# Limita connessioni per Pod
bpf:
mapDynamicSizeRatio: 0.0025
Polityka sieciowa w Kubernetesie
Domyślnie wszystkie Pody w klastrze Kubernetes mogą swobodnie komunikować się ze sobą. Jest to wygodne w przypadku programowania, ale stanowi problem bezpieczeństwa w środowisku produkcyjnym. The Polityka sieciowa pozwalają na zdefiniowanie reguł wejścia i wyjścia dla Podów.
Uwaga: wymagana wtyczka CNI
NetworkPolicies są zasobem Kubernetes, ale ich implementacja zależy od wtyczki CNI. Flannel nie obsługuje natywnie NetworkPolicy. Aby z nich skorzystać potrzebujesz Cilium, Calico lub inny CNI, który je wdraża. Zasób jest akceptowany przez API serwera ale ignorowane, jeśli CNI tego nie obsługuje.
Domyślna odmowa: podstawowa najlepsza praktyka
Pierwszy krok w celu odizolowania obciążeń i zastosowania zasad domyślna odmowa w każdej przestrzeni nazw. To odrzuca cały ruch, który nie jest wyraźnie autoryzowany:
# default-deny-all.yaml
# Nega tutto il traffico ingress e egress nel namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {} # seleziona TUTTI i pod nel namespace
policyTypes:
- Ingress
- Egress
---
# Permetti il traffico DNS (necessario per la risoluzione dei nomi)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Zasady sieciowe dla typowej aplikacji
Zobaczmy jak wyizolować aplikację trójwarstwową: frontend, backend, baza danych. Tylko frontend akceptuje ruch z zewnątrz, tylko backend może dotrzeć do bazy danych:
# network-policies-app.yaml
# Frontend: accetta traffico dall'ingress controller
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-to-frontend
namespace: production
spec:
podSelector:
matchLabels:
app: frontend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx
podSelector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 8000
- ports:
- protocol: UDP
port: 53
---
# Backend: accetta solo dal frontend, puo raggiungere DB
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8000
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
- ports:
- protocol: UDP
port: 53
---
# Database: accetta solo dal backend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-backend-to-database
namespace: production
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- protocol: TCP
port: 5432
CiliumNetworkPolicy: Warstwa zasad 7
Standardowe zasady sieciowe Kubernetes działają w warstwie 3/4 (IP i port). Cilium się rozciąga to z Polityka sieci Cilium, która umożliwia stosowanie zasad opartych na zawartości komunikacji: ścieżka HTTP, metoda gRPC, temat Kafki, zapytanie DNS.
Polityka HTTP z Cilium
Pozwalamy frontendowi wywoływać tylko określone punkty końcowe backendu:
# cilium-http-policy.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: frontend-to-backend-l7
namespace: production
spec:
endpointSelector:
matchLabels:
app: backend
ingress:
- fromEndpoints:
- matchLabels:
app: frontend
toPorts:
- ports:
- port: "8000"
protocol: TCP
rules:
http:
- method: "GET"
path: "/api/v1/products.*"
- method: "POST"
path: "/api/v1/orders"
- method: "GET"
path: "/health"
Polityka DNS z Cilium
Ogranicz domeny DNS, które Pod może rozpoznać (przydatne w zapobieganiu wyciekowi danych):
# cilium-dns-policy.yaml
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: restrict-dns-egress
namespace: production
spec:
endpointSelector:
matchLabels:
app: backend
egress:
# Permetti solo DNS verso il cluster DNS
- toEndpoints:
- matchLabels:
k8s:io.kubernetes.pod.namespace: kube-system
k8s:k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: ANY
rules:
dns:
- matchPattern: "*.internal.company.com"
- matchPattern: "*.svc.cluster.local"
- matchPattern: "api.stripe.com"
Hubble: Obserwowalność sieci
Hubble i warstwa obserwowalności Cilium. Zapewnia widoczność w czasie rzeczywistym cały ruch sieciowy klastra, z tożsamościami komunikacyjnymi opartymi na Etykiety Kubernetes zamiast adresów IP.
# Installa il CLI di Hubble
export HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
curl -L --fail --remote-name-all \
https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-amd64.tar.gz
tar xzvf hubble-linux-amd64.tar.gz
sudo mv hubble /usr/local/bin
# Port-forward al relay Hubble
cilium hubble port-forward &
# Osserva il traffico in tempo reale
hubble observe --namespace production --follow
# Filtra per Pod specifici
hubble observe \
--namespace production \
--from-pod frontend-7d9d6b8f-abc12 \
--to-pod backend-5c4f8d9-xyz99 \
--follow
# Mostra solo i drop (traffico bloccato dalle policy)
hubble observe \
--namespace production \
--verdict DROPPED \
--follow
# Statistiche per service
hubble observe \
--namespace production \
--output json | jq '.flow.destination.namespace'
Debugowanie i rozwiązywanie problemów z zasadami sieciowymi
Debugowanie NetworkPolicies to jedno z najczęstszych wyzwań w Kubernetesie. Oto jedno podejście systematyczne:
# 1. Verifica quali NetworkPolicy si applicano a un Pod
kubectl get networkpolicies -n production -o wide
# 2. Test di connettivita con un Pod temporaneo
kubectl run test-pod \
--image=nicolaka/netshoot \
--rm \
-it \
--restart=Never \
-n production \
-- bash
# All'interno del Pod:
# Test TCP
nc -zv backend-service 8000
# Test DNS
nslookup backend-service.production.svc.cluster.local
# Test HTTP
curl -v http://backend-service:8000/health
# 3. Con Cilium, usa il tool di policy verification
cilium policy get # mostra tutte le policy caricate
# 4. Testa la connettivita specifica
kubectl exec -n production frontend-pod -- \
curl -v http://backend-service:8000/api/v1/products
# 5. Con Hubble, vedi perche un pacchetto viene droppato
hubble observe \
--namespace production \
--verdict DROPPED \
--from-pod frontend-7d9d6b8f \
--follow
Izolacja wielu przestrzeni nazw
W klastrach z wieloma dzierżawcami bardzo ważne jest izolowanie przestrzeni nazw. Domyślnie NetworkPolicies nie blokują ruchu między przestrzeniami nazw. Oto jak wdrożyć całkowitą izolację:
# Isola completamente un namespace da tutti gli altri
# (ma permette il traffico interno al namespace)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: namespace-isolation
namespace: tenant-a
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
# Permetti solo traffico dallo stesso namespace
- from:
- podSelector: {}
# Permetti da monitoring namespace
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
egress:
# Permetti solo traffico verso lo stesso namespace
- to:
- podSelector: {}
# Permetti DNS
- ports:
- protocol: UDP
port: 53
# Permetti verso monitoring namespace
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
Test porównawczy wydajności: Cilium vs kube-proxy
Testy porównawcze pokazują znaczne różnice między Cilium z eBPF i kube-proxy z iptables w klastrach o dużej liczbie Usług:
| Metryczny | kube-proxy iptables | Cilium eBPF | Poprawa |
|---|---|---|---|
| Opóźnienie P50 (10 tys. svc) | 450 µs | 130 µs | -71% |
| Opóźnienie P99 (10 tys. svc) | 2,1 ms | 320 µs | -85% |
| Przepustowość (Gb/s) | 22 Gb/s | 36 Gb/s | +64% |
| Reguły aktualizacji procesora (10K svc) | 180 sek | 2 sek | -99% |
| Połączenia/sek | 220 tys | 380 tys | +73% |
Najlepsze praktyki dotyczące sieci Kubernetes
Lista kontrolna sieci produkcyjnej
- Wybierz teraz odpowiedni CNI: Migare CNI w produkcji i kompleksie. Rozważ Cilium, jeśli planujesz zaawansowane zasady sieciowe lub siatkę usług
- Domyślna odmowa w każdej przestrzeni nazw: Zawsze zaczynaj od zasady „odmawiaj wszystkiego”, a następnie dodaj wyjątki
- Spójne etykiety na kapsułach: Zasady sieciowe zależą od etykiet; stosuj jasne konwencje (aplikacja, poziom, wersja)
- Testuj zasady przed wdrożeniem: USA
cilium connectivity testlub przetestuj kapsuły, aby to sprawdzić - Włącz Hubble'a: W środowisku produkcyjnym widoczność ruchu ma kluczowe znaczenie dla debugowania i zgodności
- Śledź spadki: Skonfiguruj alerty w Prometheusie dotyczące nieoczekiwanego spadku ruchu
- Nie blokuj DNS: Zawsze pamiętaj, aby zezwolić UDP/TCP 53 na kube-dns w swoich zasadach ruchu wychodzącego
- Zasady dokumentu: Użyj adnotacji Kubernetes, aby opisać cel każdej zasady sieciowej
Anty-wzorce, których należy unikać
Najczęstsze błędy związane z zasadami sieciowymi
- Zbyt luźne zasady: Używać
namespaceSelector: {}bez matchLabels pozwala na ruch ze WSZYSTKICH przestrzeni nazw, w tym tych zagrożonych - Zapominanie DNS: Jeśli zablokujesz cały ruch wychodzący i zapomnisz port 53, Twoje Pody nie będą już rozpoznawać żadnych nazw hostów
- Etykiety nie zostały zaktualizowane: Jeśli dodasz nowe Pody bez odpowiednich etykiet, zasady sieciowe nie będą ich chronić
- Testy tylko w fazie rozwoju: NetworkPolicies może blokować nieoczekiwany ruch w środowisku produkcyjnym. Zawsze testuj w fazie testowej z realistycznym ruchem
- Użyj adresu IP zamiast selektorów: Zmiana adresów IP podów; zawsze używaj podSelector i namespaceSelector, nigdy ipBlock dla ruchu wewnętrznego
Wnioski i dalsze kroki
Model sieciowy Kubernetes z płaskim podejściem IP na Pod jest elegancki pod względem wyglądu swoją prostotą, a jednocześnie wyrafinowanym wykonaniem. Wybór wtyczki CNI jest jednym z najważniejsze decyzje architektoniczne dla klastra produkcyjnego: wpływ na dostępne funkcje wydajności, bezpieczeństwa i obserwowalności.
Cilium z eBPF to najbardziej zaawansowany dostępny obecnie CNI: zamień kube-proxy na doskonała wydajność, oferuje zasady warstwy 7, integruje obserwowalność z Hubble'em i może zastąpić tradycyjną siatkę usług dla mTLS. W przypadku nowych klastrów w produkcji, oraz wybór zalecany przez społeczność CNCF i dużych graczy, takich jak Google, Amazon i Microsoft, którzy używają go w swoich zarządzanych usługach Kubernetes.
Zasady sieciowe, prawidłowo wdrożone z podejściem domyślnej odmowy, zmniejszają powierzchnię ataku, jeśli kapsuła zostanie naruszona. nie jestem opcjonalne w produkcji: stanowią podstawowy wymóg bezpieczeństwa.
Nadchodzące artykuły z serii Kubernetes at Scale
Powiązane serie
- MLOps i uczenie maszynowe w produkcji — Obciążenia GPU w Kubernetes
- Inżynieria Platformy — Wewnętrzne platformy programistyczne na K8
- Obserwowalność i OpenTelemetry — monitorowanie klastrów







