Configuring Dynamic Request Routing
Prerequisites
To use this guide, you’ll need to have Linkerd installed on your cluster and the Helm CLI installed.
Dynamic request routing
With dynamic request routing, you can route HTTP traffic based on the content of request headers. This can be useful for performing things like A/B testing and other strategies for traffic management.
The core configuration mechanism are the Gateway API types, HTTPRoute and GRPCRoute. In this example we’ll look at the common use case using the HTTPRoute type.
In this tutorial, we’ll make use of the podinfo project to showcase dynamic request routing, by deploying in the cluster two backend and one frontend podinfo pods. Traffic will flow to just one backend, and then we’ll switch traffic to the other one just by adding a header to the frontend requests.
Setup
First we create the test
namespace, annotated by linkerd so all pods that get
created there get injected with the linkerd proxy:
kubectl create ns test --dry-run=client -o yaml \
| linkerd inject - \
| kubectl apply -f -
Then we add podinfo’s Helm repo, and install two instances of it. The first one
will respond with the message “A backend
”, the second one with “B backend
”.
helm repo add podinfo https://stefanprodan.github.io/podinfo
helm install backend-a -n test \
--set ui.message='A backend' podinfo/podinfo
helm install backend-b -n test \
--set ui.message='B backend' podinfo/podinfo
We add another podinfo instance which will forward requests only to the first
backend instance backend-a
:
helm install frontend -n test \
--set backend=http://backend-a-podinfo:9898/env podinfo/podinfo
Once those three pods are up and running, we can port-forward requests from our local machine to the frontend:
kubectl -n test port-forward svc/frontend-podinfo 9898 &
Sending Requests
Requests to /echo
on port 9898 to the frontend pod will get forwarded the pod
pointed by the Service backend-a-podinfo
:
$ curl -sX POST localhost:9898/echo \
| grep -o 'PODINFO_UI_MESSAGE=. backend'
PODINFO_UI_MESSAGE=A backend
Introducing HTTPRoute
Let’s apply the following HTTPRoute
resource to enable header-based routing:
cat <<EOF | kubectl -n test apply -f -
apiVersion: policy.linkerd.io/v1beta2
kind: HTTPRoute
metadata:
name: backend-router
namespace: test
spec:
parentRefs:
- name: backend-a-podinfo
kind: Service
group: core
port: 9898
rules:
- matches:
- headers:
- name: "x-request-id"
value: "alternative"
backendRefs:
- name: "backend-b-podinfo"
port: 9898
- backendRefs:
- name: "backend-a-podinfo"
port: 9898
EOF
In parentRefs
we specify the resources we want this HTTPRoute
instance to
act on. So here we point to the backend-a-podinfo
Service on the HTTPRoute
’s
namespace (test
), and also specify the Service port number (not the Service’s
target port).
Next, we give a list of rules that will act on the traffic hitting that Service.
The first rule contains two entries: matches
and backendRefs
.
In matches
we list the conditions that this particular rule has to match. One
matches suffices to trigger the rule (conditions are OR’ed). Inside, we use
headers
to specify a match for a particular header key and value. If multiple
headers are specified, they all need to match (matchers are AND’ed). Note we can
also specify a regex match on the value by adding a type: RegularExpression
field. By not specifying the type like we did here, we’re performing a match of
type Exact
.
In backendRefs
we specify the final destination for requests matching the
current rule, via the Service’s name
and port
.
Here we’re specifying we’d like to route to backend-b-podinfo
all the requests
having the x-request-id: alternative
header. If the header is not present,
the engine fall backs to the last rule which has no matches
entries and points
to the backend-a-podinfo
Service.
The previous requests should still reach backend-a-podinfo
only:
$ curl -sX POST localhost:9898/echo \
| grep -o 'PODINFO_UI_MESSAGE=. backend'
PODINFO_UI_MESSAGE=A backend
But if we add the x-request-id: alternative
header, they get routed to
backend-b-podinfo
:
$ curl -sX POST \
-H 'x-request-id: alternative' \
localhost:9898/echo \
| grep -o 'PODINFO_UI_MESSAGE=. backend'
PODINFO_UI_MESSAGE=B backend
Implementation notes
In the example above, we used the x-request-id
header, which is a common
header that is forwarded by podinfo. However, the same technique will work with
arbitrary headers, as long as the application forwards them.
Note also that dyanmic request routing is client-side behavior, so while the traffic source (in this case, the frontend pod) needs to be meshed, strictly speaking, the destination does not need to be meshed.