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. This is done in a way that the application does not need to be aware of the proxy.
Moving the service communication to a separate layer provides a separation of concerns. The monitoring, management and security of communication can be handled outside of the application logic.
A Kubernetes pod is a group of containers, tied together for the purposes of administration and networking. Each pod can contain one or more containers. Small containers are often used to provide common utilities for the pod. These sidecar containers extend and enhance the main container. Sidecar containers are a crosscutting concern and can be used across multiple pods.
To create a service mesh with Istio, you 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.
The side car can be injected manually 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. To see how the deployment YAML is modified, run thw following from the istio-workshop
dir:
istioctl kube-inject -f guestbook/helloworld-deployment.yaml
This adds the Istio Proxy as an additional container to the Pod and setups the necessary configuration. Inside the YAML there is now an additional container:
image: docker.io/istio/proxy_debug:0.2.12
imagePullPolicy: IfNotPresent
name: istio-proxy
Istio sidecars can also be automatically injected into a pod at creation time using a feature in Kubernetes called a mutating webhook admission controller. Note that unlike manual injection, automatic injection occurs at the pod-level. You won't see any change to the deployment itself. Instead you'll want to check individual pods (via kubectl describe) to see the injected proxy.
An admission controller is a piece of code that intercepts requests to the Kubernetes API server prior to persistence of the object, but after the request is authenticated and authorized. Admission controllers may be “validating”, “mutating”, or both. Mutating controllers may modify the objects they admit; validating controllers may not.
The admission control process proceeds in two phases. In the first phase, mutating admission controllers are run. In the second phase, validating admission controllers are run.
MutatingWebhookConfiguration describes the configuration of and admission webhook that accept or reject and may change the object.
For Istio the webhook is the sidecar injector webhook deployment called "istio-sidecar-injector". It will modify a pod before it is started to inject an istio init container and istio proxy container.
Webhooks requires a signed cert/key pair. Use install/kubernetes/webhook-create-signed-cert.sh to generate a cert/key pair signed by the Kubernetes’ CA. The resulting cert/key file is stored as a Kubernetes secret for the sidecar injector webhook to consume.
cd ~/istio/install/kubernetes
./webhook-create-signed-cert.sh \
--service istio-sidecar-injector \
--namespace istio-system \
--secret sidecar-injector-certs
Install the sidecar injection configmap:
kubectl apply -f istio-sidecar-injector-configmap-release.yaml
Set the caBundle in the webhook install YAML that the Kubernetes api-server uses to invoke the webhook.
cat istio-sidecar-injector.yaml | \
./webhook-patch-ca-bundle.sh > \
istio-sidecar-injector-with-ca-bundle.yaml
Install the sidecar injector webhook.
kubectl apply -f istio-sidecar-injector-with-ca-bundle.yaml
The sidecar injector webhook should now be running.
kubectl -n istio-system get deployment -listio=sidecar-injector
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
istio-sidecar-injector 1 1 1 1 1d
NamespaceSelector decides whether to run the webhook on an object based on whether the namespace for that object matches the selector (see https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors).
Label the default namespace with istio-injection=enabled
kubectl label namespace default istio-injection=enabled
kubectl get namespace -L istio-injection
NAME STATUS AGE ISTIO-INJECTION
default Active 1h enabled
istio-system Active 1h
kube-public Active 1h
kube-system Active 1h
To demonstrate Istio, we’re going to use this guestbook example. This example is built with Spring Boot, a frontend using Spring MVC and Thymeleaf, and two microservices. The 3 microservices that we are going to deploy are:
-
Hello World service - A simple service that returns a greeting back to the user.
-
Guestbook service - A service that keeps a registry of guests and the message they left.
-
Guestbook UI - The front end to the application that calls to the other microservices to get the list of guests, register a new guest, and get the greeting for the user when they register.
The guestbook example requires MySQL to store guestbook entries and Redis to store session information.
Note that the services must be started in a fixed order because they depend on other services being started first.
-
Deploy MySQL, Redis, the Hello World microservices, and the associated Kubernetes Services from the
istio-workshop
dir:cd ~/istio-workshop kubectl apply -f guestbook/mysql-deployment.yaml -f guestbook/mysql-service.yaml kubectl apply -f guestbook/redis-deployment.yaml -f guestbook/redis-service.yaml kubectl apply -f guestbook/helloworld-deployment.yaml -f guestbook/helloworld-service.yaml kubectl apply -f guestbook/helloworld-deployment-v2.yaml
-
Notice that each of the pods now has one Istio init container and two running containers. One is the main application container and the second is the istio proxy container.
kubectl get pod
When you get the pods you should see in the READY column 2/2 meaning that 2 of 2 containers are in the running state (it might take a minute or two to get to that state).
When you describe the pod what that shows is the details of the additional containers.
kubectl describe pods helloworld-service-v1.....
-
Verify that previous deployments are all in a state of AVAILABLE before continuing. Do not procede until they are up and running.
kubectl get -w deployment
-
Deploy the guestbook microservice.
kubectl apply -f guestbook/guestbook-deployment.yaml -f guestbook/guestbook-service.yaml
-
Verify that guestbook is available before continuing. Do not procede until the microservice is up and running.
kubectl get -w deployment
-
Deploy the guestbook UI:
kubectl apply -f guestbook/guestbook-ui-deployment.yaml -f guestbook/guestbook-ui-service.yaml
-
Access the guestbook UI in the web browser:
The guestbook UI kubernetes service has a type of LoadBalancer. This creates an external ip through which the ui can be accessed:
kubectl get svc guestbook-ui
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
guestbook-ui LoadBalancer 10.59.245.13 35.197.94.184 80:30471/TCP 2m
You can test access via a web browser and curl. You should be able to navigate to that IP to access the guestbook ui.
To curl the guest book endpoint use:
curl 35.230.4.192/echo/world
Also the Hello World service is declared with an external ip which can be curled:
kubectl get svc helloworld-service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
helloworld-service LoadBalancer 10.59.242.178 35.185.201.165 8080:31255/TCP 14m
curl 35.185.201.165:8080/hello/world
{"hostname":"helloworld-service-v2-744696b8cb-lwdng","greeting":"Hola world from helloworld-service-v2-744696b8cb-lwdng version 2.0","version":"2.0"}
- Inspect the details of the pods
Look at the details of the pod and then inspect the envoy config:
kubectl describe pod helloworld-service-v1.....
kubectl exec -it helloworld-service-v1..... -c istio-proxy bash
cd /etc/istio/proxy
more envoy-rev6.json
exit