すべてのプロダクト
Search
ドキュメントセンター

Alibaba Cloud Service Mesh:Go を使用した Envoy プロキシ用 Wasm プラグインの作成

最終更新日:Jun 22, 2026

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) イメージをサポートしています。詳細については、「Enterprise Edition インスタンスの作成」をご参照ください。

背景情報

Wasm は、新進気鋭のポータブルな実行可能コードのバイナリ形式です。コードは、(ホストにとって) メモリセーフなサンドボックス内で、ネイティブに近い速度で実行されます。サンドボックスには明確に定義されたリソース制約があり、埋め込みホスト環境 (ここではプロキシを指します) と通信するための明確に定義された API を提供します。

Wasm プラグインには、次の利点があります。

  • 俊敏性:Envoy プロキシを再起動することなくプラグインを更新できます。そのため、リクエストは期待どおりに処理されます。

  • 信頼性と隔離:プラグインはリソース制約のあるサンドボックス内にデプロイされるため、クラッシュしても Envoy プロキシを停止させることはありません。

  • セキュリティ:プラグインは、プロキシと通信するための明確に定義された API を持つサンドボックス内にデプロイされるため、適切に制御されます。

  • 多様性:プラグインは、C++、Go、Rust などの複数のプログラミング言語で記述できます。

Wasm プラグインの詳細については、WebAssembly-in-Envoy.md、および OVERVIEW.md をご参照ください。

設定例

この例では、Go で Wasm プラグインを作成します。プラグインを作成した後、Wasm バイナリファイルを生成し、イメージにパッケージ化します。イメージは OCI イメージリポジトリにアップロードする必要があります。イメージをアップロードした後、ASM で WasmPlugin リソースを設定し、指定された Envoy プロキシにプラグインを適用します。

この例では、リクエストに allow: true ヘッダーが含まれているかどうかをチェックするプラグインを開発します。含まれていない場合、ステータスコード 403 と指定された本文が返されます。含まれている場合、HTTPBin アプリケーションに正常にアクセスできます。

ステップ 1:開発環境の準備

Envoy プロキシ用に Go で Wasm プラグインを開発するには、まず次のツールをインストールする必要があります。

  • Go:Go コンパイラと関連ツールは、Go プロジェクトを作成するために使用されます。詳細については、「The Go Programming Language」をご参照ください。

  • Docker:この例では、Docker を使用して OCI イメージをビルドし、プッシュします。

  • TinyGo:Wasm プラグインの作成には Go を使用します。ただし、公式の Go コンパイラを使用して Go コードを Wasm 形式にコンパイルすることはできません。TinyGo を使用する必要があります。TinyGo のインストール方法の詳細については、「Quick install guide」をご参照ください。

Wasm プラグインが依存する SDK の詳細については、GitHub の Web サイトにある「proxy-wasm-go-sdk」をご参照ください。この Web サイトでは、Proxy-Wasm 用の Go SDK の完全なコードを見つけることができます。他の SDK を使用したい場合は、Proxy-Wasm 用の Go SDK のコードをご参照ください。

ステップ 2:プラグインコードの作成

  1. フォルダを作成し、次の内容を含む main.go ファイルを作成します。

    main.go ファイルの表示

    package main
    import (
    	"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
    	"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
    )
    func main() {
    	proxywasm.SetVMContext(&vmContext{})
    }
    type vmContext struct {
    	// Embed the default VM context here,
    	// so that we don't need to reimplement all the methods.
    	types.DefaultVMContext
    }
    // Override types.DefaultVMContext.
    func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
    	return &pluginContext{}
    }
    type pluginContext struct {
    	// Embed the default plugin context here,
    	// so that we don't need to reimplement all the methods.
    	types.DefaultPluginContext
    }
    // Override types.DefaultPluginContext.
    func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
    	return types.OnPluginStartStatusOK
    }
    // Override types.DefaultPluginContext.
    func (ctx *pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
    	return &HeaderAuthorizationHandler{}
    }
    type HeaderAuthorizationHandler struct {
    	// Embed the default http context here,
    	// so that we don't need to reimplement all the methods.
    	types.DefaultHttpContext
    }
    // Override types.DefaultHttpContext.
    func (ctx *HeaderAuthorizationHandler) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
    	// Randomly routing to the canary cluster.
    	const AuthorizationKey = "allow"
    	value, err := proxywasm.GetHttpRequestHeader(AuthorizationKey)
    	if err != nil || value != "true" {
    		proxywasm.LogDebugf("request header: 'allow' is %v, only true can passthrough", value)
    		return ctx.DenyRequest()
    	}
    	return types.ActionContinue
    }
    func (ctx *HeaderAuthorizationHandler) DenyRequest() types.Action {
    	proxywasm.SendHttpResponse(403, [][2]string{{"Content-Type", "text/plain"}}, []byte("Forbidden by ASM Wasm Plugin"), -1)
    	return types.ActionPause
    }
    
  2. 作成したフォルダで次のコマンドを実行して、SDK の依存関係を取得します。

    go mod init
    go mod tidy
  3. 次のコマンドを実行して、コードを Wasm バイナリファイルにコンパイルします。

    tinygo build -o plugin.wasm -scheduler=none -target=wasi main.go

    plugin.wasm ファイルが生成されます。このファイルは Wasm バイナリ実行可能ファイルです。

ステップ 3:Wasm プラグインの OCI イメージを作成し、Container Registry Enterprise Edition インスタンスへのプッシュ

  1. ステップ 2 で作成したフォルダに、次の内容を含む Dockerfile ファイルを作成します。

    FROM scratch
    ADD ./plugin.wasm ./plugin.wasm
  2. 次のコマンドを実行して、イメージを作成します。

    docker build -t header-authorization:v0.0.1 .
  3. イメージリポジトリを作成します。詳細については、「Coraza Wasm プラグインを使用して ASM ゲートウェイに WAF を実装する」トピックのステップ 1 のサブステップ 2.a と 2.b をご参照ください。

    この例では、名前空間は test-oci、リポジトリ名は header-authorization です。次の図は、作成されたリポジトリを示しています。

    リポジトリ詳細ページの [イメージガイド][レジストリにイメージをプッシュ] セクションには、docker login でレジストリインスタンスにログインし、docker tag [ImageId] <registry-url>/<namespace>/<repository>:[image-version] でイメージにタグを付け、docker push <registry-url>/<namespace>/<repository>:[image-version] でイメージをプッシュするコマンドが記載されています。[ImageId][image-version] を実際の値に置き換えてください。

    Container Registry Enterprise Edition インスタンスにイメージをプッシュする方法の詳細については、前の図の [レジストリにイメージをプッシュ] をご参照ください。

ステップ 4:イングレスゲートウェイへの Wasm プラグインの適用

  1. イメージをプルするための権限を設定します。詳細については、「ステップ 2:イメージをプルするための権限の設定」をご参照ください。

    次のコマンドを実行して、wasm-secret という名前の Secret を作成します。

    kubectl create secret docker-registry -n istio-system wasm-secret --docker-server=${Domain name of the Container Registry Enterprise Edition instance} --docker-username =${Username} --docker-password =${Password}
  2. 次の内容を含む 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://${Domain name of the Container Registry Enterprise Edition instance}/test-oci/header-authorization:v0.0.1
      phase: AUTHN
  3. kubectl を使用して kubeconfig ファイルの情報に基づいて ASM インスタンスに接続します。次に、次のコマンドを実行して、Wasm プラグインを ASM インスタンスに適用します。

    kubectl apply -f wasm-plugin.yaml

ステップ 5:Wasm プラグインの有効化の検証

  1. イングレスゲートウェイが存在するデータプレーン上のクラスターの kubeconfig ファイルを使用し、次のコマンドを実行して、イングレスゲートウェイに適用された Wasm プラグインのデバッグロギング機能を有効にします。

    kubectl -n istio-system exec ${Name of the pod where the ingress gateway resides} -c istio-proxy -- curl -XPOST "localhost:15000/logging?wasm=debug"
  2. 次のコマンドを実行して、イングレスゲートウェイ経由で公開されている HTTPBin アプリケーションにアクセスします。

    curl ${IP address of the ingress gateway}/status/418

    期待される出力:

    Forbidden by ASM Wasm Plugin
  3. イングレスゲートウェイが存在する Pod のログを表示します。

    ログのサンプル:

    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"}
  4. 次のコマンドを実行して、イングレスゲートウェイ経由で公開されている HTTPBin アプリケーションにアクセスします。

    curl ${IP address of the ingress gateway}/status/418 -H "allow: true"

    期待される出力:

        -=[ teapot ]=-
           _...._
         .'  _ _ `.
        | ."` ^ `". _,
        \_;`"---"`|//
          |       ;/
          \_     _/
            `"""`

    この出力は、HTTPBin アプリケーションに正常にアクセスできることを示しています。

TinyGo におけるメモリリーク

TinyGo を使用してコンパイルされた Envoy プロキシ用の Wasm プラグインにはメモリリークが存在します。proxy-wasm-go-sdk コミュニティでは、コンパイルの最適化のために nottinygc を使用することを推奨しています。nottinygc を使用してコンパイルを最適化するには、次の手順を実行します。

  1. main.go ファイルの先頭に次の import コードを追加します。

    import _ "github.com/wasilibs/nottinygc"

    依存関係が見つからない場合は、go mod tidy コマンドを実行して依存関係を自動的にダウンロードできます。

  2. 次のコマンドを実行してコードをコンパイルします。

    tinygo build -o plugin.wasm -gc=custom -tags='custommalloc nottinygc_envoy'  -target=wasi -scheduler=none main.go

    上記のコマンドは、-gc および -tags パラメーターを設定します。詳細については、「nottinygc」をご参照ください。