Model OSI w klastrze i w chmurze: kto widzi co, kiedy otwierasz stronę
- data
- kategoria
- Networking
- także w
- Containers · Cloud
- czytanie
- 7 min / 1476 słów
Na spotkaniu ktoś mówi:
"To działa na L4, ale routing robimy na L7."
Kilka osób kiwa głową.
Jeśli nie jesteś pewien, co właściwie oznaczają te liczby, prześledźmy jeden request od początku do końca.
Otwierasz sklep internetowy, klikasz "Zapłać", a przeglądarka wysyła żądanie, które przez internet, chmurę i klaster Kubernetes trafia do aplikacji payments.
Cały czas to ten sam request. Zmienia się tylko to, ile widzi dany element infrastruktury.
Pomaga metafora przesyłki:
- L1: droga,
- L2: dostawa lokalnie, w obrębie jednego budynku,
- L3: adres na kopercie,
- L4: numer mieszkania i sposób dostawy,
- L7: treść listu.
Im wyżej jesteśmy, tym więcej wiemy o tym, czego naprawdę chce użytkownik.
Po co w ogóle są warstwy
Sieć działa dlatego, że nie każdy element musi rozumieć wszystko.
Karta sieciowa nie musi znać HTTP. Router nie musi wiedzieć, że klient właśnie płaci za zamówienie. Load balancer nie zawsze musi czytać nagłówki requestu.
Każda warstwa rozwiązuje własny problem i przekazuje dane dalej.
Klasyczny model OSI ma siedem warstw. Internet w praktyce opiera się na prostszym modelu TCP/IP, ale numery OSI zostały w języku infrastruktury.
Dlatego nadal słyszysz:
- "load balancer L4",
- "routing L7",
- "firewall L3/L4".
Te liczby można czytać jako skrót:
jak głęboko dany element zagląda w ruch sieciowy?
Zobaczmy to na naszej płatności.
L1: fizyka
Klikasz "Zapłać".
Na samym dole wszystko jest fizyką: sygnałem elektrycznym, światłem w światłowodzie albo falą radiową Wi-Fi.
To L1.
Tutaj istnieją kable, porty, karty sieciowe i fizyczne switche.
W chmurze zwykle tego nie widzisz. Dostawca ukrywa sprzęt pod API, a ty odczuwasz go głównie jako limit przepustowości instancji czy interfejsu sieciowego.
L1 nie zna IP, portów ani HTTP.
Przenosi bity.
L2: kto jest moim sąsiadem
Request musi teraz zostać przekazany do kolejnego urządzenia w lokalnej sieci.
Tutaj działa L2.
Klasycznym adresem tej warstwy jest adres MAC. W IPv4 pojawia się też ARP, który pomaga ustalić, jaki adres MAC odpowiada danemu IP w lokalnej sieci.
Jeśli L3 mówi:
"Chcę dotrzeć pod ten adres IP"
to L2 odpowiada:
"Dobrze, ale któremu urządzeniu tutaj lokalnie mam przekazać ramkę?"
W Kubernetesie sieć jest wirtualna, ale zasada pozostaje podobna.
Pod payments ma własny interfejs sieciowy, często połączony z siecią węzła za pomocą pary veth.
Można to uprościć do:
pod payments <---- veth ----> sieć węzła
Po stronie węzła ruch może przechodzić przez bridge, routing kernela, eBPF albo inne mechanizmy używane przez konkretny plugin CNI.
Nie trzeba znać tych szczegółów, żeby zrozumieć najważniejsze:
pod jest podłączony do wirtualnej infrastruktury sieciowej węzła podobnie jak urządzenie do switcha.
L2 nadal nie wie, że trwa płatność.
Wie przede wszystkim, komu lokalnie przekazać dane.
L3: adres IP i droga
Teraz request musi znaleźć drogę do właściwej sieci.
Wchodzimy na L3.
Tu najważniejszy jest adres IP.
Twoja przeglądarka komunikuje się z publicznym adresem sklepu. Dalej ruch może przejść przez load balancer, sieć chmurową, węzły klastra i ostatecznie trafić do właściwego poda.
W Kubernetesie każdy pod ma własny adres IP.
Załóżmy, że payments ma:
10.42.3.17
Przeglądarka klienta oczywiście tego adresu nie zna. Po drodze infrastruktura kieruje ruch tam, gdzie trzeba.
W chmurze podobną rolę pełnią VPC lub VNet, podsieci i tablice routingu.
Routing może wyglądać logicznie tak:
10.42.0.0/16 -> sieć klastra
10.50.0.0/16 -> inna sieć
0.0.0.0/0 -> internet
Router patrzy na docelowy adres IP i odpowiada na pytanie:
"Którędy wysłać pakiet?"
Nie interesuje go, czy użytkownik płaci, loguje się czy pobiera zdjęcie produktu.
Na tym poziomie pojawiają się też reguły bezpieczeństwa.
W Kubernetesie może to być NetworkPolicy, a w chmurze security group albo firewall.
W uproszczeniu:
frontend -> payments: dozwolone
internet -> database: zabronione
To decyzja w rodzaju:
kto może komunikować się z kim?
Nie interesuje nas jeszcze, czy request to:
POST /payments
czy:
GET /health
Do tego potrzebujemy wyższej warstwy.
L4: połączenie i port
Sam adres IP nie wystarcza.
Na jednej maszynie może działać wiele usług. Potrzebujemy więc jeszcze numeru portu.
Na przykład:
10.42.3.17:8080
IP mówi, dokąd.
Port mówi, do której usługi.
To trochę jak adres budynku i numer mieszkania.
Na L4 działają przede wszystkim TCP i UDP.
TCP
TCP tworzy połączenie i pilnuje między innymi kolejności danych oraz retransmisji zgubionych fragmentów.
UDP
UDP jest prostszy. Wysyła datagramy, ale nie daje takich samych gwarancji niezawodności.
Dla naszej historii ważniejsze jest jednak co innego.
Pod payments może zniknąć i zostać zastąpiony nowym. Możemy też mieć kilka jego kopii:
payments-1
payments-2
payments-3
Dlatego klient wewnątrz klastra zwykle nie łączy się bezpośrednio z konkretnym podem.
W Kubernetesie używamy Service, który daje stabilny punkt dostępu i kieruje ruch do jednego z backendów.
Z perspektywy L4 infrastruktura może widzieć:
protokół: TCP
port: 8080
i na tej podstawie zdecydować, gdzie wysłać połączenie.
Podobnie działa sieciowy load balancer w chmurze, na przykład NLB.
Może zobaczyć:
source IP
destination IP
TCP
port 443
i rozdzielić połączenia pomiędzy backendy.
Nie musi wiedzieć, że wewnątrz leci HTTP.
Nie musi czytać URL-a.
Nie musi znać nagłówków.
To właśnie oznacza load balancing L4.
Balancer widzi połączenie.
Nie rozumie jeszcze rozmowy.
L5 i L6: warstwy, których prawie nikt nie liczy
Klasyczny OSI ma jeszcze warstwę sesji i prezentacji.
W praktycznych rozmowach infrastrukturalnych prawie nigdy nie usłyszysz:
"Mamy problem na L6."
Te funkcje nie zniknęły. Po prostu przestaliśmy nazywać je numerami.
W naszej płatności pojawia się na przykład TLS.
Gdy otwierasz:
https://shop.example.com
dane są szyfrowane.
Request ma też określoną reprezentację. Może przesyłać JSON:
{
"orderId": "12345",
"amount": 19900,
"currency": "PLN"
}
albo protobuf. Może być też skompresowany.
Współczesna infrastruktura mówi po prostu:
- TLS,
- JSON,
- protobuf,
- gzip.
Dlatego w rozmowach najczęściej przeskakujemy z:
L4
od razu do:
L7
L7: infrastruktura rozumie request
Docieramy do warstwy aplikacyjnej.
Tutaj infrastruktura może zobaczyć, że dane są requestem HTTP.
Nie widzi już tylko:
10.20.1.8 -> 10.42.3.17:8080
Może zobaczyć:
POST /api/payments
Host: shop.example.com
Content-Type: application/json
X-Preview: true
I to zmienia wszystko.
Na L3 pytaliśmy:
Z jakiego IP pochodzi ruch i dokąd idzie?
Na L4:
Na jaki port?
Na L7 możemy zapytać:
Co ten użytkownik właściwie chce zrobić?
Dlatego gateway albo proxy L7 może kierować ruch według ścieżki:
/api/catalog -> catalog-service
/api/orders -> orders-service
/api/payments -> payments-service
Wszystko może trafiać na ten sam adres IP i port 443.
Dopiero proxy HTTP czyta request i wybiera odpowiedni backend.
Canary
Mamy dwie wersje:
payments-v1
payments-v2
Chcemy przetestować nową.
Proxy L7 może zrobić:
90% -> payments-v1
10% -> payments-v2
Routing po nagłówku
Testerzy wysyłają:
X-Preview: true
Gateway może skierować tylko ich do payments-v2.
Retry i timeout
Proxy może też powiedzieć:
jeśli backend chwilowo nie odpowie, spróbuj jeszcze raz.
Albo:
jeśli nie odpowie w dwie sekundy, zakończ request.
Takie mechanizmy spotkasz w gatewayach, takich jak Envoy Gateway, w chmurowych load balancerach aplikacyjnych, takich jak ALB, oraz w service meshach.
Wspólny mianownik jest prosty:
żeby podjąć taką decyzję, infrastruktura musi rozumieć protokół aplikacyjny.
To jest L7.
Cała podróż w jednym kawałku
Użytkownik klika "Zapłać".
Przeglądarka tworzy:
POST /api/payments
Na L7 gateway widzi ścieżkę /api/payments i decyduje, że request ma trafić do payments.
TLS szyfruje komunikację, a payload może być JSON-em.
Na L4 ruch korzysta z TCP i portu 443, a później być może 8080 wewnątrz klastra. Load balancer lub Kubernetes Service wybiera backend.
Na L3 pakiety mają adresy IP. Routing w chmurze i klastrze ustala, którędy mają dotrzeć do właściwego węzła i poda.
Na L2 dane są przekazywane pomiędzy lokalnymi interfejsami i sąsiadami sieciowymi.
Na L1 wszystko ostatecznie staje się sygnałem przesyłanym przez fizyczną infrastrukturę centrum danych.
Request dociera do payments.
A odpowiedź wraca podobną drogą.
Im wyżej, tym więcej widać
To najważniejsza zasada.
Na L3 infrastruktura widzi mniej więcej:
IP -> IP
Na L4:
IP:port -> IP:port
Na L7:
POST /api/payments
a nawet:
X-Preview: true
Im wyżej, tym więcej można zrobić.
Ale za tę wiedzę trzeba zapłacić.
Proxy L4 może być relatywnie proste. Nie musi analizować HTTP.
Proxy L7 musi rozumieć protokół, parsować requesty, obsługiwać więcej stanu, a często także uczestniczyć w terminacji TLS.
Dlatego można zapamiętać:
L4 jest szybsze, prostsze i bardziej ślepe.
L7 jest mądrzejsze, ale kosztuje więcej zasobów i złożoności.
Pułapka: polityka chroni tylko ruch, który przez nią przechodzi
Wyobraź sobie ochroniarza stojącego przed jednym wejściem do biura.
Sprawdza identyfikator każdego, kto przechodzi przez drzwi.
Reguła brzmi:
tylko pracownicy działu finansowego mogą wejść.
To działa świetnie, dopóki każdy korzysta z tych drzwi.
Jeśli istnieje boczne wejście, którym można ominąć ochroniarza, reguła przestaje być pełną ochroną.
Z politykami L7 jest podobnie.
Jeśli reguła bezpieczeństwa jest egzekwowana przez proxy L7, chroni ruch, który rzeczywiście przez to proxy przechodzi.
Jeśli część ruchu może je ominąć, ta konkretna kontrola nie zadziała.
To ważne również w nowoczesnych service meshach, w których ruch może być przechwytywany i kontrolowany w różnych miejscach.
Dlatego zamiast pytać tylko:
"Czy mamy politykę?"
lepiej zapytać:
"Gdzie jest egzekwowana i czy cały interesujący nas ruch przechodzi przez ten punkt?"
Ściąga
| Warstwa | Co widzi | W klastrze | W chmurze |
|---|---|---|---|
| L1 | sygnał, bity | fizyczna sieć pod węzłami | kable, NIC, przepustowość |
| L2 | lokalnych sąsiadów, MAC | veth, sieć węzła | wirtualizowana sieć Ethernet |
| L3 | adresy IP, trasy | IP podów, routing, NetworkPolicy | VPC/VNet, routing, security groups |
| L4 | TCP/UDP, porty | Service, forwarding do podów | NLB i load balancery transportowe |
| L5/L6 | sesję, szyfrowanie, format | TLS, JSON, protobuf | TLS, certyfikaty, kodowanie |
| L7 | HTTP, URL, metodę, nagłówki | Gateway, ingress, Envoy, service mesh | ALB, proxy aplikacyjne |
Jeśli masz zapamiętać tylko trzy linijki:
L3: dokąd?
L4: do którego portu?
L7: co chce zrobić klient?
Albo w wersji z przesyłką:
L1: jak fizycznie jedzie
L2: komu przekazać lokalnie
L3: pod jaki adres
L4: do którego mieszkania
L7: co jest napisane w liście
Dlatego zdanie:
"NLB robi L4, ale routing po ścieżce wymaga L7"
oznacza po prostu:
L4 widzi połączenie. L7 rozumie request.