Kong Hybrid 展開と DB レス展開のための新しいストレージ エンジン

迅速なアプリケーションの近代化

Kong は、お客様がさまざまな環境、展開モードで Kong を展開する必要があることを理解しています。そのため、2 年前にリリースした Kong 1.1 では、データベースへの接続を必要とせずに Kong を実行できる DB レス モードを導入しました。その後、2019 年の Kong Summit で初めて Kong の Hybrid モード展開のデモを実施し、DB レスを基盤として Control Plane と Data Plane の分離をサポートする Hybrid モードを Kong 2.0 で正式にリリースしました。

それ以来、さまざまな本番環境で Kong の DB レス展開の採用が広がっています。Kong の Kubernetes Ingress Controller (KIC) と Konnect プラットフォームも、一元化された管理と展開エクスペリエンスを提供するため、Hybrid/DB レス モードに大きく依存しています。

そのため、引き続き DB レス モードへ投資し、お客様により優れたエクスペリエンスを提供することは理にかなっています。Kong は最近、人気の高い LMDB (Lightning Memory-Mapped Database) 組み込みストレージ エンジンをベースに、DB レス用の新しいストレージ エンジンを開発しました。このブログ記事では、この変更の「理由」、「実装方法」、「今後の展開」についてお話します。

理由

なぜ DB レス用のストレージ エンジンを再実装するのか疑問に思われる方もいらっしゃるでしょう。

簡単に言うと、DB レスを使用するユーザーの増加に伴い、大規模なデプロイメント向けに既存の DB レス ストレージ エンジンをスケーリングすることが難しくなっています。現在のストレージ エンジンは非常に複雑で、設定を保存するため 2 倍のメモリを消費し、ワーカーの数が増えるにつれてスケーラビリティが低下する傾向にあります。

今日の DB レスの仕組み

概念的には、Kong の DB レス モードは非常にシンプルです。データベース (Postgres など) に Kong の設定を保存し、プロキシ時にそこから読み込むのではなく、DB レスでは .yaml や .json ファイルから Kong の完全な設定を読み取り、Nginx/OpenResty によって事前に確保された共有辞書機能 (shdict) 内にすべての設定内容を保存します。その後、すべてのワーカーは、事前にロードされた設定データを shdict から直接読み取り、あたかもデータベース バックエンドであるかのように扱います。

DB レスの欠点

shdict ベースの DB レス実装には、次のような問題点があります。

  1. shdict からの読み取りはグローバルなブロッキング イベントです。つまり、shdict ゾーンから一度に読み込める Nginx ワーカーは 1 つだけです。これは、Kong ワーカーの数が増えるにつれて、理想的なスケーラビリティではなくなります。
  2. shdict は、トランザクションをネイティブにサポートしていません。つまり、データベースの内容をアトミックに切り替えるため、アクティブとスタンバイの 2 つの shdict ゾーンを維持する必要があります。これは、設定の再読み取りを複雑にするだけでなく、もう一方のゾーンがほとんど使用されていないにもかかわらず、Kong が常に実際の 2 倍の設定ストレージを必要とすることを意味します。
  3. アトミック トランザクションがないため、メモリ ゾーンの部分的な更新は安全ではありません。これは、Hybrid モードで使用する DB レス設定のインクリメンタルな更新に影響します。

実装方法

DB レスのスケーラビリティに対するニーズが高まる中、現在の shdict ベースのストレージ エンジンでは対応できないことは明らかです。そこで、以下の理由により、LMDB (Lightning Memory-Mapped Database) を DB レスの新しいストレージ エンジンとして採用しました。

  1. LMDB は完全に組み込み可能で非常に小さいため、出荷時の Nginx バイナリに静的にリンクでき、追加の依存関係を必要としません。実際、LMDB のコンパイル済みオブジェクト コードは通常 100KB 未満であり、Nginx バイナリにほとんど影響を与えません。
  2. LMDB はネイティブでマルチプロセスをサポートしており、同時に実行される読み取り/書き込みトランザクションを安全に処理することができます。これは、Nginx がマルチ ワーカー型のスケーラビリティを採用していることを考えると、非常に重要です。最も重要なことは、LMDB の読み取りは、同時書き込みトランザクションの場合でも決してブロックされないということです。これは、プロキシ モードの Kong のように、ほとんどが読み取りワークフローの場合に理想的です。
  3. LMDB は永続的なので、現在の JSON ベースの Hybrid モードの設定キャッシュを置き換えることができます。これは、Kong の再起動時間を短縮し、Kong Data Plane (DP) が Hybrid モード復元のため独自のキャッシュを維持する複雑さを軽減します。
  4. LMDB は ACID (原子性、一貫性、独立性、永続性) トランザクションを完全にサポートしています。これにより、将来的に設定のインクリメンタルな更新を構築できます。また、更新は単一のトランザクション内で実行でき、一時的なダーティ ページはトランザクションのコミット後に自動的に再利用されるため、設定保存用に同時に 2 つのメモリ領域を持つ必要性が完全になくなります。その結果、LMDB を用いた DB レスのメモリ使用量は、現在の shdict ベースのストレージよりもずっと少なくなります。

Nginx/OpenResty への LMDB の組み込み

OpenResty と LuaJIT で LMDB を利用する最も簡単な方法は、`liblmdb.so` を共有ライブラリとしてコンパイルし、LuaJIT FFI を用いて C API を直接呼び出すことです。しかし、これは以下の理由から最善策ではありません。

  1. Lua の実行は、LuaJIT VM のメモリ不足やデバッグ フックの発動など、さまざまな理由で中断される可能性があります。これらの例外が発生した場合、通常の実行は中断されます。LMDB C API を直接操作しているため、トランザクションが途中であった場合、適切に回復する方法がありません。その結果、リソース リークが発生したり、不完全な書き込みトランザクションの中断により、他の Nginx ワーカーがロックされます。
  2. LMDB には多くのカスタム構造体があり、そのフィールドはプラットフォームに依存しています。LuaJIT FFI を使用するには、Lua 内に関連するすべてのデータ構造を正確に複製する必要があり、異なるアーキテクチャでエラーや非互換性が発生する可能性があります。

LuaJIT FFI を使用して LMDB ライブラリ関数を直接呼び出すのではなく、Nginx C モジュール lua-resty-lmdb を実装しオープンソース化することで、LMDB で実行する必要のある操作を適切にカプセル化し、LuaJIT FFI で呼び出せるような安全な C API を公開しました。

lua-resty-lmdb は 2 つの部分から構成されています。1 つは Nginx の C モジュールで、LMDB リソースを管理し、LMDB データベース内のデータにアクセスする抽象化レイヤーとして動作します。このモジュールはまた、LuaJIT の FFI 機能を通じて、Lua ライブラリが呼び出す安全な C API を公開します。

lua-resty-lmdb のもう 1 つの部分は Lua ライブラリであり、Lua コードが呼び出す実際の API インターフェイスを提供します。Lua-resty-lmdb は、LMDB ライブラリと安全にやりとりするため、C モジュール lua-resty-lmdb が提供する C 言語の API を使用します。

これにより、LMDB が提供する C リソースを Lua コードで直接管理することで生じる問題のほとんどを回避できます。また、lua-resty-lmdb モジュールが公開する C 言語の API の ABI は比較的安定しているため、良好なクロスプラットフォームの互換性を確保できます。

Kong では、他の OpenResty ユーザーが安全かつ効率良くランタイム データを保持するのに有用であると考え、lua-resty-lmdb プロジェクト全体をオープンソース化することを決定しました。ソースコードや API ドキュメントは https://github.com/Kong/lua-resty-lmdb にあります。

パフォーマンス変更のベンチマーク

Kong では、パフォーマンスに基づく最適化はすべて、テストにより測定可能なパフォーマンス向上の裏付けを必要とします。LMDB により Kong プロキシのパフォーマンスが向上したことを証明するため、Kong では Kong プロキシ サーバーに合成負荷を発生させるテスト環境を構築しました。

テストのセットアップ

このベンチマークでは、Equinix Metal 上に 2 台の c3.medium.x86 物理サーバーを用意しました。これらのサーバーは、いずれも 48 スレッドの AMD EPYC 7402P 24 コア プロセッサーを 1 基搭載しています。したがって、デフォルトで Kong は 48 ワーカーを起動し、shdict mutex の競合が発生する可能性が高くなります。

DB レス構成では、10000 Routes と10000 Consumers を含む JSON ファイルを生成し、コンシューマーごとにキー認証情報を割り当てました。そして、10000 のルートすべてで、リクエスト終了プラグインとキー認証プラグインを有効にしました。その結果、JSON 設定ファイルのサイズはおよそ 5MB になり、合計でおよそ 50000 のオブジェクトが含まれています。

負荷の生成には、各ベンチマーク リクエストに使用される Route と Consumers をランダムにする Lua スクリプトと `wrk` ベンチマーク ツールを使用しました。ベンチマーク ツールの wrk は、測定結果への影響を最小限に抑えるため、Kong プロキシとは別の c3.medium.x86 インスタンスで実行し、ネットワーク I/O がボトルネックにならないように、20Gbps の LAN リンクで Kong プロキシと接続されています。

テスト ケース 1: /config エンドポイントのレイテンシ

最初のテストは、JSON ファイルを DB レスの `/config` エンドポイントに POST する単純なテストです。`X-Kong-Admin-Latency` レスポンス ヘッダーから、DB レス設定の読み取りが終了するまでの時間が分かります。

分析: LMDB は完全なトランザクションであるため、ワーカー間の複雑なロックは不要です。つまり、Admin API はすぐに戻り、競合の可能性を排除します。そのため、インクリメンタル サポートがなくても、より頻繁に設定の再読み込みを行えるようになり、Admin API のレスポンス遅延は全体で 34% ~ 44% 減少しました。

テスト ケース 2: ルーターとプラグインのイテレーターの再構築時間

2 つ目のテスト ケースは、設定をプッシュした後 Router と Plugins Iterator を再構築する際の実際の経過時間を測定します。Kong の Router と Plugins Iterator の設計の性質上、これらの操作は CPU 負荷が高く、各ワーカーは同時に利用可能なすべての Routes と Plugins オブジェクトを列挙する必要があります。ワーカーが Router と Plugins Iterator を再構築している間、そのワーカーに対するプロキシ要求が一時的に停止され、RPS が低下し、レイテンシが増加する可能性があります。

このテストでは、各ワーカーから Router と Plugins Iterator の再構築にかかった実際の経過時間を収集しました。以下はそのグラフです。

分析: グラフに示すように、LMDB ストレージ エンジンを使用することで、ワーカーが Router と Plugins Iterator の再構築に要する時間が 50% ~ 70% 削減されました。これは、特に設定が多く、トラフィック ボリュームが大きい環境において、Kong プロキシ パスのロング テール レイテンシを改善します。

テスト ケース 3: バックグラウンドでの設定更新を伴う RPS (Request Per Second: 1 秒あたりのリクエスト数) テスト

最後のテストは、設定の再読み込みによる Kong のプロキシ RPS への影響を測定します。このテストを行うため、`wrk` を使用して 20 秒間合成負荷を発生させ、負荷が発生している間に、上記の JSON 設定を `/config` エンドポイントにポストしました。

分析: LMDB のノンブロッキング読み取りが、設定のプッシュによるプロキシ RPS への影響を低減することは明らかです。同じ負荷と構成サイズで LMDB を使用した場合、全体で 25% ~ 32% の RPS 増加が確認されました。

LMDB による DB レスを試す

Kong では、最新の Kong Gateway 2.8 安定版リリースをベースにした特別なテクニカル プレビュー ビルドをリリースしています。これをテストするには、以下のコマンドで Kong と LMDB をサポートした Docker コンテナーをスピン アップしてください。

docker pull kong/kong:lmdb-preview02
docker run -d --name kong-lmdb \
    -e "KONG_DATABASE=off" \
    -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
    -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
    -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
    -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
    -e "KONG_ADMIN_LISTEN=0.0.0.0:8001, 0.0.0.0:8444 ssl" \
    -p 8000:8000 \
    -p 8443:8443 \
    -p 8001:8001 \
    -p 8444:8444 \
    kong/kong:lmdb-preview02

ARM64 または Apple Silicon ユーザーは、上記のコマンドラインの kong/kong:lmdb-preview02kong/kong:lmdb-preview02-arm64 に変更してください。

その後、新しく起動した Kong インスタンスを、DB レス モードの通常の Kong インスタンスと同じように使用できます。

http :8001/config config=@kong.yml

上記のコンテナーを Hybrid モードの DP として使用することも可能で、その場合は通常と全く同じ手順で行います。実際、LMDB テクニカル プレビュー ビルドは、CP に LMDB 機能が含まれていない場合でも、既存の Hybrid モード展開の DP として動作するはずです。

今後の展開

LMDB は、DB レスと Hybrid モードの新機能と Hybrid モードの改良のベースになる新しい基盤です。Kong がより少ないメモリ使用量で高速に動作し、非常に多くの設定を容易に扱えるようにするだけでなく、LMDB のトランザクション サポートを活用して、将来的には Hybrid モード用のインクリメンタル同期など、より高度な機能の提供が可能になる予定です。これは、Hybrid モードを推奨展開方法にするという Kong のビジョンを支える基盤です。

Kong では、引き続き LMDB 用の実装を改善し、近い将来にリリースする予定です。LMDB による DB レス/Hybrid モードの使用に関して、皆様のご意見や経験談などを Kong Nation で共有してください。テクニカル プレビューで問題が発生した場合は、GitHub リポジトリで Issue をオープンしてください。

LMDB の技術的な側面をより深く掘り下げたブログ記事を近日中に公開し、LMDB、shdict、DB レスの設計についてより詳しく説明する予定です。また、この変更によるパフォーマンスへの影響について、より技術的な分析を行う予定です。どうぞご期待ください。


Kong Enterprise の概要、価格、およびライセンス体系などの詳細は、こちらを参照してください。


記事参照: 2022 年 3 月 9 日
Datong Sun
© Kong Inc. 2022
New Storage Engine for Kong Hybrid and DB-less Deployments

タイトルとURLをコピーしました