Photo by Jin xc on Unsplash
In the race for APIs, it's best to think carefully about your API management solution. With Kubernetes gaining popularity, whether on-premises or on public clouds, we strongly suggest you adopt a Kubernetes-native solution like
Gloo Edge.
An emerging need of our customers, especially those who operate multi-tenant platforms, is the ability to do
dynamic routing. This means being able to select the appropriate upstream API in the moment, depending on the context of the request. This could be targeting a tenant's workloads, or targeting a set of more or less powerful machines based on the user's subscription, or targeting a shard of your customer database based on specific headers. Most API gateway solutions allow for path rewriting, but not all have dynamic routing.
Get ready for some dynamic routing configuration,
GitOps-flavored!
Initial route selection
When an HTTP request enters the Envoy Proxy, the HTTP Connection Manager (
HCM) first selects a route depending on
Matchers.
routes:
- matchers:
- prefix: /
headers:
- name: choice
value: first
These matchers rely on headers (and also
HTTP/2 pseudo-headers). Throughout the filter flow, headers can be modified, added, or deleted, and you might want to reconsider the route to be used, at runtime. To do this, you need dynamic routing.
So let's explore three capabilities offered by Gloo Edge.
Dynamic route selection
Fortunately, Envoy has an option called
clearRouteCache
which resets the route selected by the HCM. Gloo Edge exposes this option in three different places:
- Transformations - where you can build new headers
- JWT - where you can extract claims and set them to new headers
- ExtAuth authorization policies and custom plugins - where you can add or modify headers too
The HCM will then finalize its route selection based upon these new headers.
NOTE: if you want to know more about the Envoy filter flow with Gloo Edge, watch
my video on YouTube.
Since you can modify some headers on the fly, routing possibilities are endless! The most common business use case I've seen is for tiering purposes. Another use case I see is extracting path segments, or JWT claims to new headers, and re-route to some other upstream service.
Dynamic RouteTable selection
You can take advantage of our
delegation model, where developers and tenants can design their routes in their own namespace. You can delegate actions to any RouteTable, living in any namespace, using the special
namespace: "*"
selector.
apiVersion: gateway.solo.io/v1
kind: RouteTable
metadata:
name: 'tenant-hub'
namespace: 'gloo-system'
spec:
routes:
- matchers:
prefix: "/api"
delegateAction:
selector:
namespaces: "*"
Dynamic Upstream selection
We can even go one step further by using a special attribute from Envoy, also exposed by Gloo Edge:
clusterHeader
By using this attribute, you can tell Envoy which "upstream cluster" (~
Upstream
) you want the request to be sent.
That's a bit tricky because you need to be sure that this
Upstream
actually exists and is known to Envoy. For Gloo, it means that the
Upstream
resource must exist (it can be automatically discovered or created manually).
Let's see it in action! Say you have one
Upstream
per tenant, with the tenant ID in its name. You want to automatically route requests to the correct
Upstream
, depending on this ID, which is available in a JWT.
Here's a diagram to illustrate this concept:
Say we have a JWT with the following payload:
{
"sub": "abc",
"iss": "my-jwt-issuer"
"iat": 1516239022
}
Here is a way of doing this
JWT claim-based dynamic routing:
- First, extract the JWT
sub
claim to a new header, called x-gloo-tenant
...
jwtStaged:
afterExtAuth:
providers:
custom:
claimsToHeaders:
- claim: sub
header: x-gloo-tenant
jwks:
...
tokenSource:
headers:
- header: Authorization
prefix: Bearer
- Then, create a new header, here named
tenant-upstream
, using a transformation and the following pattern: <upstream-name>_<upstream-namespace>
:
...
stagedTransformations:
regular:
requestTransforms:
- requestTransformation:
transformationTemplate:
headers:
tenant-upstream:
text: "tenant-{{ header(\x-gloo-tenant\") }}_analytics"" # analytics being the namespace of the upstream service