2つのトラフィック方向、1つのボックス
ようこそ
多くのアーキテクチャ図では、トラフィックが一方通行として描かれています。クライアントが上、サーバーが下、矢印が下を向いている状態です。しかし現実には、トラフィックは双方向に流れます。
イングリッス(Ingress): 外部クライアントがこの経路を通じてあなたのサービスにアクセスします。ネットワークのエッジにあるリバースプロキシがTLSを終端し、リクエストをルーティングし、アクセスポリシーを強制します。
エグレス(Egress): あなたのサービスがこの経路を通じて外部サービスにアクセスします。決済処理会社のAPIを呼び出したり、Webhookのターゲットを取得したり、パートナーへのリクエストを送信したりします。通常、フォワードプロキシや、許可リストを持つNATゲートウェイを通じて行われます。
多くのアーキテクチャは、1つのボックスが両方を処理することから始まります。機能はしますが、それが機能しなくなる日が来ます。障害モードは微妙で、内部サービスが十分に存在するまで表面化せず、重要な関心分離の教訓を教えてくれます。
このレッスンの終わりまでに、以下のことを理解できるようになります:
- イングレスとエグレスが根本的に異なるトラフィックパターンであり、異なるスケーリング軸と異なる障害モードを持つ理由
- ヘアピンNATと、自分自身に接続しようとするプロキシが失敗する理由
- アーキテクチャの分岐:1つのボックスが2つになり、それぞれが排他的に所有するもの
- セキュリティ分離の利点:各側が実際の許可されたピアにロックダウンできること
- 単一ボックス設計が、分割が必要になる閾値を超えたことをどのように特定するか
なぜ方向性が異なるツールを要求するのか
1つのネットワーク境界における2つの異なるワークロード
流入トラフィック(Ingress)の特性:
- 外部の主体(広範なインターネット)によって開始される
- ユーザーベースの規模に応じて量が増減する
- TLSの終端、リクエストのルーティング、ソースごとのレート制限
- 多層防御(ディプスインディペンデンス)の懸念事項: DDoS、悪用、スクレイピング
- 公開IPは、誰でもからの接続を受け入れる必要がある
流出トラフィック(Egress)の特性:
- 自社サービスによって開始される(既知の、小規模なクライアントセット)
- トラフィック量は、サービス間および外部APIの呼び出しパターンに応じてスケーリングする
- リモートエンドポイントでのソースIPの許可リスト設定(パートナーが信頼する固定の送信元IPが1つある)
- 多層防御の懸念点:データ漏洩、内部サービスが侵害された場合の外部への呼び出し
- 自社サービス以外のあらゆる接続を拒否すべきである
重要な非対称性: イングレスは世界中からのトラフィックを受け入れるが、エグレスは自社サービスからのトラフィックのみを受け入れる。これらを同一マシンに配置すると、そのマシンは世界中から到達可能であること(イングレス用)と、自社サービスからのみ到達可能であること(エグレス用)を同時に満たさなければならない。一方の要件を満たすファイアウォールルールは、もう一方の要件に反することになる。
成長の経路: 小規模なプロジェクトでは、トラフィック量が少なくパートナーIPの許可リストも短いため、両方を1つのIPと1つのツールで隠蔽できる。プロジェクトが成長するにつれ、これらの2つの役割間の摩擦は増加し、やがて特定の障害モード(ヘアピンNAT)が分離を強制することになる。
分割を強制するバグ
無害化された障害の物語
本番環境のフロートで実際に起こりうるアーキテクチャ上の分岐を想像してください。以下の名称は変更されていますが、その構造は現場でチームが遭遇するものと同一です。
ある組織は、203.0.113.5 に単一のプロキシサーバーを運用しています。これはイングレス(ユーザー向けポート443)とエグレス(内部サービスからのアウトバウンド呼び出し用ポート1080 SOCKS5)を処理します。内部サービスはプライベートサブネットに存在し、すべてのアウトバウンドトラフィックを 203.0.113.5:1080 のそのSOCKS5プロキシ経由でルーティングします。
同じ 203.0.113.5 の背後にホストされているサービスの1つが api.example.com です。パブリックDNSは api.example.com を 203.0.113.5 に解決します。
ここで、別の内部サービスが api.example.com を呼び出す必要があります。そのアウトバウンドパスは以下の通りです:
1. 内部サービスが api.example.com を解決 → 203.0.113.5
2. 内部サービスがリクエストを 203.0.113.5:1080 のSOCKS5エグレスプロキシ経由で送信
3. プロキシが自分自身から 203.0.113.5:443 への接続を開こうとする
4. 接続拒否。 パケットは同じNATから出て、再び同じNATに入らなければならず、ほとんどのネットワークスタックはこれを拒否します。プロキシは、自身のパブリックIPアドレス経由で自分自身に接続できません。
これがヘアピンNATです: NATから出て、目的地に到達するために同じNATに再入場する必要があるパケットのことです。ルーティング層に特別なヘアピン対応がない場合、パケットはドロップされます。
なぜ後になって問題になるのか
プロジェクトの初期段階では、すべての内部サービスは、プライベートホスト名(internal-api.local)によって他の内部サービスと通信するか、自組織のパブリックサービスにコールバックしませんでした。ヘアピン経路は単に存在しませんでした。
その後、新しい機能でサービスAが api.example.com(パブリックホスト名)を呼び出す必要が生じました。ヘアピン経路が活性化しました。接続拒否。障害発生。
修正は症状を対処するものでした(リゾルバに api.example.com のパブリックIPではなくプライベートIPを返すよう強制する)。根本原因は、単一のマシンが多すぎる役割を担っていたことでした。
アーキテクチャの分岐
1台のボックスが2台になる
クリーンな解決策: プロキシを2台のマシンに分離する。
イングレスサーバー (パブリックIP 203.0.113.5):
- ポート80、443でCaddy / リバースプロキシ
- パブリックDNSレコードがここを指す
- api.example.com、app.example.com などをホストする
エグレスサーバー (別のパブリックIP 203.0.113.99):
- ポート1080でSOCKS5 / フォワードプロキシ
- ファイアウォールは、内部サブネットのIPアドレスからの着信接続のみを許可する
- 内部サービスは、すべての送信トラフィックをこのアドレス経由でルーティングする
これにより得られる利点:
1. ヘアピンの解決。 内部サービスが api.example.com を呼び出す場合、送信は 203.0.113.99(エグレス)経由でルーティングされ、その後 203.0.113.5(イングレス、別のIP)に正常に接続されます。2つのIPが異なるマシン上に存在するため、NATループは解消されます。
2. セキュリティの分離。 エグレスサーバーのファイアウォールは、少数の内部IPにのみロックダウンできます。一方、イングレスサーバーのファイアウォールは外部に対して開いたままにできます。2つの独立したルールセットがあり、それぞれが1つの役割を明確に表現します。
3. 独立したスケーリング。 イングレスの帯域幅はユーザー数に応じてスケーリングされ、エグレスの帯域幅は内部サービスのアクティビティに応じてスケーリングされます。片方をアップグレードしても、もう片方には影響しません。
4. 障害の分離。 エグレスの設定ミスがパブリックサイトを壊すことはなくなります。パブリックサイトへのDDoS攻撃がエグレスの帯域幅を枯渇させることもなくなります。
5. より明確なメンタルモデル。 各マシンには1つの役割があります。エンジニアはエグレスのことを考えずにイングレスの問題を、またはその逆でイングレスのことを考えずにエグレスの問題を推論できます。
2つの軸、2つのサイジング判断
独立したスケーリング
分離前は、どちらの方向への成長も同じマシンに負荷をかけていました。分離後は、各方向に専用のプロビジョニングが用意されます。
イングラウンドのサイジング: ユーザー数に応じてスケーリングします。容量の決定はパブリック向けのティア(リバースプロキシのレプリカ数の増加、より大きなVM、CDNの配置など)で行われます。ピーク時のユーザートラフィックに対して帯域幅の予算が計算されます。
エグレスのサイジング: 内部サービスから外部APIへの呼び出し量に応じてスケーリングします。多くの場合、Webhookの配信、決済プロセッサへの呼び出し、またはサードパーティからのデータ取得が支配的です。内部の呼び出しパターンに対して帯域幅の予算が計算されます。
障害の分離: パブリックイングラウンドに対するDDoS攻撃でも、エグレスの帯域幅を消費しなくなります(決済プロセッサへの呼び出しは依然として実行されるため)。エグレスプロキシのクラッシュでも、パブリックサイトがダウンしなくなります(ユーザーはサイトへのアクセスを継続でき、内部のアウトバウンド呼び出しのみが失敗します)。
異なるSLO: イングラウンドの可用性はユーザーにとって重要であり(サイト停止が視覚的に確認される)、エグレスの可用性はオペレーターにとって重要であり(検出に時間がかかる可能性のあるバックグラウンドの障害)です。各側が独自のSLOを持つことができます。
複数のエグレスサーバー
エグレスの役割が独立したマシンになった時点で、次の自然なステップは、HAのためにロードバランサーの背後で複数のエグレスマシンを実行することです。各新しい内部サービスは、単一のIPアドレスではなく、エグレスのホスト名(ロードバランシングされたプールに解決される)を指すようになります。
分散システムにおける他のレッスンと同じ原則です:レイヤーがステートレスになり、独自の役割を持つようになった時点で、低コストでスケールアウト(複製)が可能になります。
新しいパートナー統合
あなたの組織は、設計通りでイングレス/エグレスの分離を実行しています。エグレスサーバーには固定の公開IP(203.0.113.99)が割り当てられており、3つの既存のパートナーAPI(決済プロセッサー、SMSゲートウェイ、メールプロバイダー)に対してホワイトリスト登録されています。
プロダクトチームが4番目の統合を追加したいと考えています:世界中の顧客エンドポイントへコールバックを行うWebhook配信システムです。トラフィック予測:1分あたり10,000回、バースト時は30,000回。
成長するサービスのためのネットワーク境界を設計する
統合
あなたは、イングレスとエグレスが異なるツールを必要とする理由、実際の環境で分離を強制するヘアピンNATの失敗、そして分離が実現された後に得られる独立したスケーリング、セキュリティの分離、障害の分離について学習しました。
これら4つすべてを適用してください。
中規模のSaaS企業は、ユーザー向けに3つのプロダクトサブドメイン(app、api、admin)を運用しており、さらに4つのアウトバウンド統合(Stripe、Twilio、SendGrid、顧客向けWebhookシステム)を持っています。現在、すべてが単一の公開IPを持つ単一のプロキシマシン背後に存在しています。内部サービスがapi.example.comを呼び出そうとした際に、断続的なヘアピン障害が発生しているという報告が寄せられています。彼らは恒久的な修正策を設計したいと考えています。
このコースの次のステップ
このコースの次のステップ
あなたは、分散システムにおける最もクリーンな関心の分離リファクタリングの一つを目の当たりにしました。1つのボックスが2つになり、それぞれが明確な役割を持ち、その過程でシステムはスケーリング、セキュリティ、障害隔離の利点を継承します。
次のレッスン(cs_distsys_failure_modes_and_blast_radius)では、障害隔離の推論を拡張します。サニタイズされた DNS-SERVFAIL のポストモーテムを読み、カスケード障害のパターンを特定し、個人ではなくシステムを対象とした非責任意向きのアクションアイテムを作成します。
関連レッスン: geometry_of_ingress_egress_separation では、この分割を二部グラフとして再定式化し、カット頂点、ネットワークパーティション、およびグラフ理論がネットワーク境界について教えてくれることを探求します。
よくできました。次へ進みましょう。