Editor's note: Today’s post by Frank Budinsky, Software Engineer, IBM, Andra Cismaru, Software Engineer, Google, and Israel Shalom, Product Manager, Google, is the second post in a three-part series on Istio. It offers a closer look at request routing and policy management.
In a previous article, we looked at a simple application (Bookinfo) that is composed of four separate microservices. The article showed how to deploy an application with Kubernetes and an Istio-enabled cluster without changing any application code. The article also outlined how to view Istio provided L7 metrics on the running services.
This article follows up by taking a deeper look at Istio using Bookinfo. Specifically, we’ll look at two more features of Istio: request routing and policy management.
We created an Ingress resource for the app:
In a previous article, we looked at a simple application (Bookinfo) that is composed of four separate microservices. The article showed how to deploy an application with Kubernetes and an Istio-enabled cluster without changing any application code. The article also outlined how to view Istio provided L7 metrics on the running services.
This article follows up by taking a deeper look at Istio using Bookinfo. Specifically, we’ll look at two more features of Istio: request routing and policy management.
Running the Bookinfo Application
As before, we run the v1 version of the Bookinfo application. After installing Istio in our cluster, we start the app defined in bookinfo-v1.yaml using the following command:kubectl apply -f <(istioctl kube-inject -f bookinfo-v1.yaml) |
cat <<EOF | kubectl create -f - apiVersion: extensions/v1beta1 kind: Ingress metadata: name: bookinfo annotations: kubernetes.io/ingress.class: "istio" spec: rules: - http: paths: - path: /productpage backend: serviceName: productpage servicePort: 9080 - path: /login backend: serviceName: productpage servicePort: 9080 - path: /logout backend: serviceName: productpage servicePort: 9080 EOF |
export BOOKINFO_URL=$(kubectl get po -n istio-system -l istio=ingress -o jsonpath={.items[0].status.hostIP}):$(kubectl get svc -n istio-system istio-ingress -o jsonpath={.spec.ports[0].nodePort}) |
HTTP request routing
Existing container orchestration platforms like Kubernetes, Mesos, and other microservice frameworks allow operators to control when a particular set of pods/VMs should receive traffic (e.g., by adding/removing specific labels). Unlike existing techniques, Istio decouples traffic flow and infrastructure scaling. This allows Istio to provide a variety of traffic management features that reside outside the application code, including dynamic HTTP request routing for A/B testing, canary releases, gradual rollouts, failure recovery using timeouts, retries, circuit breakers, and fault injection to test compatibility of failure recovery policies across services.To demonstrate, we’ll deploy v2 of the reviews service and use Istio to make it visible only for a specific test user. We can create a Kubernetes deployment, reviews-v2, with this YAML file:
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: reviews-v2 spec: replicas: 1 template: metadata: labels: app: reviews version: v2 spec: containers: - name: reviews image: istio/examples-bookinfo-reviews-v2:0.2.3 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 |
Before we start reviews:v2, we’ll start the last of the four Bookinfo services, ratings, which is used by the v2 version to provide ratings stars corresponding to each review:
kubectl apply -f <(istioctl kube-inject -f bookinfo-ratings.yaml) |
With Istio, new versions don’t need to become visible based on the number of running pods. Version visibility is controlled instead by rules that specify the exact criteria. To demonstrate, we start by using Istio to specify that we want to send 100% of reviews traffic to v1 pods only.
Immediately setting a default rule for every service in the mesh is an Istio best practice. Doing so avoids accidental visibility of newer, potentially unstable versions. For the purpose of this demonstration, however, we’ll only do it for the reviews service:
cat <<EOF | istioctl create -f - apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: name: reviews-default spec: destination: name: reviews route: - labels: version: v1 weight: 100 EOF |
kubectl apply -f <(istioctl kube-inject -f bookinfo-reviews-v2.yaml) |
At this point we have all kinds of options for how we might want to expose reviews:v2. If for example we wanted to do a simple canary test, we could send 10% of the traffic to v2 using a rule like this:
apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: name: reviews-default spec: destination: name: reviews route: - labels: version: v2 weight: 10 - labels: version: v1 weight: 90 |
cat <<EOF | istioctl create -f - apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: name: reviews-test-v2 spec: destination: name: reviews precedence: 2 match: request: headers: cookie: regex: "^(.*?;)?(user=jason)(;.*)?$" route: - labels: version: v2 weight: 100 EOF |
If we login to the Bookinfo UI with the user name “tester” (no password needed), we will now see version v2 of the application (each review includes 1-5 black rating stars). Every other user is unaffected by this change.
Once the v2 version has been thoroughly tested, we can use Istio to proceed with a canary test using the rule shown previously, or we can simply migrate all of the traffic from v1 to v2, optionally in a gradual fashion by using a sequence of rules with weights less than 100 (for example: 10, 20, 30, ... 100). This traffic control is independent of the number of pods implementing each version. If, for example, we had auto scaling in place, and high traffic volumes, we would likely see a corresponding scale up of v2 and scale down of v1 pods happening independently at the same time. For more about version routing with autoscaling, check out "Canary Deployments using Istio".
In our case, we’ll send all of the traffic to v2 with one command:
cat <<EOF | istioctl replace -f - apiVersion: config.istio.io/v1alpha2 kind: RouteRule metadata: name: reviews-default spec: destination: name: reviews route: - labels: version: v2 weight: 100 EOF |
istioctl delete routerule reviews-test-v2 |
Policy enforcement
Istio provides policy enforcement functions, such as quotas, precondition checking, and access control. We can demonstrate Istio’s open and extensible framework for policies with an example: rate limiting.Let’s pretend that the Bookinfo ratings service is an external paid service--for example, Rotten Tomatoes®--with a free quota of 1 request per second (req/sec). To make sure the application doesn’t exceed this limit, we’ll specify an Istio policy to cut off requests once the limit is reached. We’ll use one of Istio’s built-in policies for this purpose.
To set a 1 req/sec quota, we first configure a memquota handler with rate limits:
cat <<EOF | istioctl create -f - apiVersion: "config.istio.io/v1alpha2" kind: memquota metadata: name: handler namespace: default spec: quotas: - name: requestcount.quota.default maxAmount: 5000 validDuration: 1s overrides: - dimensions: destination: ratings maxAmount: 1 validDuration: 1s EOF |
cat <<EOF | istioctl create -f - apiVersion: "config.istio.io/v1alpha2" kind: quota metadata: name: requestcount namespace: default spec: dimensions: source: source.labels["app"] | source.service | "unknown" sourceVersion: source.labels["version"] | "unknown" destination: destination.labels["app"] | destination.service | "unknown" destinationVersion: destination.labels["version"] | "unknown" --- apiVersion: "config.istio.io/v1alpha2" kind: rule metadata: name: quota namespace: default spec: actions: - handler: handler.memquota instances: - requestcount.quota EOF |
wrk -t1 -c1 -d20s http://$BOOKINFO_URL/productpage |
Stopping the load generator means the limit will no longer be exceeded: the black stars return when we refresh the page.
Summary
We’ve shown you how to introduce advanced features like HTTP request routing and policy injection into a service mesh configured with Istio without restarting any of the services. This lets you develop and deploy without worrying about the ongoing management of the service mesh; service-wide policies can always be added later.In the next and last installment of this series, we’ll focus on Istio’s security and authentication capabilities. We’ll discuss how to secure all interservice communications in a mesh, even against insiders with access to the network, without any changes to the application code or the deployment.