Skip to content

Istio WasmPlugin

We'll create a WasmPlugin resource in this lab and deploy it to the Kubernetes cluster.

The WasmPlugin allows us to select the workloads we want to apply the Wasm module to and point to the Wasm module.

What about EnvoyFilter?

In previous Istio versions, we'd have to use the EnvoyFilter resource to configure the Wasm plugins. We could either point to a local Wasm file (i.e., file accessible by the Istio proxy) or a remote location. Using the remote location (e.g. http://some-storage-account/main.wasm), the Istio proxy would download the Wasm file and cache it in the volume accessible to the proxy.

The WasmPlugin resource includes a feature that enables Istio proxy (or istio-agent to be precise) to download the Wasm file from an OCI-compliant registry. That means we can treat the Wasm files just like we treat Docker images. We can push them to a registry, version them using tags, and reference them from the WasmPlugin resource.

There was no need to push or publish the main.wasm file anywhere in the previous labs, as it was accessible by the Envoy proxy because everything was running locally. However, now that we want to run the Wasm module in Envoy proxies that are part of the Istio service mesh, we need to make the main.wasm file available to all those proxies they can load and run it.

Building the Wasm image

Since we'll be building and pushing the Wasm file, we'll need a very minimal Dockerfile in the project:

FROM scratch
COPY main.wasm ./plugin.wasm

This Docker file copies the main.wasm file to the container as plugin.wasm. Save the above contents to Dockerfile.

Next, we can build and push the Docker image:

docker build -t [REPOSITORY]/wasm:v1 .
docker push [REPOSITORY]/wasm:v1

Creating WasmPlugin resource

We can now create the WasmPlugin resource that tells Envoy where to download the extension from and which workloads to apply it to (we'll use httpbin workload we'll deploy next).

WasmPlugin resource
plugin.yaml
apiVersion: extensions.istio.io/v1alpha1
kind: WasmPlugin
metadata:
  name: wasm-example
  namespace: default
spec:
  selector:
    matchLabels:
      app: httpbin
  url: oci://[REPOSITORY]/wasm:v1
  pluginConfig:
    header_1: "some_value_1"
    header_2: "another_value"

You should update the REPOSITORY value in the url field before saving the above YAML to plugin.yaml and deploying it using kubectl apply -f plugin.yaml.

We'll deploy a sample workload to try out the Wasm extension. We'll use httpbin. Make sure the default namespace is labeled for Istio sidecar injection (kubectl label ns default istio-injection=enabled) and then deploy the httpbin:

httpbin.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: httpbin
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
    service: httpbin
spec:
  ports:
  - name: http
    port: 8000
    targetPort: 80
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      serviceAccountName: httpbin
      containers:
      - image: docker.io/kennethreitz/httpbin
        imagePullPolicy: IfNotPresent
        name: httpbin
        ports:
        - containerPort: 80

Save the above YAML to httpbin.yaml and deploy it using kubectl apply -f httpbin.yaml.

Before continuing, check that the httpbin Pod is up and running:

$ kubectl get po
NAME                       READY   STATUS        RESTARTS   AGE
httpbin-66cdbdb6c5-4pv44   2/2     Running       1          11m

To see if something went wrong with downloading the Wasm module, you can look at the proxy logs.

Let's try out the deployed Wasm module!

We will create a single Pod inside the cluster, and from there, we will send a request to http://httpbin:8000/get and include the hello header.

kubectl run curl --image=curlimages/curl -it --rm -- /bin/sh
Defaulted container "curl" out of: curl, istio-proxy, istio-init (init)
If you don't see a command prompt, try pressing enter.
/ $

Once you get the prompt to the curl container, send a request to the httpbin service:

curl -v -H "hello: something" http://httpbin:8000/headers
> GET /headers HTTP/1.1
> User-Agent: curl/7.35.0
> Host: httpbin:8000
> Accept: */*
>
< HTTP/1.1 200 OK
< server: envoy
< date: Mon, 22 Jun 2021 18:52:17 GMT
< content-type: application/json
< content-length: 525
< access-control-allow-origin: *
< access-control-allow-credentials: true
< x-envoy-upstream-service-time: 3
...

If we exit the pod and look at the stats, we'll notice that the hello_header_counter has increased:

kubectl exec -it [httpbin-pod-name] -c istio-proxy -- curl localhost:15000/stats/prometheus | grep hello
# TYPE hello_header_counter counter
hello_header_counter{} 1

Cleanup

To delete all resources created during this lab, run the following:

kubectl delete wasmplugin wasm-example
kubectl delete deployment httpbin
kubectl delete svc httpbin
kubectl delete sa httpbin