In the first part of the lab, you created an ASP.NET Core application, containerized, and deployed it to Google Kubernetes Engine (GKE) and configured its traffic to be managed by Istio.

This second part of the lab assumes that you already have your Kubernetes cluster and the application from the first lab running. You will see how Istio can help to manage, monitor, and secure your services with minimal code changes. Specifically, you explore features of Istio such as metrics, tracing, service visualization, dynamic traffic management, fault injection, and more.

What you'll learn

What you'll need

How will you use this tutorial?

Read it through only Read it and complete the exercises

How would rate your experience with Google Cloud Platform?

Novice Intermediate Proficient

Self-paced environment setup

If you don't already have a Google Account (Gmail or Google Apps), you must create one. Sign-in to Google Cloud Platform console (console.cloud.google.com) and create a new project:

Remember the project ID, a unique name across all Google Cloud projects (the name above has already been taken and will not work for you, sorry!). It will be referred to later in this codelab as PROJECT_ID.

Next, you'll need to enable billing in the Cloud Console in order to use Google Cloud resources.

Running through this codelab shouldn't cost you more than a few dollars, but it could be more if you decide to use more resources or if you leave them running (see "cleanup" section at the end of this document).

New users of Google Cloud Platform are eligible for a $300 free trial.

Start Cloud Shell

While Google Cloud can be operated remotely from your laptop, in this codelab you use Google Cloud Shell, a command line environment running in Google Cloud.

Activate Google Cloud Shell

From the GCP Console click the Cloud Shell icon on the top right toolbar:

Then click "Start Cloud Shell":

It should only take a few moments to provision and connect to the environment:

This virtual machine is loaded with all the development tools you'll need. It offers a persistent 5GB home directory, and runs on the Google Cloud, greatly enhancing network performance and authentication. Much, if not all, of your work in this lab can be done with simply a browser or your Google Chromebook.

Once connected to the cloud shell, you should see that you are already authenticated and that the project is already set to your PROJECT_ID.

Run the following command in the cloud shell to confirm that you are authenticated:

gcloud auth list

Command output

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

Command output

[core]
project = <PROJECT_ID>

If it is not, you can set it with this command:

gcloud config set project <PROJECT_ID>

Command output

Updated property [core/project].

Before you start the lab, make sure the application is still working from the previous lab. As a reminder, this is how you see the external IP and port of the gateway, which are listed under EXTERNAL-IP:

kubectl get svc istio-ingressgateway -n istio-system

To view the application, you can open your browser and navigate to http://<gatewayurl>:

If you don't see the application, go back to the previous lab to make sure you followed all the steps and both the application and Istio are installed and running properly.

At this point, you might be wondering "What's the benefit of Istio?". By letting Istio manage your application's traffic, you get features like metrics, tracing, dynamic traffic management, service visualization, fault injection, and more for free.

You'll start with exploring metrics in the next step.

By default, Istio generates some metrics. You can use add-ons to query and visualize these default metrics. When you installed Istio using the istio-demo-auth.yaml file, it also installed a couple of add-ons for metrics: Prometheus and Grafana.

Prometheus

Prometheus is an open-source monitoring solution. You can use Prometheus to query metrics generated by Istio.

Verify that Prometheus is running:

kubectl get svc prometheus -n istio-system

NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
prometheus   ClusterIP   10.31.243.62   <none>        9090/TCP   1d

Send some traffic to the application by visiting http://<gatewayurl> a few times or running the curl command.

Setup port forwarding for the Prometheus UI:

kubectl -n istio-system port-forward $(kubectl -n istio-system get pod -l app=prometheus -o jsonpath='{.items[0].metadata.name}') 8080:9090 &

You can now execute a query by clicking on the Web Preview button in the top-right corner of Cloud Shell and click Preview on port 8080:

You'll see the Prometheus UI in a new tab:

To learn more about Prometheus, see Querying Metrics with Prometheus.

Grafana

Grafana is another add-on for visualizing metrics.

Verify that Grafana is running:

kubectl get svc grafana -n istio-system

NAME      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
grafana   ClusterIP   10.31.248.230   <none>        3000/TCP   1d

Send some traffic to the application by visiting http://<gatewayurl> a few times or running the curl command.

Setup port forwarding for the Grafana UI:

$ kubectl -n istio-system port-forward $(kubectl -n istio-system get pod -l app=grafana -o jsonpath='{.items[0].metadata.name}') 8080:3000 &

You can view Grafana dashboards by visiting Web Preview:

To learn more about Granfana, see Visualizing Metrics with Grafana.

By default, Istio captures trace spans for all requests. This helps to visualize where the time is being spent in your services and to troubleshoot issues in complex distributed systems.

When you installed Istio using the istio-demo-auth.yaml file, it also installed Jaeger, an open source distributed tracing framework.

Verify that Jaeger is running:

kubectl get svc jaeger-query -n istio-system

NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)
jaeger-query   ClusterIP   10.31.244.137   <none>        16686/TCP

Send some traffic to the application by visiting http://<gatewayurl> a few times or running the curl command.

Setup port forwarding for Jaeger:

kubectl port-forward -n istio-system $(kubectl get pod -n istio-system -l app=jaeger -o jsonpath='{.items[0].metadata.name}') 8080:16686 &

You can view Jaeger dashboard by visiting Web Preview. To see traces of your app, select aspnetcore from the Service menu on the right:

To learn more about the available configuration options, see Distributed Tracing.

In distributed systems, it can be difficult to figure out dependencies between services. Thankfully, you can use yet another add-on of Istio, Service Graph, to visualize dependencies between your services in the service mesh.

Verify that Service Graph is running:

kubectl get svc servicegraph -n istio-system

NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)
servicegraph   ClusterIP   10.31.250.104   <none>        8088/TCP

Setup port forwarding for Service Graph:

kubectl -n istio-system port-forward $(kubectl -n istio-system get pod -l app=servicegraph -o jsonpath='{.items[0].metadata.name}') 8080:8088 &

Service Graph comes with a few different visualizations. You can see one of them by visiting Web Preview and appending /force/forcegraph.html to the URL.:

To learn more about the available visualizations, see Service Graph.

At some point, the application that you've deployed to production will require bug fixes or additional features. Let's see how that process looks like.

First, let's modify the application. Open the code editor from Cloud Shell.

Navigate to Index.cshtml under HelloWorldAspNetCore > Views > Home and update one of the carousel messages.

Find the following line:

Learn how to build ASP.NET apps that can run anywhere 

And change it to this:

Learn how to build ASP.NET apps that can run on Google Cloud Platform! 

Save the changes and then go back to Cloud Shell. Inside HelloWorldAspNetCore,publish the app to get a self-contained DLL:

dotnet publish -c Release

You can now build and publish a new container image to the registry with an incremented tag (v2 in this case):

cd bin/Release/netcoreapp2.1/publish/ 
docker build -t gcr.io/${PROJECT_ID}/hello-dotnet:v2 . 
gcloud docker -- push gcr.io/${PROJECT_ID}/hello-dotnet:v2 

After you push the container image, you can deploy the new version in the next step.

To deploy the new version, you first need to create a new deployment for it in Kubernetes. Add the following at the end of the aspnetcore.yaml file:

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: aspnetcore-v2
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: aspnetcore
        version: v2
    spec:
      containers:
      - name: aspnetcore
        image: gcr.io/YOUR-PROJECT-ID/hello-dotnet:v2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080

Deploy the new version to the default namespace with kubectl:

kubectl apply -f aspnetcore.yaml
service "aspnetcore" unchanged
deployment.extensions "aspnetcore-v1" unchanged
deployment.extensions "aspnetcore-v2" created

Verify that the expected pods are running:

kubectl get pods
NAME                          READY     STATUS    RESTARTS   AGE
aspnetcore-v1-6cf64748-mddb   2/2       Running   0          34s
aspnetcore-v2-5d765db-l9xmg   2/2       Running   0          1m

Now, test the application again. Get the external IP of the gateway:

kubectl get svc istio-ingressgateway -n istio-system

It's listed under EXTERNAL-IP. Open up the browser and visit http://<replace-with-external-ip>

When you refresh, sometimes, you'll see the message "Learn how to build ASP.NET apps that can run anywhere":

Other times, you'll see the message "Learn how to build ASP.NET apps that can run on Google Cloud Platform":

This is because both v1 and v2 deployments are exposed behind the same Kubernetes service (aspnetcore-service) and the VirtualService you created in the previous lab (aspnetcore-virtualservice) uses that service as a host.

In the next step, you pin the service to the v2 deployment using a DestinationRule.

In this step, you pin your service to use the v2 deployment and you can do that with a DestinationRule. A DestinationRule configures the set of policies to be applied to a request after a VirtualService routing operation has occurred.

A DestinationRule also defines addressable subsets, meaning named versions, of the corresponding destination host. These subsets are used in VirtualService route specifications when sending traffic to specific versions of the service.

Create a new file called aspnetcore-destinationrule.yaml:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: aspnetcore-destinationrule
spec:
  host: aspnetcore-service
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2

Next, create the DestinationRule. This creates two subsets (v1 and v2) that you can use from the VirtualService:

kubectl apply -f aspnetcore-destinationrule.yaml
destinationrule.networking.istio.io "aspnetcore-destionationrule" created

Now, go back to aspnetcore-virtualservice.yaml file to update the VirtualService to use the v2 subset:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: aspnetcore-virtualservice
spec:
  hosts:
  - "*"
  gateways:
  - aspnetcore-gateway
  http:
  - route:
    - destination:
        host: aspnetcore-service
        subset: v2

Update the VirtualService:

kubectl apply -f aspnetcore-virtualservice.yaml

Open up your browser and visit http://<replace-with-external-ip>. Even after multiple refreshes, you should see the message "Learn how to build ASP.NET apps that can run on Google Cloud Platform":

Sometimes, you might want to split traffic between versions for testing. For example, you might want to send 75% of the traffic to the v1 and 25% of the traffic to the v2 version of the service. You can easily achieve this with Istio. Create a new aspnetcore-virtualservice-weights.yaml file to refer to the two subsets with different weights:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: aspnetcore-virtualservice
spec:
  hosts:
  - "*"
  gateways:
  - aspnetcore-gateway
  http:
  - route:
    - destination:
        host: aspnetcore-service
        subset: v1
      weight: 75
    - destination:
        host: aspnetcore-service
        subset: v2
      weight: 25

Update the VirtualService:

kubectl apply -f aspnetcore-virtualservice-weights.yaml

Now, when you refresh the browser, you should see the v1 vs. v2 versions served with roughly a 3:1 ratio.

To learn more, see traffic splitting in Istio.

Another useful development task to do for testing is to inject faults or delays into the traffic and see how services behave in response.

For example, you might want to return a bad request (HTTP 400) response for 50% of the traffic to the v1 version. Create aspnetcore-virtualservice-fault-abort.yaml file to match the following:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: aspnetcore-virtualservice
spec:
  hosts:
  - "*"
  gateways:
  - aspnetcore-gateway
  http:
  - fault:
      abort:
        percent: 50
        httpStatus: 400
    route:
    - destination:
        host: aspnetcore-service
        subset: v1

Update the VirtualService:

kubectl apply -f aspnetcore-virtualservice-fault-abort.yaml

Now, when you refresh the browser, you should see that half of the time, the v1 service returns a HTTP 400s response code.

Or maybe you want to add 5 seconds delay to the requests. Create aspnetcore-virtualservice-fault-delay.yaml file to match the following:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: aspnetcore-virtualservice
spec:
  hosts:
  - "*"
  gateways:
  - aspnetcore-gateway
  http:
  - fault:
      delay:
        fixedDelay: 5s
        percent: 100
    route:
    - destination:
        host: aspnetcore-service
        subset: v1

Update the VirtualService:

kubectl apply -f aspnetcore-virtualservice-fault-delay.yaml

Now, when you refresh the browser, you should see that requests are delayed by 5 seconds.

To learn more about Istio features such as timeouts, retries, conditional rules, circuit breakers and more, see traffic management features.

Hopefully, this lab gave you an overview of what Istio can do for your services out of the box. To learn more about Istio and GKE.

Next Steps

License

This work is licensed under a Creative Commons Attribution 2.0 Generic License.

You can delete the app and uninstall Istio or you can simply delete the Kubernetes cluster.

Delete the application

To delete the application:

kubectl delete -f aspnetcore-gateway.yaml
kubectl delete -f aspnetcore-virtualservice.yaml
kubectl delete -f aspnetcore-destinationrule.yaml
kubectl delete -f aspnetcore.yaml

To confirm that the application is gone:

kubectl get gateway 
kubectl get virtualservices
kubectl get destinationrule
kubectl get pods

Uninstall Istio

To delete Istio:

kubectl delete -f install/kubernetes/istio-demo-auth.yaml

To confirm that Istio is gone:

kubectl get pods -n istio-system

Delete Kubernetes cluster

gcloud container clusters delete hello-dotnet-cluster