TT Lab
Get started
Learn Learning paths Courses

ICA — Istio Certified Associate

Designing Routing Rules

Continue in TT Lab

Goal

You separate by hand the roles of a VirtualService and a DestinationRule, and learn how the order of matching rules and AND/OR combination change the actual routing result.

Why it matters

Traffic management accounts for 40% of the ICA exam. But most practical incidents in this area arise not from syntax but from not knowing the division of roles. "Send 10% to v2" is done by the VirtualService, but "what v2 is" is defined by the DestinationRule. If you create only one of the two, Envoy is left with a route that has nowhere to go or a cluster that nobody references, and the result is a 503.

The same goes for rule order. Istio stops evaluating at the first match, so if you put a catch-all rule on top, the rules below it never run. Conversely, without a catch-all at the end, unexpected requests fall into NR. These two are items you must catch in code review, and for that they must look familiar to you.

In this lab, you actually apply the basic Kubernetes resources and write the Istio CRDs as files under /root/ica-traffic/. This is because the lab cluster does not have the Istio CRDs installed, and the ability the exam requires is also "writing manifests accurately."

Steps

  1. Create the namespace ica-traffic and attach the label istio-injection=enabled.
  2. In the namespace ica-traffic, deploy the Deployments reviews-v1 and reviews-v2, each with 1 replica. Both Pod templates must have the label app=reviews, and reviews-v1 must additionally have version=v1 and reviews-v2 must have version=v2. Use the image nginx:1.27-alpine.
  3. In the same namespace, create a Service reviews. The port is 9080, the port name is http, and the selector uses only app=reviews.
  4. In /root/ica-traffic/dr-reviews.yaml, write a DestinationRule reviews. spec.host is reviews.ica-traffic.svc.cluster.local, and the subsets are, in order, v1 (labels version: v1) and v2 (labels version: v2).
  5. In /root/ica-traffic/vs-reviews-weight.yaml, write a VirtualService. spec.hosts[0] is reviews.ica-traffic.svc.cluster.local; put two destinations in one http rule and give subset v1 a weight of 90 and subset v2 a weight of 10.
  6. In /root/ica-traffic/vs-reviews-header.yaml, write a VirtualService. There are two rules. The first rule sends requests whose header x-qa-user is true to subset v2, and the second rule sends to subset v1 without a match.
  7. In /root/ica-traffic/vs-reviews-rewrite.yaml, write a VirtualService. Rewrite the path of requests whose uri.prefix is /api/v2/ to / and send them to subset v2. Do not use redirect.
  8. In /root/ica-traffic/vs-reviews-final.yaml, write a VirtualService named reviews-final. Put three rules in order: (1) if the header x-qa-user: true, v2; (2) if uri.prefix is /api/v2/ and at the same time method.exact is GET, v2; (3) a catch-all with no match goes to v1.

Notes

Creating a namespace to be brought into the mesh

Create the namespace ica-traffic and attach the label istio-injection=enabled.

Creating only the namespace does not inject a sidecar. You must attach one more label that turns on automatic injection. The label key is istio-injection.

Deploying two Deployments with different version labels

In the namespace ica-traffic, deploy the Deployments reviews-v1 and reviews-v2, each with 1 replica. Both Pod templates must have the label app=reviews, and reviews-v1 must additionally have version=v1 and reviews-v2 must have version=v2. Use the image nginx:1.27-alpine.

For the Pods of the two Deployments to be grouped under the same Service, the common label (app) must be the same, and for them to split into subsets, the distinguishing label (version) must differ. Labels are meaningful only when they are on the Pod template.

Creating a Service that groups the two versions

In the same namespace, create a Service reviews. The port is 9080, the port name is http, and the selector uses only app=reviews.

If you put version in the Service selector, only one version is caught and weighted distribution becomes impossible. Distinguishing versions is the DestinationRule's job, not the Service's. Name the port so that the protocol can be told from it.

Defining subsets

In /root/ica-traffic/dr-reviews.yaml, write a DestinationRule reviews. spec.host is reviews.ica-traffic.svc.cluster.local, and the subsets are, in order, v1 (labels version: v1) and v2 (labels version: v2).

A subset's labels must be literally the same as the Pod labels. If they differ, the cluster is created but has 0 endpoints and you run into a 503 UH. Write the host as an FQDN instead of a short name.

90/10 weighted distribution

In /root/ica-traffic/vs-reviews-weight.yaml, write a VirtualService. spec.hosts[0] is reviews.ica-traffic.svc.cluster.local; put two destinations in one http rule and give subset v1 a weight of 90 and subset v2 a weight of 10.

Weights attach to each destination in the route array. They must sum to 100, and the subset names must be exactly the same as those you created in step 4.

Header-based routing and a catch-all

In /root/ica-traffic/vs-reviews-header.yaml, write a VirtualService. There are two rules. The first rule sends requests whose header x-qa-user is true to subset v2, and the second rule sends to subset v1 without a match.

Rules are evaluated from top to bottom and the first match wins. Put the rule with the header condition first and the rule with no match at all last. The exact value is a string.

Rewriting a URI prefix

In /root/ica-traffic/vs-reviews-rewrite.yaml, write a VirtualService. Rewrite the path of requests whose uri.prefix is /api/v2/ to / and send them to subset v2. Do not use redirect.

A rewrite changes the path while proxying the request, and a redirect returns a 3xx to the client. What you need in this step is the side that changes the path without the backend noticing.

Merging the three rules into one VirtualService

In /root/ica-traffic/vs-reviews-final.yaml, write a VirtualService named reviews-final. Put three rules in order: (1) if the header x-qa-user: true, v2; (2) if uri.prefix is /api/v2/ and at the same time method.exact is GET, v2; (3) a catch-all with no match goes to v1.

The most specific rule goes at the very top and the catch-all at the very bottom. To combine path and method with AND, do not make two match items; put the two conditions side by side inside one item.