Service Mesh (ASM) は、カスタム処理ロジックを実装するために、Envoy プロキシ内への Wasm プラグインのデプロイをサポートしています。 Proxy-Wasm コミュニティは、Wasm 開発用の Rust SDK を提供しています。このトピックでは、ASM の Envoy プロキシ向け Wasm プラグインを Rust で記述する方法について説明します。
背景情報
Wasm は、ネイティブバイナリに近い実行効率を提供し、ランタイムサンドボックスを備えているため、セキュリティが強化されます。 現在、Wasm には、組み込みの ガベージコレクション を使用する言語でのパフォーマンスの問題など、いくつかの欠点があります。 そのため、C++ や Rust など、手動メモリ管理が可能な言語を使用することをお勧めします。 C++ と比較して、Rust は現時点ではコンパイルとビルドの段階でより便利ですが、学習曲線は少し高くなっています。 実際の状況に基づいて選択できます。
前提条件
クラスターが ASM インスタンスに追加され、ASM インスタンスのバージョンが 1.18 以降であること。 詳細については、「ASM インスタンスへのクラスターの追加」をご参照ください。
サイドカーインジェクションが有効になっていること。 詳細については、「サイドカーインジェクションポリシーの設定」をご参照ください。
イングレスゲートウェイが作成されていること。 詳細については、「イングレスゲートウェイの作成」をご参照ください。
HTTPBin アプリケーションがデプロイされ、アクセス可能であること。 詳細については、「HTTPBin アプリケーションのデプロイ」をご参照ください。
Container Registry Enterprise Edition インスタンスが作成されていること。 詳細については、「Container Registry Enterprise Edition インスタンスの作成」をご参照ください。
使用方法
このトピックでは、Rust で Wasm プラグインを記述する方法について説明します。 Wasm バイナリを生成した後、それをイメージにパッケージ化します。 イメージをビルドした後、イメージサービスの OCI イメージリポジトリにアップロードします。 アップロードが完了したら、ASM で Wasm プラグインリソースを設定して、指定された Envoy プロキシに適用します。
このプラグインは、リクエストヘッダー allow: true
がリクエストに存在するかどうかを判断するために使用されます。 存在しない場合は、403 ステータスコードと指定されたレスポンス本文が返されます。 存在する場合は、HTTPBin アプリケーションに正常にアクセスできます。
手順 1:開発環境を準備する
rustup をインストールします。 具体的な操作については、「Rust のインストール」をご参照ください。
次のコマンドを実行して、Wasm バイナリをコンパイルするために必要なツールチェーンをインストールします。
rustup target add wasm32-wasi
すでにインストールされている場合は、次のコマンドを実行して Rust を更新します。
rustup update
手順 2:プラグインを記述する
新しいプラグインディレクトリ
rust-example
を作成し、このディレクトリに切り替えて、次のコマンドを実行します。cargo init --lib
生成された Cargo.toml ファイルに次の内容を追加します。
[lib] // C/C++ から呼び出すことができる動的ライブラリであることを宣言します。 crate-type = ["cdylib"] [dependencies] log = "0.4.8" proxy-wasm = "0.2.2"
src/lib.rs
に次の内容を追加します。use log::info; use proxy_wasm::traits::*; use proxy_wasm::types::*; proxy_wasm::main! {{ proxy_wasm::set_log_level(LogLevel::Trace); proxy_wasm::set_root_context(|_| -> Box<dyn RootContext> { Box::new(HttpHeadersRoot) }); }} struct HttpHeadersRoot; // いくつかの基本的なユーティリティ関数。この行を追加すると、さまざまなユーティリティ関数を self で直接呼び出すことができます。 // 例:self.set_property(path, value) impl Context for HttpHeadersRoot {} impl RootContext for HttpHeadersRoot { fn get_type(&self) -> Option<ContextType> { Some(ContextType::HttpContext) } fn create_http_context(&self, context_id: u32) -> Option<Box<dyn HttpContext>> { Some(Box::new(HttpHeaders { context_id })) } } struct HttpHeaders { context_id: u32, } impl Context for HttpHeaders {} impl HttpContext for HttpHeaders { fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action { info!("#{} wasm-rust: on_http_request_headers", self.context_id); match self.get_http_request_header("allow") { Some(allow) if allow == "true" => { Action::Continue } _ => { info!("#{} wasm-rust: allow ヘッダーが見つからないか true ではないため、デフォルトで拒否します", self.context_id); self.send_http_response( 403, vec![("Content-Type", "text/plain")], Some(b"ASM Wasm プラグインによって禁止されています。rust バージョン\n"), ); Action::Pause } } } }
次のコマンドを実行して、プラグインをコンパイルします。
cargo build --target wasm32-wasi --release
コンパイルが成功すると、ターゲットファイルが生成され、最終的な WASM バイナリファイルは
target/wasm32-wasi/release/rust_example.wasm
になります。
手順 3:OCI イメージを作成し、Container Registry にプッシュする
次の内容で Dockerfile を作成します。
FROM scratch
// 生成された wasm バイナリファイルをイメージにコピーし、plugin.wasm に名前を変更します。
ADD target/wasm32-wasi/release/rust_example.wasm ./plugin.wasm
イメージのビルドとプッシュの手順については、「Wasm プラグインの OCI イメージを作成し、Container Registry Enterprise Edition インスタンスにプッシュする」をご参照ください。 イメージ名とタグを指定します。
手順 4:Wasm プラグインをゲートウェイに適用する
具体的な操作手順については、「イングレスゲートウェイに Wasm プラグインを適用する」をご参照ください。 Wasm プラグインで設定された url が正しいイメージアドレスを指定していることを確認します。
手順 5:プラグインが有効であることを確認する
データプレーンクラスターの kubeconfig を使用して、次のコマンドを実行し、ゲートウェイの Wasm コンポーネントのデバッグログを有効にします。
kubectl -n istio-system exec ${gateway pod name} -c istio-proxy -- curl -XPOST "localhost:15000/logging?wasm=debug"
次のコマンドを実行して、HTTPBin アプリケーションにアクセスします。
curl ${ASM gateway IP}/status/418
予期される出力:
ASM Wasm プラグインによって禁止されています。rust バージョン
ゲートウェイ Pod のログを確認します。 ログの例は次のとおりです。
2024-09-05T08:33:31.079869Z info envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1195 wasm log istio-system.header-authorization: #2 wasm-rust: on_http_request_headers thread=35 2024-09-05T08:33:31.079943Z info envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1195 wasm log istio-system.header-authorization: #2 wasm-rust: allow ヘッダーが見つからないか true ではないため、デフォルトで拒否します thread=35 {"authority_for":"xx.xx.xx.xx","bytes_received":"0","bytes_sent":"43","downstream_local_address":"xx.xx.xx.xx:80","downstream_remote_address":"xx.xx.xx.xx:xxxxx","duration":"0","istio_policy_status":"-","method":"GET","path":"/status/418","protocol":"HTTP/1.1","request_id":"d5250d1a-54b3-406d-8bea-5a51b617b579","requested_server_name":"-","response_code":"403","response_flags":"-","route_name":"httpbin","start_time":"2024-09-05T08:33:31.079Z","trace_id":"-","upstream_cluster":"outbound|8000||httpbin.default.svc.cluster.local","upstream_host":"-","upstream_local_address":"-","upstream_response_time":"-","upstream_service_time":"-","upstream_transport_failure_reason":"-","user_agent":"curl/8.9.0-DEV","x_forwarded_for":"xx.xx.xx.xx"}
リクエストヘッダー
allow: true
を追加し、ゲートウェイの httpbin アプリケーションに再度アクセスします。curl ${ASM gateway IP}/status/418 -H "allow: true"
予期される出力:
-=[ teapot ]=- _...._ .' _ _ `. | ."` ^ `". _, \_;`"---"`|// | ;/ \_ _/ `"""`
ご覧のとおり、リクエストヘッダー
allow: true
を追加するとアクセスが成功し、プラグインが有効になります。