├── .gitignore ├── README.md ├── TROUBLESHOOTING.md ├── exercise-1 └── README.md ├── exercise-10 └── README.md ├── exercise-2 ├── README.md └── optional.md ├── exercise-3 └── README.md ├── exercise-4 └── README.md ├── exercise-5 └── README.md ├── exercise-6 └── README.md ├── exercise-7 └── README.md ├── exercise-8 └── README.md ├── exercise-9 └── README.md ├── guestbook ├── broken-redis-service.yaml ├── deleteGuestBook.sh ├── deployGuestBookIstio.sh ├── deployGuestBookIstioWindows.txt ├── deployGuestBookKube.sh ├── guestbook-deployment.yaml ├── guestbook-ingress.yaml ├── guestbook-service.yaml ├── guestbook-ui-deployment.yaml ├── guestbook-ui-service.yaml ├── helloworld-deployment-v2.yaml ├── helloworld-deployment.yaml ├── helloworld-service.yaml ├── mixer-kubernetes-rules.yaml ├── mixer-rule-denial-v2.yaml ├── mixer-rule-denial.yaml ├── mysql-deployment.yaml ├── mysql-pvc.yaml ├── mysql-service.yaml ├── rate-limit-ui-service.yaml ├── redis-deployment.yaml ├── redis-service.yaml ├── route-rule-80-20.yaml ├── route-rule-canary.yaml ├── route-rule-delay-guestbook.yaml ├── route-rule-force-hello-v1.yaml ├── route-rule-helloworld-service-503.yaml ├── route-rule-user-agent-chrome.yaml ├── telemetry-global.yaml └── telemtry_rule.yaml ├── kubernetes ├── helloworldservice-deployment.yaml └── helloworldservice-service.yaml └── setup └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | hs_err_pid* 3 | target 4 | .idea 5 | 6 | target/ 7 | !.mvn/wrapper/maven-wrapper.jar 8 | 9 | ### STS ### 10 | .apt_generated 11 | .classpath 12 | .factorypath 13 | .project 14 | .settings 15 | .springBeans 16 | 17 | ### IntelliJ IDEA ### 18 | .idea 19 | *.iws 20 | *.iml 21 | *.ipr 22 | 23 | ### NetBeans ### 24 | nbproject/private/ 25 | build/ 26 | nbbuild/ 27 | dist/ 28 | nbdist/ 29 | .nb-gradle/ 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Workshop Setup 2 | - [Setup for the workshop](setup/README.md) 3 | - [Exercise 1 - Google Cloud SDK Setup](exercise-1/README.md) 4 | 5 | ## Exploring Kubernetes 6 | 7 | - [Exercise 2 - Deploying a microservice to Kubernetes](exercise-2/README.md) 8 | - [Exercise 3 - Creating a Kubernetes Service](exercise-3/README.md) 9 | - [Exercise 4 - Scaling In and Out](exercise-4/README.md) 10 | 11 | ## Creating a Service Mesh with Istio 12 | 13 | - [Exercise 5 - Installing Istio](exercise-5/README.md) 14 | - [Exercise 6 - Creating a Service Mesh with Istio Proxy](exercise-6/README.md) 15 | - [Exercise 7 - Istio Ingress Controller](exercise-7/README.md) 16 | - [Exercise 8 - Request Routing and Canary Deployments](exercise-8/README.md) 17 | - [Exercise 9 - Fault Injection](exercise-9/README.md) 18 | - [Exercise 10 - Telemetry and Rate Limiting with Mixer](exercise-10/README.md) 19 | 20 | 21 | ## Credits 22 | These workshop exercises are built with the help from a number of amazing Kubernetes and Istio Experts from Google and Grand Cloud. 23 | 24 | #### Ray Tsang [@saturnism](https://twitter.com/saturnism) 25 | The Kubernetes and Istio Exercises are dervied from the work of Ray Tsang [@saturnism](https://twitter.com/saturnism) and these repositories: 26 | 27 | [https://github.com/saturnism/spring-boot-docker](https://github.com/saturnism/spring-boot-docker) 28 | 29 | [https://github.com/saturnism/istio-by-example-java](https://github.com/saturnism/istio-by-example-java) 30 | 31 | #### Zach Butcher [@ZachButcher](https://twitter.com/ZackButcher) 32 | Zach was insturmental in helping write the Istio tutorials. 33 | 34 | #### Kelsey Hightower [@kelseyhightower](https://twitter.com/kelseyhightower) 35 | The Istio Ingress Tutorial is largely based on the work of Kelsey and this repository: 36 | 37 | [https://github.com/kelseyhightower/istio-ingress-tutorial](https://github.com/kelseyhightower/istio-ingress-tutorial) 38 | 39 | Kelsey's tutorial uses more advance features of Kubernetes to taint some of the nodes so that the ingress controller runs on dedicated nodes. The ingress controller is then deployed as a daemonset. 40 | -------------------------------------------------------------------------------- /TROUBLESHOOTING.md: -------------------------------------------------------------------------------- 1 | ##Kubernetes Troubleshooting 2 | 3 | The most important tool to know for debugging problems is the describe resource command. 4 | It can be used to describe any Kubernetes resource. 5 | 6 | `kubectl describe pod helloworld-service-v1-119527584-jwfzh` 7 | 8 | `kubectl describe service helloworld-service` 9 | 10 | kubectl top can be used to see resource utilization. A very common problem is the pod was not able to be scheduled. 11 | 12 | `kubectl top nodes` 13 | 14 | `kubectl top pods` 15 | -------------------------------------------------------------------------------- /exercise-1/README.md: -------------------------------------------------------------------------------- 1 | ## Exercise 1 - Startup a Kubernetes Cluster 2 | 3 | #### Set Default Region and Zones 4 | 5 | `gcloud config set compute/zone us-central1-f` 6 | 7 | `gcloud config set compute/region us-central1` 8 | 9 | #### Create a Kubernetes Cluster using the Google Container Engine. 10 | 11 | Google Container Engine is Google’s hosted version of Kubernetes. 12 | 13 | Note if you have multiple gcloud accounts then specify a project name using the --project flag. By default it can be left off. You can get your project name from the command line with: 14 | 15 | `gcloud config get-value core/project` 16 | 17 | Or the project name can be found in the URL of Google Cloud Console. For example if you look in the console it should be something like: 18 | 19 | https://console.cloud.google.com/kubernetes/list?project=workshopcluster-177619 20 | 21 | The project name in that case is workshopcluster-177619 22 | 23 | `gcloud container clusters create guestbook --num-nodes 4 --scopes cloud-platform --project ` 24 | 25 | ## Explanation 26 | #### By Ray Tsang [@saturnism](https://twitter.com/saturnism) 27 | 28 | This will take a few minutes to run. Behind the scenes, it will create Google Compute Engine instances, and configure each instance as a Kubernetes node. These instances don’t include the Kubernetes Master node. In Google Container Engine, the Kubernetes Master node is managed service so that you don’t have to worry about it! 29 | 30 | The scopes parameter is important for this lab. Scopes determine what Google Cloud Platform resources these newly created instances can access. By default, instances are able to read from Google Cloud Storage, write metrics to Google Cloud Monitoring, etc. For our lab, we add the cloud-platform scope to give us more privileges, such as writing to Cloud Storage as well. 31 | 32 | #### [Continue to Exercise 2 - Deploying a microservice to Kubernetes](../exercise-2/README.md) 33 | -------------------------------------------------------------------------------- /exercise-10/README.md: -------------------------------------------------------------------------------- 1 | ## Exercise 10 - Telemetry and Rate Limiting with Mixer 2 | 3 | #### Instal Istio Monitoring and Metrics Tools 4 | 5 | ``` 6 | kubectl apply -f install/kubernetes/addons/prometheus.yaml 7 | kubectl apply -f install/kubernetes/addons/grafana.yaml 8 | kubectl apply -f install/kubernetes/addons/servicegraph.yaml 9 | kubectl apply -f install/kubernetes/addons/zipkin.yaml 10 | ``` 11 | 12 | #### Adding Telemetry Rules 13 | 14 | Validate that the selected service has no service-specific rules already applied. 15 | 16 | ``` 17 | istioctl mixer rule get helloworld-ui.default.svc.cluster.local helloworld-ui.default.svc.cluster.local 18 | Error: the server could not find the requested resource 19 | ``` 20 | 21 | Push the new configuration to Mixer for a specific service. 22 | 23 | ``` 24 | istioctl mixer rule create helloworld-service.default.svc.cluster.local helloworld-service.default.svc.cluster.local -f telemetry_rule.yaml 25 | istioctl mixer rule create helloworld-ui.default.svc.cluster.local helloworld-ui.default.svc.cluster.local -f telemetry_rule.yaml 26 | ``` 27 | 28 | If the service had service-specific rules you would want to add them to the telemetry rules. 29 | 30 | Send traffic to that hello world ui service by either going to the web page or using the curl command. Refresh the page several times or issue the curl command a few times to generate traffic. 31 | 32 | ``` 33 | watch curl http://104.198.198.111/echo/dog2 -A mobile 34 | ``` 35 | 36 | Verify that the new metric is being collected. Browse to the Grafana dashboard by viewing the kubernetes services and finding the external IP. The browse to: 37 | 38 | http://104.197.73.207:3000/dashboard/db/istio-dashboard 39 | 40 | #### Apply Across the Service mesh 41 | 42 | But...that’s tedious to do for every service in your mesh. Instead, let’s apply our telemetry configuration to the whole mesh: 43 | 44 | ``` 45 | istioctl mixer rule create global global -f global_telemetry.yaml 46 | ``` 47 | 48 | (Note: we have to use a different config file because in 0.1 Mixer rules are full document writes; in 0.2 configuration is much more granular) 49 | 50 | We can also see the logs Mixer creates for our services: 51 | 52 | ``` 53 | kubectl logs $(kubectl get pods -l istio=mixer -o jsonpath='{.items[0].metadata.name}') | grep \"combined_log\" 54 | ``` 55 | 56 | #### Rate Limiting with Mixer 57 | 58 | Then apply a 1 request per second rate limit from the UI to the helloworld-service 59 | 60 | ``` 61 | istioctl mixer rule create global helloworld-service.default.svc.cluster.local -f rate-limit-ui-service.yaml 62 | ``` 63 | 64 | Then we can drive traffic to the UI to see the rate limit in action: 65 | 66 | ``` 67 | watch -n 0.1 curl -i /echo/foo 68 | ``` 69 | 70 | and in grafana we can see the 429’s. 71 | 72 | http://104.197.73.207:3000/dashboard/db/istio-dashboard 73 | -------------------------------------------------------------------------------- /exercise-2/README.md: -------------------------------------------------------------------------------- 1 | ## Exercise 2 - Deploying a microservice to Kubernetes 2 | 3 | #### Deploy Hello World 4 | 5 | 1 - Deploy hello world service to kubernetes 6 | 7 | `kubectl apply -f kubernetes/helloworldservice-deployment.yaml --record` 8 | 9 | `kubectl get pods` 10 | 11 | >NAME READY STATUS RESTARTS AGE 12 | 13 | >helloworld-service-v1-.... 1/1 Running 0 20s 14 | 15 | 2 - Note the name of the pod above for use in the command below. Then delete one of the hello world pods. 16 | 17 | `kubectl delete pod kubernetes/helloworld-service-v1-...` 18 | 19 | 3 - Kubernetes will automatically restart this pod for you. Verify it is restarted 20 | 21 | `kubectl get pods` 22 | 23 | >NAME READY STATUS RESTARTS AGE 24 | 25 | >helloworld-service-v1-.... 1/1 Running 0 20s 26 | 27 | 4 - All of the container output to STDOUT and STDERR will be accessible as Kubernetes logs: 28 | 29 | `kubectl logs helloworld-service-v1-...` 30 | 31 | `kubectl logs -f helloworld-service-v1-...`` 32 | 33 | ## Explanation 34 | 35 | #### By Ray Tsang [@saturnism](https://twitter.com/saturnism) 36 | 37 | We will be using yaml files throughout this workshop. Every file describes a resource that needs to be deployed into Kubernetes. We won’t be able to go into details on the contents, but you are definitely encouraged to read them and see how pods, services, and others are declared. 38 | 39 | The pod deploys a microservice that is a container whose images contains a self-executing JAR files. The source is available at [istio-by-example-java](https://github.com/saturnism/istio-by-example-java) if you are interested in seeing it. 40 | 41 | In this first example we deployed a Kubernetes pod by specifying a deployment using this [helloworldservice-deployment.yaml](helloworldservice-deployment.yaml). 42 | 43 | A Kubernetes pod is a group of containers, tied together for the purposes of administration and networking. It can contain one or more containers. All containers within a single pod will share the same networking interface, IP address, volumes, etc. All containers within the same pod instance will live and die together. It’s especially useful when you have, for example, a container that runs the application, and another container that periodically polls logs/metrics from the application container. 44 | 45 | You can start a single Pod in Kubernetes by creating a Pod resource. However, a Pod created this way would be known as a Naked Pod. If a Naked Pod dies/exits, it will not be restarted by Kubernetes. A better way to start a pod, is by using a higher-level construct such as Replication Controller, Replica Set, or a Deployment. 46 | 47 | Prior to Kubernetes 1.2, Replication Controller is the preferred way deploy and manage your application instances. Kubernetes 1.2 introduced two new concepts - Replica Set, and Deployments. 48 | 49 | Replica Set is the next-generation Replication Controller. The only difference between a Replica Set and a Replication Controller right now is the selector support. Replica Set supports the new set-based selector requirements whereas a Replication Controller only supports equality-based selector requirements. 50 | 51 | For example, Replication Controller can only select pods based on equality, such as "environment = prod", whereas Replica Sets can select using the "in" operator, such as "environment in (prod, qa)". Learn more about the different selectors in the [Labels guide](http://kubernetes.io/docs/user-guide/labels). 52 | 53 | Deployment provides declarative updates for Pods and Replica Sets. You only need to describe the desired state in a Deployment object, and the Deployment controller will change the actual state to the desired state at a controlled rate for you. You can use deployments to easily: 54 | - Create a Deployment to bring up a Replica Set and Pods. 55 | - Check the status of a Deployment to see if it succeeds or not. 56 | - Later, update that Deployment to recreate the Pods (for example, to use a new image, or configuration). 57 | - Rollback to an earlier Deployment revision if the current Deployment isn’t stable. 58 | - Pause and resume a Deployment. 59 | 60 | In this workshop, because we are working with Kubernetes 1.7+, we will be using Deployment extensively. 61 | 62 | There are other containers running too. The interesting one is the pause container. The atomic unit Kubernetes can manage is actually a Pod, not a container. A Pod can be composed of multiple tightly-coupled containers that is guaranteed to scheduled onto the same node, and will share the same Pod IP address, and can mount the same volumes.. What that essentially means is that if you run multiple containers in the same Pod, they will share the same namespaces. 63 | 64 | A pause container is how Kubernetes uses Docker containers to create shared namespaces so that the actual application containers within the same Pod can share resources. 65 | 66 | #### Optional - [Peering under the covers of Kubernetes](optional.md) 67 | 68 | #### [Continue to Exercise 3 - Creating a Kubernetes Service](../exercise-3/README.md) 69 | -------------------------------------------------------------------------------- /exercise-2/optional.md: -------------------------------------------------------------------------------- 1 | ## Exercise 2 - Optional 2 | ## Peering under the covers of node 3 | 4 | `kubectl get pods -owide` 5 | 6 | That will list the node the pod is running on. For example you should see: 7 | 8 | `NODE gke-guestbook-...` 9 | 10 | `gcloud compute ssh ` 11 | 12 | `sudo docker ps` 13 | 14 | `someuser@:~$ exit` 15 | 16 | The Pod name is automatically assigned as the hostname of the container: 17 | 18 | ``` 19 | kubectl exec -ti helloworld-service-v1-... /bin/bash 20 | 21 | root@helloworld-...:/data# hostname 22 | helloworld-service-.... 23 | 24 | root@helloworld-...:/app/src# hostname -i 25 | 10.104.1.5 26 | 27 | root@helloworld-...:/app/src# exit 28 | 29 | root@helloworld-...:/data# hostname -i 30 | 10.104.1.5 31 | ``` 32 | 33 | #### [Continue to Exercise 3 - Creating a Kubernetes Service](../exercise-3/README.md) 34 | -------------------------------------------------------------------------------- /exercise-3/README.md: -------------------------------------------------------------------------------- 1 | ## Exercise 3 - Creating a Kubernetes Service 2 | 3 | Each Pod has a unique IP address - but the address is ephemeral. The Pod IP addresses are not stable and it can change when Pods start and/or restart. A service provides a single access point to a set of pods matching some constraints. A Service IP address is stable. 4 | 5 | In Kubernetes, you can instruct the underlying infrastructure to create an external load balancer, by specifying the Service Type as a LoadBalancer. If you open up [helloworldservice-service.yaml](helloworldservice-service.yaml) you will see that it has a type: LoadBalancer 6 | 7 | #### Create the Hello World Service “service” 8 | 9 | `kubectl apply -f kubernetes/helloworldservice-service.yaml --record` 10 | 11 | `kubectl get services` 12 | 13 | The external ip will start as pending. After a short period the EXTERNAL IP will be populated. This is the external IP of the Load Balancer. 14 | 15 | #### Curl the external ip to test the helloworld service: 16 | 17 | `curl 104.154.120.67:8080/hello/world` 18 | 19 | #### Explanation 20 | #### By Ray Tsang [@saturnism](https://twitter.com/saturnism) 21 | 22 | Open the [helloworldservice-service.yaml](helloworldservice-service.yaml) to examine the service descriptor. The important part about this file is the selector section. This is how a service knows which pod to route the traffic to, by matching the selector labels with the labels of the pods. 23 | 24 | The other important part to notice in this file is the type of service is a Load Balancer. This tells GCE that an externally facing load balancer should be created for this service so that it is accessible from the outside. 25 | 26 | Since we are running two instances of the Hello World Service (one instance in one pod), and that the IP addresses are not only unique, but also ephemeral - how will a client reach our services? We need a way to discover the service. 27 | 28 | In Kubernetes, Service Discovery is a first class citizen. We created a Service that will: 29 | act as a load balancer to load balance the requests to the pods, and 30 | provide a stable IP address, allow discovery from the API, and also create a DNS name! 31 | 32 | #### Optional - curl the service using a DNS name 33 | 34 | If you login into another container you can access the helloworldservice via the DNS name. For example start tutum/curl to get a shell and curl the service using the service name: 35 | 36 | ``` 37 | kubectl run curl --image=tutum/curl -i --tty 38 | 39 | root@busybox:/data# wget -qO- http://helloworld-service:8080/hello/Batman 40 | {"greeting":"Hello Batman from helloworld-service-... with 1.0","hostname":"helloworld-service-...","version":"1.0"} 41 | 42 | root@busybox:/data# exit 43 | ``` 44 | 45 | #### [Continue to Exercise 4 - Scaling In and Out](../exercise-4/README.md) 46 | -------------------------------------------------------------------------------- /exercise-4/README.md: -------------------------------------------------------------------------------- 1 | ## Exercise 4 - Scaling In and Out 2 | 3 | #### Scale the number of Hello World Service “pods 4 | 5 | 1 - Scaling the number of replicas of our Hello World service is as simple as running : 6 | 7 | `kubectl get deployment` 8 | 9 | `kubectl scale deployment helloworld-service-v1 --replicas=4` 10 | 11 | `kubectl get deployment` 12 | 13 | `kubectl get pods` 14 | 15 | 2 - Scale out even more 16 | 17 | `kubectl scale deployment helloworld-service-v1 --replicas=12` 18 | 19 | If you look at the pod status some of the Pods will show a `Pending` state. That is because we only have four physical nodes, and the underlying infrastructure has run out of capacity to run the containers with the requested resources. 20 | 21 | 3 - Pick a Pod name that is associated with the Pending state to confirm the lack of resources in the detailed status: 22 | 23 | `kubectl describe pod helloworld-service...` 24 | 25 | 4 - We can easily spin up another Compute Engine instance to append to the cluster. 26 | 27 | `gcloud container clusters resize guestbook --size=5` 28 | 29 | `gcloud compute instances list` 30 | 31 | 5 - Verify the new instance has joined the Kubernetes cluster, you’ll should be able to see it with this command: 32 | 33 | `kubectl get nodes` 34 | 35 | `kubectl scale deployment helloworld-service-v1 --replicas=4` 36 | 37 | #### [Continue to Exercise 5 - Installing Istio](../exercise-5/README.md) 38 | -------------------------------------------------------------------------------- /exercise-5/README.md: -------------------------------------------------------------------------------- 1 | ## Exercise 5 - Installing Istio 2 | 3 | #### Clean up 4 | 5 | Start with a clean slate and delete all deployed services from the cluster: 6 | 7 | ``` 8 | kubectl delete all 9 | ``` 10 | 11 | #### Download Istio 12 | 13 | Either download it directly or get the latest using curl: 14 | 15 | https://github.com/istio/istio/releases 16 | 17 | ``` 18 | curl -L https://git.io/getLatestIstio | sh - 19 | export PATH=$PWD/istio-0.2.10/bin:$PATH 20 | ``` 21 | 22 | #### Running istioctl 23 | 24 | Istio related commands need to have `istioctl` in the path. Verify it is available by running: 25 | 26 | `istioctl -h` 27 | 28 | 29 | #### Install Istio on the Kubernetes Cluster 30 | 31 | 1 - First grant cluster admin permissions to the current user (admin permissions are required to create the necessary RBAC rules for Istio): 32 | 33 | ``` 34 | kubectl create clusterrolebinding cluster-admin-binding \ 35 | --clusterrole=cluster-admin \ 36 | --user=$(gcloud config get-value core/account) 37 | ``` 38 | 2 - Next install Istio on the Kubernetes cluster: 39 | 40 | Change to the Istio directory (istio-0.2.10) and and install istio in the kubernetes cluster 41 | 42 | ``` 43 | cd istio-0.2.10 44 | kubectl apply -f install/kubernetes/istio.yaml 45 | ``` 46 | 47 | 48 | #### Viewing the Istio Deployments 49 | 50 | Istio is deployed in a separate Kubernetes namespace `istio-system` You can watch the state of Istio and other services and pods using the watch command. For example in 2 separate terminal windows run: 51 | 52 | ``` 53 | watch -n 30 kubectl get po --all-namespaces 54 | watch -n 30 kubectl get svc --all-namespaces 55 | ``` 56 | 57 | #### [Continue to Exercise 6 - Creating a Service Mesh with Istio Proxy](../exercise-6/README.md) 58 | -------------------------------------------------------------------------------- /exercise-6/README.md: -------------------------------------------------------------------------------- 1 | ## Exercise 6 - Creating a Service Mesh with Istio Proxy 2 | 3 | #### What is a Service Mesh? 4 | 5 | Cloud Native Applications require a new approach to managing the communication between each service. This problem is best solved by creating a dedicated infrastructure layer that handles service-to-service communication. With Istio this infrastructure layer is created by deploying a lightweight proxy alongside each application service. It is done in a way that the application does not need to be aware of the proxy. 6 | 7 | By moving the service communication to a separate layer it provides a separation of concerns where the monitoring, management and security of communication can be handled outside of the application logic. 8 | 9 | #### Istio Proxy Side Car 10 | 11 | To create a service mesh with Istio we update the deployment of the Pods to add the Istio Proxy (based on the Lyft Envoy Proxy) as a side car to each Pod. The Proxy is then run as a separate container that manages all communication with that Pod. This can be done either manually or with the latest version of Kubernetes automatically. 12 | 13 | Manually the side car can be injected by running the istioctl kube-inject command which modifies the yaml file before creating the deployments. This injects the Proxy into the deployment by updating the YAML to add the Proxy as a sidecar. When this command is used the microservices are now packaged with an Proxy sidecar that manages incoming and outgoing calls for the service. 14 | 15 | Istio sidecars can also be automatically injected into a Pod before deployment using an alpha feature in Kubernetes called Initializers. We will not be covering that feature in this workshop. 16 | 17 | To see how the deployment yaml is modified run the following command: 18 | 19 | ``` 20 | istioctl kube-inject -f guestbook/helloworld-deployment.yaml 21 | ``` 22 | 23 | Inside the yaml there is now an additional container: 24 | 25 | ``` 26 | image: docker.io/istio/proxy_debug:0.2.9 27 | imagePullPolicy: IfNotPresent 28 | name: istio-proxy 29 | ``` 30 | 31 | This adds the Istio Proxy as an additional container to the Pod and setups the necessary configuration. 32 | 33 | #### Deploy all of the Guest Book Services to get started 34 | 35 | To deploy all of the guest book related components each deployment needs to be wrapped with a call to istioctl. The following script does this for each of the components. 36 | 37 | 1 - Start the Guest Book services using the following script - note there is a delay between when the services get deployed because of interdependencies required to start the services: 38 | 39 | ``` 40 | guestbook/deployGuestBookIstio.sh 41 | ``` 42 | 43 | Note for windows - You will need to manually run each command from that file and set the directly properly. 44 | 45 | 2 - Find the public IP of the Guest Book UI by running describe on the service and look for EXTERNAL-IP in the output (it will take several minutes to be assigned, so give it a minute or two): 46 | 47 | ``` 48 | kubectl describe services guestbook-ui 49 | ``` 50 | 51 | 3 - Access the guestbook via the Guest Book EXTERNAL-IP address by navigating the browser to http://EXTERNAL-IP/. 52 | 53 | 4 - The Guest Book UI also has a rest endpoint that can be used for testing routing rules. It takes the last part of the URL as the name to create a greeting for. Verify it works by running: 54 | 55 | ``` 56 | curl http://EXTERNAL-IP/echo/universe 57 | ``` 58 | 59 | 5 - Try curling the echo endpoint multiple times and notice how it round robins between v1 and v2 of the hello world service: 60 | 61 | ``` 62 | $ curl 146.148.33.1/echo/universe 63 | 64 | {"greeting":{"hostname":"helloworld-service-v1-286408581-9204h","greeting":"Hello universe from helloworld-service-v1-286408581-9204h with 1.0","version":"1.0"}, 65 | 66 | $ curl 146.148.33.1/echo/universe 67 | 68 | {"greeting":{"hostname":"helloworld-service-v2-1009285752-n2tpb","greeting":"Hello universe from helloworld-service-v2-1009285752-n2tpb with 2.0","version":"2.0"} 69 | 70 | ``` 71 | 72 | We will change this behavior in future exercises. 73 | 74 | #### [Continue to Exercise 7 - Istio Ingress Controller](../exercise-7/README.md) 75 | -------------------------------------------------------------------------------- /exercise-7/README.md: -------------------------------------------------------------------------------- 1 | ## Exercise 7 - Istio Ingress Controller 2 | 3 | The components deployed on the service mesh by default are not exposed outside the cluster. External access to individual services so far has been provided by creating an external load balancer on each service. 4 | 5 | A Kubernetes Ingress rule can be created that routes external requests through the Istio Ingress Controller to the backing services. 6 | 7 | #### Configure Guess Book Ingress Routes with the Istio Ingress Controller 8 | 9 | 1 - Configure the Guess Book UI default route with the Istio Ingress Controller: 10 | 11 | ``` 12 | kubectl apply -f guestbook/guestbook-ingress.yaml 13 | ``` 14 | 15 | In this file notice that the ingress class is specified as `kubernetes.io/ingress.class: istio` which routes the request to Istio. 16 | 17 | The second thing to notice is that the request is routed to different services, either helloworld-service or guestbook-ui depending on the request. 18 | 19 | 2 - Find the external IP of the Istio Ingress controller: 20 | 21 | ``` 22 | kubectl get svc -n istio-system 23 | 24 | NAMESPACE NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE 25 | istio-system istio-ingress 10.31.244.185 35.188.171.180 80:31920/TCP,443:32165/TCP 1h 26 | 27 | ``` 28 | 29 | 3 - Browse to the website of the guest Book using the INGRESS IP to see the Guest Book UI. 30 | 31 | http://INGRESS_IP/hello/world 32 | 33 | Also you should be able to curl the Guest Book using: 34 | 35 | ``` 36 | curl http://INGRESS_IP/echo/universe 37 | ``` 38 | 39 | And the hello world service with: 40 | 41 | ``` 42 | http://INGRESS_IP/hello/world 43 | ``` 44 | 45 | #### [Continue to Exercise 8 - Request Routing and Canary Deployments](../exercise-8/README.md) 46 | -------------------------------------------------------------------------------- /exercise-8/README.md: -------------------------------------------------------------------------------- 1 | ## Exercise 8 - Request Routing and Canary Deployments 2 | 3 | Because there are 2 version of the HelloWorld Service Deployment (v1 and v2), before modifying any of the routes a default route needs to be set to just V1. Otherwise it will just round robin between V1 and V2 4 | 5 | #### Configure the default route for hello world service 6 | 7 | 1 - Set the default version for all requests to the hello world service using : 8 | 9 | ``` 10 | istioctl create -f guestbook/force-hello-v1.yaml 11 | ``` 12 | 13 | This ingress rule forces v1 of the service by giving it a weight of 100. 14 | 15 | 2 - Now when you curl the echo service it should always return V1 of the hello world service: 16 | 17 | ``` 18 | $ curl 35.188.171.180/echo/universe 19 | 20 | {"greeting":{"hostname":"helloworld-service-v1-286408581-9204h","greeting":"Hello universe from helloworld-service-v1-286408581-9204h with 1.0","version":"1.0"}," 21 | 22 | $ curl 35.188.171.180/echo/universe 23 | 24 | {"greeting":{"hostname":"helloworld-service-v1-286408581-9204h","greeting":"Hello universe from helloworld-service-v1-286408581-9204h with 1.0","version":"1.0"}," 25 | 26 | ``` 27 | 28 | #### Canary Deployments 29 | 30 | Currently the routing rule routes only to V1 of the hello world service which is not real useful. What we want to do next is a canary deployment and push some of the traffic to V2. This can be done by creating another rule with a higher precedence that routes some of the traffic to V2 based on http headers. As an example we will use the following canary deployment in canary-helloworld.yaml This rule routes all traffic from a mobile user agent to version 2.0 of the service. 31 | 32 | ``` 33 | destination: 34 | name: helloworld-service 35 | match: 36 | request: 37 | headers: 38 | user-agent: 39 | exact: mobile 40 | precedence: 2 41 | route: 42 | - labels: 43 | version: "2.0" 44 | ``` 45 | 46 | Note that rules with a higher precedence number are applied first. If a precedence is not specified then it defaults to 0. So with these 2 rules in place the one with precedence 2 will be applied first. 47 | 48 | Test this out by creating the rule: 49 | 50 | ``` 51 | istioctl create -f guestbook/canary-helloworld.yaml 52 | ``` 53 | 54 | Now when you curl the end point set the user agent to be mobile and you should only see V2: 55 | 56 | ``` 57 | $ curl http://104.198.198.111/echo/universe -A mobile 58 | 59 | {"greeting":{"hostname":"helloworld-service-v2-3297856697-6m4bp","greeting":"Hello dog2 from helloworld-service-v2-3297856697-6m4bp with 2.0","version":"2.0"} 60 | 61 | $ curl 35.188.171.180/echo/universe 62 | 63 | {"greeting":{"hostname":"helloworld-service-v1-286408581-9204h","greeting":"Hello universe from helloworld-service-v1-286408581-9204h with 1.0","version":"1.0"}," 64 | 65 | ``` 66 | 67 | An important point to note is that the user-agent http header is propagated in the span baggage. Look at these two classes for details: 68 | 69 | https://github.com/retroryan/istio-by-example-java/blob/master/spring-boot-example/spring-istio-support/src/main/java/com/example/istio/IstioHttpSpanInjector.java 70 | 71 | https://github.com/retroryan/istio-by-example-java/blob/master/spring-boot-example/spring-istio-support/src/main/java/com/example/istio/IstioHttpSpanExtractor.java 72 | 73 | #### Optional - Route based on the browser 74 | 75 | It is also possible to route it based on the Web Browser. For example the following routes to version 2.0 if the browser is chrome: 76 | 77 | ``` 78 | match: 79 | request: 80 | headers: 81 | user-agent: 82 | regex: ".*Chrome.*" 83 | route: 84 | - labels: 85 | version: "2.0" 86 | ``` 87 | 88 | To apply this route run: 89 | 90 | ``` 91 | istioctl create -f route-ui-user-agent-chrome.yaml 92 | ``` 93 | 94 | Then navigate to the home page in chrome and another browser. 95 | 96 | #### [Continue to Exercise 9 - Fault Injection](../exercise-9/README.md) 97 | -------------------------------------------------------------------------------- /exercise-9/README.md: -------------------------------------------------------------------------------- 1 | ## Exercise 9 - Fault Injection and Rate Limiting 2 | 3 | #### Overview of Istio Mixer 4 | 5 | https://istio.io/docs/concepts/policy-and-control/mixer.html 6 | 7 | #### Setting Up Mixer for Kubernetes 8 | 9 | First we configure Mixer for a kubernetes environment, setting up the kubernetes adapter to produce attributes about the kubernetes deployment (e.g. the labels on the target and source pods). 10 | 11 | ``` 12 | istioctl mixer rule create global global -f mixer-kubernetes.yaml 13 | ``` 14 | 15 | #### Service Isolation Using Mixer 16 | 17 | Block Access to the hello world service by adding the mixer-rule-denial.yaml rule shown below: 18 | 19 | ``` 20 | rules: 21 | - selector: source.labels["app"]=="helloworld-ui" 22 | aspects: 23 | - kind: denials 24 | ``` 25 | 26 | ``` 27 | istioctl mixer rule create global helloworld-service.default.svc.cluster.local -f mixer-rule-denial.yaml 28 | ``` 29 | 30 | #### Block Access to v2 of the hello world service 31 | 32 | ``` 33 | rules: 34 | - selector: destination.labels["app"]=="helloworld-ui" && source.labels["version"] == "v2" 35 | aspects: 36 | - kind: denials 37 | ``` 38 | 39 | ``` 40 | istioctl mixer rule create global helloworld-service.default.svc.cluster.local -f mixer-rule-denial-v2.yaml 41 | ``` 42 | 43 | Then we clean up the rules to get everything working again: 44 | 45 | ``` 46 | istioctl mixer rule delete global helloworld-service.default.svc.cluster.local 47 | ``` 48 | 49 | #### [Continue to Exercise 10 - Telemetry and Rate Limiting with Mixer](../exercise-10/README.md) 50 | -------------------------------------------------------------------------------- /guestbook/broken-redis-service.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # DO NOT NAME THE PORT REDIS!!! This routes the connection through Istio and breaks the application 16 | 17 | kind: Service 18 | apiVersion: v1 19 | metadata: 20 | name: redis 21 | labels: 22 | app: redis 23 | visualize: "true" 24 | spec: 25 | ports: 26 | - port: 6379 27 | name: redis 28 | selector: 29 | app: redis 30 | -------------------------------------------------------------------------------- /guestbook/deleteGuestBook.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | 5 | kubectl delete -f $SCRIPTDIR/mysql-pvc.yaml -f $SCRIPTDIR/mysql-deployment.yaml -f $SCRIPTDIR/mysql-service.yaml 6 | kubectl delete -f $SCRIPTDIR/redis-deployment.yaml -f $SCRIPTDIR/redis-service.yaml 7 | kubectl delete -f $SCRIPTDIR/helloworld-deployment.yaml -f $SCRIPTDIR/helloworld-service.yaml 8 | kubectl delete -f $SCRIPTDIR/helloworld-deployment-v2.yaml 9 | kubectl delete -f $SCRIPTDIR/guestbook-deployment.yaml -f $SCRIPTDIR/guestbook-service.yaml 10 | kubectl delete -f $SCRIPTDIR/guestbook-ui-deployment.yaml -f $SCRIPTDIR/guestbook-ui-service.yaml 11 | -------------------------------------------------------------------------------- /guestbook/deployGuestBookIstio.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | 5 | 6 | kubectl apply -f guestbook/mysql-pvc.yaml -f <(istioctl kube-inject -f guestbook/mysql-deployment.yaml) -f guestbook/mysql-service.yaml 7 | kubectl apply -f <(istioctl kube-inject -f guestbook/redis-deployment.yaml) -f guestbook/redis-service.yaml 8 | kubectl apply -f <(istioctl kube-inject -f guestbook/helloworld-deployment.yaml) -f guestbook/helloworld-service.yaml 9 | kubectl apply -f <(istioctl kube-inject -f guestbook/helloworld-deployment-v2.yaml) 10 | 11 | ### Wait for mysql and redis startup before starting guestbook !!!! 12 | sleep 120 13 | 14 | kubectl apply -f <(istioctl kube-inject -f guestbook/guestbook-deployment.yaml) -f guestbook/guestbook-service.yaml 15 | 16 | ### Wait for guestbook service startup before starting Guest Book UI" 17 | sleep 120 18 | 19 | kubectl apply -f <(istioctl kube-inject -f guestbook/guestbook-ui-deployment.yaml) -f guestbook/guestbook-ui-service.yaml 20 | -------------------------------------------------------------------------------- /guestbook/deployGuestBookIstioWindows.txt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | 5 | kubectl apply -f $SCRIPTDIR/mysql-pvc.yaml -f <(istioctl kube-inject -f $SCRIPTDIR/mysql-deployment.yaml) -f $SCRIPTDIR/mysql-service.yaml 6 | kubectl apply -f <(istioctl kube-inject -f $SCRIPTDIR/redis-deployment.yaml) -f $SCRIPTDIR/redis-service.yaml 7 | kubectl apply -f <(istioctl kube-inject -f $SCRIPTDIR/helloworld-deployment.yaml) -f $SCRIPTDIR/helloworld-service.yaml 8 | kubectl apply -f <(istioctl kube-inject -f $SCRIPTDIR/helloworld-deployment-v2.yaml) 9 | echo "Waiting for mysql and redis startup before starting guestbook" 10 | sleep 120 11 | kubectl apply -f <(istioctl kube-inject -f $SCRIPTDIR/guestbook-deployment.yaml) -f $SCRIPTDIR/guestbook-service.yaml 12 | echo "Waiting for guestbook service startup before starting Guest Book UI" 13 | sleep 120 14 | kubectl apply -f <(istioctl kube-inject -f $SCRIPTDIR/guestbook-ui-deployment.yaml) -f $SCRIPTDIR/guestbook-ui-service.yaml 15 | -------------------------------------------------------------------------------- /guestbook/deployGuestBookKube.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPTDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) 4 | 5 | kubectl apply -f $SCRIPTDIR/mysql-pvc.yaml -f $SCRIPTDIR/mysql-deployment.yaml -f $SCRIPTDIR/mysql-service.yaml 6 | kubectl apply -f $SCRIPTDIR/redis-deployment.yaml -f $SCRIPTDIR/redis-service.yaml 7 | kubectl apply -f $SCRIPTDIR/helloworld-deployment.yaml -f $SCRIPTDIR/helloworld-service.yaml 8 | kubectl apply -f $SCRIPTDIR/helloworld-deployment-v2.yaml 9 | echo "Waiting for mysql and redis startup before starting guestbook" 10 | sleep 120 11 | kubectl apply -f $SCRIPTDIR/guestbook-deployment.yaml -f $SCRIPTDIR/guestbook-service.yaml 12 | sleep 120 13 | echo "Waiting for guestbook startup before starting UI" 14 | kubectl apply -f $SCRIPTDIR/guestbook-ui-deployment.yaml -f $SCRIPTDIR/guestbook-ui-service.yaml 15 | -------------------------------------------------------------------------------- /guestbook/guestbook-deployment.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: extensions/v1beta1 16 | kind: Deployment 17 | metadata: 18 | labels: 19 | app: guestbook-service 20 | visualize: "true" 21 | name: guestbook-service 22 | spec: 23 | replicas: 1 24 | selector: 25 | matchLabels: 26 | app: guestbook-service 27 | serving: "true" 28 | template: 29 | metadata: 30 | labels: 31 | app: guestbook-service 32 | serving: "true" 33 | version: latest 34 | visualize: "true" 35 | layer: controller-plane 36 | spec: 37 | containers: 38 | - name: guestbook-service 39 | image: retroryan/guestbook-service-istio:java-1.0 40 | ports: 41 | - containerPort: 8080 42 | name: http 43 | -------------------------------------------------------------------------------- /guestbook/guestbook-ingress.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: extensions/v1beta1 16 | kind: Ingress 17 | metadata: 18 | annotations: 19 | kubernetes.io/ingress.class: istio 20 | name: simple-ingress 21 | spec: 22 | rules: 23 | - http: 24 | paths: 25 | - path: /hello/.* 26 | backend: 27 | serviceName: helloworld-service 28 | servicePort: 8080 29 | - path: /.* 30 | backend: 31 | serviceName: guestbook-ui 32 | servicePort: 80 33 | -------------------------------------------------------------------------------- /guestbook/guestbook-service.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | kind: Service 16 | apiVersion: v1 17 | metadata: 18 | name: guestbook-service 19 | labels: 20 | app: guestbook-service 21 | visualize: "true" 22 | spec: 23 | ports: 24 | - port: 8080 25 | targetPort: 8080 26 | name: http 27 | selector: 28 | app: guestbook-service 29 | serving: "true" 30 | -------------------------------------------------------------------------------- /guestbook/guestbook-ui-deployment.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: extensions/v1beta1 16 | kind: Deployment 17 | metadata: 18 | name: guestbook-ui 19 | labels: 20 | app: guestbook-ui 21 | visualize: "true" 22 | spec: 23 | replicas: 1 24 | selector: 25 | matchLabels: 26 | app: guestbook-ui 27 | serving: "true" 28 | template: 29 | metadata: 30 | labels: 31 | app: guestbook-ui 32 | version: latest 33 | serving: "true" 34 | visualize: "true" 35 | layer: frontend 36 | annotations: 37 | visualizer/uses: helloworld-service,guestbook-service,redis 38 | spec: 39 | containers: 40 | - name: guestbook-ui 41 | image: retroryan/guestbook-ui-istio:java-1.0 42 | ports: 43 | - name: http 44 | containerPort: 8080 45 | -------------------------------------------------------------------------------- /guestbook/guestbook-ui-service.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | kind: Service 16 | apiVersion: v1 17 | metadata: 18 | name: guestbook-ui 19 | labels: 20 | app: guestbook-ui 21 | visualize: "true" 22 | spec: 23 | type: LoadBalancer 24 | ports: 25 | - port: 80 26 | targetPort: 8080 27 | name: http 28 | selector: 29 | app: guestbook-ui 30 | serving: "true" 31 | -------------------------------------------------------------------------------- /guestbook/helloworld-deployment-v2.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: extensions/v1beta1 16 | kind: Deployment 17 | metadata: 18 | name: helloworld-service-v2 19 | labels: 20 | app: helloworld-service-v2 21 | visualize: "true" 22 | version: "2.0" 23 | spec: 24 | replicas: 1 25 | selector: 26 | matchLabels: 27 | app: helloworld-service 28 | serving: "true" 29 | template: 30 | metadata: 31 | labels: 32 | app: helloworld-service 33 | version: "latest" 34 | serving: "true" 35 | visualize: "true" 36 | version: "2.0" 37 | layer: controller-plane 38 | spec: 39 | containers: 40 | - name: helloworld-service 41 | image: retroryan/helloworld-service-istio:java-1.0 42 | env: 43 | - name: version 44 | value: "2.0" 45 | - name: GREETING 46 | value: "Hola $name from $hostname version $version" 47 | ports: 48 | - name: http 49 | containerPort: 8080 50 | -------------------------------------------------------------------------------- /guestbook/helloworld-deployment.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: extensions/v1beta1 16 | kind: Deployment 17 | metadata: 18 | name: helloworld-service-v1 19 | labels: 20 | app: helloworld-service-v1 21 | visualize: "true" 22 | version: "1.0" 23 | spec: 24 | replicas: 1 25 | selector: 26 | matchLabels: 27 | app: helloworld-service 28 | serving: "true" 29 | template: 30 | metadata: 31 | labels: 32 | app: helloworld-service 33 | version: "latest" 34 | serving: "true" 35 | visualize: "true" 36 | version: "1.0" 37 | layer: controller-plane 38 | spec: 39 | containers: 40 | - name: helloworld-service 41 | image: retroryan/helloworld-service-istio:java-1.0 42 | env: 43 | - name: version 44 | value: "1.0" 45 | ports: 46 | - name: http 47 | containerPort: 8080 48 | -------------------------------------------------------------------------------- /guestbook/helloworld-service.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | kind: Service 16 | apiVersion: v1 17 | metadata: 18 | name: helloworld-service 19 | labels: 20 | app: helloworld-service 21 | visualize: "true" 22 | spec: 23 | ports: 24 | - port: 8080 25 | targetPort: 8080 26 | name: http 27 | selector: 28 | app: helloworld-service 29 | serving: "true" 30 | -------------------------------------------------------------------------------- /guestbook/mixer-kubernetes-rules.yaml: -------------------------------------------------------------------------------- 1 | rules: 2 | - selector: # must be empty for preprocessing adapters 3 | aspects: 4 | # when running local without a kubeconfig file specified in globalconfig, 5 | # this aspect should be commented out. It is only needed when the attributes 6 | # it produces are needed elsewhere in the config. 7 | - kind: attributes 8 | params: 9 | input_expressions: 10 | sourceUID: source.uid | "" 11 | targetUID: target.uid | "" 12 | originUID: origin.uid | "" 13 | targetService: request.headers["authority"] | request.host | "" 14 | attribute_bindings: 15 | source.ip: sourcePodIp 16 | source.labels: sourceLabels 17 | source.name: sourcePodName 18 | source.namespace: sourceNamespace 19 | source.service: sourceService 20 | source.serviceAccount: sourceServiceAccountName 21 | target.ip: targetPodIp 22 | target.labels: targetLabels 23 | target.name: targetPodName 24 | target.namespace: targetNamespace 25 | target.service: targetService 26 | target.serviceAccount: targetServiceAccountName 27 | -------------------------------------------------------------------------------- /guestbook/mixer-rule-denial-v2.yaml: -------------------------------------------------------------------------------- 1 | rules: 2 | - selector: target.labels["app"]=="helloworld-ui" && source.labels["version"] == "2.0" 3 | aspects: 4 | - kind: denials 5 | -------------------------------------------------------------------------------- /guestbook/mixer-rule-denial.yaml: -------------------------------------------------------------------------------- 1 | rules: 2 | - selector: source.labels["app"]=="helloworld-ui" 3 | aspects: 4 | - kind: denials 5 | -------------------------------------------------------------------------------- /guestbook/mysql-deployment.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: extensions/v1beta1 16 | kind: Deployment 17 | metadata: 18 | name: mysql 19 | labels: 20 | app: mysql 21 | visualize: "true" 22 | spec: 23 | replicas: 1 24 | template: 25 | metadata: 26 | labels: 27 | app: mysql 28 | visualize: "true" 29 | layer: data-plane 30 | spec: 31 | containers: 32 | - name: mysql 33 | image: mysql:5.6 34 | livenessProbe: 35 | tcpSocket: 36 | port: 3306 37 | env: 38 | - name: MYSQL_ROOT_PASSWORD 39 | # change this 40 | value: yourpassword 41 | - name: MYSQL_DATABASE 42 | value: app 43 | ports: 44 | - containerPort: 3306 45 | name: mysql 46 | volumeMounts: 47 | # name must match the volume name below 48 | - name: mysql-persistent-storage 49 | # mount path within the container 50 | mountPath: /var/lib/mysql 51 | volumes: 52 | - name: mysql-persistent-storage 53 | persistentVolumeClaim: 54 | claimName: mysql-pvc 55 | -------------------------------------------------------------------------------- /guestbook/mysql-pvc.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | kind: PersistentVolumeClaim 16 | apiVersion: v1 17 | metadata: 18 | name: mysql-pvc 19 | annotations: 20 | volume.beta.kubernetes.io/storage-class: standard 21 | spec: 22 | accessModes: 23 | - ReadWriteOnce 24 | resources: 25 | requests: 26 | storage: 5Gi 27 | -------------------------------------------------------------------------------- /guestbook/mysql-service.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: v1 16 | kind: Service 17 | metadata: 18 | name: mysql 19 | labels: 20 | app: mysql 21 | visualize: "true" 22 | spec: 23 | ports: 24 | # the port that this service should serve on 25 | - port: 3306 26 | name: mysql 27 | # label keys and values that must match in order to receive traffic for this service 28 | selector: 29 | app: mysql 30 | -------------------------------------------------------------------------------- /guestbook/rate-limit-ui-service.yaml: -------------------------------------------------------------------------------- 1 | rules: 2 | - aspects: 3 | - kind: quotas 4 | params: 5 | quotas: 6 | - descriptorName: RequestCount 7 | maxAmount: 1 8 | expiration: 1s 9 | 10 | -------------------------------------------------------------------------------- /guestbook/redis-deployment.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: extensions/v1beta1 16 | kind: Deployment 17 | metadata: 18 | name: redis 19 | labels: 20 | app: redis 21 | visualize: "true" 22 | spec: 23 | replicas: 1 24 | template: 25 | metadata: 26 | labels: 27 | app: redis 28 | version: "4.0.2" 29 | visualize: "true" 30 | spec: 31 | containers: 32 | - name: redis 33 | image: redis 34 | livenessProbe: 35 | tcpSocket: 36 | port: 6379 37 | ports: 38 | - name: redis-server 39 | containerPort: 6379 40 | -------------------------------------------------------------------------------- /guestbook/redis-service.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Google Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # DO NOT NAME THE PORT REDIS!!! This routes the connection through Istio and breaks the application 16 | 17 | kind: Service 18 | apiVersion: v1 19 | metadata: 20 | name: redis 21 | labels: 22 | app: redis 23 | visualize: "true" 24 | spec: 25 | ports: 26 | - port: 6379 27 | selector: 28 | app: redis 29 | -------------------------------------------------------------------------------- /guestbook/route-rule-80-20.yaml: -------------------------------------------------------------------------------- 1 | type: route-rule 2 | name: route8020-helloworld-service 3 | spec: 4 | destination: helloworld-service.default.svc.cluster.local 5 | precedence: 1 6 | route: 7 | - tags: 8 | version: "1.0" 9 | weight: 80 10 | - tags: 11 | version: "2.0" 12 | weight: 20 13 | -------------------------------------------------------------------------------- /guestbook/route-rule-canary.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: config.istio.io/v1alpha2 2 | kind: RouteRule 3 | metadata: 4 | name: canary-helloworld 5 | namespace: default 6 | spec: 7 | destination: 8 | name: helloworld-service 9 | match: 10 | request: 11 | headers: 12 | user-agent: 13 | exact: mobile 14 | precedence: 2 15 | route: 16 | - labels: 17 | version: "2.0" 18 | -------------------------------------------------------------------------------- /guestbook/route-rule-delay-guestbook.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: config.istio.io/v1alpha2 2 | kind: RouteRule 3 | metadata: 4 | name: guestbook-test-delay 5 | namespace: default 6 | spec: 7 | destination: 8 | name: helloworld-service 9 | precedence: 2 10 | match: 11 | request: 12 | headers: 13 | user-agent: 14 | exact: mobile 15 | route: 16 | - labels: 17 | version: v2 18 | httpFault: 19 | delay: 20 | percent: 100 21 | fixedDelay: 5s 22 | -------------------------------------------------------------------------------- /guestbook/route-rule-force-hello-v1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: config.istio.io/v1alpha2 2 | kind: RouteRule 3 | metadata: 4 | name: helloworld-default 5 | namespace: default 6 | spec: 7 | destination: 8 | name: helloworld-service 9 | precedence: 1 10 | route: 11 | - labels: 12 | version: "1.0" 13 | -------------------------------------------------------------------------------- /guestbook/route-rule-helloworld-service-503.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: config.istio.io/v1alpha2 2 | kind: RouteRule 3 | metadata: 4 | name: helloworld-service-503 5 | namespace: default 6 | spec: 7 | destination: 8 | name: helloworld-service 9 | precedence: 10 10 | route: 11 | - labels: 12 | version: "2.0" 13 | httpFault: 14 | abort: 15 | percent: 100 16 | httpStatus: 503 17 | -------------------------------------------------------------------------------- /guestbook/route-rule-user-agent-chrome.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: config.istio.io/v1alpha2 2 | kind: RouteRule 3 | metadata: 4 | name: helloworld-service-chrome 5 | namespace: default 6 | spec: 7 | destination: 8 | name: helloworld-service 9 | precedence: 3 10 | match: 11 | request: 12 | headers: 13 | user-agent: 14 | regex: ".*Chrome.*" 15 | route: 16 | - labels: 17 | version: "2.0" 18 | -------------------------------------------------------------------------------- /guestbook/telemetry-global.yaml: -------------------------------------------------------------------------------- 1 | revision: "2022" 2 | rules: 3 | - aspects: 4 | - kind: attributes 5 | params: 6 | attribute_bindings: 7 | source.ip: sourcePodIp 8 | source.labels: sourceLabels 9 | source.name: sourcePodName 10 | source.namespace: sourceNamespace 11 | source.service: sourceService 12 | source.serviceAccount: sourceServiceAccountName 13 | target.ip: targetPodIp 14 | target.labels: targetLabels 15 | target.name: targetPodName 16 | target.namespace: targetNamespace 17 | target.service: targetService 18 | target.serviceAccount: targetServiceAccountName 19 | input_expressions: 20 | originUID: origin.uid | "" 21 | sourceUID: source.uid | "" 22 | targetService: request.headers["authority"] | request.host | "" 23 | targetUID: target.uid | "" 24 | - kind: quotas 25 | params: 26 | quotas: 27 | - descriptorName: RequestCount 28 | expiration: 1s 29 | maxAmount: 5000 30 | - adapter: prometheus 31 | kind: metrics 32 | params: 33 | metrics: 34 | - descriptor_name: request_count 35 | labels: 36 | method: request.path | "unknown" 37 | response_code: response.code | 200 38 | service: target.labels["app"] | "unknown" 39 | source: source.labels["app"] | "unknown" 40 | target: target.service | "unknown" 41 | version: target.labels["version"] | "unknown" 42 | value: "1" 43 | - descriptor_name: request_duration 44 | labels: 45 | method: request.path | "unknown" 46 | response_code: response.code | 200 47 | service: target.labels["app"] | "unknown" 48 | source: source.labels["app"] | "unknown" 49 | target: target.service | "unknown" 50 | version: target.labels["version"] | "unknown" 51 | value: response.latency | response.duration | "0ms" 52 | - kind: access-logs 53 | params: 54 | log: 55 | descriptor_name: accesslog.common 56 | labels: 57 | method: request.method 58 | originIp: origin.ip 59 | protocol: request.scheme 60 | responseCode: response.code 61 | responseSize: response.size 62 | sourceUser: origin.user 63 | timestamp: request.time 64 | url: request.path 65 | template_expressions: 66 | method: request.method 67 | originIp: origin.ip 68 | protocol: request.scheme 69 | responseCode: response.code 70 | responseSize: response.size 71 | sourceUser: origin.user 72 | timestamp: request.time 73 | url: request.path 74 | logName: access_log 75 | subject: namespace:ns 76 | -------------------------------------------------------------------------------- /guestbook/telemtry_rule.yaml: -------------------------------------------------------------------------------- 1 | revision: "1" 2 | rules: 3 | - aspects: 4 | - adapter: prometheus 5 | kind: metrics 6 | params: 7 | metrics: 8 | - descriptor_name: response_size 9 | value: response.size | 0 10 | labels: 11 | source: source.service | "unknown" 12 | target: target.service | "unknown" 13 | service: target.labels["app"] | "unknown" 14 | method: request.path | "unknown" 15 | version: target.labels["version"] | "unknown" 16 | response_code: response.code | 200 17 | - kind: access-logs 18 | params: 19 | logName: combined_log 20 | log: 21 | descriptor_name: accesslog.combined 22 | template_expressions: 23 | originIp: origin.ip 24 | sourceUser: origin.user 25 | timestamp: request.time 26 | method: request.method 27 | url: request.path 28 | protocol: request.scheme 29 | responseCode: response.code 30 | responseSize: response.size 31 | referer: request.referer 32 | userAgent: request.headers["user-agent"] 33 | labels: 34 | originIp: origin.ip 35 | sourceUser: origin.user 36 | timestamp: request.time 37 | method: request.method 38 | url: request.path 39 | protocol: request.scheme 40 | responseCode: response.code 41 | responseSize: response.size 42 | referer: request.referer 43 | userAgent: request.headers["user-agent"] 44 | -------------------------------------------------------------------------------- /kubernetes/helloworldservice-deployment.yaml: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Copyright 2015 Google Inc. All rights reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | ############################################################################### 16 | apiVersion: extensions/v1beta1 17 | kind: Deployment 18 | metadata: 19 | name: helloworld-service-v1 20 | labels: 21 | app: helloworld-service-v1 22 | visualize: "true" 23 | version: v1 24 | spec: 25 | replicas: 1 26 | selector: 27 | matchLabels: 28 | app: helloworld-service 29 | template: 30 | metadata: 31 | labels: 32 | app: helloworld-service 33 | version: v1 34 | visualize: "true" 35 | domain: front-end 36 | spec: 37 | containers: 38 | - name: helloworld-service 39 | image: retroryan/helloworld:1.0 40 | env: 41 | - name: VERSION 42 | value: "1.0" 43 | ports: 44 | - name: http 45 | containerPort: 8080 46 | -------------------------------------------------------------------------------- /kubernetes/helloworldservice-service.yaml: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Copyright 2015 Google Inc. All rights reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | ############################################################################### 16 | 17 | kind: Service 18 | apiVersion: v1 19 | metadata: 20 | name: helloworld-service 21 | labels: 22 | app: helloworld-service 23 | visualize: "true" 24 | spec: 25 | type: LoadBalancer 26 | ports: 27 | - name: http 28 | port: 8080 29 | targetPort: 8080 30 | selector: 31 | app: helloworld-service 32 | -------------------------------------------------------------------------------- /setup/README.md: -------------------------------------------------------------------------------- 1 | ## Workshop Setup 2 | 3 | ### Google Cloud Shell or Local Install 4 | 5 | This workshop can either be run all locally using the following setup instructions or it can be run in Google Cloud Shell. 6 | 7 | #### Setup [CLOUD SDK](https://cloud.google.com/sdk/) 8 | 9 | #### Install Cloud SDK 10 | 11 | `./google-cloud-sdk/install.sh` 12 | 13 | #### Initialize Cloud SDK 14 | 15 | `./google-cloud-sdk/bin/gcloud init` 16 | 17 | #### Login to Cloud 18 | 19 | `gcloud auth login` 20 | 21 | #### Verify Cloud SDK 22 | 23 | `gcloud config list` 24 | 25 | #### Install kubectl 26 | 27 | `gcloud components install kubectl` 28 | 29 | #### Verify kubectl 30 | `kubectl version` 31 | 32 | #### Get the Workshop Source: 33 | 34 | `git clone https://github.com/retroryan/istio-workshop` 35 | 36 | 37 | #### Download Istio 38 | 39 | Only download Istio - do not install it yet! 40 | 41 | Either download it directly or get the latest: 42 | 43 | https://github.com/istio/istio/releases 44 | 45 | ``` 46 | curl -L https://git.io/getLatestIstio | sh - 47 | export PATH=$PWD/istio-0.2.10/bin:$PATH 48 | ``` 49 | 50 | #### Windows Setup 51 | 52 | If you install [Git for Windows](https://git-scm.com/downloads) you get Curl automatically too. There are some advantages: 53 | 54 | Git takes care of the PATH setup during installation automatically. 55 | You get the GNU bash, a really powerful shell, in my opinion much better than the native Windows console. 56 | You get many other useful Linux tools like tail, cat, grep, gzip, pdftotext, less, sort, tar, vim and even Perl. 57 | 58 | #### [Continue to Exercise 1](../exercise-1/README.md) 59 | --------------------------------------------------------------------------------