WebAssembly for Proxies は、開発者が WebAssembly(Wasm)を使用してポータブルプラグインを作成できるようにする新しい仕様です。これらのプラグインは、さまざまなプロキシサーバーで実行できます。Service Mesh(ASM)は、WebAssembly for Proxies 仕様をサポートしています。このトピックでは、ASM の Envoy プロキシ用の Go で Wasm プラグインを作成する方法について説明します。
前提条件
バージョン 1.18 以降の ASM インスタンスにクラスターが追加されています。ASM インスタンスへのクラスターの追加方法の詳細については、「ASM インスタンスへのクラスターの追加」をご参照ください。
自動サイドカープロキシインジェクションが有効になっています。詳細については、「サイドカープロキシインジェクションポリシーの設定」をご参照ください。
イングレスゲートウェイがデプロイされています。詳細については、「イングレスゲートウェイの作成」をご参照ください。
HTTPBin アプリケーションがデプロイされており、アクセスできます。HTTPBin アプリケーションのデプロイ方法の詳細については、「HTTPBin アプリケーションのデプロイ」をご参照ください。
Container Registry Enterprise Edition インスタンスが作成されています。Container Registry Enterprise Edition インスタンスは、Open Container Initiative(OCI)イメージをサポートしています。詳細については、「Container Registry Enterprise Edition インスタンスの作成」をご参照ください。
背景情報
Wasm は、実行可能コード用の有望でポータブルなバイナリ形式です。コードは、メモリセーフ(ホストの場合)サンドボックス内でほぼネイティブの速度で実行されます。サンドボックスには明確に定義されたリソース制約があり、埋め込みホスト環境(ここではプロキシを指します)と通信するための明確に定義された API を提供します。
Wasm プラグインには、次の利点があります。
アジリティ:Envoy プロキシを再起動せずにプラグインを更新できます。したがって、リクエストは期待どおりに処理できます。
信頼性と分離:プラグインはリソース制約のあるサンドボックス内にデプロイされるため、Envoy プロキシをダウンさせずにクラッシュする可能性があります。
セキュリティ:プラグインは、プロキシと通信するための明確に定義された API を備えたサンドボックス内にデプロイされるため、適切に制御されます。
多様性:プラグインは、C++、Go、Rust など、複数のプログラミング言語で記述できます。
Wasm プラグインの詳細については、WebAssembly-in-Envoy.md および OVERVIEW.md をご参照ください。
設定例
この例では、Wasm プラグインは Go で記述されています。プラグインが記述された後、Wasm バイナリファイルが生成され、イメージにパッケージ化されます。イメージは、OCI イメージリポジトリにアップロードする必要があります。イメージがアップロードされたら、ASM で WasmPlugin リソースを設定し、指定された Envoy プロキシにプラグインを適用します。
この例では、リクエストに allow: true ヘッダーが含まれているかどうかを確認するプラグインが開発されています。含まれていない場合は、ステータスコード 403 と指定された本文が返されます。含まれている場合は、HTTPBin アプリケーションに期待どおりにアクセスできます。
ステップ 1:開発環境を準備する
Envoy プロキシ用の Go で Wasm プラグインを開発するには、最初に次のツールをインストールする必要があります。
Go:Go コンパイラと関連ツールは、Go プロジェクトを作成するために使用されます。詳細については、The Go Programming Language をご参照ください。
Docker:この例では、Docker を使用して OCI イメージをビルドおよびプッシュします。
TinyGo:Go は Wasm プラグインを作成するために使用されます。ただし、公式の Go コンパイラを使用して Go コードを Wasm 形式にコンパイルすることはできません。TinyGo を使用する必要があります。TinyGo のインストール方法の詳細については、クイックインストールガイド をご参照ください。
Wasm プラグインが依存する SDK の詳細については、GitHub Web サイトの proxy-wasm-go-sdk をご参照ください。Web サイトで Proxy-Wasm 用 Go SDK の完全なコードを見つけることができます。他の SDK を使用する場合は、Proxy-Wasm 用 Go SDK のコードを参照してください。
ステップ 2:プラグインコードを作成する
フォルダーを作成し、次の内容を含む main.go ファイルを作成します。
作成したフォルダーで次のコマンドを実行して、SDK の依存関係を取得します。
go mod init go mod tidy次のコマンドを実行して、コードを Wasm バイナリファイルにコンパイルします。
tinygo build -o plugin.wasm -scheduler=none -target=wasi main.goplugin.wasm ファイルが生成されます。このファイルは、Wasm バイナリ実行可能ファイルです。
ステップ 3:Wasm プラグインの OCI イメージを作成し、Container Registry Enterprise Edition インスタンスにプッシュする
ステップ 2 で作成したフォルダーに、次の内容を含む Dockerfile ファイルを作成します。
FROM scratch ADD ./plugin.wasm ./plugin.wasm次のコマンドを実行して、イメージを作成します。
docker build -t header-authorization:v0.0.1 .イメージリポジトリを作成します。詳細については、「Coraza Wasm プラグインを使用して ASM ゲートウェイに WAF 機能を実装する」トピックのステップ 1 のサブステップ 2.a および 2.b をご参照ください。
この例では、名前空間は
test-ociで、リポジトリ名はheader-authorizationです。次の図は、作成されたリポジトリを示しています。
Container Registry Enterprise Edition インスタンスにイメージをプッシュする方法の詳細については、前の図の レジストリにイメージをプッシュする をご参照ください。
ステップ 4:イングレスゲートウェイに Wasm プラグインを適用する
イメージをプルするための権限を設定します。詳細については、「ステップ 2:イメージをプルするための権限を設定する」をご参照ください。
次のコマンドを実行して、
wasm-secretという名前のシークレットを作成します。kubectl create secret docker-registry -n istio-system wasm-secret --docker-server=${Container Registry Enterprise Edition インスタンスのドメイン名} --docker-username =${ユーザー名} --docker-password =${パスワード}次の内容を含む asm-plugin.yaml ファイルを作成します。
apiVersion: extensions.istio.io/v1alpha1 kind: WasmPlugin metadata: name: header-authorization namespace: istio-system spec: imagePullPolicy: IfNotPresent imagePullSecret: wasm-secret selector: matchLabels: istio: ingressgateway url: oci://${Container Registry Enterprise Edition インスタンスのドメイン名}/test-oci/header-authorization:v0.0.1 phase: AUTHNkubeconfig ファイルの情報に基づいて kubectl を使用して ASM インスタンスに接続します。次に、次のコマンドを実行して、Wasm プラグインを ASM インスタンスに適用します。
kubectl apply -f wasm-plugin.yaml
ステップ 5:Wasm プラグインが有効になっていることを確認する
イングレスゲートウェイが存在するデータプレーン上のクラスターの kubeconfig ファイルを使用して、次のコマンドを実行し、イングレスゲートウェイに適用されている Wasm プラグインのデバッグログ機能を有効にします。
kubectl -n istio-system exec ${イングレスゲートウェイが存在するポッドの名前} -c istio-proxy -- curl -XPOST "localhost:15000/logging?wasm=debug"次のコマンドを実行して、イングレスゲートウェイを介して公開されている HTTPBin アプリケーションにアクセスします。
curl ${イングレスゲートウェイの IP アドレス}/status/418予期される出力:
Forbidden by ASM Wasm Pluginイングレスゲートウェイが存在するポッドのログを表示します。
ログの例:
2024-03-08T08:16:46.747394Z debug envoy wasm external/envoy/source/extensions/common/wasm/context.cc:1168 wasm log istio-system.header-authorization: request header: 'allow' is , only true can passthrough thread=24 {"bytes_received":"0","bytes_sent":"28","downstream_local_address":"xxxxxxx","downstream_remote_address":"xxxxxxxx","duration":"0","istio_policy_status":"-","method":"GET","path":"/status/418","protocol":"HTTP/1.1","request_id":"780c8493-13e4-4f97-9771-486efe30347c","requested_server_name":"-","response_code":"403","response_flags":"-","route_name":"httpbin","start_time":"2024-03-08T08:16:46.747Z","trace_id":"-","upstream_cluster":"outbound|8000||httpbin.default.svc.cluster.local","upstream_host":"-","upstream_local_address":"-","upstream_service_time":"-","upstream_response_time":"-","upstream_transport_failure_reason":"-","user_agent":"curl/8.4.0","x_forwarded_for":"xxxxxx","authority_for":"xxxxxx"}次のコマンドを実行して、イングレスゲートウェイを介して公開されている HTTPBin アプリケーションにアクセスします。
curl ${イングレスゲートウェイの IP アドレス}/status/418 -H "allow: true"予期される出力:
-=[ teapot ]=- _...._ .' _ _ `. | ."` ^ `". _, \_;`"---"`|// | ;/ \_ _/ `"""`出力は、HTTPBin アプリケーションに期待どおりにアクセスできることを示しています。
TinyGo のメモリリーク
TinyGo を使用してコンパイルされた Envoy プロキシ用 Wasm プラグインにはメモリリークが存在します。proxy-wasm-go-sdk コミュニティは、コンパイルの最適化に nottinygc を使用することを推奨しています。nottinygc を使用してコンパイルを最適化するには、次の手順を実行します。
main.go ファイルの先頭に次の
importコードを追加します。import _ "github.com/wasilibs/nottinygc"依存関係が見つからない場合は、
go mod tidyコマンドを実行して、依存関係を自動的にダウンロードできます。次のコマンドを実行して、コードをコンパイルします。
tinygo build -o plugin.wasm -gc=custom -tags='custommalloc nottinygc_envoy' -target=wasi -scheduler=none main.go前のコマンドは、
-gcパラメーターと-tagsパラメーターを設定します。詳細については、nottinygc をご参照ください。