In my other blog post this week, I talk about the new Kubernetes Gateway API and the release of Envoy Gateway 0.2. In this post we’re going to get hands-on with Envoy Gateway and the Gateway API. Below are instructions that walk you step-by-step through installing Envoy Gateway, and a simple use case of exposing an HTTP app outside the cluster, through an Envoy proxy.
You don’t even need to play along at home if it’s inconvenient; I’ve included the output of every command as well, so you can see how it all works without needing a Kubernetes cluster.
And if you’re a fan of GUIs, at the end of the post I include screenshots and details of Tetrate’s proof-of-concept Envoy Gateway GUI based on Backstage, to show how easy it is to build such things against the Gateway API.
Let’s get started!
Create a Kubernetes Cluster
The first thing we need is a Kubernetes cluster to run Envoy Gateway in. Be careful using a cluster that’s used for other things (so maybe not your company’s prod environment…). The easiest and safest way is to use minikube to bring up a cluster on your local machine.
$ minikube start –driver=docker --cpus=2 --memory=2g minikube v1.27.0 on Arch 22.0.0 (x86_64) ▪ KUBECONFIG=... ❗ For more information, see: https://github.com/kubernetes/kubernetes/issues/112135 Using Docker Desktop driver with root privileges Starting control plane node minikube in cluster minikube Pulling base image ... Creating docker container (CPUs=2, Memory=2048MB) ... Preparing Kubernetes v1.25.2 on Docker 20.10.17 ... ▪ Generating certificates and keys ... ▪ Booting up control plane ... ▪ Configuring RBAC rules ... Verifying Kubernetes components... ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5 Enabled addons: storage-provisioner Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
Install Envoy Gateway
Recall that Envoy Gateway is configured by the new Gateway API, rather than the old Ingress one. Gateway API hasn’t been merged into upstream Kubernetes yet, so our cluster won’t have it available. We “install” the API by deploying CRDs for it. The Envoy Gateway project provides a single file which will install Gateway API and deploy Envoy Gateway.
$ kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v0.2.0/install.yaml
This creates a lot of resources; I’ll address them in a couple sections. First, Gateway API:
customresourcedefinition.apiextensions.k8s.io/gatewayclasses.gateway.networking.k8s.io created customresourcedefinition.apiextensions.k8s.io/gateways.gateway.networking.k8s.io created customresourcedefinition.apiextensions.k8s.io/httproutes.gateway.networking.k8s.io created customresourcedefinition.apiextensions.k8s.io/referencegrants.gateway.networking.k8s.io created customresourcedefinition.apiextensions.k8s.io/referencepolicies.gateway.networking.k8s.io created customresourcedefinition.apiextensions.k8s.io/tcproutes.gateway.networking.k8s.io created customresourcedefinition.apiextensions.k8s.io/tlsroutes.gateway.networking.k8s.io created customresourcedefinition.apiextensions.k8s.io/udproutes.gateway.networking.k8s.io created namespace/gateway-system created validatingwebhookconfiguration.admissionregistration.k8s.io/gateway-api-admission created service/gateway-api-admission-server created deployment.apps/gateway-api-admission-server created serviceaccount/gateway-api-admission created clusterrole.rbac.authorization.k8s.io/gateway-api-admission created clusterrolebinding.rbac.authorization.k8s.io/gateway-api-admission created role.rbac.authorization.k8s.io/gateway-api-admission created rolebinding.rbac.authorization.k8s.io/gateway-api-admission created job.batch/gateway-api-admission created job.batch/gateway-api-admission-patch created
As you can see, this is mostly CRDs. But note that installing Gateway API also deployed some workload resources including Deployments, etc – the Gateway API comes with a webhook admission controller to validate the resources we deploy, which we can see with the following command:
$ kubectl get pods --namespace gateway-system NAME READY STATUS RESTARTS AGE gateway-api-admission-2dhk5 0/1 Completed 0 70s gateway-api-admission-patch-dbdbc 0/1 Completed 1 70s gateway-api-admission-server-68485ffc97-gt8v4 1/1 Running 0 70s
Let’s also take a look at the new CRDs adding to the cluster’s API surface.
$ kubectl api-resources | grep gateway.networking gatewayclasses gc gateway.networking.k8s.io/v1beta1 false GatewayClass gateways gtw gateway.networking.k8s.io/v1beta1 true Gateway httproutes gateway.networking.k8s.io/v1beta1 true HTTPRoute referencegrants refgrant gateway.networking.k8s.io/v1alpha2 true ReferenceGrant referencepolicies refpol gateway.networking.k8s.io/v1alpha2 true ReferencePolicy tcproutes gateway.networking.k8s.io/v1alpha2 true TCPRoute tlsroutes gateway.networking.k8s.io/v1alpha2 true TLSRoute udproutes gateway.networking.k8s.io/v1alpha2 true UDPRoute
Now let’s look at Envoy Gateway itself.
namespace/envoy-gateway-system created customresourcedefinition.apiextensions.k8s.io/envoyproxies.config.gateway.envoyproxy.io created serviceaccount/envoy-gateway created role.rbac.authorization.k8s.io/leader-election-role created clusterrole.rbac.authorization.k8s.io/envoy-gateway-role created clusterrole.rbac.authorization.k8s.io/metrics-reader created clusterrole.rbac.authorization.k8s.io/proxy-role created rolebinding.rbac.authorization.k8s.io/leader-election-rolebinding created clusterrolebinding.rbac.authorization.k8s.io/envoy-gateway-rolebinding created clusterrolebinding.rbac.authorization.k8s.io/proxy-rolebinding created configmap/envoy-gateway-config created service/envoy-gateway created service/envoy-gateway-metrics-service created deployment.apps/envoy-gateway created
As you might expect, these are workload resources and associated security and networking. After a short time we can see the running controller:
$ kubectl get pods --namespace envoy-gateway-system NAME READY STATUS RESTARTS AGE envoy-gateway-dc74c4d97-pntbj 2/2 Running 0 35s
Install a Test App
We’ll also need something for the gateway to actually forward traffic to—something that acts like one of our apps. We can use httpbin for that, and the EG project provides convenient manifests for it.
$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/master/samples/httpbin/httpbin.yaml serviceaccount/httpbin created service/httpbin created deployment.apps/httpbin created
This will run in the default namespace:
$ kubectl get pods -n default NAME READY STATUS RESTARTS AGE httpbin-9dbd644c7-fhtw5 1/1 Running 0 4m16s
Configure Envoy Gateway
Now we can get on to configuring Envoy Gateway to do some request routing. The first thing we need to do is register the EG controller we just deployed, so that other resources can reference it to identify which gateway they’re configuring, in case you deploy several in one cluster. The single field, controllerName, matches a value which the controller registers with the Kubernetes cluster it’s running in.
$ kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1beta1 kind: GatewayClass metadata: name: my-envoy-gateway spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller EOF gatewayclass.gateway.networking.k8s.io/eg created
We can see some limited information about this new instance, and assuming our description was valid we’ll see ACCEPTED: True
.
$ kubectl get gatewayclass -o wide NAME CONTROLLER ACCEPTED AGE DESCRIPTION eg gateway.envoyproxy.io/gatewayclass-controller True 2m38s
Next, let’s configure a Gateway object—this opens the network port(s) we’d like the Envoy proxy(s) to listen on. For this simple demo, we’ll bind to a high port and not use TLS, so I’ve called it “insecure-port”.
$ kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1beta1 kind: Gateway metadata: name: insecure-port spec: gatewayClassName: my-envoy-gateway listeners: - name: http protocol: HTTP port: 8080 EOF
When we inspect that, we’ll see it doesn’t say it’s ready. This approval is held back since we’re using a local development cluster that can’t make the kind of cloud load balancers a “real” one would be able to (hence the address field is empty too). Don’t worry about this, it’ll still work fine.
$ kubectl -n default get gateway -o wide NAME CLASS ADDRESS READY AGE insecure-port my-envoy-gateway 2m54s
Finally we can set up routing for some HTTP traffic. In this simple example, we match any request for the vhost “www.example.com”, on any path, and send it to the httpbin instance we deployed earlier.
$ kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1beta1 kind: HTTPRoute metadata: name: httpbin spec: parentRefs: [ {name: insecure-port} ] hostnames: ["www.example.com"] rules: - matches: - path: {type: PathPrefix, value: /} backendRefs: - {group: "", kind: Service, name: httpbin, port: 8000, weight: 1} EOF httproute.gateway.networking.k8s.io/httpbin created
Inspecting this kind of resource doesn’t currently show us much, but we can see it was successfully deployed at least.
$ kubectlget httproute -n default -o wide NAME HOSTNAMES AGE httpbin ["www.example.com"] 58s
One last thing to note is that only now is an instance of Envoy actually started (before it was just the controller, which doesn’t itself handle traffic). This is an optimization on EG’s behalf: lazy creation of proxies. Check the “age” field on your system and see how it’s very recent.
$ kubectl get pods -n envoy-gateway-system NAME READY STATUS RESTARTS AGE envoy-default-insecure-port-5879556bd4-r5ggw 1/1 Running 0 90s envoy-gateway-dc74c4d97-pntbj 2/2 Running 0 13m
Initiate Test Traffic
Like I said before, we can’t get a real cloud load balancer, since we’re using a local development cluster. But minikube has a neat feature which will expose the cluster’s services to our local machine—including the proxy that EG spun up. The 8080 port we opened will then be available on loopback.
$ minikube tunnel
That command blocks, so open a new terminal to send a request that’ll be routed to httpbin, as per the rules we deployed.
$ curl --header "Host: www.example.com" 127.0.0.1:8080/headers HTTP/1.1 200 OK server: envoy date: Fri, 07 Oct 2022 12:41:32 GMT content-type: application/json content-length: 175 access-control-allow-origin: * access-control-allow-credentials: true x-envoy-upstream-service-time: 14 { "headers": { "Accept": "application/json, */*", "Host": "www.example.com", "User-Agent": "curl/7.79.1", "X-Envoy-Expected-Rq-Timeout-Ms": "15000" } }
So there we have it. It works! That was just a simple demo, but you can explore the rest of the Gateway API surface and start using more of the features – Envoy Gateway’s docs will be a good guide for that. Do be kind of Envoy Gateway at this early point—it’s still a work in progress!
An Example GUI
The instructions I gave are for the command line, but a good GUI is a great way to interact with any system. EG’s implementation of the standard Gateway API will allow a thriving ecosystem of interoperable interfaces to emerge. To kick this area off, Tetrate has built a proof-of-concept UI for EG, using Backstage, the emerging standard for developer interfaces. Below I’ll show a couple screenshots of how it looks inspecting the demo setup from above.
If you’d like to play with this, or fork it, we’ve published the code—fair warning that this is pre-alpha, and just an example of what’s possible to build in a week! If you’re at KubeCon Detroit swing by our booth and we can show it to you in person!
###
If you’re getting started with Istio and Envoy, check out Tetrate Academy where you’ll find a ton of free courses, on-demand workshops, as well as Tetrate’s Certified Istio Administrator exam.
For the easiest way to install, manage, and upgrade Istio, check out our open source Tetrate Istio Distro (TID). TID is a vetted, upstream distribution of Istio—a hardened image of Istio with continued support that is simpler to install, manage, and upgrade.