Två trafikriktningar, en box
Välkommen
De flesta arkitekturdigram visar trafik som går ett sätt: klient uppe, server nere, pil som pekar nedåt. I verkligheten går trafiken båda sätten.
Ingress: Externa klienter når dina tjänster via denna väg. En reverse proxy vid nätverkets kant avslutar TLS, riktar begärningar och tillämpar åtkomstpolicy.
Egress: Dina tjänster når externa tjänster via denna väg. Att anropa en betalningsprocessor API, hämta en webhook-mål, skicka en begäran till en partner. Ofta via en forward proxy eller NAT-gateway med en tillåtandelista.
Många arkitekturer börjar med en enda enhet som hanterar båda riktningarna. Det fungerar, tills den dag det inte längre gör. Felet är subtilt, dyker upp först när tillräckligt många interna tjänster finns, och lär en viktig lektion om separation av intressen.
Till slutet av denna lektion kommer du att förstå:
- Varför inkommande och utgående trafik representerar fundamentalt olika trafikmönster med olika skalningsaxlar och olika felmoder
- Hairpin NAT och varför en proxy som försöker ansluta till sig själv misslyckas
- Arkitekturen gaffel: en enhet blir två, och vad respektive enhet då exklusivt äger
- Säkerhetsvinster genom isolering: varje sida kan låsas till sina faktiskt tillåtna parter
- Hur du identifierar när din design med en enda enhet har passerat tröskeln där uppdelningen blir nödvändig
Varför riktningarna kräver olika verktyg
Två olika arbetsbelastningar vid en nätverksgräns
Karakteristika för inkommande trafik:
- Initierad av externa parter (det stora internet)
- Volymen skalar med din användarbaser
- TLS-avslutning, begärandesrouting, hastighetsbegränsning per källa
- Försvar i flera lager: DDoS, missbruk, skrapning
- Den publika IP-adressen måste acceptera anslutningar från vem som helst
Karakteristika för utgående trafik:
- Initierad av dina egna tjänster (en känd, liten mängd klienter)
- Volymen skalar med dina mönster för tjänst-till-tjänst- och externa API-anrop
- Käll-IP-allowlisting på fjärrändpunkter (du har en fast utgående IP som partnerföretag litar på)
- Överväganden kring djup i försvar: dataexfiltrering, komprometterade interna tjänster som ringer ut
- Bör avvisa anslutningar från alla utom dina egna tjänster
Den viktiga asymmetrin: Ingress accepterar trafik från världen; egress accepterar trafik endast från dina egna tjänster. Att placera dem på samma maskin innebär att maskinen samtidigt måste vara nåbar från världen (för ingress) och endast nåbar från dina tjänster (för egress). Brandväggsreglerna som uppfyller det ena arbetar mot det andra.
Växtpåsen: Ett litet projekt kan dölja båda bakom en IP och ett verktyg, eftersom volymen är liten och partner-IP-allowlistan är kort. När projektet växer ökar friktionen mellan de två rollerna, och en dag tvingar ett specifikt felmönster (hairpin NAT) uppdelningen.
Buggen som tvingar uppdelningen
En sanerad incidentberättelse
Tänk dig en verklig arkitekturell gaffel som inträffar i produktionsflottor. Namnen nedan har ändrats; formen är identisk med det som team stöter på i praktiken.
En organisation driver en enda proxyserver på 203.0.113.5. Den hanterar inkommande trafik (port 443 för användare) & utgående trafik (port 1080 SOCKS5 för interna tjänster som ringer utåt). Interna tjänster ligger i privata delnät & rutterar all utgående trafik genom den SOCKS5-proxy på 203.0.113.5:1080.
En av tjänsterna som hostas bakom samma 203.0.113.5 är api.example.com. Offentlig DNS löser upp api.example.com till 203.0.113.5.
Nu behöver en annan intern tjänst anropa api.example.com. Dess utgående väg:
1. Intern tjänst löser upp api.example.com → 203.0.113.5
2. Intern tjänst skickar förfrågan genom SOCKS5-utgående proxy på 203.0.113.5:1080
3. Proxyn försöker öppna en anslutning från sig själv till 203.0.113.5:443
4. Connection refused. Paketet skulle behöva lämna & återinträda samma NAT, vilket de flesta nätverksstackar avvisar. Proxyn kan inte ansluta till sig själv via sin egen publika IP.
Detta är hairpin NAT: ett paket som lämnar en NAT & behöver återinträda samma NAT för att nå sin destination. Utan speciellt hairpin-stöd i routningsskiktet släcks paketet.
Varför det dyker upp sent
Tidigt i projektets livstid kommunicerade varje intern tjänste antingen med andra interna tjänster via privat värdnamn (internal-api.local) eller gjorde inte återanrop till sin egen organisations publika tjänster. Hairpin-sökvägen existerade helt enkelt inte.
Därefter krävde en ny funktion att tjänst A skulle anropa api.example.com (ett publikt värdnamn). Hairpin-sökvägen aktiverades. Connection refused. Avbrott.
Lagringen åtgärde symptomet (tvinga resolvern att ge api.example.com's privata IP istället för den publika). Rotorsaken: en enda maskin gjorde för många jobb.
Den arkitektoniska gaffeln
En box blir två
Det rena fixet: separera proxyn till två maskiner.
Ingress-server (offentlig IP 203.0.113.5):
- Caddy / omvänd proxy på portarna 80, 443
- Offentliga DNS-poster pekar hit
- Värd för api.example.com, app.example.com m.m.
Egress-server (annan offentlig IP 203.0.113.99):
- SOCKS5 / framåtriktad proxy på port 1080
- Brandväggen begränsar inkommande anslutningar till endast interna subnät-IP:er
- Interna tjänster routar all utgående trafik via denna adress
Detta ger:
1. Hairpin löst. En intern tjänst som anropar api.example.com routar utgående trafik via 203.0.113.99 (egress), som sedan ansluter normalt till 203.0.113.5 (ingress, en annan IP). NAT-loopen försvinner eftersom de två IP:erna finns på olika maskiner.
2. Säkerhetsisolering. Egress-serverns brandvägg kan låsas ner till en liten uppsättning interna IP:er. Ingress-serverns brandvägg hålls öppen för världen. Två separata regeluppsättningar, där var och en uttrycker en roll tydligt.
3. Oberoende skalning. Ingress-bandbredd skalar med användare; egress-bandbredd skalar med intern tjänsteaktivitet. Uppgradera den ena utan att röra den andra.
4. Felisolering. En felkonfigurerad egress bryr inte längre den publika sajten. En DDoS mot den publika sajten svälter inte längre egress-bandbredden.
5. Tydligare mentalt modell. Varje maskin har ett jobb. Ingenjörer resonemang om ingress-frågor utan att tänka på egress, och vice versa.
Två axlar, två storleksbestämningar
Oberoende skalning
Före uppdelningen belastade tillväxt i någon som helst riktning samma maskin. Efter uppdelningen har varje riktning sin egen resursförsörjning.
Ingress-storlek: skalerar med användare. Kapacitetsbeslut ligger i det publika lagret (fler reverse proxy-replikor, större VM:er, CDN framför). Bandbreddsbudget beräknas mot användartrafiken vid toppbelastning.
Egress-storlek: skalerar med volymen av interna tjänste- till externa API-anrop. Oftast dominerad av webhook-leverans, anrop till betalningsprocessorer eller hämtningar av tredjepartsdata. Bandbreddsbudget beräknas mot interna anropsmönster.
Felisolering: en DDoS mot den publika ingressen äter inte längre egress-bandbredd (dessa anrop till betalningsprocessorer går igenom ändå). En krasch i egress-proxyen tar inte längre ner den publika sajten (användare når fortfarande sajten; endast interna utgående anrop misslyckas).
Olika SLO:er: ingress-tillgänglighet är viktig för användare (synlig sajtstörning); egress-tillgänglighet är viktig för operatörer (bakgrundsfel som kan ta längre tid att upptäcka). Varje sida kan ha sitt eget SLO.
Flera egress-server
När egress-rollen är en egen maskin är nästa uppenbara åtgärd att köra flera egress-maskiner bakom en lastbalanserare för HA. Varje ny intern tjänst pekar mot egress-värdnamnet (som löses upp till lastbalanserade poolen) istället för mot en enskild IP-adress.
Samma lektion som resten av distribuerade system: när en tier blir stateless och har en egen roll, kan den multipliceras billigt.
En ny partnerintegration
Din organisation kör uppdelningen ingress/egress som designad. Egress-servern har en fast publik IP-adress (203.0.113.99) som du har allowlistat med tre befintliga partner-API:er (en betalningsprocessor, ett SMS-gateway och en e-postleverantör).
Ett produktteam vill lägga till en fjärde integration: ett webhook-leveranssystem som ringer tillbaka till kundändpunkter världen över. Volymprognos: 10 000 anrop per minut, med toppar upp till 30 000.
Designa en nätverksgräns för en växande tjänst
Syntes
Du har lärt dig varför inkommande och utgående trafik kräver olika verktyg, det hairpin NAT-fel som tvingar fram uppdelningen i verkliga flottor, och hur oberoende skalning, säkerhetsisolering och felfördröjning uppstår när uppdelningen är på plats.
Tillämpa alla fyra.
Ett medelstort SaaS-företag driver tre produktunderdomäner (app, api, admin) för sina användare, samt fyra utgående integrationer (Stripe, Twilio, SendGrid och ett kundwebhook-system). Idag ligger allt bakom en enda proxy-maskin med en offentlig IP-adress. De har börjat få rapporter om tillfälliga hairpin-fel när interna tjänster försöker anropa api.example.com. De vill designa en permanent lösning.
Vart kursen går härnäst
Vart kursen går härnäst
Du har nu sett en av de renaste separation-of-concerns-refaktoreringarna inom distribuerade system: en maskin blir två, var och en med en tydlig roll, & systemet får fördelar med skalbarhet, säkerhet & felisolering på vägen dit.
Nästa lektion (cs_distsys_failure_modes_and_blast_radius) utöjer resonemanget kring isolering av fel. Du kommer att läsa ett rensat postmortem-dokument om DNS-SERVFAIL, identifiera det kaskadliknande felförloppet och skriva skuldfrifriande åtgärder som riktar sig mot systemet snarare än till individer.
Systudie: geometry_of_ingress_egress_separation omformulerar uppdelningen som en bipartit graf och utforskar skärningsnoder, nätverkspartitioner och vad grafteori säger om en nätverksgräns.
Bra jobbat. Framåt.