Tuesday, May 10, 2022

Some fun with traefik routing on the edge of a reverse proxy

Path-based routing with Traefik

In a recent blog post, I mentioned that I use the traefik edge router / reverse proxy to put a custom domain in front of my IBM Cloud Code Engine apps. Today, I want to share details on how I configured path-based routing, i.e., depending on the path in the URI a different Code Engine app serves the request. The use case is to implement a microservices-based solution architecture with multiple backend services reachable over a single host name (on a custom domain).

Reverse proxy, edge router, and more

A reverse proxy is a dedicated service or application that mediates between (browser) clients and backend services. Depending on how the reverse proxy is deployed and configured, it helps to improve scalability, performance, resilience, and security. It shields the backends from the clients in the Internet or intranet. Some web servers like Apache, nginx, or caddy can act as reverse proxy. And there are dedicated applications like HAproxy or Squid. I used traefik which labels itself as dynamic edge router, reverse proxy, and load balancer - basically similar to an API gateway, or os...

Routing, transforming, forwarding

For the process of path-based routing, there are three phases. The first is to intercept the right request, then transform the metadata (and possibly payload) as required, and, last, to forward the request to the desired service. The result of that request needs to be transformed, too.

The file routes.yml in the repository branch path-based-routing has the traefik configuration for all those phases. The section "routers" has three router definitions, including this one:

    app2-router:
      entryPoints: ["websecure"]
      priority: 10
      rule: "Host(`traefiktest.example.com`) && PathPrefix(`/app_path2/`)"
      service: "app2-service"
      middlewares:
      - "stripprefixes"
      - "app2-header"

When there is a request for "traefktest.example.com" and the path starts with "app_path2", then transform the request using the specified middlewares, then forward it to the configured service.

One defined middleware is to strip away the given path prefixes. The reason is that the backend services do not need and should not need to know about them. They implement their API independent of any proxy, router or gateway.

    stripprefixes:
      stripPrefix:
        prefixes:
          - "/app_path2"
          - "/app_path3"
        forceSlash: false

The second middleware is to forward the right host name, basically telling the backend (Code Engine) the right app URI. The configuration looks similar to the service definition:

    app2-service:
      loadBalancer:
        servers:
        - url: "https://appname2.project_id.region.codeengine.appdomain.cloud/"

Forward the request to that specified host. To implement resilience, I could have more servers and deployed in multiple geographic regions.

Conclusions

The traefik configuration at its core is similar to other reverse proxies and edge routers or API gateways. It follows the phases to route an incoming request after some transformations to the right backend service. You can find my sample code and configuration on GitHub to study the details.

If you have feedback, suggestions, or questions about this post, please reach out to me on Twitter (@data_henrik) or LinkedIn.