IP Anycast について
Cloudflare は現在120か国、320都市以上に500を超えるエッジデータセンターを保有しています。これらはIP Anycast という技術を用いて同じIPアドレスでその時点でのユーザーから一番近いエッジを判別しています。
また技術的に必然性のある場合を除き、全てのエッジの全てのサーバで全ての機能を動作させるというのが基本設計です。
このブログサイトでは今までCloudflareの様々な機能をテストし手順としてまとめてきており、非常に多くの機能が存在していることをお分かりいただけたかと思いますが、パブリッククラウドと異なりそれらの機能は可能な限り内部ルーティングなしで動作するように設計されており非常にモノリシックです。このため、ユーザーはどこにいても一番近いエッジと自動で通信が確立され同じ機能が提供されることになります。ゼロトラスト系のサービスを使う場合これはセキュリティと通信速度を両立させる大きなメリットとなっています。
これにより高い可用性と処理速度の追及を実現しています。このためCloudflareのSLAは100%になっています。
SLA 100% のサービスは障害が起きない?
これは難しい問題です。まずSLAというのは技術用語ではなくビジネス用語であるという点が重要です。特にネットワーク系のサービスはユーザーから見たサービスステータスとベンダーから見たサービスステータスには常に乖離があります。
技術的にCloudflareの立場で言えば世界中の全エッジが同時にダウンすることは考えられないのでサービスの継続性は100%と言えます。極論を言えば現在日本には10のエッジが存在していますが、それらが全てダウンしたとしてもどこかの国のエッジが動作していればベンダーとしてのサービスは継続されています。
ただしユーザーの立場からするとそうはいきません。IP Anycastを用いているため、エッジのダウンを検知し自動的に生きているエッジへ動的にリクエストは再ルーティングされたとしても輻輳遅延が発生します。つまりしばらくの間はつながらないわけです。東京の一番大きいエッジがダウンした場合などは近隣のISPを巻き込んだ巨大な輻輳遅延が発生ししばらくの間ネットワークは混乱し、ユーザーからすると大規模障害に見えます。インターネットという大規模なパケット交換式ネットワークの性質から言えばこれを防ぐことは難しいのが実情です。Cloudflareでは[Argo Smart Routing]というインターネット上で発生している輻輳遅延を検知し動的にそれを回避するルーティング情報をクライアントに提供するサービスが存在しており、輻輳遅延回避を行うことは可能ですが勿論100%ではありません。インターネットはそういうものです。
IP Anycast は本当に一番近いエッジと通信をするか?
いえ、厳密には保証されていません。今一般的に使われている経路情報を交換するBGP(Border Gateway Protocol)の性質上、ほとんどの場合そうなる、ということを意味しています。それを理解して行くために(というより私もそれをもう一度正しく理解するために)IP Anycastをちゃんとおさらいしていきます。
IP Unicast
IP ユニキャストは送信者と宛先の間の1対1の関連付けを使用して、特定の単一のノードにメッセージを配信します。普段意識しない場合ブラウザがウェブサイトにアクセスをする場合、こちらが用いられます。
IP Anycast
1対1の多数の関係を使用して、通常は送信元に最も近いノードのグループから任意の1つにメッセージを配信します。1対多ではなく、1対1の多数の関係、というのがポイントです。ややこしいので図で説明します。
二つのプライベートネットワークがあるとします。境界線にはルーターが配置されておりインターネットへ接続します。これらのルーターはBGP等(ほかにもOSPFやRIPなどがありますがこの記事では割愛します)のプロトコルを用いて経路情報を交換します。多くの場合契約先のISPからそれを入手しています。一般的には動的モードで動作させる(内部用ルーターはその限りではない)ので時間と共に経路情報はアップデートされていきます。経路情報とは以下のようなものです。
宛先IPアドレス | Next ホップ | メトリック | インターフェース |
---|---|---|---|
2606:4700:3036::ac43:b061 | 2606:4700:3036::ac43:b062 | 5 | eth0 |
2606:4700:3036::ac43:b061
へ行きたければ2606:4700:3036::ac43:b062
のeth0
というNICに通信を投げなさい。5
の距離で目的のサーバに行きますと、という意味です。
(余談ですがIPv6のこの可読性の悪さはなんとかならないかなと思っています。v6の導入が阻害される要因の一つかもしれません。とはいえネットワークに携わる人はv6を推進する義務を持っていると思っているのでこの記事では全てv6を使います)
クライアントがルーターに経路情報が存在していない宛先と通信を行いたい場合はデフォルトルートというものが使われます。
宛先IPアドレス | Next ホップ | メトリック | インターフェース |
---|---|---|---|
::/0 | fe80::3 | 1 | eth0 |
つまりわからない場合はfe80::3
に聞きましょう、という意味です。聞かれたfe80::3
のルーターはまたわからなければ自身のデフォルトルートに聞きに行きます。宛先が判明するまで延々とそれを繰り返すわけです。
ではここで2つの同じIPを持ったCloudflareエッジがあるとします。
BGPやデフォルトルートの技術特性から、ネットワーク的に近い方の情報を入手する確率がはるかに高いため、NetworkAのルーターはEdgeAへの経路情報を入手し、NetworkBのルーターはEdgeBへの経路情報を入手します。これにより近いエッジと自動で通信が確立されるということになります。
1対1の多数の関係 とIP Unicast
上の図で示したように、通信自体はIP Unicastとして行われます。別の言い方をするとUnicastとは技術的な通信方式を指し、Anycastとはその運用上の形態を指します。Anycastというコンセプトを支えるために個別の通信はUnicastで行われる、ということです。
同一IPの異なるノードの経路情報
ではルーターAがエッジBの情報を入手するとどうなるでしょうか。
宛先IPアドレス | Next ホップ | メトリック | インターフェース |
---|---|---|---|
2606:4700:3036::ac43:b061 | 2606:4700:3036::ac43:b062 | 5 | eth0 |
2606:4700:3036::ac43:b061 | 2606:4700:3036::ac43:b063 | 10 | eth0 |
同じ宛先IPアドレスを持つ重複した経路情報は持つことができます。その際距離情報を表すメトリックの数字がより小さい方がActiveとなり使用されます。動的にこれが計算される場合一般的にはBGPではAS_PATHというネットワーク経路が最小化する経路が選択されますが、OSPFよって求められた最短パスがIGPメトリックとして採用され設定されるケースもあるようです。(と、理解していましたが、今そんなことはない!という指摘が複数ありました)ルーターベンダーごとに挙動は異なったオプションがあるようです。
OSPF と ダイクストラ法
OSPF(Open Shortest Path First) は単独でルーティングプロトコルとして運用されるケースもあれば、内部の環境はOSPF、インターネットに面するDynamicな環境はBGPと併用されるケースもあります。
最短経路の算定において、用いられるアルゴリズムはダイクストラ法と呼ばれるものです。
まず個別ノード間の通信コストを算定します。これは単純に通信帯域によってのみ算定されます。その後出発地点から目的地からまでのコストを合計し、一番小さいものが使用される、という仕組みです。
AS_PATH
BGPで一番経路選択に優先されるのがこちらです。
Network | Next Hop | Metric | LocPrf | Weight | Path |
---|---|---|---|---|---|
* 2001:db8:1::/48 | 2001:db8:0:1::2 | 0 | 100 | 65002 65001 i | |
*> | 2001:db8:0:1::1 | 0 | 100 | 65001 i |
2001:db8:1::/48:
への通信は2001:db8:0:1::2
と2001:db8:0:1::1
の2種類が存在していますがPathの値が小さい方、つまり2001:db8:0:1::1
が優先されます。
1行目は2つのASを通りますが、2行目は1つのASのみで通信が完結するため、2行目が選択されます。
2606:4700:3036::ac43:b062
経由でアクセスしていた2606:4700:3036::ac43:b061
に障害が発生しノードダウンとなった場合、それを検知したルーター、もしくはBGPで別ルーターから伝わって来た新しい経路情報(アドバタイズされた、と言います)によりその経路情報は使えないものとされ今度は2606:4700:3036::ac43:b063
経由で2606:4700:3036::ac43:b061
にアクセスします。これが先に言ったAnycastとは運用上の形態で実態はUnicastである、というものの実態です。これは冗長構成の構築に役立ちます。これによりSLA100%をCloudflareは実現しています。ただし2606:4700:3036::ac43:b062
経由でアクセスしていた2606:4700:3036::ac43:b061
に障害が起きているという情報が伝わるまでには一定のタイムラグが発生します。伝わった後近隣のルーターは一気に通信の宛先を変えようとするので一時的にネットワークが混雑したりして輻輳遅延が発生します。
Cloudflare Workers でやってみる
CloudflareはAWS等と異なりエッジの情報をそこまで隠匿化していません。Workersを使うとエッジのGPS情報やコロケーション番号などを教えてくれます。またリクエストを送ったクライアントIPを自動でログに抽出してくれますので色々便利です。
2つの関数を準備しました。
export default {
async fetch(request) {
const someHost = 'https://client.harunobukameda-913.workers.dev';
const url = someHost + '';
async function gatherResponse(response) {
const { headers } = response;
const contentType = headers.get('content-type') || '';
if (contentType.includes('application/json')) {
return JSON.stringify(await response.json());
} else if (contentType.includes('application/text')) {
return response.text();
} else if (contentType.includes('text/html')) {
return response.text();
} else {
return response.text();
}
}
const init = {
headers: {
'content-type': 'text/html;charset=UTF-8',
},
};
const response = await fetch(url, init);
const results = await gatherResponse(response);
return new Response(results, init);
},
};
export default {
async fetch(request, env, ctx) {
return new Response('Hello World!');
},
};
Cloudflareでは同じアカウント内部でのWorkersからWorkersの呼び出しを禁止しています。その場合Service BindingというJSベースのRPC機能を使う必要があります。このため2つ目の関数は別のCloudflareアカウントで起動しています。
https://anycast.harunobukameda.workers.dev/
をブラウザで呼び出すとこの関数がhttps://client.harunobukameda-913.workers.dev/
の出力を読み込んで戻してくれます。
ログを見る両方とも同じエッジで動作していることがわかります。
"cf": {
"longitude": "139.64830",
DNSエントリには以下の通り2つのIPアドレスが設定されています。2つ設定されている理由はシンプルに冗長化目的です。
Name: anycast.harunobukameda.workers.dev
Address: 2606:4700:3036::ac43:b061
Name: anycast.harunobukameda.workers.dev
Address: 2606:4700:3037::6815:4039
一方呼び出されている関数のログには呼び出し元のIPアドレスが以下のように記録されています。
"headers": {
"accept-encoding": "gzip, br",
"cf-connecting-ip": "2a06:98c0:3600::103",
どういうことでしょうか。
IP Anycast ではノードは複数のIPアドレスを持つことが一般的
インターネット上では複数ノードが共通のIPアドレスを持つとしても内部的には別のIPアドレスを持たないといろいろ運用上の問題が発生します。
- 障害時の切り分け
どのIPアドレスを保有するノードがどの問題を起こしたか?等を判別するためには異なるIPアドレスを持っておく必要があります。このため内部コールなどではそのIPアドレスが使われます。 - メンテナンス用
同じIPアドレスでは特定ノードへのメンテナンス指示を出す事ができません。このため管理用IPアドレスが必要です。
Soft Unicast / Port Slicing
クラウドフレアではその内部用IPアドレスに対する枯渇の問題に対応するためSoft Unicast / Port Slicing という技術を用いています。ただしこれはCloudflare固有の技術であるため少し本題からは外れます。
最大2048台のサーバが同じIPアドレスを共有しルーターはポート番号を各サーバに割り振っています。単純なNATを用いたIPアドレスの共有に比べてポート番号の衝突が発生しないため通信遅延を防ぐことが可能です。IPv6であればこの問題は解決するかもしれませんがまだまだIPv4にも対応できるDualスタックの出番は長そうです。
IPアドレスと物理NIC
ではもう少し下のレイヤにおりていきます。
いままでの話でNetworkAのクライアントはEdgeAとEdgeBの違いを知り、よりコストが軽い方と通信を行うということがわかりました。ではNetworkAのクライアントはEdgeAのどの物理ハードウェア(物理NIC)と通信を行うのでしょうか?結論からするとNetworkAはそれを知る必要はりません。IPアドレスのみでインターネット上の住所は完結します。ただしEdgeAの内部ネットワークはそういうわけにはいきません。NetworkAから受け取ったリクエストをEdgeAの適切なハードウェアにルーティングする必要があります。
ARP
それを行うのがARP(Address Resolution Protocol)です。各ハードウェアはMACアドレスという固有の番号を持ちます。MACアドレスはIEEE(Institute of Electrical and Electronics Engineers、電気電子技術者協会)によってハードウェアごとに固有であることが保証されています。勿論意図的に衝突させることはできますが、商用で購入したハードウェアが衝突することはありません。
NDP
しかしながらIPv6の世界ではARPは使用されません。理由はいろいろありますが、たとえばARPはMACアドレスとIPアドレスの紐付けの宣言にブロードキャストを使います。つまり、全ノードに情報を通達します。IPv6が利用される環境はかなりノードの台数が増えることが予想されます。そうなるとブロードキャストでは効率が悪く、特定の複数ノードにのみ情報を送付するマルチキャストが使われます。
IPアドレスとMACアドレスの紐づけは以下のような形のテーブルになります。
fe80::1ff:fe23:4567:890a dev eth0 lladdr 00:1a:2b:3c:4d:5e REACHABLE
2001:0db8:85a3::8a2e:370:7334 dev eth0 lladdr 00:1a:2b:3c:4d:5f STALE
fe80::1c3b:adff:fe7b:1b2c dev eth1 lladdr 00:1a:2b:3c:4d:60 DELAY
この情報は内部的に取り扱われインターネット上で交換されないのが一般的です。また物理的なハードウェアのありかを知っている人が管理する必要があります。つまりトラストアンカーはルーターではありません。ほとんどの場合、ハードウェアが直接ルーターにつながっていることはないからです。では誰が管理するのか?といえばスイッチです。スイッチは物理的な線で各ハードウェアとつながっているため、どのポートがどのMACアドレスにつながっているかを別のテーブルで管理しています。
MAC アドレス | Port |
---|---|
00:1a:2b:3c:4d:5e | 1 |
00:1a:2b:3c:4d:5f | 2 |
00:1a:2b:3c:4d:60 | 3 |
運用上の主な課題
最後にAnycastを運用するうえで課題となる主な要因ことについてまとめていきます。
課題が無ければ全ベンダーがこの方式を採用するわけで、やはり気を付けないといけない部分もあります。
ホットポテトルーティング
前述の通り経路情報は接続先ISP等に大きく依存しています。このため、そのISPが誤った経路情報や、特定の経路に通信を誘導する経路情報を流した際に通信が偏ってしまいます。先程したメトリックは手動で変更し、それをアドバタイズすることも可能です。ISPによっては別のISPとの通信で課金が発生するケースもあるため、より安い経路に通信を流したいというケースもあるかもしれません。
通信の管理
同様の理由で通信の制御が難しくなります。
例えばCloudflareが新しいアーキテクチャを搭載した巨大な新しい巨大なエッジに多くの通信を誘導させたい、としても技術的にそれは不可能です。クライアントからすればすべてが同じIPアドレスを有したエッジとして見えるだけです。新しいエッジ用に別のIPアドレスを払い出せばできなくなないですが・・・
ネットワークの可視性
これは上に上げた通りです。クライアント側には共通IPとしてログが出るため、そのログだけではどのエッジに問題があったか?と判別できません。CloudflareではRay-ID
という固有IDをエラー画面に表示させることでユーザーからの問い合わせに対応できるようにしています。
セッション管理
例えばTCPの3-wayハンドシェイクの場合、同じノードと常にアクセスを行わなければセッションが確立されません。セッション確立後も通信先のノードが変更になるとセッションが失われます。このためCloudflareではZTNA系サービスも含めてUDPをデフォルトで用いています。UDPはコネクションレスプロトコルであるためです。DNS等も同じ理由でIP Anycastと相性が良く、AWSでもRoute 53 はAnycastで動作しています。
また大きいデータを送る際のフラグメンテーションも同じ理由で気を付ける必要があります。
DDoS攻撃との相性
DDoS攻撃は分散したボットネットワークが特定のターゲットを狙い処理能力を奪ってしまう攻撃です。これはIP Anycastとは相性がいいと言えます。なぜなら特定のターゲット
すなわち特定のIPアドレス
への通信は、クライアントが分散していれば勝手にターゲットも分散するからです。これはClouflareのように対DDoSを提供しているサービスにとっては都合がよい、とも言えます。ただし、企業ネットワークの場合攻撃が等しくすべてのデータセンターに分散されるため被害が広範囲に及びやすい、とも言えます。
最後に
勉強しながら書いたので間違いなどがあれば教えて下さい!別の視点で入れるべき話もあれば是非教えてください。間違ったままの方が恥ずかしいですし、鉞大歓迎です。
Discussion
いくつか気になった点指摘させてください…!長々とすみません 🙇
以下のスライドの12ページを見る限りでは、インターネット上のCloudflareではないAS間の経路はCloudflare側で制御することは難しいため、できるだけCloudflareのコントロール下にあるネットワークを通るようにして、内部(not インターネット)で経路を最適化するサービスなのかな?と思いました。(コールドポテトルーティング的な話に近いかもしれないです)
図を見る限りここで言うルータはクライアント側の一般家庭等を想定しているのかな?と思ったのですが、その場合はBGPやOSPFなどを使うことはなくデフォルトルートのみを持っていることが多いと思います。(シングルホームの場合は転送先が1つしかない)
上流のISPは複数のASと繋がっているので、AS境界のルータはフルルートを持っていることが多いと思います。逆にデフォルトルートは持っておらず、知らない転送先はパケットを捨てることが多いと思います。
BGPはOSPFのように他のルータの隣接関係を把握していないため、ダイクストラ法使えなさそうです。ダイクストラ法はノード数が増えると計算量が爆発してしまうので、大規模なネットワークではスケールしない問題もあります。
BGPの場合は原則の考え方としてはASパスが短くなるようなルーティングを行うことでOSPFより計算量を落としてスケールしやすくしているのかなと思います。
ルータはprefix単位で広報するため、ネットワーク内の特定の1ノードのダウンを外部のASに広報することはできないので、上記の仕組みを実現する場合はprefix単位で経路を落とさないといけなさそうです。
ここからは僕の想像ですが、Cloudflareのような大規模なグローバルネットワークを持っているASの場合は外部に広報する経路をノード障害の度に切り替えるよりも、AS内で別のエッジ等に転送して外部からみた経路の安定性を保つ方を取りそうだなと思いました。
コメントありがとうございます!
今のArgoってv2 になっていてケースバイケースで意図的に公衆網に通信を流すケースがあります。まずCloudflareのエッジ通しは積極的に公衆網を使用する状態になっており例えば
Client - EdgeA - EdgeB - SaaSへの通信であったとしてもEdgeA - EdgeBで遅延が予想されるのであれば
Client - EdgeA - SaaS の経路に通信を回すケースがあり、それを担っているのがArgo v2と理解しています。
両方をまとめてくっしゃっと書きました(笑)
実際家庭であればNetworkABそれぞれのルーターはISPのルーティングテーブルになります。
AS_PATHが優先されるのはご指摘の通りで書き直します!んが、OSPFで求められたIGPメトリックがMEDとしてセットされ経路判別に使われると思っていましたが違いますでしょうか?AS_PATHが優先されるのでほとんど意味がない?
例示のIPアドレスが隣り合っているから抽象化して書くにもわかりづらいですね。
ちょっと別のアドレス帯にすればもう少し意図が伝わりますでしょうか?
記事の順番とは前後しますが、まず「OSPFとダイクストラ法」の部分についてコメントしたいと思います。
OSPFとBGPの説明をされようとしているのだと思いますが、一般的にOSPFの最短パス計算のアルゴリズムがBGPで使われるケースはないかと思います。BGPで使われているアルゴリズムはいわゆるパスベクターと呼ばれるアルゴリズムで、最短パス計算のアルゴリズムとは異なります。近年BGP-LSというLink Stateを配布するBGPの拡張プロトコルがありますがOSPFとBGPの一般的な説明には直接関係ないのでここでは触れません。
OSPFもBGPもDynamic Routing Protocolと呼ばれるカテゴリーに属している経路制御プロトコルなので、「内部のStaticな環境はOSPF」という表現は誤解を招くとおもいます。どちらもDyanmicに経路が変動する環境のために作られたルーティングプロトコルです。OSPFはIGPと呼ばれる種類の経路制御プロトコルで、主に組織”内”の経路制御に使われ、BGPはEGPと呼ばれる種類のプロトコルで主に組織"間"の経路制御に使われます。近年BGPはデータセンター内でのユースケースや、コンテナやVMの管理ネットワークでのユースケースが出てきていますがOSPFとBGPの一般的な説明には直接関係ないのでここでは触れません。
ですので「共通で」というのがOSPFとBGPの両方を指しているのであれば、それは誤りです。
OSPFにおけるLinkダウンの説明をされるのであれば、「BGPで別ルーターから」以降の部分は省いたほうがよさそうです。若干混同されているようですが、OSPFとBGPは別のカテゴリーに属しているルーティングプロトコルで、上で触れたように使われているアルゴリズムも違います。また細かい話になりますがBGPで受け取った経路をOSPFに再配布する場合、OSPFへはExternal LSAという外部経路の形で注入され、最短パス計算のアルゴリズムの適用外になります。また、こうした組織"内"での経路の変動はBGPへ露出させないというのが、現在の一般的なネットワーク設計になっています。
経路切り替えの際にコントロールトラフィックの増大はありますが、今現在一般的に使われているネットワーク帯域で、それが原因で輻輳遅延が発生することは稀だと思います。また、一般にネットワーク機器のパケット処理はハードウェアで行われているので、仮にコントロールプレーンの処理速度が低下したとしても、それが原因で輻輳遅延が発生することは考えにくのではないでしょうか?Cloudflareのネットワークであればなおさらだと思います。
つぎに「ホットポテトルーティング」について、
上のコメントの方が指摘されているように、Cloudflareのルーティングはいわゆるコールドポテトルーティングですね、ホットポテトルーティングという用語は、インターネットサービスプロバイダーというものが生まれた時にできた言葉で、パケットは熱いイモと一緒で、もらったらできるだけ早く誰か他の人に渡してしまえという経路制御の考え方を示したものです。これはインターネットサービスプロバイダーというものが、そもそも自ネットワーク宛のパケットは基本的になく、いわゆるトランジット(経由)サービスが本質であることから生まれた概念です。
Cloudflareの場合、キャッシュにしてもオリジンにしてもトラフィックの宛先が自ネットワークであるため、その構造を生かして、信頼性が高く高速なルーティングが出来ているというのがメリットでしょうから、そのことを指すのであればコールドポテトルーティングと呼ぶべきかと思います。
そのため、できるだけ早くホットポテトルーティングのネットワークから、Cloudflareのコールドポテトルーティングのネットワークに引き込む必要性があるわけです。
実は、Cloudflareのネットワークで一番知りたいのは、具体的にどうやってそれを実現しているかだったりします。Coudflareの内部ネットワークのアーキテクチャは、障害時のレポートからもわかるように、まぁ言い方はあれですが、よくあるClosのように見えます。ネットワークエンジニアとしては、むしろ外から来るトラフィックをいかに、より近くのCloudflareのエッジに到達させているのかに興味があります。Anycastを使っているのは分かりましたが、それだけなのでしょうか?Souce IPからGeoLocationに変換して、トラフィックの傾向を保持しているデータベースと付き合わせて、BGPのAttributeやDNSのReponseに反映したりしないのでしょうかね?またCloudflareは320の拠点でと良く言いますが、その後ろはある程度の階層化がされてるように思えます。障害時には19のデータセンタで影響がとも言われましたが、おそらく内部ネットワークトラッフィックの最適化もいろいろやっているのではないかと推測されます。
せっかくの機会なので、そのあたりのことも何か公表できる部分があれば是非おしえていただきたいです。
ありがとうございます!諸々、特にOSPF/BGP周りは修正しました。
いかがでしょうか。
ホットポテトのあたりは一般的な話として書いていますでCloudflare関係ない部分になり、そのまま残しておきます。
この辺りはあまり情報がないので公式Blogとかで出たらまたお伝え出来るかな?といったところですが、過去聞いたのは「思っているほど内部通信は専用ネットワークを通っていない」でした。
またおっしゃる通りいろんなブログやドキュメントで階層化されていることは示唆されていますがそのあたりの中身は公開されないです。あくまでユーザーからすると500 over のPOPが等しくAnycastで動作しているということです。
とはいえ複数のアカウントで同じサービスを起動すると同じエッジでも異なるIPv6が払い出されることは観測できるので中身はもう少しいろいろやっていることは推察できますがそのあたりは私も分かりません。
図が1個ずれていましたね。失礼しました。修正しました。
コメントは最後が個人的見解の部分が大きいので非表示にしました。