[Tutorial] Rewriting Express Routes with Gloo Edge

Rewriting Express Routes with Gloo Edge

During this tutorial we are going to leverage Gloo Edge as an API Gateway and Kubernetes Ingress Controller to rewrite the routes of a simple Express app. You can clone the source code from here. The repository itself is packed with Scenarios that show off more features of Gloo Edge, but for this tutorial, we will only be focusing on rewriting routes, which is only but a very small part of the features that Gloo Edge is capable of, but it is a great place to start learning.

Prerequisites

  • Have a Kubernetes environment with kubectl. I am using Docker Desktop for Mac with Kubernetes enabled, but minikube should also work just fine as an alternative local Kubernetes environment.
  • A linux environment to execute curl commands and install glooctl and Gloo Edge

 

Background

The Gloo Edge discovery services watch for new services added to the Kubernetes cluster. When we deploy our Express app called Tasks, Gloo Edge will automatically create an Upstream for the Task service.

Virtual Services define a set of route rules that live under a domain or set of domains. Route rules consist of matchers, which specify the kind of function calls to match, and the name of the destination (or destinations) where to route them.

Upstreams define destinations for routes. Upstreams tell Gloo Edge what to route to.

In this tutorial the upstream will be the Kubernetes Task Service, each Kubernetes Service will have a Virtual Service to control the flow of traffic.

 

Steps

  • Clone the code locally
  • Install glooctl
  • Install Gloo Edge API Gateway
  • Deploy the Task application into Kubernetes
  • Create rewrite rules in the Virtual Service to control traffic to the application using glooctl
  • Export Virtual Services to yaml for quick deployment and to maintain good “infrastructure as code”
  • Clean up

 

Clone the code locally

First we are going to install the code locally and change into the root directory of the repository.
git clone https://github.com/cmwylie19/task-service.git
cd task-service

 

Install glooctl
To install glooctl, execute the proceeding command which downloads the binary and places it into your path.
curl -sL https://run.solo.io/gloo/install | sh
export PATH=$HOME/.gloo/bin:$PATH

 

Install Gloo Edge API Gateway

The easiest way to install Gloo Edge is using glooctl.

glooctl install gateway

 

Deploy the Task application into Kubernetes

Now we will go ahead and deploy the Task Deployment and Service into Kubernetes.
kubectl apply -f k8s/task-service.yaml

 

Rewrite routes in a Virtual Service

Gloo Edge watches for new services to be added to the Kubernetes Cluster. When the application is created, Gloo Edge automatically creates an Upstream for the Kubernetes Service. Remember, upstream is the destination for the routes in the Virtual Service which will route to your Kubernetes Service. Gloo Edge also creates Virtual Services for the Kubernetes Services that control how traffic is routed to the Kubernetes Service.

By default, Gloo Edge will not route traffic until we add routing rules on a Virtual Service.

Now, before we get ahead of ourselves and start writing rules to reroute the service it is important to understand which routes we are rewriting.

Currently in the Task app we have the following endpoints:

POST /create - create task
GET / - retrieve all posts
GET /:id - retrieve specific post
PUT /:id - update specific post
DELETE /:id - delete specific post
GET /check/healthz - check if the service is up

NOTE– If you haven’t already I would encourage you to read the README.md to learn how to interact with the application locally, and the DeployInK8s.md if you have questions about how this app got containerized or how to deploy it in Kubernetes and expose it to the outside world.

Now that we deployed Gloo Edge, it will act as a Kubernetes Ingress Controller, and we have a Virtual Service in front of our application. We will need to create new routes to communicate with the application.

To see the existing Virtual Service that is in front of our application, do the following command:

glooctl get vs default -o yaml

The first thing we are going to do is update the Virtual Service to create new routes for the existing POST /create and GET / endpoints. The goal is to rewrite the /create route to be /api/v1/create and and the / route to /api/v1/tasks. These routes already exist in the exported Virtual Service k8s/vs-rewrite.yaml.

This yaml file contains the two new routes that were alluded to earlier, /api/v1/create and /api/v1/tasks:

routes:
      - matchers:
          - exact: /api/v1/create
        options:
          prefixRewrite: /create
        routeAction:
          single:
            upstream:
              name: default-task-service-8080
              namespace: gloo-system
      - matchers:
          - exact: /api/v1/tasks
        options:
          prefixRewrite: /
        routeAction:
          single:
            upstream:
              name: default-task-service-8080
              namespace: gloo-system

Go ahead and apply the Virtual Service with the new routes:

kubectl apply -f k8s/vs-rewrite.yaml

Now that we have applied the new routing rules in the Virtual Service we will test the new routes to make sure they are working as expected.

Before we do, we will use glooctl to checkout the VirtualService that houses these routes.


glooctl get vs

output:

+-----------------+--------------+---------+------+----------+-----------------+---------------------------------------+
| VIRTUAL SERVICE | DISPLAY NAME | DOMAINS | SSL  |  STATUS  | LISTENERPLUGINS |                ROUTES                 |
+-----------------+--------------+---------+------+----------+-----------------+---------------------------------------+
| default         |              | *       | none | Accepted |                 | /api/v1/create ->                     |
|                 |              |         |      |          |                 | gloo-system.default-task-service-8080 |
|                 |              |         |      |          |                 | (upstream)                            |
|                 |              |         |      |          |                 | /api/v1/tasks ->                      |
|                 |              |         |      |          |                 | gloo-system.default-task-service-8080 |
|                 |              |         |      |          |                 | (upstream)                            |
+-----------------+--------------+---------+------+----------+-----------------+---------------------------------------+

We can see by the output that we have a VirtualService default with routes /api/v1/create and /api/v1/tasks routing to an Upstream default-task-service-8080 in the namespace gloo-system.

SIDE NOTE If you ever find yourself stuck debugging, a good place to start is looking at the logs from the gloo pod in gloo-system.

kubectl get pods -n gloo-system
kubectl logs -f <pod-name>

Now that we have applied the Virtual Service from the yaml file, we are going to test it. First we are going to test our new route /api/v1/create

curl -X POST -H "Content-Type: application/json" -d '{"name":"test"}' $(glooctl proxy url)/api/v1/create 

expected output:

Created{
  "id": "0ea005c7686",
  "name": "test",
  "complete": false
}%  

You see a similar output to the one that you ran when you POST /create locally.

Now we will test the GET / with the new route specified in the Virtual Service.

curl $(glooctl proxy url)/api/v1/tasks                                                

expected output:

[{"id":"0ea005c7686","name":"test","complete":false}]% 

Again, you should have seen the same results as you did when you executed before running locally.

Next, we are going to write a new routing rule for the GET /check/healthz endpoint. We are going to write this rule now using the glooctl cli tool which will automatically add the routing rule to our Virtual Service.

glooctl add route \
  --path-exact /api/v1/healthz \
  --dest-name default-task-service-8080 \
  --prefix-rewrite /check/healthz

It goes without saying but make sure the --dest-name is the appropriate upstream for our app. I have used the wrong upstream once or twice and had to dig into the logs of the gloo pod to figure out why my routes were not working.

This command will create a new route, /api/v1/healthz on the Upstream default-task-service-8080 from the old route /check/healthz.

Lets test this new route: If all goes correctly, we should recievea response of pong back from the task-service.

curl $(glooctl proxy url)/api/v1/healthz 

expected output:

pong!% 

Now that we understand how to create routes in the Virtual Service yaml and from the command line with glooctl, lets export the the Virtual Service to yaml to maintain good infrastructure as code.

You can find the name of the Virtual Service with the command: glooctl get vs

Now, lets export the default Virtual Service to yaml, sanitize it(delete fields, status, metadata.generation, getadata,creationTimestamp, metadata.uid, matadata.selfLink, metadata.resourceVersion, and put it back into GIT under k8s/vs-rewrite-healthz.yaml so that we can quickly apply the yaml file to get our Virtual Service up and running without having to manually create routes again or to allow a GitOps tool like ArgoCD to manage our configuration as code by comparing the Yamls in the k8s directory with the resources in Kubernetes. If you are not familiar with yaml sanitization, check out the k8s/vs-rewrite-final.yaml to see how a correctly sanitized yaml file should look.

kubectl get vs -n gloo-system default -o yaml > k8s/vs-rewrite-default.yaml

 

The last thing I want to touch on in this tutorial is how to add a route that has a parameter to the Virtual Service, like our routes that have the format of /:id. Those routes will match the prefix of /api/v1/tasks/. Make SURE to add the trailing / or they will not match!

 

The finished Virtual Service with each route rewritten can be found in k8s/vs-rewrite-final.yaml.

Apply the finished Virtual Service and try out the routes:
kubectl apply -f k8s/vs-rewrite-final.yaml

Your new, more elegant and descriptive endpoints resemble the following:

POST /api/v1/create - create task
GET /api/v1/tasks - retrieve all posts
GET /api/v1/tasks/:id - retrieve specific post
PUT /api/v1/tasks/:id - update specific post
DELETE /api/v1/tasks/:id - delete specific post
GET /api/v1/healthz - check if the service is up

 

After you sanitize the yaml file for the default VirtualService you have completed this tutorial. In future tutorials I plan on using the same Express Task application to demonstrate even more advanced capabilities of Gloo Edge.

Clean Up

Delete the virtual service

kubectl delete vs default -n gloo-system

Delete the upstream

kubectl delete upstream -n gloo-system default-task-service-8080

Delete the task-service Deployment and Service

kubectl delete svc,deployment task-service

 

Learn More

You can learn more about Gloo Edge by checking out the docs!

Feel free to reach out to me to talk shop or for questions!

https://youtu.be/CSKRJo8QlbQ