Deploying Filters to Gloo

In this tutorial we’ll deploy an existing WebAssembly (WASM) module from the WebAssembly Hub directly to Envoy via Gloo installed to our kubernetes cluster.

Prepare environment

To get started, let’s deploy a sample service that we can call through Envoy. We’ll deploy the sample petstore API:

kubectl apply -f https://raw.githubusercontent.com/solo-io/gloo/master/example/petstore/petstore.yaml

You should now have the petstore running:

kubectl get po 

NAME                        READY   STATUS    RESTARTS   AGE
petstore-5dcf5d6b66-n8tjt   1/1     Running   0          2m20s

Deploying Envoy

In this tutorial, we’ll use Gloo, an API Gateway based on Envoy that has built-in wasm support but these steps should also work for base Envoy.

First, install Gloo via the helm chart:

helm repo update
kubectl create ns gloo-system
helm install gloo-gateway gloo/gloo --namespace gloo-system --set global.wasm.enabled=true

Gloo will be installed to the gloo-system namespace.

Gloo version 1.3.6 or greater required. Check your installed version of Gloo with glooctl version

Create a Route

Lastly, we’ll create a route to be able to call our petstore service. Let’s add a route using a Gloo VirtualService:

Apply the virtual service manifest

cat <<EOF | kubectl apply -f -
apiVersion: gateway.solo.io/v1
kind: VirtualService
metadata:
  name: default
  namespace: gloo-system  
spec:
  virtualHost:
    domains:
    - '*'
    routes:
    - matchers:
      - prefix: /
      routeAction:
        single:
          upstream:
            name: default-petstore-8080
            namespace: gloo-system
EOF

Next, we’ll get the Gloo Gateway’s external IP by running the following:


URL=$(kubectl get svc -n gloo-system gateway-proxy -o jsonpath='{.status.loadBalancer.ingress[*].ip}')

URL=$(minikube ip):$(kubectl get svc -n gloo-system gateway-proxy -o jsonpath='{.spec.ports[?(@.name == "http")].nodePort}')`

Now let’s curl that URL:

curl -v $URL/api/pets

*   Trying 35.184.102.75...
* TCP_NODELAY set
* Connected to 35.184.102.75 (35.184.102.75) port 80 (#0)
> GET /api/pets HTTP/1.1
> Host: 35.184.102.75
> User-Agent: curl/7.54.0
> Accept: */*
> 
< HTTP/1.1 200 OK
< content-type: application/xml
< date: Tue, 10 Dec 2019 16:02:00 GMT
< content-length: 86
< x-envoy-upstream-service-time: 2
< server: envoy
< 
[{"id":1,"name":"Dog","status":"available"},{"id":2,"name":"Cat","status":"pending"}]

If you’re able to get to this point, we are able to call to our petstore app through Gloo.

Deploying a WASM module from the Hub

If you don’t have wasme installed, try the following or refer to the installation guide for getting the WebAssembly Hub CLI wasme:

curl -sL https://run.solo.io/wasme/install | sh
export PATH=$HOME/.wasme/bin:$PATH

Let’s run wasme list to see what’s available on the hub:

wasme list --published
NAME                                   TAG                                 SIZE    SHA      UPDATED
...
webassemblyhub.io/ilackarms/add-header v0.1                             1.0 MB  8c001279 12 Feb 20 19:10 UTC
...

Let’s try deploying one of these to Gloo:

wasme deploy gloo webassemblyhub.io/ilackarms/add-header:v0.1 --id=myfilter --config 'world'

This filter adds the header hello: <value> to responses, where <value> is the value of the --config string.

The deployment should have added our filter to the Gloo Gateway. Let’s check this with kubectl:

kubectl get gateway -n gloo-system '-ojsonpath={.items[0].spec.httpGateway.options.wasm}'
map[config:world image:webassemblyhub.io/ilackarms/add-header:v0.1 name:myfilter rootId:add_header_root_id]

If we try our request again, we should see the hello: world header was added by our filter:

curl -v $URL/api/pets
*   Trying 34.73.225.160...
* TCP_NODELAY set
* Connected to 34.73.225.160 (34.73.225.160) port 80 (#0)
> GET /api/pets HTTP/1.1
> Host: 34.73.225.160
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: application/xml
< date: Fri, 20 Dec 2019 19:17:13 GMT
< content-length: 86
< x-envoy-upstream-service-time: 0
< hello: world
< location: envoy-wasm
< server: envoy
<
[{"id":1,"name":"Dog","status":"available"},{"id":2,"name":"Cat","status":"pending"}]

Note that deploying filters is dynamic and does not require restarting the proxy.

Cleaning up

We can clean up our filter with the wasme undeploy command:

wasme undeploy gloo --id=myfilter

Then re-try the curl:

curl -v $URL/api/pets
*   Trying 34.73.225.160...
* TCP_NODELAY set
* Connected to 34.73.225.160 (34.73.225.160) port 80 (#0)
> GET /api/pets HTTP/1.1
> Host: 34.73.225.160
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: application/xml
< date: Fri, 20 Dec 2019 19:19:13 GMT
< content-length: 86
< x-envoy-upstream-service-time: 1
< server: envoy
<
[{"id":1,"name":"Dog","status":"available"},{"id":2,"name":"Cat","status":"pending"}]
* Connection #0 to host 34.73.225.160 left intact

Cool! We’ve just seen how easy it is to dynamically add and remove filters from Envoy using wasme.

For more information and support using wasme and the Web Assembly Hub, visit the Solo.io slack channel at https://slack.solo.io.