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

Alibaba Cloud Service Mesh:Envoy プロキシ向けの Go 言語による Wasm プラグインの作成

最終更新日:Mar 12, 2026

Service Mesh (ASM) は、WebAssembly for Proxies 仕様をサポートしており、これにより、ポータブルなプラグインを WebAssembly(Wasm)にコンパイルすることで Envoy プロキシの動作を拡張できます。これらのプラグインは、さまざまなプロキシサーバー上で実行可能です。Wasm プラグインは、メモリ安全なサンドボックス内でネイティブに近い速度で実行され、プロキシの再起動なしに更新できます。

本トピックでは、ヘッダーに基づく権限付与を強制する Go 言語ベースの Wasm プラグインの作成、Open Container Initiative(OCI)イメージへのパッケージング、および ASM イングレスゲートウェイへのデプロイ手順について説明します。

Envoy における Wasm プラグインの仕組み

Wasm プラグインは、ホストプロキシと通信するための明確に定義された API を備えたサンドボックス化された環境内で実行されます。このアーキテクチャには、以下の利点があります:

利点説明
ホットリロード可能Envoy プロキシを再起動せずにプラグインを更新できるため、トラフィックの継続的な処理が保証されます
障害分離プラグインのクラッシュが Envoy プロキシ全体の停止を引き起こしません
セキュアサンドボックスにより、プラグインの機能が明確に定義された API サーフェスに制限されます
マルチ言語対応C++、Go、Rust のいずれかでプラグインを作成できます

詳細については、「Envoy における WebAssemblyおよびproxy-wasm-go-sdk の概要をご参照ください。

エンドツーエンドのワークフロー

本チュートリアルで作成するプラグインは、着信リクエストに allow: true ヘッダーが含まれているかどうかをチェックします。このヘッダーが存在しないリクエストには 403 Forbidden 応答が返されます。ヘッダーが存在するリクエストはバックエンドサービスへ透過的に転送されます。

  1. proxy-wasm-go-sdk を使用して Go 言語でプラグインのロジックを記述します。

  2. TinyGo を使用して Go コードを Wasm バイナリにコンパイルします。

  3. バイナリを OCI イメージとしてパッケージ化し、Container Registry Enterprise Edition にプッシュします。

  4. ASM で WasmPlugin リソースを作成し、Envoy プロキシにプラグインを適用します。

前提条件

開始する前に、以下の条件を満たしていることを確認してください。

ステップ 1:開発環境のセットアップ

ローカルマシンに以下のツールをインストールします。

ツール目的参考情報
GoGo コンパイラおよびツールチェーンgo.dev
DockerOCI イメージのビルドおよびプッシュdocker.com
TinyGoGo を Wasm にコンパイル(標準 Go コンパイラは Wasm 出力をサポートしていません)TinyGo インストールガイド

本プラグインは、proxy-wasm-go-sdk に依存しており、これは完全な Go SDK API および追加のサンプルを含んでいます。

ステップ 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{})
    }
    
    // vmContext implements types.VMContext.
    // Embedding DefaultVMContext provides default implementations for all methods.
    type vmContext struct {
    	types.DefaultVMContext
    }
    
    // NewPluginContext creates a new plug-in context for each Wasm VM.
    func (*vmContext) NewPluginContext(contextID uint32) types.PluginContext {
    	return &pluginContext{}
    }
    
    // pluginContext implements types.PluginContext.
    type pluginContext struct {
    	types.DefaultPluginContext
    }
    
    func (ctx *pluginContext) OnPluginStart(pluginConfigurationSize int) types.OnPluginStartStatus {
    	return types.OnPluginStartStatusOK
    }
    
    // NewHttpContext creates an HTTP context for each request.
    func (ctx *pluginContext) NewHttpContext(contextID uint32) types.HttpContext {
    	return &HeaderAuthorizationHandler{}
    }
    
    // HeaderAuthorizationHandler checks the "allow" header on each request.
    type HeaderAuthorizationHandler struct {
    	types.DefaultHttpContext
    }
    
    // OnHttpRequestHeaders inspects request headers.
    // If the "allow" header is missing or not set to "true", the request is denied with 403.
    func (ctx *HeaderAuthorizationHandler) OnHttpRequestHeaders(numHeaders int, endOfStream bool) types.Action {
    	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
    }
    
    // DenyRequest sends a 403 response and pauses further processing.
    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. Go モジュールを初期化し、依存関係をダウンロードします。

    go mod init
    go mod tidy
  3. コードを Wasm バイナリにコンパイルします。この操作により、現在のディレクトリに plugin.wasm ファイルが生成されます。

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

ステップ 3:OCI イメージのパッケージ化およびプッシュ

  1. 同じプロジェクトディレクトリで、Dockerfile を作成します。

    FROM scratch
    ADD ./plugin.wasm ./plugin.wasm
  2. イメージをビルドします。

    docker build -t header-authorization:v0.0.1 .
  3. Container Registry Enterprise Edition にイメージリポジトリを作成します。詳細な手順については、「Coraza Wasm プラグインを使用した ASM ゲートウェイ上での WAF 機能の実装」のステップ 1 のサブステップ 2.a および 2.b をご参照ください。本例では、名前空間は test-oci、リポジトリ名は header-authorization です。

    image

  4. イメージにタグを付けて Container Registry Enterprise Edition インスタンスにプッシュします。リポジトリの詳細ページに表示される「レジストリへのイメージのプッシュ」の手順に従ってください。

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

  1. イメージのプル用のシークレットを作成します。背景情報については、「ステップ 2:イメージプルの権限の設定」をご参照ください。以下のプレースホルダーを実際の値に置き換えてください。

    プレースホルダー説明
    <your-registry-domain>Container Registry Enterprise Edition インスタンスのドメイン名
    <your-username>レジストリのユーザー名
    <your-password>レジストリのパスワード
    kubectl create secret docker-registry -n istio-system wasm-secret \
      --docker-server=<your-registry-domain> \
      --docker-username=<your-username> \
      --docker-password=<your-password>
  2. asm-plugin.yaml というファイルを作成し、以下の内容を記述します。 <your-registry-domain> を、ご利用の Container Registry Enterprise Edition インスタンスのドメイン名に置き換えてください。

    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://<your-registry-domain>/test-oci/header-authorization:v0.0.1
      phase: AUTHN
  3. ASM インスタンスの kubeconfig ファイルを使用して接続し、リソースを適用します。

    kubectl apply -f asm-plugin.yaml

ステップ 5:プラグインの検証

  1. イングレスゲートウェイ上で Wasm プラグインのデバッグログを有効化します。イングレスゲートウェイが実行されているデータプレーンクラスターの kubeconfig ファイルを使用します。<ingress-gateway-pod> を、イングレスゲートウェイを実行している Pod の名前に置き換えてください。

    kubectl -n istio-system exec <ingress-gateway-pod> -c istio-proxy -- \
      curl -XPOST "localhost:15000/logging?wasm=debug"
  2. allow ヘッダーを**含まない**リクエストを送信します。期待される出力は以下のとおりです。

    curl <ingress-gateway-ip>/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. allow: true ヘッダーを**含む**リクエストを送信します。期待される出力は以下のとおりです。ティーポット応答により、HTTPBin アプリケーションへのアクセスが可能であり、プラグインが正しく動作していることが確認できます。

    curl <ingress-gateway-ip>/status/418 -H "allow: true"
       -=[ teapot ]=-
    
              _...._
            .'  _ _ `.
           | ."` ^ `". _,
           \_;`"---"`|//
             |       ;/
             \_     _/
               `"""`

TinyGo のメモリリークを nottinygc で修正

TinyGo でコンパイルされた Wasm プラグインは、メモリリークを示す場合があります。proxy-wasm-go-sdk コミュニティでは、この問題を解決するために、nottinygc をカスタムガーベジコレクターとして使用することを推奨しています。

  1. main.go の先頭に、以下の import 文を追加します。この依存関係がまだ利用できない場合は、go mod tidy を実行してダウンロードしてください。

    import _ "github.com/wasilibs/nottinygc"
  2. カスタム GC フラグを指定して Wasm バイナリを再構築します。-gc=custom フラグにより、TinyGo のデフォルトガーベジコレクターが nottinygc に置き換えられ、-tags='custommalloc nottinygc_envoy' により、Envoy 向けに最適化されたアロケーターが有効化されます。

    tinygo build -o plugin.wasm -gc=custom -tags='custommalloc nottinygc_envoy' \
      -target=wasi -scheduler=none main.go
  3. OCI イメージを再構築し、更新版を Container Registry Enterprise Edition にプッシュした後、WasmPlugin リソースを再適用して修正を展開します。