Module 2: Exercises
This exercise section provides hands-on practice with Istio traffic management. You will learn how to configure Gateways, create VirtualServices with various routing patterns, implement path-based routing, perform weighted traffic splitting, and combine these components for complete traffic control.
Prerequisites
Before starting these exercises, ensure you have:
-
Access to an OpenShift cluster with OpenShift Service Mesh 3.4 (Istio 1.30) installed
-
Your namespace (
$NAMESPACE) configured with sidecar injection enabled (from Module 1) -
The
ocCLI tool installed and configured -
Basic understanding of Kubernetes Services and Deployments
Exercise 1: Create and Configure a Gateway
In this exercise, you will create a Gateway resource to allow external traffic into your service mesh.
Step 1: Set Your Namespace
Ensure your namespace variable is set:
export NAMESPACE=user1-sandbox
Verify the namespace exists and has sidecar injection enabled:
oc get namespace $NAMESPACE --show-labels | grep istio.io/rev
The exercises send test requests with oc exec from the curl-client Deployment that you created in Module 1 (Exercise 1, Step 7). Check that it is running:
oc get deployment curl-client -n $NAMESPACE
Step 2: Deploy an Istio Ingress Gateway
OpenShift Service Mesh 3 does not install an ingress gateway together with the control plane. Deploy one into your namespace with gateway injection: the inject.istio.io/templates: gateway annotation makes the sidecar injector configure the istio-proxy container as a gateway, and image: auto is replaced with the proxy image of your control plane revision:
oc apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: istio-ingressgateway
namespace: $NAMESPACE
spec:
type: ClusterIP
selector:
istio: ingressgateway-user1
ports:
- name: http2
port: 80
targetPort: 8080
- name: https
port: 443
targetPort: 8443
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: istio-ingressgateway
namespace: $NAMESPACE
spec:
replicas: 1
selector:
matchLabels:
istio: ingressgateway-user1
template:
metadata:
annotations:
inject.istio.io/templates: gateway
labels:
istio: ingressgateway-user1
app: istio-ingressgateway
sidecar.istio.io/inject: "true"
spec:
containers:
- name: istio-proxy
image: auto
EOF
The gateway pods carry the label istio: ingressgateway-user1, which is unique to you. By default, a Gateway resource selects its gateway pods by label in all namespaces, not only in its own namespace. If every attendee labeled their gateway pods istio: ingressgateway, each attendee’s Gateway would also configure the gateway pods of all other attendees.
Check that the Istio ingress gateway is running:
oc rollout status deployment/istio-ingressgateway -n $NAMESPACE
oc get pods \
-n $NAMESPACE \
-l app=istio-ingressgateway
Note the service name and external access method:
oc get svc -n $NAMESPACE istio-ingressgateway
Step 3: Create a Basic Gateway
Create a Gateway that accepts HTTP traffic on port 80. Its selector istio: ingressgateway-user1 matches only your gateway pods:
oc apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: my-gateway
namespace: $NAMESPACE
spec:
selector:
istio: ingressgateway-user1
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
EOF
Step 4: Verify Gateway Creation
Verify the Gateway was created successfully:
oc get gateways.networking.istio.io -n $NAMESPACE
Use the full resource name gateways.networking.istio.io. On clusters that also have the Kubernetes Gateway API installed, oc get gateway lists gateways.gateway.networking.k8s.io instead and does not show the Istio Gateway.
|
View the Gateway configuration:
oc get gateways.networking.istio.io my-gateway \
-n $NAMESPACE \
-o yaml
Step 5: Create a Gateway with HTTPS
Create an additional Gateway that accepts HTTPS traffic on port 443:
oc apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: my-https-gateway
namespace: $NAMESPACE
spec:
selector:
istio: ingressgateway-user1
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: my-tls-secret
hosts:
- "myapp-user1.example.com"
EOF
In a production environment, you would need to create the TLS secret referenced in the Gateway configuration. The host names in this module contain your username (myapp-user1.example.com) so that they do not collide with the hosts of other attendees on a shared cluster.
|
Exercise 2: Create VirtualServices with Basic Routing
In this exercise, you will create VirtualServices to route traffic to your services.
Step 1: Deploy Sample Applications
Deploy two sample applications to use for routing:
oc apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: ${NAMESPACE}
spec:
replicas: 2
selector:
matchLabels:
app: frontend
version: v1
template:
metadata:
labels:
app: frontend
version: v1
spec:
containers:
- name: nginx
image: quay.io/nginx/nginx-unprivileged:latest
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: frontend
namespace: ${NAMESPACE}
spec:
selector:
app: frontend
ports:
- port: 80
targetPort: 8080
name: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
namespace: ${NAMESPACE}
spec:
replicas: 2
selector:
matchLabels:
app: backend
version: v1
template:
metadata:
labels:
app: backend
version: v1
spec:
containers:
- name: nginx
image: quay.io/nginx/nginx-unprivileged:latest
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: backend
namespace: ${NAMESPACE}
spec:
selector:
app: backend
ports:
- port: 80
targetPort: 8080
name: http
EOF
Wait for the pods to be ready:
oc get pods \
-n $NAMESPACE \
-l app=frontend
oc get pods \
-n $NAMESPACE \
-l app=backend
Step 2: Create a Basic VirtualService
Create a VirtualService that routes all traffic to the frontend service:
oc apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: frontend
namespace: ${NAMESPACE}
spec:
hosts:
- frontend
http:
- route:
- destination:
host: frontend
weight: 100
EOF
Exercise 3: Implement Path-Based Routing
In this exercise, you will create a VirtualService that routes traffic based on URI paths.
Step 1: Create a Path-Based VirtualService
Create a VirtualService that routes different paths to different services:
oc apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: myapp
namespace: $NAMESPACE
spec:
hosts:
- myapp-user1.example.com
gateways:
- my-gateway
http:
- match:
- uri:
prefix: "/api"
route:
- destination:
host: backend
weight: 100
- match:
- uri:
prefix: "/"
route:
- destination:
host: frontend
weight: 100
EOF
This VirtualService:
-
Routes requests to
/api/*to the backend service -
Routes all other requests to the frontend service
Step 2: Verify Path-Based Routing
Check the VirtualService configuration:
oc get virtualservice myapp \
-n $NAMESPACE \
-o yaml
Step 3: Test Path-Based Routing
Get the ingress gateway external address:
INGRESS_HOST=$(oc get svc \
-n $NAMESPACE \
istio-ingressgateway \
-o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
echo $INGRESS_HOST
If no external address is available, you can use port-forwarding:
oc port-forward -n $NAMESPACE svc/istio-ingressgateway 8080:80 &
Test the frontend route:
curl -H "Host: myapp-user1.example.com" http://localhost:8080/
Test the backend API route:
curl -H "Host: myapp-user1.example.com" http://localhost:8080/api
The backend is a plain NGINX server with no /api page, so this returns 404 Not Found from the backend. The 404 page comes from NGINX, which shows that the request was routed to the backend.
Step 4: Create More Complex Path Matching
Create a VirtualService with multiple path patterns:
oc apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: complex-routing
namespace: $NAMESPACE
spec:
hosts:
- complex-user1.example.com
gateways:
- my-gateway
http:
- match:
- uri:
exact: "/api/v1/users"
route:
- destination:
host: backend
weight: 100
- match:
- uri:
prefix: "/api/v2"
route:
- destination:
host: backend
weight: 100
- match:
- uri:
regex: '^/static/.*\.(jpg|png|gif)$'
route:
- destination:
host: frontend
weight: 100
- route:
- destination:
host: frontend
weight: 100
EOF
This example demonstrates:
-
Exact path matching (
/api/v1/users) -
Prefix matching (
/api/v2/*) -
Regex matching (static image files)
-
Default route (catch-all)
Exercise 4: Implement Weighted Traffic Splitting
In this exercise, you will implement weighted traffic splitting for canary deployments.
Step 1: Deploy Multiple Versions
Deploy a second version of the frontend application:
oc apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-v2
namespace: ${NAMESPACE}
spec:
replicas: 2
selector:
matchLabels:
app: frontend
version: v2
template:
metadata:
labels:
app: frontend
version: v2
spec:
containers:
- name: nginx
image: quay.io/nginx/nginx-unprivileged:latest
ports:
- containerPort: 8080
EOF
Wait for the new pods to be ready:
oc get pods \
-n $NAMESPACE \
-l app=frontend
Step 2: Create DestinationRule with Subsets
Create a DestinationRule to define service subsets (versions):
oc apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
name: frontend
namespace: ${NAMESPACE}
spec:
host: frontend
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
EOF
Verify the DestinationRule:
oc get destinationrule -n $NAMESPACE
oc get destinationrule frontend \
-n $NAMESPACE \
-o yaml
Step 3: Create VirtualService with Weighted Splitting
Update the frontend VirtualService from Exercise 2 so that it splits traffic 90% to v1 and 10% to v2:
oc apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: frontend
namespace: ${NAMESPACE}
spec:
hosts:
- frontend
http:
- route:
- destination:
host: frontend
subset: v1
weight: 90
- destination:
host: frontend
subset: v2
weight: 10
EOF
Define the routes for a host in a single VirtualService. If a second VirtualService (for example frontend-canary) also defines the host frontend for sidecars, Istio applies only one of them, and istioctl analyze reports IST0109.
|
Step 4: Test Weighted Traffic Splitting
Send multiple requests and observe the traffic distribution. You can check the Envoy access logs (enabled in Module 1, Exercise 2, Step 6) to see which version receives each request:
for i in {1..20}; do
oc exec \
-n $NAMESPACE \
deploy/curl-client \
-c curl -- \
curl -s http://frontend > /dev/null
done
Check the logs to see traffic distribution:
oc logs \
-n $NAMESPACE \
-l app=frontend,version=v1 \
-c istio-proxy \
--tail=10
oc logs \
-n $NAMESPACE \
-l app=frontend,version=v2 \
-c istio-proxy \
--tail=10
Step 5: Adjust Traffic Weights
Update the VirtualService to increase traffic to v2 (50/50 split):
oc apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: frontend
namespace: ${NAMESPACE}
spec:
hosts:
- frontend
http:
- route:
- destination:
host: frontend
subset: v1
weight: 50
- destination:
host: frontend
subset: v2
weight: 50
EOF
Exercise 5: Combine Gateway and VirtualService
In this exercise, you will combine a Gateway and VirtualService to provide complete external access to your services.
Step 1: Create a Complete Gateway Configuration
Ensure you have a Gateway that accepts traffic for your application:
oc get gateways.networking.istio.io my-gateway -n $NAMESPACE
If it doesn’t exist, create it:
oc apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: my-gateway
namespace: $NAMESPACE
spec:
selector:
istio: ingressgateway-user1
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "myapp-user1.example.com"
EOF
Step 2: Create VirtualService for Gateway
Create a VirtualService that works with the Gateway to route external traffic:
oc apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: myapp-external
namespace: $NAMESPACE
spec:
hosts:
- myapp-user1.example.com
gateways:
- my-gateway
http:
- match:
- uri:
prefix: "/api"
route:
- destination:
host: backend
port:
number: 80
weight: 100
- route:
- destination:
host: frontend
port:
number: 80
weight: 100
EOF
Step 3: Verify Gateway and VirtualService Integration
Check both resources:
oc get gateways.networking.istio.io,virtualservice -n $NAMESPACE
View the complete configuration:
oc get gateways.networking.istio.io my-gateway \
-n $NAMESPACE \
-o yaml
oc get virtualservice myapp-external \
-n $NAMESPACE \
-o yaml
Step 4: Test External Access
Get the ingress gateway address:
INGRESS_HOST=$(oc get svc \
-n $NAMESPACE \
istio-ingressgateway \
-o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
INGRESS_PORT=$(oc get svc \
-n $NAMESPACE \
istio-ingressgateway \
-o jsonpath='{.spec.ports[?(@.name=="http2")].port}')
If using port-forwarding and the port-forward from Exercise 3 is no longer running, start it again (if it is still running, this command fails with address already in use, and you can keep using the existing one):
oc port-forward -n $NAMESPACE svc/istio-ingressgateway 8080:80 &
Test the frontend:
curl -H "Host: myapp-user1.example.com" http://localhost:8080/
Test the API:
curl -H "Host: myapp-user1.example.com" http://localhost:8080/api
Step 5: Add Header-Based Routing
Enhance the VirtualService to include header-based routing:
oc apply -f - <<EOF
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: myapp-external
namespace: $NAMESPACE
spec:
hosts:
- myapp-user1.example.com
gateways:
- my-gateway
http:
- match:
- headers:
x-canary:
exact: "true"
route:
- destination:
host: frontend
subset: v2
weight: 100
- match:
- uri:
prefix: "/api"
route:
- destination:
host: backend
port:
number: 80
weight: 100
- route:
- destination:
host: frontend
port:
number: 80
weight: 100
EOF
Test with the canary header:
curl \
-H "Host: myapp-user1.example.com" \
-H "x-canary: true" \
http://localhost:8080/
Summary
In these exercises, you have:
-
Created and configured Gateways to accept external traffic
-
Created VirtualServices with basic routing patterns
-
Implemented path-based routing to direct traffic based on URI paths
-
Configured weighted traffic splitting for canary deployments
-
Combined Gateways and VirtualServices for complete external traffic management
-
Used DestinationRules to define service subsets for version-based routing
These skills form the foundation of Istio traffic management and are essential for implementing advanced deployment strategies in production environments.
Troubleshooting
If you encounter issues:
-
Gateway not accepting traffic: Verify the Gateway selector (
istio: ingressgateway-user1) matches the labels of your ingress gateway pods and check that the gateway pods are running -
VirtualService not routing: Ensure the VirtualService hosts match the Gateway hosts, and verify the destination services exist
-
Traffic not splitting correctly: Check that DestinationRule subsets match the pod labels, and verify the weight percentages sum to 100
-
External access not working: Verify the ingress gateway service has an external IP or use port-forwarding for testing
-
Path matching not working: Check the match conditions are correct and that routes are ordered properly (most specific first)