 Well it's good to to be back in person and and see some some friendly faces or at least right at least some partial faces. So one step back to normalcy. So I'm really excited to be here today. My name is Danian Hansen. I'm a software engineer with Tetrate. I've been involved with various CNCF projects contributor and maintainer since the early days of Kubernetes and CNCF. But today I'm here to talk to you about Envoy and specifically Envoy extensibility using WebAssembly or WASM for short. You know one of the powerful aspects of Envoy is its extensibility and Envoy provides several different ways to be extended. One option is if you need to extend Envoy you can write a native C++ filter. If you know C++ that's great. If the filter is generalized enough you may even be able to add that filter to the native Envoy filters. But if it's not then you have to maintain that filter on your own and you have to maintain that Envoy binary on your own. Because you'd have to go ahead and you would take the Envoy source code, add your filter to it and compile it with your filter and maintain your own Envoy binary. Another option is Lua base filters. Envoy exposes a minimalistic Lua API so you can write Lua scripts. Again if you know Lua could be a great option for you especially if it's very basic functionality that you need to add. But today I'm going to talk to you about the last and the newest option which is Wasm filters. So let's first start off talking about what is Wasm. So it's a binary instruction format. So it's a format to group a bunch of ones and zeros together into fields that can be interpreted by a stack based virtual machine. So a stack based virtual machine will pull and put values onto a stack and Wasm says that it's designed to be a portable compilation target. So you can go ahead and you can write a Wasm module in the language of your choice and then you compile it into this Wasm module that is that binary instruction format. And it is portable in the sense that it can run inside a web browser. It could run inside a Node.js application. And what we're going to talk about in detail today is actually running it inside of a server which is of course Envoy. So let's discuss some of the benefits of Wasm. So Wasm currently supports over 30 different languages that you can write your Wasm modules in. It is fast. So it gets built very quickly. The binary format is very efficient. So you can load your modules very quickly. It's safe. So the Wasm module actually runs inside of a sandbox called a Wasm VM which we'll talk about in more detail. And so the Wasm VM has no direct access to the host. Any type of access is done through APIs and we'll talk about those APIs in more detail in future slides also. And I mentioned that it is portable. So again, it could run in various environments. But let's talk about Wasm and Envoy in more detail. So Wasm or Envoy supports Wasm runtimes by default. It's the V8 runtime which is a Wasm runtime that is built in C++ and again the default for Envoy. And as we know, Envoy has a multi-threading model architecture. And so each worker thread would have one or more of these Wasm VMs. And these VMs would be created if you create a filter like an HTTP filter or a network filter that you want to provide this additional customized logic using Wasm. But there's also a VM that can run in the main thread. And this is called a Singleton VM. So one of these VMs that runs in the main thread that's responsible for more, you know, very similar type of tasks that the main thread is responsible for. Some kind of global tasks. And we'll talk about some examples of these global tasks in more detail. And Proxy Wasm is a standard. It's an implementation agnostics standard of interfaces for running Wasm in proxies. And Envoy has implemented this standard. And the standard defines these low level, it's considered an ABI, but this low level interactions between Envoy and your Wasm VMs. And instead of using this low level standard ABI, Proxy Wasm also supports several different SDKs, language specific SDKs like Rust, Go, and the Go SDK is what I'm going to go ahead and demonstrate later on in today's talk. And those SDKs provide the functions, the callbacks that you would use for writing your Wasm modules. And so the Wasm service, so I mentioned earlier that there are a few different, that the VMs don't actually have access directly to the host. The VMs communicate through APIs and Envoy exposes several different APIs. If you want to go ahead and use a stats or log API to, along with a timer API to periodically go ahead and send metrics to a sync, a stats sync. There's also the shared data and message QAPIs that allow for interaction between your VMs. So your worker thread VMs, they can be collecting metrics and then storing those metrics in this, using the shared data API. And the Wasm service extension, that Singleton VM, can periodically go ahead and pull those metrics from the shared data and then again export those or vice versa. The Wasm service could periodically use the HTTP or GRPC APIs to make external GRPC or HTTP calls and then store that data in shared data. Or again with a message Q, the Wasm service could be a subscriber for your worker thread VMs that periodically publish some sort of data to the message Q. And so this Wasm capability is managed through the Envoy configuration. And so depending on whether you're creating a HTTP filter or a network filter or you're using the Wasm Singleton service, all of this is configured through the Envoy configuration. And one of the key aspects of that configuration, you tell Envoy, where does this Wasm module exist? Now just like other services and configuration of Envoy, you could have a control plane like Istio and use XDS to go ahead and grab that extension configuration or ECDS is a service that can be used to manage the extension configuration. And instead of just having this module locally, the module can exist remotely. And again, part of this configuration, Envoy will go ahead and grab that Wasm module remotely and then instantiate it. Well, and here's a configuration of the Wasm service. Again, there's only one Wasm service within Envoy that runs in the main thread. And then as I mentioned, you could go ahead and have a local file name or store your Wasm module remotely. And the dot Wasm extension, this is our byte code that the WebAssembly byte code that was compiled. And here's one of the key parameters is a Singleton True. And you see that all of the Wasm service configuration is done inside the Bootstrap extensions. And there's really two key areas of this configuration, right? There's configuration of the VM itself. And you see here, we're specifying the V8 runtime. And again, the V8 runtime is the default run runtime for Envoy. And we specify where that module exists. Along with this other configuration, this is a configuration here, the MyConfig value and MyValue, the key value pair. This is a configuration that you actually pass into the module itself. And I'll show an example of that in my demonstration. Here's an example, Wasm HTTP filter configuration. You see under the HTTP filter section of the Envoy configuration, we specify that this is a Wasm HTTP filter. We give it its typed config, a name, an ID for the VM. In this example, we're passing no configuration. But just like the other example, we specify for the HTTP filter where this Wasm byte code exists. So let's jump into a demo. I'll pause here for a second in case anyone wants to follow along. I have the demonstration hosted in my GitHub repo. Let me exit out of here. And let's go pull this up. So here is the repo where I am hosting the demo. And instead of, I thought maybe I'd be short on time. So instead of going through each step of the demo, I went ahead and kind of jumped through, set some things up, have already created the Wasm module. We'll talk through what that looks like. And I use the Go SDK because that's what I program in. But in the proxy Wasm repo, there's links to all the other SDKs, language specific SDKs, if you prefer a different language. Let's actually, let me pull up my IDE. And show you what this module looks like. How does that look? Is that big enough where everyone can read? A little bigger? Let's see how we do this here. Can I make it bigger? Don't know how to make it larger here. So just bear with me. And again, if you'd like, you can go ahead and jump to my repo. I apologize for that. So what we define here is, you know, main is, function main is the entry point into a Go program. And it calls, it makes a call. Oh, here, we can do this. Does that zoom in for you guys as well? Okay, cool. All right. Here we go. Coming up with solutions. So it makes a call to the proxy Wasm set VM context. So there is a VM context that's created for each Wasm VM. And it's responsible for managing plugins. And we'll walk through what the plugin looks like as well. We're using the default Vm context. We create, so not only do we create this VM context, but now we need to create a plugin context. So we're essentially now plumbing through or plumbing through attributes of our Wasm module throughout our code. So the VM context, as I mentioned, it's responsible for creating and managing plugin context. And you have a plugin that then manages your filters, either your HTTP or network filters, which are represented as context in their own. And so we have a context ID. And in the demo, we're going to go ahead and add headers for responses. And we're also going to take advantage of the counters, the counter API, and add a header counter. Every time we see a request, not only are we going to add the headers, but we're also going to go ahead and manage a counter. Here's our plugin context, where we again plumb through the additional headers, the context, and the hello header counter. And so this hello header counter, what it will do is it's going to look for a specific header key. And based on that key, it will increment the counter and we'll see this in the HTTP context. So we need to start up the plugin of VM. Again, there's a single VM context per Wasm VM. But we could have multiple plugins within the VM context. And so we get the configuration of the of the plugin. We go ahead and log a critical error if there's any issues loading the configuration. And when I say the configuration, in this example, I'm going to run Envoy locally and pass through this Envoy config. And in the slide that I showed you earlier, there was no configuration that was being added. But in my HTTP filter, you're going to see that I actually have some configuration here. And the values that I'm passing in is this header one equals some value, header two equals a second value, just some basic configuration that I'm passing into my Wasm module. And so this is where, right, the plugin goes goes ahead and reads that configuration gets the configuration and stores it in this value, or in this variable, excuse me. And we will go ahead and skip any, any configuration that has a comment in front of it. But what we're doing is, is we're going to go ahead and we're going to, we're going to look for any configuration that has this equals value separator. And again, if we go back to the Envoy YAML, that's exactly what our configuration does. So instead of actually creating any of these values within the code, I'm going to go ahead and just read them from the configuration. I create a new HTTP context and go ahead and continue to plumb through the headers, the response headers, and the hello header counter. And you, and this HTTP headers type is of the default HTTP context and continue to plumb through the additional headers and the header counter. And this is where we go ahead and we say, get the request header. If the request header includes hello, we don't care about the value, we just go ahead and ignore the value here. If the, if the request header, right, so we're operating on a request header here for this HTTP context. And if we don't find the hello key, we don't care about the value. But if we don't find it, we'll just continue, right? We don't want to you know, drop the connection or drop the request, anything like that, we'll just continue. But if we do find it, we're going to go ahead and increment that hello header counter. We're going to also log that we have incremented the counter. And then we also have an on HTTP response headers, right? So whenever this method is called, we'll log and we'll see that in the Envoy logs. And we add additional headers. Right? So the additional headers are those headers that we go ahead and pass into the configuration. We'll see that as we go ahead and test this out. All right. So let's go here. And so one of the first things we're going to do here, let's see, is compile our, our wasm module. And the go SDK actually uses tiny go, which replaces the goes native compiler into replaces the native compiler so that the compiled programs are much smaller in size. There are some caveats and limitations. So if you're interested in the SDK, go ahead and take a look at the docs and make sure you familiarize yourself with those caveats. But let's go ahead. And what we do here is we're going to go ahead and build our wasm module from this main dot go. We now have the main dot wasm. And again, this is, this is the module that we go ahead. And if we go back to the Envoy config here, we'll see that we've got main dot wasm. Right? So we're telling the, the wasm VM that this main dot wasm is, is local and what's used to instantiate the VM. So let me start Envoy. I'm going to have to pull up another screen here. So let's start Envoy again here and let me curl. So here we go. So in the left hand side of the screen here, we've got Envoy running. We specified where the config file is. And on the right hand side, and again, I've got Envoy running locally here on my laptop. And on the right hand side represents a client that is hitting Envoy. And a couple of things that I'm doing with curl here is I'm specifying a header. Hello is the key, me is the value. And then EnvoyCon.danian.com, that just resolves to my loop back address or local host. And in my Envoy configuration, I have a listener on port 10,000. Maybe I should actually jump over here. And again, here's the listener on port 1000 and the configuration that I'm passing into Envoy. Let me show you here the configuration a little bit more. So on this listener, I've got my filter chains, and I've got just Envoy configured to set up a direct response for a root to respond with a 200 status code and a hello world. So what we see here is Envoy does respond as we go ahead and hit that listener. And because it's running with our WASM module, you see a couple of things here, is that we've got those header, those response headers that were set in our configuration. And then again, Envoy is responding with hello world, a direct response. Now, one of the things that we're doing in the WASM module is actually adding a metric counter so that actually before I get into that, on the left hand side here, you see the logging that I set up within the module. And so we are logging the different methods that we're calling. We're logging that the hello header counter was incremented in that it's done, the filter is done processing. And so if I do hello me again, you see again we're able to hit Envoy's listener. And now what I did here is that Envoy, my local Envoy is exposing the admin or manage admin endpoint on 8001. And so I can go ahead and get the stats. I mentioned that the WASM module is collecting metrics. I created this hello header counter metric. And every time that we go ahead and curl that listener, it increments this hello header counter metric. And one of the things I could have done to expand this is as I mentioned that Singleton service, we can take advantage of the other Envoy APIs to go ahead and store these metrics in the shared data, pull those metrics, and then periodically export those to our stats sync. So let's see here. That's running. How are we doing with time? Two minutes? All right. Let me, I should have time to show you this as well. So I mentioned, right? So this example is just Envoy running locally. But I also showed you that Envoy, that WASM, that the extension config can be managed through an XDS server. And so I've got, let's see, I've got Istio running. Let's make sure that we're good to go here. So I've got Istio running. And I had, let me show you what I have here. So I've got, I've got an Envoy filter, and I've got an example application, my HTTP bin application that is exposed using an Istio gateway and an Istio virtual service. And, and what I'm going to do is with this Envoy filter is tell the Envoy sidecar for this application that it is to use this WASM module, the same WASM module that I showed locally. I'm now storing that instead of locally, I'm still storing it remotely. And so the Istio agent that runs with the Envoy sidecar will go ahead and pull this WASM module validated against the WASM spec, make sure it's safe to run, and then go ahead and run it in a WASM VM. And so if we go ahead and we apply let's see here, manifest and HTTP bin. HTTP bin app is running with its sidecar. And if I go headers, you see that through the Istio gateway, I'm able to, oh wait, no, that's not, it actually didn't hit it. Now we hit it. You see that I received a 200 response from my app through the Istio gateway and virtual service. And so now let's go ahead. And those headers that response headers that I'm setting with my WASM module, they don't exist. We don't see any of these response headers. But let me go ahead really quickly because I know I'm running out of time here. Let me go ahead and add the Envoy filter. And again, one of the powerful aspects of WASM modules is we don't have to reload Envoy, your modules dynamically linked into the Envoy process. And now, when I curl, now you see the two headers that are being added to the HTTP response. So yeah, that's it. I hope you found this time informational and it gives you a little insight into WASM modules, being able to extend Envoy through WASM. And I'll be around. So if you have any additional questions, feel free to reach out to me. Okay.