istio 的 Bookinfo Application 示例拆解

Istio 文档中给出了一个 Bookinfo Application 示例,这里拆解它的实现。

Bookinfo APP 组成

Bookinfo APP 由四个子系统组成,分别是:

  • productpage,产品页,展示图书信息,依赖 details 和 reviews
  • details,图书详情查询
  • reviews,用户评论查询,依赖 ratings
  • ratings,图书排行榜查询

这些子系统分别用 python、ruby、java、node 开发,其中 reviews 系统有三个版本:v1、v2、v3。

Bookinfo Application

示例中把上述四个系统部署在 Kubernetes 中,每个系统由 Service、ServiceAccount、Deployment 组成。reviews 有三个版本,对应三个 Deployment。

部署到 istio 网格

要把 Bookinfo Application 部署在 istio 网格中,仅仅在安装了 istio 的 kubernetes 创建应用是不行的,需要在带有 istio-injection=enabled 标签的 namespace 中创建才可以。

所以 文档 中的第一步操作在目标 namespace 上打标签:

kubectl label namespace default istio-injection=enabled

然后在打了标签的 namespace 中创建应用:

kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml

如果不想给 namesapce 打标签,可以用下面的命令调整 yaml 文件:

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

验证 Productpage

部署完成后,验证 productpage,在集群内用它的 cluster ip 或者 pod ip 访问:

$ kubectl get svc productpage -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR productpage ClusterIP 10.110.29.37 <none> 9080/TCP 76d app=productpage
$ curl 10.110.29.37:9080 ...省略... </body> </html>

或者在 Productpage 等容器内安装 curl,用 curl 验证:

$ kubectl exec -it productpage-v1-667bc85676-sgbqp /bin/sh $ apt-get update $ apt-get install -y curl $ curl http://productpage:9080 ...省略... </body> </html>

Productpage 是 bookinfo app 的入口,使用 python 开发,从代码中找到另外几个服务的接口:

$ kubectl exec -it productpage-v1-8554d58bff-wlkg7 cat productpage.py >/tmp/productpage.py

查看代码得知其它几个服务的 http 接口,后面将使用这些接口验证服务:

details: http://details:9080/details/0 ratings: http://ratings:9080/ratings/0 reviews: http://reviews:9080/reviews/0

Productpage service 部署 yaml:

apiVersion: v1 kind: Service metadata: name: productpage labels: app: productpage service: productpage spec: ports: - port: 9080 name: http selector: app: productpage --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-productpage --- apiVersion: apps/v1 kind: Deployment metadata: name: productpage-v1 labels: app: productpage version: v1 spec: replicas: 1 selector: matchLabels: app: productpage version: v1 template: metadata: labels: app: productpage version: v1 spec: serviceAccountName: bookinfo-productpage containers: - name: productpage image: docker.io/istio/examples-bookinfo-productpage-v1:1.15.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080

验证 Detail

完成部署后,验证 details 服务:

$ kubectl get svc details -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR details ClusterIP 10.107.162.106 <none> 9080/TCP 76d app=details
$ curl http://10.107.162.106:9080/details/0 {"id":0,"author":"William Shakespeare","year":1595,"type":"paperback","pages":200,"publisher...省略...

details 服务的部署文件:

################################################################################## # Details service ################################################################################### apiVersion: v1 kind: Service metadata: name: details labels: app: details service: details spec: ports: - port: 9080 name: http selector: app: details --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-details --- apiVersion: apps/v1 kind: Deployment metadata: name: details-v1 labels: app: details version: v1 spec: replicas: 1 selector: matchLabels: app: details version: v1 template: metadata: labels: app: details version: v1 spec: serviceAccountName: bookinfo-details containers: - name: details image: docker.io/istio/examples-bookinfo-details-v1:1.15.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 ---

验证 Ratings

完成部署后,验证 ratings 服务:

$ kubectl get svc ratings -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR ratings ClusterIP 10.107.67.216 <none> 9080/TCP 76d app=ratings
$ curl http://10.107.67.216:9080/ratings/0 {"id":0,"ratings":{"Reviewer1":5,"Reviewer2":4}}$

ratings 服务的部署文件:

################################################################################### # Ratings service ################################################################################## apiVersion: v1 kind: Service metadata: name: ratings labels: app: ratings service: ratings spec: ports: - port: 9080 name: http selector: app: ratings --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-ratings --- apiVersion: apps/v1 kind: Deployment metadata: name: ratings-v1 labels: app: ratings version: v1 spec: replicas: 1 selector: matchLabels: app: ratings version: v1 template: metadata: labels: app: ratings version: v1 spec: serviceAccountName: bookinfo-ratings containers: - name: ratings image: docker.io/istio/examples-bookinfo-ratings-v1:1.15.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080

验证 Reviews

完成部署后,验证 reviews 服务:

$ kubectl get svc reviews -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR reviews ClusterIP 10.100.249.106 <none> 9080/TCP 76d app=reviews
$ curl http://10.100.249.106:9080/reviews/0 {"id": "0","reviews": [{ "reviewer": "Reviewer1", "text": "An extremely entertaining play by Shakespeare....省略...

Reviews 有三个版本,对应三个 Deployment,隶属于同一个 Service:

################################################################################### # Reviews service ################################################################################### apiVersion: v1 kind: Service metadata: name: reviews labels: app: reviews service: reviews spec: ports: - port: 9080 name: http selector: app: reviews --- apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-reviews

三个版本的 deployment:

V1:

apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v1 labels: app: reviews version: v1 spec: replicas: 1 selector: matchLabels: app: reviews version: v1 template: metadata: labels: app: reviews version: v1 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v1:1.15.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080

V2:

apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v2 labels: app: reviews version: v2 spec: replicas: 1 selector: matchLabels: app: reviews version: v2 template: metadata: labels: app: reviews version: v2 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v2:1.15.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080

V3:

apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v3 labels: app: reviews version: v3 spec: replicas: 1 selector: matchLabels: app: reviews version: v3 template: metadata: labels: app: reviews version: v3 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v3:1.15.0 imagePullPolicy: IfNotPresent ports: - containerPort: 9080

创建 Gateway,ingress envoy 开始监听

创建 Gateway,指示边界 envoy 监听 80 端口,允许通过 ingress envoy 的 80 端口访问 Bookinfo:

$ kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml

Gateway 定义:

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: - "*"

如果这时候还没有创建 VirtualService,ingress envoy 还不知道要转发到哪里。

创建 VirtualService,转发请求

创建 VirtualService,指示 ingress envoy 将外部到来的请求转发到 productpage :

$ kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml

VirtualService 定义:

apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: bookinfo spec: hosts: - "*" gateways: - bookinfo-gateway http: - match: - uri: exact: /productpage - uri: prefix: /static - uri: exact: /login - uri: exact: /logout - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080

这时可以通过边界 envoy 访问 bookinfo。这里的 hosts 配置是 *,可以根据需要改成具体的域名。

通过边界 envoy 访问应用

边界 envoy 是 istio 的组件之一,由两个服务组成:

$ kubectl -n istio-system get deployment | grep gateway istio-egressgateway 1/1 1 1 45h istio-ingressgateway 1/1 1 1 45h

一个负责从外部流入网格的流量(ingress),一个负责从网格流向外部的流量(egress)。

约定 istio-ingressgateway 处理外部进入网格的流量,istio-egressgateway 处理网格流向外部的流量,离开网格的流量的处理参考 Engress Control

通过 kubernetes 提供的方式访问 istio-ingressgateway:

$ kubectl -n istio-system get service | grep gateway istio-egressgateway ClusterIP 10.111.134.223 <none> 80/TCP,443/TCP,15443/TCP istio-ingressgateway LoadBalancer 10.101.187.91 <pending> 15020:31270/TCP,80:31380/TCP, 443:31390/TCP,31400:31400/TCP, 15029:31248/TCP,15030:30079/TCP, 15031:30269/TCP,15032:30249/TCP, 15443:31829/TCP

可以看到 istio-ingressgateway 使用 LoadBalancer 模式,它的 80 端口对应的映射端口是 31380。

如果是用 minikube 部署的 kubernetes,可以用下面的方式获取 istio-ingressgateway 的访问地址:

$ minikube service list |---------------|------------------------|--------------------------------| | NAMESPACE | NAME | URL | |---------------|------------------------|--------------------------------| | istio-system | istio-ingressgateway | http://192.168.99.100:31270 | | | | http://192.168.99.100:31380 | | | | http://192.168.99.100:31390 | | | | http://192.168.99.100:31400 | | | | http://192.168.99.100:31248 | | | | http://192.168.99.100:30079 | | | | http://192.168.99.100:30269 | | | | http://192.168.99.100:30249 | | | | http://192.168.99.100:31829 | |---------------|------------------------|--------------------------------|

这里使用了 minikube,用下面的 url 访问 bookinfo,path 为 productpage,与 VirtualService 中的配置一致:

http://192.168.99.100:31380/productpage

bookinfo网页

虽然可以通过边界 envoy 访问应用,但是因为没有设置转发策略,四个子系统之间的互相访问和通过 kubernetes 中的 svc 访问效果相同。

刷新页面会看到页面在变化:

bookinfo网页

bookinfo网页

这是因为 reviews 服务有 v1、v2、v3 三个版本,访问 productpage 时,productpage 随机从 reviews 的三个版本的 pod 中获取用户评论数据,所以会看到不同的页面。

可以为每个服务创建一个 VirtualService 和 Destination,精细管控每个服务的转发策略。

设置网格内的转发策略:DestinationRule

为每个服务创建一个 DestinationRule,设置转发策略:

$ kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml

这里在 DestinationRule 中将 Pod 按照 Label 进行了分组,拆分成多个 subnet:

productpage:

apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: productpage spec: host: productpage subsets: - name: v1 labels: version: v1

ratings:

apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: ratings spec: host: ratings subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2 - name: v2-mysql labels: version: v2-mysql - name: v2-mysql-vm labels: version: v2-mysql-vm

details:

apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: details spec: host: details subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2

reviews:

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

转发策略被 VirtualService 引用后才生效,参考 Apply a virtual service,还需要为每个服务创建一个 VirtualService,在 VirtualService 中引用 DestinationRule 中的 subset:

$ kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml

productpage 的 VirtualService:

apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: productpage spec: hosts: - productpage http: - route: - destination: host: productpage subset: v1

details 的 VirtualService:

apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: details spec: hosts: - details http: - route: - destination: host: details subset: v1

ratings 的 VirtualService:

apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: ratings spec: hosts: - ratings http: - route: - destination: host: ratings subset: v1

reviews 的 VirtualService:

apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: reviews spec: hosts: - reviews http: - route: - destination: host: reviews subset: v1

reviews 的 VirtualService 绑定了 reviews 的 subset: v1,这时候访问 bookinfo,页面不再变化。

bookinfo网页

按照用户转发

更新 reviews 的 VirtualService,将 jason 用户的请求转发到 v2 版本:

$ kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-test-v2.yaml
apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: reviews spec: hosts: - reviews http: - match: - headers: end-user: exact: jason route: - destination: host: reviews subset: v2 - route: - destination: host: reviews subset: v1

点击界面右上角的 login,以用户 jason 的身份登录(密码为空),然后刷新页面,会发现页面变成了 v2 版本:

bookinfo网页

退出 jason 账号后,页面又回到了 v1 版本。

更多操作

Traffic Management 中有更多示范操作,譬如按照 user 转发、错误注入、流量整形、断路器、流量复制等,这些内容放在后面单独整理。

参考