DropboxがNginxからEnvoyへ移行した理由と方法
(dropbox.tech)数千万の同時接続、毎秒数百万リクエスト、テラ単位の帯域を使用しているDropboxが、Nginxと比べたEnvoyの利点をうまく説明した記事
従来 : nginx(オープンソース版) + python2 + Jinja2 + YAML
→ 1つだけ変わっても全体の再デプロイが必要
→ 動的な部分はLuaで開発
→ 複雑なロジックはGoベースのプロキシであるBandaidで処理
この10年近くうまく動いていたが、現在の環境にはあまり合っていない
→ 内部および外部(非公開)APIは徐々にRESTからgRPCへ移行中であり、プロキシのトランスコーディング機能が必要
→ Protocol Buffersが内部サービス定義の標準に
→ すべてのソフトウェアは言語に関係なくBazelでビルドおよびテスト
→ 主要インフラプロジェクトのオープンソースコミュニティに社員がかなり積極的に参加中
Nginxは運用面でも維持コストが高い
→ Config生成ロジックが柔軟すぎて、YAML、Jinja2、Pythonに分散している
→ 監視がLua / Log parsing / システムベース監視の混在
→ サードパーティモジュールへの依存が高まるにつれて、安定性/性能に影響し、頻繁なアップグレードによるコストが発生
→ nginx自体のデプロイおよびプロセス管理は他のサービスと大きく異なる。syslog、logrotateなど基本システムとはかなり異なるものに依存
そこで、10年ぶりに初めてNginxを置き換えるものを探すことにした
- なぜBandaid(Dropboxが独自開発したGoベースのプロキシ)ではないのか?
→ GoはC/C++よりリソースを多く消費する。
→ GoのTLSスタックはFIPS対応ではない(米国連邦情報処理標準)
→ 内部ツールのため外部コミュニティの支援を受けられない
現在 : Envoyベースのトラフィックインフラへ移行中
----- NginxよりEnvoyが優れていた点 ------
- 性能 *
Nginxのアーキテクチャはイベントドリブン / マルチプロセス。SO_REUSEPORT & EPOLLEXCLUSIVEをサポート
イベントループベースだが完全なノンブロッキングではない。ファイルオープン/ロギング時にはイベントループが停止する可能性がある。(aio、aio_writeおよびThreadpoolを有効化しても)
これによってテールレイテンシが発生し、数秒単位の遅延が生じることもある
Envoyも同様にイベントドリブンアーキテクチャだが、プロセスではなくスレッドベース
SO_REUSEPORTをサポート(BPFフィルタ対応)、libeventによるイベントループをサポート
イベントループでブロッキングI/Oはなく、イベントロギングもノンブロッキング方式で実装。
理論的には似たような性能特性を示しそうで、実際にほとんどのワークロードテストでも似ていた。
ただし、Nginxはロングテールでレイテンシがより大きかった。I/Oが多いときにイベントループが停止したため。
統計収集がなければNginxはEnvoyと近い性能を示すが、内部で使用中のLua統計収集ツールがhigh-RPSテストでNginxを3倍遅くしていた。(これはmutexで同期されるlua_shared_dictのため)。Dropboxの統計収集方式にも問題はあったが、これを効率的に再開発するのは断念した。(Nginx内部にインストルメンテーションすると将来のアップグレードが難しくなると見込んだため)
とにかく、このような問題がEnvoyにはなかったため、移行後は従来Nginxが使っていたサーバーの最大60%を削減できた。
- Observability *
無料版Nginxはstub statusモジュールで7種類のstatしか提供しない
これでは当然不足していたため、log_by_luaハンドラを付けてさらに多くのstatを提供していた。
また、error.logパーサーを通じてエラー情報を出力し、nginx内部状態値を出力するための別個のexporterも存在。
基本的なEnvoyセットアップはPrometheus形式で数千種類の異なるメトリクスを提供
プロキシトラフィック情報からサーバーの内部状態情報、
クラスタ別/アップストリーム別/仮想ホスト別の統計値や、リスナー別のTCP/HTTP/TLSダウンストリーム統計情報など
このような多様な統計に加えて、EnvoyはTracing Providerをプラグイン可能。
トラフィックチームだけでなく、アプリケーション開発者にも有用。
最後に、EnvoyはgRPCを通じてアクセスログをストリーミング可能。
これにより、トラフィックチームがsyslog-to-hiveブリッジを支える負担が軽減される。
カスタムTCP/UDPリスナーを付けるより、一般的なgRPCサービスを実行するほうがはるかに簡単で安全。
- Integration *
Nginxの統合は非常にUnix的。Configurationが非常に静的。
configファイルやTLS証明書、allowlist/blocklistなどをファイルに依存。
単純で後方互換性があるため、いくつかのシェルスクリプトで自動化は可能だが、
システムが大きくなるにつれて、テスト可能性と標準化がますます重要になる。
Envoyはこのような統合に対して独自の方法を持っている。
xDSと呼ばれるAPIを提供し、protobufとgRPCの利用を推奨。
EnvoyはこのxDSにクエリを送って動的リソースを見つける。
- このxDSは今やEnvoyを超えて、Universal Data Place API(UDPA)という名前でL4/L7ロードバランサーのde facto標準になろうと進化しており、私たちの経験ではこれがうまく進んでいる。EnvoyだけでなくKatran eBPF/XDP L4ロードバランサーでもUDPAを使おうとしている。
Dropboxは内部でgRPCを通じてサービスが連携しているため、はるかに都合がよい。
- Configuration *
Nginxは人間が読みやすい設定ファイルという大きな利点を持っている。
しかしこの利点は、設定が複雑化し自動生成されるにつれて失われていった。
DropboxではPython2、Jinja2、YAMLなどを通じて生成されていたため、データモデルも絡み合って複雑だった。
Envoyは設定に対する統一されたデータモデルを持っている。すべての設定値はProtocol Bufferで定義される。データモデリングの問題が解消され、設定値に型情報が追加される。
Dropbox内部ではprotobufが多く使われているため、統合も容易だった
- Extensibility *
Nginxの拡張にはCモジュールの作成が必要。安全なモジュールを書くにはシニア開発者が必要。より軽量なモジュール開発のためにPerl / JSインターフェースも提供しているが非常に限定的。そのため最も一般的に使われる方法はlua-nginx-moduleを通すこと。
Envoyの主な拡張メカニズムはC++プラグインだが、ドキュメントはnginxほど充実していないものの非常に単純。これは整理されコメントの行き届いたインターフェース、C++14言語と標準ライブラリのおかげ
Envoyが他のWebサーバーと大きく異なる点は、WebAssembly(WASM)をサポートしていること。
これによりRustのようなさまざまな言語で拡張開発できる。
まだDropboxではWASMを使っていないが、いつかproxy-wasm向けGo SDKがサポートされれば変わるかもしれない
- Building and Testing *
Nginxは基本的にカスタムシェルベースの設定とmakeベースのビルドを使用。単純で優れているが、これをBazelでビルドされるモノレポに統合するにはかなりの労力がかかる
NginxにはPerlベースのintegration testはあるが、unit testはない。
EnvoyはすでにビルドシステムがBazelベースで、簡単に私たちのモノレポへ統合できた。
gtest/gmockベースのunit testとintegration test frameworkをサポート
- Security *
Nginxのコードは非常に小さく、外部依存も少ないため、セキュリティ上の脆弱性は多くない。
Envoyはコード量が多いため、そのぶん攻撃面も多く見える。そのためEnvoyは現代的なセキュリティプラクティスに大きく依存している。AddressSanitizer、ThreadSanitizer、MemorySanitizerなどを使用。
- Features *
この部分は主観的な意見が多いので参考までに
Nginxは当初、ごく少ないリソースで静的ファイルを配信するWebサーバーとして始まった。
つまりstatic serving、caching、range cachingが主機能
プロキシの観点では、Nginxには現代のインフラで求められる機能が多く不足している。
バックエンドとのHTTP/2接続もできず、マルチプレックスされるgRPCプロキシにも対応せず、gRPCトランスコーディングも不可など。
オープンコア型のライセンスモデルのため、いくつかの主要機能は「コミュニティ版」には含まれていない
Envoyはそもそもingress/egress proxyとして始まり、gRPC負荷の高い環境で多く使われている。
Webサービス機能は非常に初歩的なレベル。ファイル配信もできず、キャッシュもまだ開発中で、brotliも未対応など
このような環境向けには、Envoyをアップストリームクラスタとして使うNginxセットアップも使用中
EnvoyがHTTPキャッシュに対応すれば、このような静的配信環境も移行できると見込んでいる
EnvoyはgRPC関連機能を多くサポート
-
gRPC proxying
-
HTTP/2 to backends
-
gRPC → HTTP bridge (+ reverse.)
-
gRPC-WEB
-
gRPC JSON transcoder
またEnvoyはoutbound proxyとしても利用可能
-
Egress Proxy
-
Courier gRPCライブラリによるサードパーティソフトウェアのサービスディスカバリ
- Community *
Nginxの開発は中央集権的で、その大半が見えない。
Envoyの開発はオープンで分散的。GitHubのissue/PRで進み、メーリングリストやSlackなどでも活発
----- Dropboxの現在のMigration状況 -----
NginxとEnvoyを半年前から並行運用しながら、DNSを通じて段階的にトラフィックを移行中
何の問題もなく移行できたわけではなく、小さな問題はあったが深刻な障害はなかった。
"unusual" または "non-RFC" 的な挙動によって経験した問題に対する解決策も整理している(本文に詳細があるので参照)
** 今後やること **
-
HTTP/3 : Envoyも実験的サポートを開始。UDP高速化のためにLinuxカーネルをアップグレードしたら試してみる予定
-
内部xDSベースのロードバランサーとOutlier Detection
-
WASMベースのEnvoy拡張
-
Bandaid(Goベースのプロキシ)をEnvoyに置き換え
-
Envoy MobileでモバイルアプリにもEnvoyを適用
3件のコメント
良い内容を簡潔に
まとめてくださって
ありがとうございます。
ありがとうございます。:)
詳細な整理と親切なご意見まで添えてくださり、理解にとても役立ちました。ありがとうございます :)