Dwa kierunki ruchu, jedno urządzenie
Witaj
Większość diagramów architektonicznych przedstawia ruch w jednym kierunku: klient na górze, serwer na dole, strzałka skierowana w dół. W rzeczywistości ruch odbywa się w obu kierunkach.
Ruch przychodzący (Ingress): zewnętrzni klienci docierają do Twoich usług przez ten ścieżkę. Proxy odwrócony na krawędzi sieci kończy połączenie TLS, kieruje żądania i egzekwuje politykę dostępu.
Ruch wychodzący (Egress): Twoje usługi docierają do zewnętrznych usług przez ten ścieżkę. Wywoływanie API procesora płatności, pobieranie celu webhooka, wysyłanie żądania do partnera. Często przez proxy przodem lub bramkę NAT z listą dozwolonych.
Wiele architektur zaczyna od jednego serwera obsługującego oba kierunki ruchu. Działa to, dopóki nie przestanie. Tryb awarii jest subtelny, ujawnia się dopiero po wystarczającej liczbie usług wewnętrznych, & naucza ważnej lekcji dotyczącej rozdzielenia obowiązków.
Po zakończeniu tej lekcji zrozumiesz:
- Dlaczego ruch przychodzący (ingress) & wychodzący (egress) reprezentują fundamentalnie różne wzorce ruchu, z różnymi osiami skalowania & różnymi trybami awarii
- Hairpin NAT & dlaczego proxy, który próbuje połączyć się z samym sobą, zawodzi
- Rozbicie architektoniczne: jeden serwer staje się dwoma, & co każdy z nich od tego momentu obsługuje wyłącznie
- Zyski z izolacji bezpieczeństwa: każda strona może zablokować dostęp do swoich rzeczywistych, dozwolonych partnerów
- Jak rozpoznać, kiedy Twój projekt oparty na jednym serwerze przekroczył próg, w którym podział staje się konieczny
Dlaczego kierunki ruchu wymagają różnych narzędzi
Dwa różne obciążenia na jednej granicy sieci
Cechy ruchu przychodzącego (ingress):
- Inicjowany przez podmioty zewnętrzne (szeroki internet)
- Wolumen skaluje się wraz z Twoją bazą użytkowników
- Terminacja TLS, routowanie żądań, limitowanie przepływności per źródło
- Zagadnienia obrony wielowarstwowej: DDoS, nadużycia, scrapowanie
- Publiczny adres IP musi akceptować połączenia od dowolnego źródła
Cechy ruchu wychodzącego (egress):
- Inicjowane przez Twoje własne usługi (znany, mały zestaw klientów)
- Wolumen rośnie wraz z wzorcami wywołań między Twoimi usługami oraz zewnętrznymi API
- Biała lista adresów IP źródłowych na zdalnych końcówkach (masz jeden stały adres IP wychodzący, któremu partnerzy ufają)
- Zmartwienia dotyczące obrony wielopoziomowej: wyciek danych, skompromitowane wewnętrzne usługi wykonujące połączenia wychodzące
- Powinno odrzucać połączenia od kogokolwiek innego niż Twoje własne usługi
Kluczowa asymetria: ruch przychodzący (ingress) akceptuje ruch z całego świata; ruch wychodzący (egress) akceptuje ruch wyłącznie od Twoich własnych usług. Umieszczenie ich na tej samej maszynie oznacza, że ta maszyna musi być jednocześnie osiągalna z całego świata (dla ruchu przychodzącego) oraz osiągalna wyłącznie z Twoich usług (dla ruchu wychodzącego). Zasady zapory sieciowej, które spełniają jeden warunek, działają na niekorzyść drugiego.
Ścieżka rozwoju: mały projekt może ukryć oba role za jednym adresem IP i jednym narzędziem, ponieważ wolumen jest mały, a biała lista adresów IP partnerów jest krótka. W miarę rozwoju projektu tarcie między tymi dwiema rolami rośnie, a pewnego dnia konkretny tryb awarii (hairpin NAT) wymusza rozdzielenie.
Błąd wymuszający rozdzielenie
Zdezynfekowana historia awarii
Wyobraź sobie rzeczywisty rozgałęźnik architektoniczny, który występuje w środowiskach produkcyjnych. Nazwy poniżej zostały zmienione; kształt jest identyczny z tym, z którym zespoły spotykają się w praktyce.
Organizacja uruchamia pojedynczy serwer proxy na adresie 203.0.113.5. Obsługuje on ruch przychodzący (port 443 dla użytkowników) oraz wychodzący (port 1080 SOCKS5 dla wewnętrznych usług wywołujących zapytania wychodzące). Wewnętrzne usługi znajdują się w prywatnych podsieciach i kierują cały ruch wychodzący przez ten proxy SOCKS5 na 203.0.113.5:1080.
Jedną z usług hostowaną za tym samym adresem 203.0.113.5 jest api.example.com. Publiczne DNS rozwiązuje api.example.com do 203.0.113.5.
Teraz inna wewnętrzna usługa musi wywołać api.example.com. Jej ścieżka wychodząca:
1. Wewnętrzna usługa rozwiązuje api.example.com → 203.0.113.5
2. Wewnętrzna usługa wysyła żądanie przez proxy egress SOCKS5 na 203.0.113.5:1080
3. Proxy próbuje otworzyć połączenie od siebie do 203.0.113.5:443
4. Odmowa połączenia. Pakiet musiałby wyjść z NAT i ponownie wejść do tego samego NAT, co większość stosów sieciowych odrzuca. Proxy nie może połączyć się z samym sobą za pomocą własnego adresu IP publicznego.
To jest hairpin NAT: pakiet, który wychodzi z NAT i musi ponownie wejść do tego samego NAT, aby dotrzeć do swojego docelowego miejsca. Bez specjalnego wsparcia hairpin na warstwie routingu, pakiet zostaje odrzucony.
Dlaczego problem ujawnia się późno
Na wczesnym etapie życia projektu każdy wewnętrzny serwis komunikował się z innymi wewnętrznymi serwisami za pomocą prywatnego nazwy hosta (internal-api.local) lub nie wywoływał z powrotem publicznych usług własnej organizacji. Ścieżka hairpin po prostu nie istniała.
Następnie nowa funkcja wymagała, aby serwis A wywołał api.example.com (publiczną nazwę hosta). Ścieżka hairpin została aktywowana. Odmowa połączenia. Awaria.
Poprawka załatała objaw (wymuszenie na resolverze zwracania prywatnego adresu IP api.example.com zamiast publicznego). Przyczyna główna: pojedynczy serwer wykonywał zbyt wiele zadań.
Rozgałęzienie architektoniczne
Jeden serwer staje się dwoma
Czyste rozwiązanie: rozdzielenie proxy na dwa serwery.
Serwer wejściowy (publiczny adres IP 203.0.113.5):
- Caddy / proxy odwrócony na portach 80, 443
- Publiczne rekordy DNS wskazują tutaj
- Hostuje api.example.com, app.example.com itp.
Serwer wyjściowy (inny publiczny adres IP 203.0.113.99):
- SOCKS5 / proxy przesyłający na porcie 1080
- Zapora sieciowa ogranicza przychodzące połączenia wyłącznie do adresów IP wewnętrznej podsieci
- Usługi wewnętrzne kierują cały ruch wychodzący przez ten adres
Co to daje:
1. Rozwiązanie problemu hairpin. Usługa wewnętrzna wywołująca api.example.com kieruje ruch wychodzący przez 203.0.113.99 (egress), który następnie nawiązuje normalne połączenie z 203.0.113.5 (ingress, inny adres IP). Pętla NAT znika, ponieważ dwa adresy IP znajdują się na różnych maszynach.
2. Izolacja bezpieczeństwa. Zapora sieciowa serwera egress może być zablokowana do małego zestawu wewnętrznych adresów IP. Zapora sieciowa serwera ingress pozostaje otwarta dla świata. Dwa osobne zestawy reguł, z których każdy czytelnie wyraża jedną rolę.
3. Niezależne skalowanie. Przepustowość ingress skaluje się wraz z liczbą użytkowników; przepustowość egress skaluje się wraz z aktywnością usług wewnętrznych. Można ulepszyć jedno, nie dotykając drugiego.
4. Izolacja awarii. Błędnie skonfigurowany egress nie powoduje już awarii publicznej strony. Atak DDoS na publiczną stronę nie powoduje już wyczerpania przepustowości egress.
5. Jasniejszy model mentalny. Każda maszyna ma jedno zadanie. Inżynierowie rozważają kwestie ingress bez myślenia o egress, i odwrotnie.
Dwie osie, dwie decyzje dotyczące skalowania
Niezależne skalowanie
Przed rozdzieleniem, wzrost obciążenia w dowolnym kierunku obciążał ten sam serwer. Po rozdzieleniu każdy kierunek ma własną infrastrukturę.
Rozmiarowanie wejścia (Ingress): skaluje się wraz z liczbą użytkowników. Decyzje dotyczące pojemności podejmowane są na poziomie warstwy publicznej (więcej replik proxy odwrotnego, większe maszyny wirtualne, CDN przed nimi). Budżet przepustowości obliczany jest na podstawie ruchu użytkowników w szczycie.
Rozmiarowanie wyjścia (Egress): skaluje się wraz z wewnętrznym woluminem wywołań API do zewnętrznych usług. Często dominują go dostarczanie webhooków, wywołania procesorów płatności lub pobieranie danych od dostawców zewnętrznych. Budżet przepustowości obliczany jest na podstawie wzorców wewnętrznych wywołań.
Izolacja awarii: atak DDoS skierowany na publiczne wejście (ingress) nie zużywa już przepustowości wyjścia (egress) (te wywołania do procesorów płatności i tak przechodzą). Awaria proxy wyjściowego nie powoduje już wyłączenia strony publicznej (użytkownicy nadal mają dostęp do strony; zawiodą tylko wewnętrzne wywołania wychodzące).
Różne SLO: dostępność wejścia (ingress) jest ważna dla użytkowników (widoczna awaria strony); dostępność wyjścia (egress) jest ważna dla operatorów (awarie w tle, które mogą być wykryte później). Każda ze stron może mieć własne SLO.
Wiele serwerów wyjściowych (Egress)
Gdy rola wyjściowa (egress) staje się osobną maszyną, kolejnym oczywistym krokiem jest uruchomienie kilku maszyn wyjściowych za balancerem obciążenia w celu zapewnienia wysokiej dostępności (HA). Każda nowa wewnętrzna usługa wskazuje na nazwę hosta wyjściowego (która rozwiązuje się do puli zrównoważonej obciążeniem), a nie na pojedynczy adres IP.
Ta sama lekcja co w przypadku pozostałych systemów rozproszonych: gdy warstwa staje się bezstanowa i ma własną rolę, można ją tanio mnożyć.
Nowa integracja partnerska
Twoja organizacja działa zgodnie z zaprojektowanym podziałem na ruch przychodzący (ingress) i wychodzący (egress). Serwer egress ma stały publiczny adres IP (203.0.113.99), który został dodany do listy dozwolonych (allowlist) dla trzech istniejących API partnerskich (procesora płatności, bramki SMS i dostawcy e-mail).
Zespół produktowy chce dodać czwartą integrację: system dostarczania webhooków, który wykonuje wywołania zwrotne do endpointów klientów na całym świecie. Prognoza wolumenu: 10 000 wywołań na minutę, z pikiem do 30 000.
Zaprojektuj granicę sieci dla rosnącej usługi
Synteza
Dowiedziałeś się, dlaczego ruch przychodzący (ingress) i wychodzący (egress) wymagają różnych narzędzi, jak awaria hairpin NAT wymusza rozdzielenie w rzeczywistych wdrożeniach oraz jak niezależne skalowanie, izolacja bezpieczeństwa i izolacja awarii przynoszą korzyści po wdrożeniu tego rozdzielenia.
Zastosuj wszystkie cztery.
Średnia firma SaaS obsługuje trzy poddomeny produktowe (app, api, admin) dla swoich użytkowników oraz cztery integracje wychodzące (Stripe, Twilio, SendGrid, system webhooków klientów). Obecnie wszystko działa za pojedynczym serwerem proxy pod jednym adresem IP publicznym. Firma zaczęła otrzymywać zgłoszenia o okresowych awariach hairpin, gdy wewnętrzne usługi próbują wywołać api.example.com. Chcą zaprojektować trwałe rozwiązanie.
Kierunek dalszego rozwoju tego kursu
Kierunek dalszego rozwoju tego kursu
Zobaczyłeś właśnie jeden z najczystszych refaktorów opartych na rozdzieleniu obowiązków w systemach rozproszonych: jedna maszyna staje się dwiema, każda z jasno zdefiniowaną rolą, a system zyskuje w ten sposób korzyści w zakresie skalowalności, bezpieczeństwa oraz izolacji awarii.
Następna lekcja (cs_distsys_failure_modes_and_blast_radius) rozszerza rozumowanie dotyczące izolacji awarii. Przeczytasz zanonimizowany postmortem dotyczący błędu DNS-SERVFAIL, zidentyfikujesz wzorzec kaskadowej awarii oraz napiszesz bezwinne (blameless) punkty akcji, które koncentrują się na systemach, a nie na ludziach.
Lekcja towarzysząca: geometry_of_ingress_egress_separation reinterpretuje rozdzielenie jako graf dwudzielny i bada wierzchołki cięcia, podziały sieci oraz to, co teoria grafów mówi o granicy sieci.
Bardzo dobrze. Dalej.