Facebookはレイヤ4ロードバランサをIPVS(LVS)からXDPベースのものに乗り換えつつある
4月に開催されたnetdev 2.1で面白いセッションがあったのでメモ。
Facebookが使用しているレイヤ4のロードバランサに関する発表で、従来はIPVS (LVS) を使用していたが、XDPベースで自ら開発したものに移行しつつある、という内容。
XDP Production Usage: DDoS Protection and L4LB (slide)
XDP (eXpress Data Path) については以前のエントリで簡単に紹介した。
XDPを改めて簡単に紹介すると、Linuxカーネルのネットワークスタックの最下部 (NICに一番近い場所) でパケット処理を行う仕組みのことで、オーバーヘッドが非常に小さい高速パケット処理の実現を目的としている。XDPはeBPFを用いており、eBPFが提供するmapやhelper functionといった機能を用いて柔軟なプログラムが書けることに加え、JITによる高速化やカーネル自身の堅牢性をそのまま享受することができるとされている。
XDPがリリースされたのは2016年10月のカーネル4.8なので、半年程で実際の事例が出てきたことになる。
FacebookにおけるXDPのユースケース: L4LB & DDoS防御
FacebookはIPVS (LVS) ベースのレイヤ4ロードバランサ (L4LB, 通称Shiv) を利用していることが知られている。
Shivの役割はConsistent Hashによる負荷分散先の決定とIP encapsulation (IPIP) によるパケット転送である。また、ローカルにセッションキャッシュを持つことにより、Consistent Hashの弱点であるリマップ時のセッション切断問題をカバーしている。
今回の発表では、これらの機能をXDPの基盤上で実装したのちIPVSベースの既存環境を置き換えていることが明かされた。スライドでは下のグラフが示され、XDPで実装された新しいShivがIPVSベースのそれと比較して高いスループットを発揮しつつもCPU使用率が低く抑えられている、と述べられている*1。
(スライドp3より; それぞれ1本だけラインが離れているものがXDP, 固まっているものがIPVS)
また、DDoS防御のための仕組み (Dropletと呼ばれる) も併せて紹介されている。
DDoS防御にあたっては、攻撃パケットを可能な限り早い段階で検出・遮断することが望ましい。加えて、攻撃パケット検出のシグネチャはプログラマブルに構成・変更可能であることが望ましい。彼らはこれもXDP上に実装している。L4LBのプログラムの前にDDoS検出・遮断のプログラムが実行されるようにすることで、L4LBが余分なリソースを消費することを防いでいる。
(スライドp10より; #0 XDP Dumpはデバッグ用のもの)
ちなみに、XDPによるDDoS防御についてはCloudflareのエンジニアも同様の発表を行っている。こちらはよりBPFにフォーカスした内容。
XDP in practice: integrating XDP in our DDoS mitigation pipeline (slide)
実装の詳細
冒頭のビデオで発表部分が12分弱で収まっていることからも分かる通り、やっていること自体は非常にシンプルである。
ただその裏では、(発表内で直接は触れられていないものの) XDPやその基礎となるeBPFの機能がふんだんに使われていることが見て取れる。これらの機能はFacebookのエンジニア自身によって実装されたのちLinuxカーネルにマージされたものも少なくない。
BPF_MAP_TYPE_LRU_HASH: セッション維持のためのLRU cache
Consistent Hashを用いるL4LBではその特性上、分散先のリアルサーバが増減したタイミングで幾許かのTCPセッションが意図せず切断されてしまう。この弱点を補うため、いくつかのL4LB実装ではセッションテーブルを併用している。TCP SYNパケットがL4LBに到達すると同時にセッションテーブルにセッション情報を記録すれば、仮にConsistent Hashのリマップが発生したとしてもセッションテーブルに基づいてTCPセッションを維持できる、という仕組みである。ただセッションテーブルのサイズは有限であるため、何らかの方法でセッションテーブルのメンテナンスを行う必要がある。
そこで登場するのがeBPF map objectのひとつ、BPF_MAP_TYPE_LRU_HASH
である。その名前が示す通り、Least Recently Used (LRU) なハッシュテーブルである。これを利用すれば、先に述べた要件を満たすセッションテーブルを容易に実現できる。
この機能は冒頭で紹介したセッションのスピーカーであるMartin Lauによって開発され、kernel 4.10以降で利用可能である。
xdp_adjust_head(): IP encapsulation
ShivはL3DSRによるリアルサーバへのパケット転送にIPIP方式を採用している。これはリアルサーバ宛てIPヘッダで元のVIP宛てパケットをカプセル化することでパケット転送を実現するものである*2。
これをXDPで実現するため、メモリページ上であらかじめ空間 (headroom) を確保しておき、そこにデータ(ここではリアルサーバ宛てのIPヘッダ)を追加するインタフェースが用意されている。このインタフェースがeBPF helper functionのひとつ、bpf_xdp_adjust_head()
である。この機能も同じくMartin Lauによって開発され、kernel 4.10以降で利用可能である。
なお、NICドライバには通常のXDPサポートとは別にheadroom確保のサポートが要求される。
BPF_MAP_TYPE_PROG_ARRAY & tail call: 複数のeBPFプログラムを連携させる
XDPはその仕様上、1個のネットワークインタフェースに対して1個のeBPFプログラムのみアタッチすることができる。これは1個のネットワークインタフェースに対して複数の独立した処理を適用したい場合に都合が悪い。
これを解決するのがBPF_MAP_TYPE_PROG_ARRAY
と bpf_tail_call()
である。
上記エントリで紹介した通り、BPF_MAP_TYPE_PROG_ARRAY
とbpf_tail_call()
を組み合わせることで、複数のeBPFプログラムを連結して呼び出したり実行中のeBPFプログラムを動的に入れ替えたりできる。ちなみにこの機能を実装したAlexei StarovoitovもFacebookに在籍している*3。
余談: XDPの近況
2016年10月にXDPがリリースされてから約半年が経過したが、その間に様々なアップデートが行われた。
まず大きなトピックとしてNICドライバのサポート拡充が挙げられる。当初はMellanox mlx4
のみであったものが徐々に拡充され、先日遂にIntel ixgbe
サポートがnet-nextにマージされた。恐らくカーネル4.12でリリースされると思われ、XDPが利用可能な環境が大幅に広がることになる。また変わり種として、CaviumのARM SoCであるThunderXでのサポートもnet-nextにマージされている。
現時点では、下記のNICドライバで利用可能あるいは近日中に利用可能となる:
mlx4
: Mellanox ConnectX-3mlx5
: Mellanox ConnectX-4 or laternfp
: Netronome Agilioqede
: QLogic FastlinQvirtio_net
: Virtiobnxt_en
: Broadcom NetXtreme C-Seriesixgbe
: Intel X5xx (82598, 82599) (from kernel 4.12?)thunderx
: Cavium ThunderX (from kernel 4.12?)
また、NICドライバのサポートが無くてもXDPプログラムを実行できるようにする仕組み (“Generic XDP”) も登場した。
kernel/git/torvalds/linux.git - Linux kernel source tree - net: Generic XDP
これは主に開発用やお試し用を意図しており、パフォーマンスに関しては考慮されていない。これまでXDPプログラムを実行するには、対応NICが挿さったハードウェアを用意するか、あるいはe1000, virtio_netの環境を用意する必要があった。Generic XDPの登場により、より手軽に開発環境を整えられるようになる。
なお、各カーネルバージョン毎にサポートされるeBPF/XDP関連の機能やドライバの情報はiovisor/bccのドキュメントに纏まっている:
bcc/kernel-versions.md at master · iovisor/bcc · GitHub
XDPリリース直後の2016年12月にPLUMgrid*4がVMWareに買収される出来事があったものの、特に大きな影響も無くXDP周辺の発展は続いている様子である。eBPF自体を含め機能がどんどん拡充されており、かつXDPのユースケースも徐々に出始めてきたことで、今後の動向がますます楽しみ。