Istio和Service Mesh

Istio是Google、IBM和Lyft联合开源的微服务 Service Mesh 框架,旨在解决大量微服务的发现、连接、管理、监控以及安全等问题。Istio对应用是透明的,不需要改动任何服务代码就可以实现透明的服务治理。

Istio的主要特性包括:

  • HTTP、gRPC和TCP网络流量的自动负载均衡
  • 丰富的路由规则,细粒度的网络流量行为控制
  • 流量加密、服务间认证,以及强身份声明
  • 全范围(Fleet-wide)策略执行
  • 深度遥测和报告

Istio原理

Istio从逻辑上可以分为数据平面和控制平面:

  • 数据平面主要由一系列的智能代理(Envoy)组成,管理微服务之间的网络通信
  • 控制平面负责管理和配置这些智能代理,并动态执行策略

Istio架构可以如下图所示

Istio - 图1

主要由以下组件构成

  • Envoy:Lyft开源的高性能代理总线,支持动态服务发现、负载均衡、TLS终止、HTTP/2和gPRC代理、健康检查、性能测量等功能。Envoy以sidecar的方式部署在相关的服务的Pod中。
  • Mixer:负责访问控制、执行策略并从Envoy代理中收集遥测数据。Mixer支持灵活的插件模型,方便扩展(支持GCP、AWS、Prometheus、Heapster等多种后端)
  • Istio-Auth:提供服务间和终端用户的认证机制
  • Pilot:动态管理Envoy示例的生命周期,提供服务发现、流量管理、智能路由以及超时、熔断等弹性控制的功能。其与Envoy的关系如下图所示

Istio - 图2

在数据平面上,除了Envoy,还可以选择使用 nginxmeshlinkerd 作为网络代理。比如,使用nginxmesh时,Istio的控制平面(Pilot、Mixer、Auth)保持不变,但用Nginx Sidecar取代Envoy:

Istio - 图3

安装

Istio目前仅支持Kubernetes,在部署Istio之前需要先部署好Kubernetes集群并配置好kubectl客户端。

下载Istio

  1. curl -L https://git.io/getLatestIstio | sh -
  2. cd istio-0.5.1/
  3. cp bin/istioctl /usr/local/bin

部署Istio服务

两种方式(选择其一执行)

  • 禁止Auth:kubectl apply -f install/kubernetes/istio.yaml
  • 启用Auth:kubectl apply -f install/kubernetes/istio-auth.yaml

部署完成后,可以检查 isotio-system namespace 中的服务是否正常运行:

  1. $ kubectl -n istio-system get pod
  2. NAME READY STATUS RESTARTS AGE
  3. istio-ca-5869f745dd-vdblc 1/1 Running 0 43s
  4. istio-ingress-8458c655f5-9mc8f 1/1 Running 0 45s
  5. istio-mixer-6f8d7c548-9bkqt 3/3 Running 0 1m
  6. istio-pilot-6c96cc4859-cjx4b 2/2 Running 1 47s
  7. $ kubectl -n istio-system get svc
  8. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  9. istio-ingress LoadBalancer 10.108.221.199 <pending> 80:31857/TCP,443:30803/TCP 53s
  10. istio-mixer ClusterIP 10.104.170.172 <none> 9091/TCP,15004/TCP,9093/TCP,9094/TCP,9102/TCP,9125/UDP,42422/TCP 1m
  11. istio-pilot ClusterIP 10.109.122.23 <none> 15003/TCP,8080/TCP,9093/TCP,443/TCP 55s

部署Prometheus、Grafana和Zipkin插件

  1. kubectl apply -f install/kubernetes/addons/grafana.yaml
  2. kubectl apply -f install/kubernetes/addons/servicegraph.yaml
  3. kubectl apply -f install/kubernetes/addons/zipkin.yaml
  4. kubectl apply -f install/kubernetes/addons/prometheus.yaml
  5. # kubectl apply -f install/kubernetes/addons/zipkin-to-stackdriver.yaml

等一会所有Pod启动后,可以通过NodePort或负载均衡服务的外网IP来访问这些服务。比如通过NodePort方式,先查询服务的NodePort

  1. $ kubectl -n istio-system get svc grafana -o jsonpath='{.spec.ports[0].nodePort}'
  2. 32070
  3. $ kubectl -n istio-system get svc servicegraph -o jsonpath='{.spec.ports[0].nodePort}'
  4. 31072
  5. $ kubectl -n istio-system get svc zipkin -o jsonpath='{.spec.ports[0].nodePort}'
  6. 30032
  7. $ kubectl -n istio-system get svc prometheus -o jsonpath='{.spec.ports[0].nodePort}'
  8. 30890

通过http://<kubernetes-ip>:32070/dashboard/db/istio-dashboard访问Grafana服务

Istio - 图4

通过http://<kubernetes-ip>:31072/dotviz访问ServiceGraph服务,展示服务之间调用关系图

Istio - 图5

通过http://<kubernetes-ip>:30032访问Zipkin跟踪页面

Istio - 图6

通过http://<kubernetes-ip>:30890访问Prometheus页面

Istio - 图7

部署示例应用

在部署应用时,需要通过istioctl kube-inject给Pod自动插入Envoy容器,即

  1. wget https://raw.githubusercontent.com/istio/istio/master/blog/bookinfo-v1.yaml
  2. # inject with istioctl
  3. kubectl apply -f <(istioctl kube-inject -f bookinfo-v1.yaml)
  4. # create ingress
  5. cat <<EOF | kubectl create -f -
  6. apiVersion: extensions/v1beta1
  7. kind: Ingress
  8. metadata:
  9. name: bookinfo
  10. annotations:
  11. kubernetes.io/ingress.class: "istio"
  12. spec:
  13. rules:
  14. - http:
  15. paths:
  16. - path: /productpage
  17. backend:
  18. serviceName: productpage
  19. servicePort: 9080
  20. - path: /login
  21. backend:
  22. serviceName: productpage
  23. servicePort: 9080
  24. - path: /logout
  25. backend:
  26. serviceName: productpage
  27. servicePort: 9080
  28. EOF

原始应用如下图所示

Istio - 图8

istioctl kube-inject在原始应用的每个Pod中插入了一个Envoy容器

Istio - 图9

服务启动后,可以通过Ingress地址http://<ingress-address>/productpage来访问BookInfo应用

  1. $ kubectl describe ingress
  2. Name: gateway
  3. Namespace: default
  4. Address: 192.168.0.77
  5. Default backend: default-http-backend:80 (10.8.0.4:8080)
  6. Rules:
  7. Host Path Backends
  8. ---- ---- --------
  9. *
  10. /productpage productpage:9080 (<none>)
  11. /login productpage:9080 (<none>)
  12. /logout productpage:9080 (<none>)
  13. Annotations:
  14. Events: <none>

Istio - 图10

金丝雀部署

首先部署v2版本的应用,并配置默认路由到v1版本:

  1. wget https://raw.githubusercontent.com/istio/istio/master/blog/bookinfo-ratings.yaml
  2. kubectl apply -f <(istioctl kube-inject -f bookinfo-ratings.yaml)
  3. wget https://raw.githubusercontent.com/istio/istio/master/blog/bookinfo-reviews-v2.yaml
  4. kubectl apply -f <(istioctl kube-inject -f bookinfo-reviews-v2.yaml)
  5. # create default route
  6. cat <<EOF | istioctl create -f -
  7. apiVersion: config.istio.io/v1alpha2
  8. kind: RouteRule
  9. metadata:
  10. name: reviews-default
  11. spec:
  12. destination:
  13. name: reviews
  14. route:
  15. - labels:
  16. version: v1
  17. weight: 100
  18. EOF

示例一:将 10% 请求发送到 v2 版本而其余 90% 发送到 v1 版本

  1. cat <<EOF | istioctl create -f -
  2. apiVersion: config.istio.io/v1alpha2
  3. kind: RouteRule
  4. metadata:
  5. name: reviews-default
  6. spec:
  7. destination:
  8. name: reviews
  9. route:
  10. - labels:
  11. version: v2
  12. weight: 10
  13. - labels:
  14. version: v1
  15. weight: 90
  16. EOF

示例二:将特定用户的请求全部发到 v2 版本

  1. cat <<EOF | istioctl create -f -
  2. apiVersion: config.istio.io/v1alpha2
  3. kind: RouteRule
  4. metadata:
  5. name: reviews-test-v2
  6. spec:
  7. destination:
  8. name: reviews
  9. precedence: 2
  10. match:
  11. request:
  12. headers:
  13. cookie:
  14. regex: "^(.*?;)?(user=jason)(;.*)?$"
  15. route:
  16. - labels:
  17. version: v2
  18. weight: 100
  19. EOF

示例三:全部切换到 v2 版本

  1. cat <<EOF | istioctl replace -f -
  2. apiVersion: config.istio.io/v1alpha2
  3. kind: RouteRule
  4. metadata:
  5. name: reviews-default
  6. spec:
  7. destination:
  8. name: reviews
  9. route:
  10. - labels:
  11. version: v2
  12. weight: 100
  13. EOF

示例四:限制并发访问

  1. # configure a memquota handler with rate limits
  2. cat <<EOF | istioctl create -f -
  3. apiVersion: "config.istio.io/v1alpha2"
  4. kind: memquota
  5. metadata:
  6. name: handler
  7. namespace: default
  8. spec:
  9. quotas:
  10. - name: requestcount.quota.default
  11. maxAmount: 5000
  12. validDuration: 1s
  13. overrides:
  14. - dimensions:
  15. destination: ratings
  16. maxAmount: 1
  17. validDuration: 1s
  18. EOF
  19. # create quota instance that maps incoming attributes to quota dimensions, and createrule that uses it with the memquota handler
  20. cat <<EOF | istioctl create -f -
  21. apiVersion: "config.istio.io/v1alpha2"
  22. kind: quota
  23. metadata:
  24. name: requestcount
  25. namespace: default
  26. spec:
  27. dimensions:
  28. source: source.labels["app"] | source.service | "unknown"
  29. sourceVersion: source.labels["version"] | "unknown"
  30. destination: destination.labels["app"] | destination.service | "unknown"
  31. destinationVersion: destination.labels["version"] | "unknown"
  32. ---
  33. apiVersion: "config.istio.io/v1alpha2"
  34. kind: rule
  35. metadata:
  36. name: quota
  37. namespace: default
  38. spec:
  39. actions:
  40. - handler: handler.memquota
  41. instances:
  42. - requestcount.quota
  43. EOF

为了查看访问次数限制的效果,可以使用 wrk 给应用加一些压力:

  1. export BOOKINFO_URL=$(kubectl get po -n istio-system -l istio=ingress -o jsonpath={.items[0].status.hostIP}):$(kubectl get svc -n istio-system istio-ingress -o jsonpath={.spec.ports[0].nodePort})
  2. wrk -t1 -c1 -d20s http://$BOOKINFO_URL/productpage

参考文档