Todos los productos
Search
Centro de documentos

Alibaba Cloud Service Mesh:Escribir un plugin de Wasm en Go para un proxy Envoy

Última actualización: Sep 05, 2026

WebAssembly for Proxies es una nueva especificación que permite a los desarrolladores escribir plugins portátiles mediante WebAssembly (Wasm). Estos plugins se ejecutan en diversos servidores proxy. Service Mesh (ASM) admite la especificación WebAssembly for Proxies. Este tema describe cómo escribir un plugin de Wasm en Go para un proxy Envoy en ASM.

Requisitos previos

  • Se agregó un clúster a una instancia de ASM cuya versión sea 1.18 o posterior. Para obtener más información sobre cómo agregar un clúster a una instancia de ASM, consulte Add a cluster to an ASM instance.

  • La inyección automática de proxy sidecar está habilitada. Para obtener más información, consulte Configure sidecar injection policies.

  • Se desplegó una puerta de enlace de entrada. Para obtener más información, consulte Create an ingress gateway.

  • Se desplegó una aplicación HTTPBin y esta es accesible. Para obtener más información sobre cómo desplegar una aplicación HTTPBin, consulte Deploy the HTTPBin application.

  • Se creó una instancia de Container Registry Enterprise Edition. Las instancias de Container Registry Enterprise Edition admiten imágenes de Open Container Initiative (OCI). Para obtener más información, consulte Create an Enterprise Edition instance.

Contexto

Wasm es un formato binario portátil y emergente para código ejecutable. El código se ejecuta a una velocidad casi nativa dentro de un sandbox seguro para la memoria del host. Este sandbox tiene restricciones de recursos claramente definidas y proporciona una API bien definida para comunicarse con el entorno host de embedding (en este caso, un proxy).

Los plugins de Wasm ofrecen los siguientes beneficios:

  • Agilidad: Los plugins se actualizan sin necesidad de reiniciar el proxy Envoy, por lo que las solicitudes se procesan según lo esperado.

  • Confiabilidad y aislamiento: Dado que los plugins se despliegan dentro de un sandbox con restricciones de recursos, pueden fallar sin afectar al proxy Envoy.

  • Seguridad: Al desplegarse dentro de un sandbox con una API claramente definida para la comunicación con el proxy, los plugins están estrictamente controlados.

  • Diversidad: Los plugins se escriben en varios lenguajes de programación, como C++, Go y Rust.

Para obtener más información sobre los plugins de Wasm, consulte WebAssembly-in-Envoy.md y OVERVIEW.md.

Ejemplo de configuración

En este ejemplo, se escribe un plugin de Wasm en Go. Una vez escrito el plugin, se genera un archivo binario de Wasm y luego se empaqueta en una imagen. La imagen debe cargarse en un repositorio de imágenes OCI. Después de cargar la imagen, configure el recurso WasmPlugin en ASM y aplique el plugin al proxy Envoy especificado.

En este ejemplo, se desarrolla un plugin para comprobar si una solicitud contiene el encabezado allow: true. Si no lo contiene, se devuelve el código de estado 403 y un cuerpo específico. Si lo contiene, se accede a la aplicación HTTPBin según lo esperado.

Paso 1: Preparar un entorno de desarrollo

Para desarrollar un plugin de Wasm en Go para un proxy Envoy, primero debe instalar las siguientes herramientas:

  • Go: El compilador de Go y las herramientas relacionadas se utilizan para escribir proyectos en Go. Para obtener más información, consulte The Go Programming Language.

  • Docker: En este ejemplo, Docker se usa para compilar y enviar imágenes OCI.

  • TinyGo: Aunque se usa Go para escribir el plugin de Wasm, no es posible utilizar el compilador oficial de Go para compilar código Go en formato Wasm. Debe usar TinyGo. Para obtener más información sobre cómo instalar TinyGo, consulte Quick install guide.

Para obtener más información sobre los SDK de los que depende el plugin de Wasm, consulte proxy-wasm-go-sdk en el sitio web de GitHub. Allí encontrará el código completo del SDK de Go para Proxy-Wasm. Si desea usar otros SDK, consulte el código del SDK de Go para Proxy-Wasm como referencia.

Paso 2: Escribir el código del plugin

  1. Cree una carpeta y cree un archivo main.go que contenga el siguiente contenido:

    Show the main.go file

    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. Ejecute los siguientes comandos en la carpeta creada para obtener las dependencias del SDK:

    go mod init
    go mod tidy
  3. Ejecute el siguiente comando para compilar el código en un archivo binario de Wasm:

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

    Se genera un archivo plugin.wasm. Este archivo es el ejecutable binario de Wasm.

Paso 3: Crear una imagen OCI del plugin de Wasm y enviarla a la instancia de Container Registry Enterprise Edition

  1. En la carpeta creada en Step 2, cree un archivo Dockerfile que contenga el siguiente contenido:

    FROM scratch
    ADD ./plugin.wasm ./plugin.wasm
  2. Ejecute los siguientes comandos para crear una imagen:

    docker build -t header-authorization:v0.0.1 .
  3. Cree un repositorio de imágenes. Para obtener más información, consulte los subpasos 2.a y 2.b del Paso 1 en el tema Implement a WAF on an ASM gateway with the Coraza Wasm plugin.

    En este ejemplo, el namespace es test-oci y el nombre del repositorio es header-authorization. La siguiente figura muestra el repositorio creado.

    En la página de detalles del repositorio, dentro de Image Guide, la sección Push image to the Registry proporciona los comandos para iniciar sesión en la instancia del Registry con docker login, etiquetar la imagen con docker tag [ImageId] <registry-url>/<namespace>/<repository>:[image-version] y enviar la imagen con docker push <registry-url>/<namespace>/<repository>:[image-version]. Reemplace [ImageId] y [image-version] con los valores reales.

    Para obtener más información sobre cómo enviar una imagen a la instancia de Container Registry Enterprise Edition, consulte Push image to the registry en la figura anterior.

Paso 4: Aplicar el plugin de Wasm a la puerta de enlace de entrada

  1. Configure los permisos para extraer imágenes. Para obtener más información, consulte Step 2: Configure permissions to pull images.

    Ejecute el siguiente comando para crear un Secret llamado wasm-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. Cree un archivo asm-plugin.yaml que contenga el siguiente contenido:

    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. Use kubectl para conectarse a la instancia de ASM según la información del archivo kubeconfig. Luego, ejecute el siguiente comando para aplicar el plugin de Wasm a la instancia de ASM:

    kubectl apply -f wasm-plugin.yaml

Paso 5: Comprobar que el plugin de Wasm surte efecto

  1. Utilice el archivo kubeconfig del clúster en el plano de datos donde reside la puerta de enlace de entrada y ejecute el siguiente comando para habilitar la función de registro de depuración para el plugin de Wasm aplicado a dicha puerta de enlace:

    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. Ejecute el siguiente comando para acceder a la aplicación HTTPBin expuesta a través de la puerta de enlace de entrada:

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

    Salida esperada:

    Forbidden by ASM Wasm Plugin
  3. Consulte los registros del pod donde reside la puerta de enlace de entrada.

    Registros de ejemplo:

    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. Ejecute el siguiente comando para acceder a la aplicación HTTPBin expuesta a través de la puerta de enlace de entrada:

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

    Salida esperada:

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

    La salida indica que se puede acceder a la aplicación HTTPBin según lo esperado.

Fugas de memoria en TinyGo

Existen fugas de memoria en el plugin de Wasm para un proxy Envoy compilado con TinyGo. La comunidad de proxy-wasm-go-sdk recomienda usar nottinygc para optimizar la compilación. Siga estos pasos para utilizar nottinygc en la optimización de la compilación:

  1. Agregue el siguiente código import al inicio del archivo main.go:

    import _ "github.com/wasilibs/nottinygc"

    Si no se encuentran dependencias, ejecute el comando go mod tidy para descargarlas automáticamente.

  2. Ejecute el siguiente comando para compilar el código:

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

    El comando anterior establece los parámetros -gc y -tags. Para obtener más información, consulte nottinygc.