Istio - Bookinfo Application

Deploy the demo of Bookinfo application:

# kubectl apply -f <(istioctl kube-inject -f samples/bookinfo/platform/kube/bookinfo.yaml)
tail -40 samples/bookinfo/platform/kube/bookinfo.yaml
kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml

Output:

---
##################################################################################################
# Productpage services
##################################################################################################
apiVersion: v1
kind: Service
metadata:
  name: productpage
  labels:
    app: productpage
    service: productpage
spec:
  ports:
  - port: 9080
    name: http
  selector:
    app: productpage
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: productpage-v1
  labels:
    app: productpage
    version: v1
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: productpage
        version: v1
    spec:
      containers:
      - name: productpage
        image: istio/examples-bookinfo-productpage-v1:1.10.1
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9080
---

Example with istioctl:

istioctl kube-inject -f samples/bookinfo/platform/kube/bookinfo.yaml | tail -172
sleep 400

Output:

...
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: productpage
    version: v1
  name: productpage-v1
spec:
  replicas: 1
  strategy: {}
  template:
    metadata:
      annotations:
        sidecar.istio.io/status: '{"version":"1d03c7b8369fddca69b40289a75eabb02e48b68ad5516e6975265f215d382f74","initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-certs"],"imagePullSecrets":null}'
      creationTimestamp: null
      labels:
        app: productpage
        version: v1
    spec:
      containers:
      - image: istio/examples-bookinfo-productpage-v1:1.10.1
        imagePullPolicy: IfNotPresent
        name: productpage
        ports:
        - containerPort: 9080
        resources: {}
...
        image: docker.io/istio/proxyv2:1.1.0
        imagePullPolicy: IfNotPresent
        name: istio-proxy
        ports:
        - containerPort: 15090
          name: http-envoy-prom
          protocol: TCP

The Bookinfo application is broken into four separate microservices:

  • productpage - the productpage microservice calls the details and reviews microservices to populate the page.
  • details - the details microservice contains book information.
  • reviews - the reviews microservice contains book reviews. It also calls the ratings microservice.
  • ratings - the ratings microservice contains book ranking information that accompanies a book review.

There are 3 versions of the reviews microservice:

  • Version v1 - doesn't call the ratings service.

    Bookinfo v1

  • Version v2 - calls the ratings service, and displays each rating as 1 to 5 black stars.

    Bookinfo v2

  • Version v3 - calls the ratings service, and displays each rating as 1 to 5 red stars.

    Bookinfo v3

Bookinfo application architecture:

Application Architecture with Istio

Confirm all services and pods are correctly defined and running:

kubectl get svc,deployment,pods -o wide

Output:

NAME                  TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE   SELECTOR
service/details       ClusterIP   10.100.84.225    <none>        9080/TCP   2m    app=details
service/kubernetes    ClusterIP   10.100.0.1       <none>        443/TCP    21m   <none>
service/productpage   ClusterIP   10.100.111.89    <none>        9080/TCP   2m    app=productpage
service/ratings       ClusterIP   10.100.217.110   <none>        9080/TCP   2m    app=ratings
service/reviews       ClusterIP   10.100.83.162    <none>        9080/TCP   2m    app=reviews

NAME                                   DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS    IMAGES                                          SELECTOR
deployment.extensions/details-v1       1         1         1            1           2m    details       istio/examples-bookinfo-details-v1:1.10.1       app=details,version=v1
deployment.extensions/productpage-v1   1         1         1            1           2m    productpage   istio/examples-bookinfo-productpage-v1:1.10.1   app=productpage,version=v1
deployment.extensions/ratings-v1       1         1         1            1           2m    ratings       istio/examples-bookinfo-ratings-v1:1.10.1       app=ratings,version=v1
deployment.extensions/reviews-v1       1         1         1            1           2m    reviews       istio/examples-bookinfo-reviews-v1:1.10.1       app=reviews,version=v1
deployment.extensions/reviews-v2       1         1         1            1           2m    reviews       istio/examples-bookinfo-reviews-v2:1.10.1       app=reviews,version=v2
deployment.extensions/reviews-v3       1         1         1            1           2m    reviews       istio/examples-bookinfo-reviews-v3:1.10.1       app=reviews,version=v3

NAME                                  READY   STATUS    RESTARTS   AGE   IP               NODE                                              NOMINATED NODE
pod/details-v1-68868454f5-sphh7       2/2     Running   0          2m    192.168.13.128   ip-192-168-26-217.eu-central-1.compute.internal   <none>
pod/productpage-v1-5cb458d74f-wwcqc   2/2     Running   0          2m    192.168.76.160   ip-192-168-69-19.eu-central-1.compute.internal    <none>
pod/ratings-v1-76f4c9765f-lzgpb       2/2     Running   0          2m    192.168.91.69    ip-192-168-69-19.eu-central-1.compute.internal    <none>
pod/reviews-v1-56f6855586-rnkjj       2/2     Running   0          2m    192.168.77.69    ip-192-168-69-19.eu-central-1.compute.internal    <none>
pod/reviews-v2-65c9df47f8-sq2vh       2/2     Running   0          2m    192.168.8.68     ip-192-168-26-217.eu-central-1.compute.internal   <none>
pod/reviews-v3-6cf47594fd-nw8hv       2/2     Running   0          2m    192.168.6.236    ip-192-168-26-217.eu-central-1.compute.internal   <none>

Check the container details - you should see also container istio-proxy next to productpage container.

kubectl describe pod -l app=productpage

Output:

...
Containers:
  productpage:
    Container ID:   docker://62984fbf7913e8cd91e5188571c7efad781880966a0d9b36279f368ad9cbf2a0
    Image:          istio/examples-bookinfo-productpage-v1:1.10.1
...
  istio-proxy:
    Container ID:  docker://17a2c6c87b1e8f315417b284973452332ea34162543af46776075ad1f43db327
    Image:         docker.io/istio/proxyv2:1.1.0
...

The kubectl logs command will show you the output of the envoy proxy (istio-proxy):

kubectl logs $(kubectl get pod -l app=productpage -o jsonpath="{.items[0].metadata.name}") istio-proxy | head -70

Output:

...
2019-03-29T09:49:07.660863Z     info    Effective config: binaryPath: /usr/local/bin/envoy
concurrency: 2
configPath: /etc/istio/proxy
connectTimeout: 10s
discoveryAddress: istio-pilot.istio-system:15010
drainDuration: 45s
parentShutdownDuration: 60s
proxyAdminPort: 15000
serviceCluster: productpage.default
statNameLength: 189
tracing:
  zipkin:
    address: zipkin.istio-system:9411

2019-03-29T09:49:07.660886Z     info    Monitored certs: []envoy.CertSource{envoy.CertSource{Directory:"/etc/certs/", Files:[]string{"cert-chain.pem", "key.pem", "root-cert.pem"}}}
2019-03-29T09:49:07.660896Z     info    PilotSAN []string(nil)
2019-03-29T09:49:07.660996Z     info    Opening status port 15020

2019-03-29T09:49:07.661159Z     info    Starting proxy agent
2019-03-29T09:49:07.661340Z     info    Received new config, resetting budget
2019-03-29T09:49:07.661349Z     info    Reconciling retry (budget 10)
2019-03-29T09:49:07.661359Z     info    Epoch 0 starting
2019-03-29T09:49:07.662335Z     info    Envoy command: [-c /etc/istio/proxy/envoy-rev0.json --restart-epoch 0 --drain-time-s 45 --parent-shutdown-time-s 60 --service-cluster productpage.default --service-node sidecar~192.168.76.160~productpage-v1-5cb458d74f-wwcqc.default~default.svc.cluster.local --max-obj-name-len 189 --allow-unknown-fields -l warning --concurrency 2]
...

Define the Istio gateway for the application:

cat samples/bookinfo/networking/bookinfo-gateway.yaml
kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml
sleep 5

Output:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: bookinfo-gateway
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: bookinfo
spec:
  hosts:
  - "*"
  gateways:
  - bookinfo-gateway
  http:
  - match:
    - uri:
        exact: /productpage
    - uri:
        exact: /login
    - uri:
        exact: /logout
    - uri:
        prefix: /api/v1/products
    route:
    - destination:
        host: productpage
        port:
          number: 9080

Create and display default destination rules (subsets) for the Bookinfo services:

kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml
kubectl get destinationrules -o yaml

Display the destination rules:

Output:

...
- apiVersion: networking.istio.io/v1alpha3
  kind: DestinationRule
...
    name: reviews
    namespace: default
...
  spec:
    host: reviews
    subsets:
    - labels:
        version: v1
      name: v1
    - labels:
        version: v2
      name: v2
    - labels:
        version: v3
      name: v3
...

Confirm the gateway and virtualsevice has been created:

kubectl get gateway,virtualservice,destinationrule

Output:

NAME                                           AGE
gateway.networking.istio.io/bookinfo-gateway   13s

NAME                                          GATEWAYS             HOSTS   AGE
virtualservice.networking.istio.io/bookinfo   [bookinfo-gateway]   [*]     13s

NAME                                              HOST          AGE
destinationrule.networking.istio.io/details       details       8s
destinationrule.networking.istio.io/productpage   productpage   8s
destinationrule.networking.istio.io/ratings       ratings       8s
destinationrule.networking.istio.io/reviews       reviews       8s

Gateway -> VirtualService -> DestinationRule

Check the SSL certificate:

echo | openssl s_client -showcerts -connect ${MY_DOMAIN}:443 2>/dev/null | openssl x509 -inform pem -noout -text

Output:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            03:ba:eb:a2:34:43:0c:ae:7b:63:64:4d:4a:ee:c1:25:b4:35
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
        Validity
            Not Before: Mar 29 08:46:52 2019 GMT
            Not After : Jun 27 08:46:52 2019 GMT
        Subject: CN = *.mylabs.dev
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                AB:60:E9:ED:3F:40:72:83:7D:62:08:F9:EB:8F:EA:1C:42:CC:76:4E
            X509v3 Authority Key Identifier:
                keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1

            Authority Information Access:
                OCSP - URI:http://ocsp.int-x3.letsencrypt.org
                CA Issuers - URI:http://cert.int-x3.letsencrypt.org/

            X509v3 Subject Alternative Name:
                DNS:*.mylabs.dev, DNS:mylabs.dev
            X509v3 Certificate Policies:
                Policy: 2.23.140.1.2.1
                Policy: 1.3.6.1.4.1.44947.1.1.1
                  CPS: http://cps.letsencrypt.org
...

You can see it in the certificate transparency log: https://crt.sh/?q=mylabs.dev)

crt.sh Certificate

SSL Certificate in Kiali https://kiali.mylabs.dev web page:

Kiali SSL Certificate displayed in browser

You can also use the cert-manager directly to see the status of the certificate:

kubectl describe certificates ingress-cert-${LETSENCRYPT_ENVIRONMENT} -n istio-system

Output:

Name:         ingress-cert-production
Namespace:    istio-system
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"certmanager.k8s.io/v1alpha1","kind":"Certificate","metadata":{"annotations":{},"name":"ingress-cert-production","namespace"...
API Version:  certmanager.k8s.io/v1alpha1
Kind:         Certificate
Metadata:
  Creation Timestamp:  2019-03-29T09:43:02Z
  Generation:          1
  Resource Version:    2854
  Self Link:           /apis/certmanager.k8s.io/v1alpha1/namespaces/istio-system/certificates/ingress-cert-production
  UID:                 0b677790-5207-11e9-ac06-02dc9c152bfa
Spec:
  Acme:
    Config:
      Dns 01:
        Provider:  aws-route53
      Domains:
        *.mylabs.dev
        mylabs.dev
  Common Name:  *.mylabs.dev
  Dns Names:
    *.mylabs.dev
    mylabs.dev
  Issuer Ref:
    Kind:       ClusterIssuer
    Name:       letsencrypt-production-dns
  Secret Name:  ingress-cert-production
Status:
  Conditions:
    Last Transition Time:  2019-03-29T09:46:53Z
    Message:               Certificate is up to date and has not expired
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Not After:               2019-06-27T08:46:52Z
Events:
  Type     Reason          Age                  From          Message
  ----     ------          ----                 ----          -------
  Warning  IssuerNotReady  9m9s (x2 over 9m9s)  cert-manager  Issuer letsencrypt-production-dns not ready
  Normal   Generated       9m8s                 cert-manager  Generated new private key
  Normal   OrderCreated    9m8s                 cert-manager  Created Order resource "ingress-cert-production-3383842614"
  Normal   OrderComplete   5m18s                cert-manager  Order "ingress-cert-production-3383842614" completed successfully
  Normal   CertIssued      5m18s                cert-manager  Certificate issued successfully

cert-manager high level overview

Confirm the app is running:

curl -o /dev/null -s -w "%{http_code}" http://${MY_DOMAIN}/productpage; echo

Output:

200

Generate some traffic for next 5 minutes to gather some data for monitoring:

siege --log=/tmp/siege --concurrent=1 -q --internet --time=10M http://${MY_DOMAIN}/productpage &> /dev/null &

In case of DNS issue you can use the services exposed on ports directly from loadbalancer:

kubectl -n istio-system get service istio-ingressgateway -o jsonpath="{.status.loadBalancer.ingress[0].hostname}"; echo

Output:

abd0be556520611e9ac0602dc9c152bf-2144127322.eu-central-1.elb.amazonaws.com
  • Kiali: http://<IP ADDRESS OF CLUSTER INGRESS>:15029
  • Prometheus: http://<IP ADDRESS OF CLUSTER INGRESS>:15030
  • Grafana: http://<IP ADDRESS OF CLUSTER INGRESS>:15031
  • Tracing: http://<IP ADDRESS OF CLUSTER INGRESS>:15032

Open the Bookinfo site in your browser http://mylabs.dev/productpage and refresh the page several times - you should see different versions of reviews shown in productpage, presented in a round robin style (red stars, black stars, no stars), since we haven't yet used Istio to control the version routing.

Bookinfo v1, v3, v2

Check the flows in Kiali:

Istio Graph

Open the browser with these pages:

Istio