ICA — Istio Certified Associate
Designing Routing Rules
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
- Create the namespace
ica-trafficand attach the labelistio-injection=enabled. - In the namespace
ica-traffic, deploy the Deploymentsreviews-v1andreviews-v2, each with 1 replica. Both Pod templates must have the labelapp=reviews, andreviews-v1must additionally haveversion=v1andreviews-v2must haveversion=v2. Use the imagenginx:1.27-alpine. - In the same namespace, create a Service
reviews. The port is9080, the port name ishttp, and the selector uses onlyapp=reviews. - In
/root/ica-traffic/dr-reviews.yaml, write a DestinationRulereviews.spec.hostisreviews.ica-traffic.svc.cluster.local, and the subsets are, in order,v1(labelsversion: v1) andv2(labelsversion: v2). - In
/root/ica-traffic/vs-reviews-weight.yaml, write a VirtualService.spec.hosts[0]isreviews.ica-traffic.svc.cluster.local; put two destinations in one http rule and give subsetv1a weight of 90 and subsetv2a weight of 10. - In
/root/ica-traffic/vs-reviews-header.yaml, write a VirtualService. There are two rules. The first rule sends requests whose headerx-qa-useristrueto subsetv2, and the second rule sends to subsetv1without a match. - In
/root/ica-traffic/vs-reviews-rewrite.yaml, write a VirtualService. Rewrite the path of requests whoseuri.prefixis/api/v2/to/and send them to subsetv2. Do not use redirect. - In
/root/ica-traffic/vs-reviews-final.yaml, write a VirtualService namedreviews-final. Put three rules in order: (1) if the headerx-qa-user: true,v2; (2) ifuri.prefixis/api/v2/and at the same timemethod.exactisGET,v2; (3) a catch-all with no match goes tov1.
Notes
- For a manifest skeleton, it is quick to generate one with
kubectl create deployment ... --dry-run=client -o yamland edit it. - Check file syntax with
yq '.spec' 파일명(the placeholder is the file name). If a value comes out asnull, that path is wrong. - Common mistake 1: putting
versionin the Service selector so that only one version is caught. Then the weights are meaningless. - Common mistake 2: writing a subset name such as
v1as the DestinationRule's subset label. The labels must match the Pod labels. - For the AND condition in step 8, you must put
uriandmethodside by side inside a single match item. If you split them into two items, it becomes OR.
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.