Istio (Service Mesh) 101

Istio   Kubernetes
By Vikrant
December 24, 2018

If you are working in kubernetes world then you should have heard the word Istio, if not Istio then at-least service mesh. I heard about istio couple of times, I tried it sometime back in my lab environment but didn’t get the chance to look into it more closely. Today, I found sometime to dig deep into it and try out some example. In this post, I am sharing the basic exercise which I completed on my minikube setup. In future posts, we will dig more on istio topic.

What is istio?

Istio is a service mesh which helps you control, observe, secure and control services E/W traffic mainly. You may think that we can achieve the same using K8 networking layer which a already provides similar features, if it doesn’t then same can be introduced in your app. Your app can export prometheus formatted stats, it can use OpenTracer to introduce distributed tracing capabilities, and to secure traffic mTLS can be introduced then why we need it?

Istio decouples all the points mentioned above from your service. You have sidecar proxy container running along with service which can do everything for you, reducing the pain of app developer. Istio is a control plane for envoy proxy dataplane. Envoy is a proxy created by Lyft to solve the internal challenges with large scale K8 setup.

Setup Information:

  • Minikube running with kubernetes version v1.14.0 with 5GB RAM and 4 CPUs.

  • Deployed istio following the instructions from official [istio documentation)(https://istio.io/docs/setup/kubernetes/install/kubernetes/). You should see following PODs in istio-system namespace.

$ kubectl get pod -n istio-system
NAME                                      READY   STATUS      RESTARTS   AGE
grafana-67c69bb567-s25fw                  1/1     Running     3          2d5h
istio-citadel-78c9c8b75f-6srt4            1/1     Running     3          2d5h
istio-cleanup-secrets-1.1.4-g7m26         0/1     Completed   0          2d5h
istio-egressgateway-6df84c5bd4-7r8gh      1/1     Running     3          2d5h
istio-galley-65fc98ffd4-ltstm             1/1     Running     2          32h
istio-grafana-post-install-1.1.4-qhd6g    0/1     Completed   0          2d5h
istio-ingressgateway-78f9cbb78f-tvhzk     1/1     Running     3          2d5h
istio-pilot-6b75486f59-czlkv              2/2     Running     6          2d5h
istio-policy-784c66bc85-cbn9z             2/2     Running     16         2d5h
istio-security-post-install-1.1.4-s7ghg   0/1     Completed   0          2d5h
istio-sidecar-injector-bf946798-252v4     1/1     Running     9          2d5h
istio-telemetry-8476d56f55-b45mv          2/2     Running     16         2d5h
istio-tracing-5d8f57c8ff-9h5mz            1/1     Running     3          2d5h
kiali-d4d886dd7-5lzsh                     1/1     Running     2          33h
prometheus-5554746896-s7v24               1/1     Running     3          2d5h

istio-pilot and istio-telemetry are two specical PODs with two containers, rest all are having one container in POD.

$ kubectl logs -f istio-pilot-6b75486f59-czlkv -n istio-system -c discovery
  • istio repo comes with some default examples, famous one is bookinfo to keep the things simple for a new learner, I started with helloworld example instead. Manifest will container two helloworld deployment with different version labels and one service.
apiVersion: v1
kind: Service
metadata:
  name: helloworld
  labels:
    app: helloworld
spec:
  ports:
  - port: 5000
    name: http
  selector:
    app: helloworld
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: helloworld-v1
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: helloworld
        version: v1
    spec:
      containers:
      - name: helloworld
        image: istio/examples-helloworld-v1
        resources:
          requests:
            cpu: "100m"
        imagePullPolicy: IfNotPresent #Always
        ports:
        - containerPort: 5000
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: helloworld-v2
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: helloworld
        version: v2
    spec:
      containers:
      - name: helloworld
        image: istio/examples-helloworld-v2
        resources:
          requests:
            cpu: "100m"
        imagePullPolicy: IfNotPresent #Always
        ports:
        - containerPort: 5000

Use the following command to deploy it. This command will insert istio-proxy containers as a sidecar or you can make the sidecar injecting default.

$ kubectl create -f <(istioctl kube-inject -f samples/helloworld/custom_helloworld.yaml)

Following two deployments and service should be present in default namespace.

$ kubectl get pod -o wide
NAME                             READY     STATUS    RESTARTS   AGE       IP            NODE
helloworld-v1-575d9d9fcd-vgcx6   2/2       Running   0          1h        172.17.0.14   minikube
helloworld-v2-54b97b8585-v6hbv   2/2       Running   0          1h        172.17.0.3    minikube

$ kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
helloworld   ClusterIP   10.103.57.51   <none>        5000/TCP    8h
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP    2h
  • Deploy the curl POD which will be used to call the helloworld app. I deployed another POD with curl1. You just need to replace curl with curl1 in below manifest everywhere.
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: curl-deploy
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: curl
    spec:
      containers:
      - name: curl
        image: radial/busyboxplus:curl
        imagePullPolicy: Always
        command:
        - sh
        - -c
        - while true; do sleep 1; done
---
apiVersion: v1
kind: Service
metadata:
  name: curl
  labels:
    app: curl
spec:
  ports:
  - port: 15001
    name: curlport
  selector:
    app: curl

Again use the istioctl to inject the sidecare istio-proxy.

kubectl create -f <(istioctl kube-inject -f samples/helloworld/curl-deploy.yaml)

We should have following PODs running after following above steps.

$ kubectl get pod --show-labels
NAME                             READY   STATUS    RESTARTS   AGE    LABELS
curl-deploy-fb5d48bb8-q8l57      2/2     Running   0          91m    app=curl,pod-template-hash=fb5d48bb8
curl-deploy1-5df76f78bb-bzw4t    2/2     Running   0          91m    app=curl1,pod-template-hash=5df76f78bb
helloworld-v1-5dbccc9889-75hhv   2/2     Running   0          123m   app=helloworld,pod-template-hash=5dbccc9889,version=v1
helloworld-v2-585b7495cc-6ffhf   2/2     Running   0          123m   app=helloworld,pod-template-hash=585b7495cc,version=v2
  • Connect with terminal of both curl pods and issue curl command couple of times to see the RR response from helloworld service.
$ kubectl exec -it curl-deploy-5f7684bb65-468vt -c curl sh

[ root@curl-deploy-5f7684bb65-468vt:/ ]$ curl helloworld.default.svc.cluster.local:5000/hello
Hello version: v1, instance: helloworld-v1-575d9d9fcd-vgcx6

[ root@curl-deploy-5f7684bb65-468vt:/ ]$ curl helloworld.default.svc.cluster.local:5000/hello
Hello version: v2, instance: helloworld-v2-54b97b8585-v6hbv
  • Using istio DestinationRule CRD. Create destination rule which will map the version v1 and v2 of helloworld app with subsets which are understandable by istio. Using subset v1 for helloworld version v1 and same for v2. Redirect the traffic coming from POD with label app: curl to v1 version of helloworld. Traffic coming from app: curl1 will be redirected to both versions of helloworld.
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: helloworld-virtualservice
spec:
  hosts:
  - "helloworld"
  http:
  - match:
    - sourceLabels:
        app: curl
    route:
    - destination:
        host: helloworld
        subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: helloworld-destinationrule
spec:
  host: helloworld
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  • Curl from POD with label app: curl will always get a response from the v1 version of helloworld, however the curl1 running with label app: curl1 will get response from both v1 and v2 versions of helloworld.
[ root@curl-deploy-5f7684bb65-468vt:/ ]$ curl helloworld.default.svc.cluster.local:5000/hello
Hello version: v1, instance: helloworld-v1-575d9d9fcd-vgcx6