Istio 流量管理

Istio 提供了强大的流量管理功能,如智能路由、服务发现与负载均衡、故障恢复、故障注入等。

istio-traffic-management

流量管理的功能由 Pilot 配合 Envoy 负责,并接管进入和离开容器的所有流量:

  • 流量管理的核心组件是 Pilot,负责管理和配置服务网格中的所有 Envoy 实例
  • 而 Envoy 实例则负责维护负载均衡以及健康检查信息,从而允许其在目标实例之间智能分配流量,同时遵循其指定的路由规则

pilot

request-flow

API 版本

Istio 0.7.X 及以前版本仅支持 config.istio.io/v1alpha2,0.8.0 将其升级为 networking.istio.io/v1alpha3,并且重命名了流量管理的几个资源对象:

  • RouteRule -> VirtualService:定义服务网格内对服务的请求如何进行路由控制 ,支持根据 host、sourceLabels 、http headers 等不同的路由方式,也支持百分比、超时、重试、错误注入等功能。
  • DestinationPolicy -> DestinationRule:定义 VirtualService 之后的路由策略,包括断路器、负载均衡以及 TLS 等。
  • EgressRule -> ServiceEntry:定义了服务网格之外的服务,支持两种类型:网格内部和网格外部。网格内的条目和其他的内部服务类似,用于显式的将服务加入网格。可以用来把服务作为服务网格扩展的一部分加入不受管理的基础设置(例如加入到基于 Kubernetes 的服务网格中的虚拟机)中。网格外的条目用于表达网格外的服务。对这种条目来说,双向 TLS 认证是禁止的,策略实现需要在客户端执行,而不像内部服务请求中的服务端执行。
  • Ingress -> Gateway:定义边缘网络流量的负载均衡。

服务发现和负载均衡

为了接管流量,Istio 假设所有容器在启动时自动将自己注册到 Istio 中(通过自动或手动给 Pod 注入 Envoy sidecar 容器)。Envoy 收到外部请求后,会对请求作负载均衡,并支持轮询、随机和加权最少请求等负载均衡算法。除此之外,Envoy 还会以熔断机制定期检查服务后端容器的健康状态,自动移除不健康的容器和加回恢复正常的容器。容器内也可以返回 HTTP 503 显示将自己从负载均衡中移除。

流量管理 - 图4

流量接管

Istio 假定进入和离开服务网络的所有流量都会通过 Envoy 代理进行传输。Envoy sidecar 使用 iptables 把进入 Pod 和从 Pod 发出的流量转发到 Envoy 进程监听的端口(即 15001 端口)上:

  1. *nat
  2. :PREROUTING ACCEPT [0:0]
  3. :INPUT ACCEPT [1:60]
  4. :OUTPUT ACCEPT [482:44962]
  5. :POSTROUTING ACCEPT [482:44962]
  6. :ISTIO_INBOUND - [0:0]
  7. :ISTIO_IN_REDIRECT - [0:0]
  8. :ISTIO_OUTPUT - [0:0]
  9. :ISTIO_REDIRECT - [0:0]
  10. -A PREROUTING -p tcp -j ISTIO_INBOUND
  11. -A OUTPUT -p tcp -j ISTIO_OUTPUT
  12. -A ISTIO_INBOUND -p tcp -m tcp --dport 9080 -j ISTIO_IN_REDIRECT
  13. -A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15001
  14. -A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -j ISTIO_REDIRECT
  15. -A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN
  16. -A ISTIO_OUTPUT -m owner --gid-owner 1337 -j RETURN
  17. -A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN
  18. -A ISTIO_OUTPUT -j ISTIO_REDIRECT
  19. -A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001

故障恢复

Istio 提供了一系列开箱即用的故障恢复功能,如

  • 超时处理
  • 重试处理,如限制最大重试时间以及可变重试间隔
  • 健康检查,如自动移除不健康的容器
  • 请求限制,如并发请求数和并发连接数
  • 熔断

这些功能均可以使用 VirtualService 动态配置。比如以下为用户 jason 的请求返回 500 (而其他用户均可正常访问):

  1. apiVersion: networking.istio.io/v1alpha3
  2. kind: VirtualService
  3. metadata:
  4. name: ratings
  5. spec:
  6. hosts:
  7. - ratings
  8. http:
  9. - match:
  10. - headers:
  11. cookie:
  12. regex: "^(.*?;)?(user=jason)(;.*)?$"
  13. fault:
  14. abort:
  15. percent: 100
  16. httpStatus: 500
  17. route:
  18. - destination:
  19. host: ratings
  20. subset: v1
  21. - route:
  22. - destination:
  23. host: ratings
  24. subset: v1

熔断示例:

  1. cat <<EOF | istioctl create -f -
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: DestinationRule
  4. metadata:
  5. name: httpbin
  6. spec:
  7. host: httpbin
  8. trafficPolicy:
  9. connectionPool:
  10. tcp:
  11. maxConnections: 1
  12. http:
  13. http1MaxPendingRequests: 1
  14. maxRequestsPerConnection: 1
  15. outlierDetection:
  16. http:
  17. consecutiveErrors: 1
  18. interval: 1s
  19. baseEjectionTime: 3m
  20. maxEjectionPercent: 100
  21. EOF

故障注入

Istio 支持为应用注入故障,以模拟实际生产中碰到的各种问题,包括

  • 注入延迟(模拟网络延迟和服务过载)
  • 注入失败(模拟应用失效)

这些故障均可以使用 VirtualService 动态配置。如以下配置 2 秒的延迟:

  1. apiVersion: networking.istio.io/v1alpha3
  2. kind: VirtualService
  3. metadata:
  4. name: ratings
  5. spec:
  6. hosts:
  7. - ratings
  8. http:
  9. - fault:
  10. delay:
  11. percent: 100
  12. fixedDelay: 2s
  13. route:
  14. - destination:
  15. host: ratings
  16. subset: v1

金丝雀部署

service-versions

首先部署 bookinfo,并配置默认路由为 v1 版本:

  1. # 以下命令假设 bookinfo 示例程序已部署,如未部署,可以执行下面的命令
  2. $ kubectl apply -f <(istioctl kube-inject -f samples/bookinfo/platform/kube/bookinfo.yaml)
  3. # 此时,三个版本的 reviews 服务以负载均衡的方式轮询。
  4. # 创建默认路由,全部请求转发到 v1
  5. $ istioctl create -f samples/bookinfo/routing/route-rule-all-v1.yaml
  6. $ kubectl get virtualservice reviews -o yaml
  7. apiVersion: networking.istio.io/v1alpha3
  8. kind: VirtualService
  9. metadata:
  10. name: reviews
  11. spec:
  12. hosts:
  13. - reviews
  14. http:
  15. - route:
  16. - destination:
  17. host: reviews
  18. subset: v1

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

  1. cat <<EOF | istioctl create -f -
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: VirtualService
  4. metadata:
  5. name: reviews
  6. spec:
  7. hosts:
  8. - reviews
  9. http:
  10. - route:
  11. - destination:
  12. host: reviews
  13. subset: v1
  14. weight: 75
  15. - destination:
  16. host: reviews
  17. subset: v2
  18. weight: 25
  19. EOF

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

  1. cat <<EOF | istioctl replace -f -
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: VirtualService
  4. metadata:
  5. name: ratings
  6. spec:
  7. hosts:
  8. - ratings
  9. http:
  10. - match:
  11. - sourceLabels:
  12. app: reviews
  13. version: v2
  14. headers:
  15. end-user:
  16. exact: jason
  17. EOF

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

  1. cat <<EOF | istioctl replace -f -
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: VirtualService
  4. metadata:
  5. name: reviews
  6. spec:
  7. hosts:
  8. - reviews
  9. http:
  10. - route:
  11. - destination:
  12. host: reviews
  13. subset: v2
  14. EOF

示例四:限制并发访问

  1. cat <<EOF | istioctl create -f -
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: DestinationRule
  4. metadata:
  5. name: reviews
  6. spec:
  7. host: reviews
  8. subsets:
  9. - name: v1
  10. labels:
  11. version: v1
  12. trafficPolicy:
  13. connectionPool:
  14. tcp:
  15. maxConnections: 100
  16. 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

Gateway

Istio 在部署时会自动创建一个 Istio Gateway,用来控制 Ingress 访问。

  1. # prepare
  2. kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml)
  3. openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/tls.key -out /tmp/tls.crt -subj "/CN=httpbin.example.com"
  4. # get ingress external IP (suppose load balancer service)
  5. kubectl get svc istio-ingressgateway -n istio-system
  6. export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
  7. export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http")].port}')
  8. export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].port}')
  9. # create gateway
  10. cat <<EOF | istioctl create -f -
  11. apiVersion: networking.istio.io/v1alpha3
  12. kind: Gateway
  13. metadata:
  14. name: httpbin-gateway
  15. spec:
  16. selector:
  17. istio: ingressgateway # use Istio default gateway implementation
  18. servers:
  19. - port:
  20. number: 80
  21. name: http
  22. protocol: HTTP
  23. hosts:
  24. - "httpbin.example.com"
  25. EOF
  26. # configure routes for the gateway
  27. cat <<EOF | istioctl create -f -
  28. apiVersion: networking.istio.io/v1alpha3
  29. kind: VirtualService
  30. metadata:
  31. name: httpbin
  32. spec:
  33. hosts:
  34. - "httpbin.example.com"
  35. gateways:
  36. - httpbin-gateway
  37. http:
  38. - match:
  39. - uri:
  40. prefix: /status
  41. - uri:
  42. prefix: /delay
  43. route:
  44. - destination:
  45. port:
  46. number: 8000
  47. host: httpbin
  48. EOF
  49. # validate 200
  50. curl --resolve httpbin.example.com:$INGRESS_PORT:$INGRESS_HOST -HHost:httpbin.example.com -I http://httpbin.example.com:$INGRESS_PORT/status/200
  51. # invalidate 404
  52. curl --resolve httpbin.example.com:$INGRESS_PORT:$INGRESS_HOST -HHost:httpbin.example.com -I http://httpbin.example.com:$INGRESS_PORT/headers

使用 TLS:

  1. kubectl create -n istio-system secret tls istio-ingressgateway-certs --key /tmp/tls.key --cert /tmp/tls.crt
  2. cat <<EOF | istioctl replace -f -
  3. apiVersion: networking.istio.io/v1alpha3
  4. kind: Gateway
  5. metadata:
  6. name: httpbin-gateway
  7. spec:
  8. selector:
  9. istio: ingressgateway # use istio default ingress gateway
  10. servers:
  11. - port:
  12. number: 80
  13. name: http
  14. protocol: HTTP
  15. hosts:
  16. - "httpbin.example.com"
  17. - port:
  18. number: 443
  19. name: https
  20. protocol: HTTPS
  21. tls:
  22. mode: SIMPLE
  23. serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
  24. privateKey: /etc/istio/ingressgateway-certs/tls.key
  25. hosts:
  26. - "httpbin.example.com"
  27. EOF
  28. # validate 200
  29. curl --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST -HHost:httpbin.example.com -I -k https://httpbin.example.com:$SECURE_INGRESS_PORT/status/200

Egress 流量

默认情况下,Istio 接管了容器的内外网流量,从容器内部无法访问 Kubernetes 集群外的服务。可以通过 ServiceEntry 为需要的容器开放 Egress 访问,如

  1. $ cat <<EOF | istioctl create -f -
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: ServiceEntry
  4. metadata:
  5. name: httpbin-ext
  6. spec:
  7. hosts:
  8. - httpbin.org
  9. ports:
  10. - number: 80
  11. name: http
  12. protocol: HTTP
  13. EOF
  14. $ cat <<EOF | istioctl create -f -
  15. apiVersion: networking.istio.io/v1alpha3
  16. kind: VirtualService
  17. metadata:
  18. name: httpbin-ext
  19. spec:
  20. hosts:
  21. - httpbin.org
  22. http:
  23. - timeout: 3s
  24. route:
  25. - destination:
  26. host: httpbin.org
  27. weight: 100
  28. EOF

需要注意的是 ServiceEntry 仅支持 HTTP、TCP 和 HTTPS,对于其他协议需要通过 --includeIPRanges 的方式设置 IP 地址范围,如

  1. helm template @install/kubernetes/helm/istio@ --name istio --namespace istio-system --set global.proxy.includeIPRanges="10.0.0.1/24" -x @templates/sidecar-injector-configmap.yaml@ | kubectl apply -f -

流量镜像

  1. cat <<EOF | istioctl replace -f -
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: VirtualService
  4. metadata:
  5. name: httpbin
  6. spec:
  7. hosts:
  8. - httpbin
  9. http:
  10. - route:
  11. - destination:
  12. host: httpbin
  13. subset: v1
  14. weight: 100
  15. mirror:
  16. host: httpbin
  17. subset: v2
  18. EOF

参考文档