Traffic Shifting
Traffic splitting and shifting are powerful features that enable operators to dynamically shift traffic to different backend Services. This can be used to implement A/B experiments, red/green deploys, canary rollouts, fault injection, and more.
Traffic splitting is accomplished with HTTPRoute and GRPCRoute types.
Note
Linkerd Production Tip
Prerequisites
To use this guide, you’ll need a Kubernetes cluster running Linkerd and Linkerd-Viz.
Set up the demo
We will set up a minimal demo which involves a load generator and two backends
called v1 and v2 respectively. You could imagine that these represent two
different versions of a service and that we would like to test v2 on a small
sample of traffic before rolling it out completely.
For load generation we’ll use Slow-Cooker and for the backends we’ll use BB.
To add these components to your cluster and include them in the Linkerd data plane, run:
cat <<EOF | linkerd inject - | kubectl apply -f -
---
apiVersion: v1
kind: Namespace
metadata:
  name: traffic-shift-demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: v1
  namespace: traffic-shift-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: bb
      version: v1
  template:
    metadata:
      labels:
        app: bb
        version: v1
    spec:
      containers:
      - name: terminus
        image: buoyantio/bb:v0.0.6
        args:
        - terminus
        - "--h1-server-port=8080"
        - "--response-text=v1"
        ports:
        - containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: v2
  namespace: traffic-shift-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: bb
      version: v2
  template:
    metadata:
      labels:
        app: bb
        version: v2
    spec:
      containers:
      - name: terminus
        image: buoyantio/bb:v0.0.6
        args:
        - terminus
        - "--h1-server-port=8080"
        - "--response-text=v2"
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: bb
  namespace: traffic-shift-demo
spec:
  ports:
  - name: http
    port: 8080
    targetPort: 8080
  selector:
    app: bb
    version: v1
---
apiVersion: v1
kind: Service
metadata:
  name: bb-v2
  namespace: traffic-shift-demo
spec:
  ports:
  - name: http
    port: 8080
    targetPort: 8080
  selector:
    app: bb
    version: v2
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: slow-cooker
  namespace: traffic-shift-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: slow-cooker
  template:
    metadata:
      labels:
        app: slow-cooker
    spec:
      containers:
      - args:
        - -c
        - |
          sleep 5 # wait for pods to start
          /slow_cooker/slow_cooker --qps 10 http://bb:8080
        command:
        - /bin/sh
        image: buoyantio/slow_cooker:1.3.0
        name: slow-cooker
EOF
We can see that slow-cooker is sending traffic to the v1 backend:
> linkerd viz -n traffic-shift-demo stat --from deploy/slow-cooker deploy
NAME   MESHED   SUCCESS       RPS   LATENCY_P50   LATENCY_P95   LATENCY_P99   TCP_CONN
v1        1/1   100.00%   10.1rps           1ms           1ms           8ms          1
Shifting Traffic
Now let’s create an HTTPRoute and split 10% of traffic to the v2 backend:
cat <<EOF | kubectl apply -f -
---
apiVersion: policy.linkerd.io/v1beta2
kind: HTTPRoute
metadata:
  name: bb-route
  namespace: traffic-shift-demo
spec:
  parentRefs:
    - name: bb
      kind: Service
      group: core
      port: 8080
  rules:
    - backendRefs:
      - name: bb
        port: 8080
        weight: 90
      - name: bb-v2
        port: 8080
        weight: 10
EOF
Notice in this HTTPRoute, the parentRef is the bb Service resource that
slow-cooker is talking to. This means that whenever a meshed client talks to
the bb Service, it will use this HTTPRoute. You may also notice that the bb
Service appears again in the list of backendRefs with a weight of 90. This
means that 90% of traffic sent to the bb Service will continue on to the
endpoints of that Service. The other 10% of requests will get routed to the
bb-v2 Service.
We can see this by looking at the traffic stats (keep in mind that the stat
command looks at metrics over a 1 minute window, so it may take up to 1 minute
before the stats look like this):
> linkerd viz -n traffic-shift-demo stat --from deploy/slow-cooker deploy
NAME   MESHED   SUCCESS      RPS   LATENCY_P50   LATENCY_P95   LATENCY_P99   TCP_CONN
v1        1/1   100.00%   9.0rps           1ms           1ms           1ms          1
v2        1/1   100.00%   1.0rps           1ms           1ms           1ms          1
From here, we can continue to tweak the weights in the HTTPRoute to gradually
shift traffic over to the bb-v2 Service or shift things back if it’s looking
dicey. To conclude this demo, let’s shift 100% of traffic over to bb-v2:
cat <<EOF | kubectl apply -f -
---
apiVersion: policy.linkerd.io/v1beta2
kind: HTTPRoute
metadata:
  name: bb-route
  namespace: traffic-shift-demo
spec:
  parentRefs:
    - name: bb
      kind: Service
      group: core
      port: 8080
  rules:
    - backendRefs:
      - name: bb-v2
        port: 8080
        weight: 100
EOF
> linkerd viz -n traffic-shift-demo stat --from deploy/slow-cooker deploy
NAME   MESHED   SUCCESS       RPS   LATENCY_P50   LATENCY_P95   LATENCY_P99   TCP_CONN
v1        1/1         -         -             -             -             -          -
v2        1/1   100.00%   10.0rps           1ms           1ms           2ms          1


