借助 Istio 让服务更具弹性 | 周末送福利

Posted 分布式实验室

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了借助 Istio 让服务更具弹性 | 周末送福利相关的知识,希望对你有一定的参考价值。

本文介绍如何借助 Istio 提供的功能,来让我们的服务更具弹性。这主要包括配置服务的负载均衡策略,配置服务的连接池,配置服务的健康检测机制,配置服务熔断,配置服务重试,配置服务限流。通过上述这些配置,我们可以让我们服务在遇到故障时更具弹性。

整体介绍

借助 Istio 让服务更具弹性 | 周末送福利


弹性是指系统能够优雅地处理故障并从故障中恢复。为了保持弹性,必须快速有效地检测故障并进行恢复。尽管我们服务设计时会尽量的做高可用方案,但是服务出现故障或者服务间的通信网络出现故障的情况是无法避免的,当故障出现时,我们应该尽量保证服务的可用性,不再让故障变的更严重,影响整个应用的稳定性,这就要求我们要让服务更具弹性。

Istio 提供了许多开箱即用的提升服务弹性的功能,包括负载均衡、连接池、健康检测、服务熔断、服务超时、服务重试、服务限流。这些功能都是服务治理的必备功能。通过这些功能,我们可以让服务更具弹性。当我们服务流量突增,并发突然变高,如果我们没有对服务进行限流,就很有可能导致服务出现故障。当网络出现瞬时故障导致服务调用失败,如果服务没有重试机制就可能导致调用方服务故障,当一个服务调用的后端服务已经出现大面积调用失败的情况,如果我们没有对调用的服务进行熔断处理,此时又开启了服务调用重试机制,这很有可能会导致后端服务压力成倍增加,进而压垮后端服务。虽然一个服务出现故障的概率可能很低,但是当服务数量变多,低概率的事件可能就会必然发生,当服务数量持续增长,服务间的调用关系复杂,如果不做服务故障处理,提升服务的弹性,就很有可能因为一个服务的故障导致其他服务也出现故障,进行导致级联故障,甚至可能导致整个应用出现不可用的现象,严重影响我们应用拆分为微服务后的服务可用性指标。应用用户体验变差,导致用户流失,进而影响业务发展,如果经常出现这种故障,这对业务来说无疑是毁灭性的打击。

实验前的准备

进行实验前,需要先执行如下的前置步骤。

下载实验时用到的源码仓库:

 
   
   
 
  1. $ sudo yum install -y git


  2. $ git clone https://github.com/mgxian/istio-lab

  3. Cloning into 'istio-lab'...

  4. remote: Enumerating objects: 252, done.

  5. remote: Counting objects: 100% (252/252), done.

  6. remote: Compressing objects: 100% (177/177), done.

  7. remote: Total 779 (delta 157), reused 166 (delta 74), pack-reused 527

  8. Receiving objects: 100% (779/779), 283.37 KiB | 243.00 KiB/s, done.

  9. Resolving deltas: 100% (451/451), done.

  10. $ cd istio-lab


开启 default 命名空间的自动注入功能:

 
   
   
 
  1. $ kubectl label namespace default istio-injection=enabled

  2. namespace/default labeled


部署用于测试的服务:

 
   
   
 
  1. $ kubectl apply -f service/go/service-go.yaml


  2. $ kubectl get podNAME READY STATUS RESTARTS AGE

  3. service-go-v1-7cc5c6f574-lrp2h 2/2 Running 0 76s

  4. service-go-v2-7656dcc478-svn5c 2/2 Running 0 76s


服务实例负载均衡

借助 Istio 让服务更具弹性 | 周末送福利


Istio 可以通过设置 DestinationRule 来指定服务的负载均衡策略,Istio 提供了两类常用的负载均衡策略,分别是简单的负载均衡(simple)和一致性哈希负载均衡(consistentHash)。可以为服务设置默认的负载均衡策略,也可以在单独的服务子集(subset)中设置负载均衡策略,服务子集中的设置会覆盖服务设置的默认负载均衡策略。我们还可以设置端口级别的负载均衡策略(portLevelSettings),可以为服务设置默认的端口级别的负载均衡策略,当然我们也可以在单独的服务子集上设置端口级别的负载均衡策略。

简单负载均衡

简单的负载均衡策略提供了如下的4种负载均衡算法:

  • 轮询(ROUND_ROBIN ):把请求依次转发给后端健康实例,默认算法。

  • 最少连接(LEAST_CONN):把请求转发给活跃请求最少的后端健康实例,此处的活跃请求数是 Istio 自己维护的,是 Istio 调用后端实例且正在等待返回响应的请求数,由于实例可能还有其他客户端在调用,没有经过 Istio 统计,所以 Istio 维护的活跃请求数并不是此时实例真正的活跃请求数。

  • 随机(RANDOM):把请求随机转发给后端健康实例。


使用示例:

 
   
   
 
  1. apiVersion: networking.istio.io/v1alpha3

  2. kind: DestinationRule

  3. metadata:

  4. name: service-go

  5. spec:

  6. host: service-go

  7. trafficPolicy:

  8. loadBalancer:

  9. simple: ROUND_ROBIN

  10. subsets:

  11. - name: v1

  12. labels:

  13. version: v1

  14. - name: v2

  15. labels:

  16. version: v2

  17. trafficPolicy:

  18. loadBalancer:

  19. simple: LEAST_CONN

  20. portLevelSettings:

  21. - port:

  22. number: 80

  23. loadBalancer:

  24. simple: RANDOM


7-9 行定义了默认的负载均衡策略为轮询方式。

17-19 行定义了对于名称为 v2 的实例子集负载均衡策略为最少连接方式。

20-24 行定义了端口级别的负载均衡策略,指定 80 端口的负载均衡策略为随机方式。

一致性哈希负载均衡

一致性哈希负载均衡策略只适合于 HTTP 协议的请求,可以基于请求头,Cookie 或者来源 IP 做会话粘连,让同一用户的请求一直转发到后端同一实例,当实例出现故障时会选择新的实例。当添加删除新实例时,会有部分用户重新会话粘连失效。

使用示例:

 
   
   
 
  1. apiVersion: networking.istio.io/v1alpha3

  2. kind: DestinationRule

  3. metadata:

  4. name: service-go

  5. spec:

  6. host: service-go

  7. trafficPolicy:

  8. loadBalancer:

  9. consistentHash:

  10. httpHeaderName: x-lb-test

  11. subsets:

  12. - name: v1

  13. labels:

  14. version: v1

  15. - name: v2

  16. labels:

  17. version: v2


7-9行定义了默认的负载均衡策略为一致性哈希,基于 x-lb-test 头进行一致性哈希负载均衡。

实验

创建测试 Pod:

 
   
   
 
  1. $ kubectl apply -f kubernetes/dns-test.yaml


创建 service-go 服务的 virtualservice 规则:

 
   
   
 
  1. $ kubectl apply -f istio/route/virtual-service-go.yaml


没有创建负载均衡规则的访问测试:

 
   
   
 
  1. $ kubectl exec dns-test -c dns-test -- curl -s -H "X-lb-test: 1" http://service-go/env

  2. {"message":"go v1"}


  3. $ kubectl exec dns-test -c dns-test -- curl -s -H "X-lb-test: 1" http://service-go/env

  4. {"message":"go v2"}


创建用于测试的一致性哈希负载均衡规则:

 
   
   
 
  1. $ kubectl apply -f istio/resilience/destination-rule-go-lb-hash.yaml


创建负载均衡规则后的访问测试:

 
   
   
 
  1. $ kubectl exec dns-test -c dns-test -- curl -s -H "X-lb-test: 1" http://service-go/env

  2. {"message":"go v2"}


  3. $ kubectl exec dns-test -c dns-test -- curl -s -H "X-lb-test: 2" http://service-go/env

  4. {"message":"go v2"}


  5. $ kubectl exec dns-test -c dns-test -- curl -s -H "X-lb-test: 3" http://service-go/env

  6. {"message":"go v1"}


总结

从以上的实验结果可以看出,实验部署了两个版本的 service-go 服务实例,每个版本一个 Pod ,默认情况下访问 service-go 服务会轮询的转发到后端 Pod 上,因此多次访问你会看到两个版本的响应结果,当配置了一致性哈希负载均衡规则以后,以固定的 X-lb-test 请求头值时,多次访问你只能获取到同一个版本的服务实例响应信息。

注意:实验结果可能与上面的结果并不完全一致,但是结论是一致的,使用同样的请求头会访问 service-go 服务,只会得到同一个版本的服务响应结果。
清理测试:

 
   
   
 
  1. $ kubectl delete -f kubernetes/dns-test.yaml

  2. $ kubectl delete -f istio/route/virtual-service-go.yaml

  3. $ kubectl delete -f istio/resilience/destination-rule-go-lb-hash.yaml


服务实例连接池

借助 Istio 让服务更具弹性 | 周末送福利


Istio 可以通过设置 DestinationRule 来指定服务的连接池的设置,Istio 提供了两类常用协议的连接池配置,分别是 TCP 连接池配置和 HTTP 连接池配置。与负载均衡策略设置相似,连接池的配置也支持服务默认级别的配置,服务子集的配置以及端口级别的连接池配置。TCP 连接池和 HTTP 连接池可以一同设置。

TCP 连接池

TCP 连接池对 TCP 和 HTTP 协议均提供支持,示例如下:

 
   
   
 
  1. apiVersion: networking.istio.io/v1alpha3

  2. kind: DestinationRule

  3. metadata:

  4. name: service-go

  5. spec:

  6. host: service-go

  7. trafficPolicy:

  8. connectionPool:

  9. tcp:

  10. maxConnections: 10

  11. connectTimeout: 30ms

  12. subsets:

  13. - name: v1

  14. labels:

  15. version: v1

  16. - name: v2

  17. labels:

  18. version: v2


8-11 行设置 TCP 连接池中的最大连接数为 10,连接超时时间为 30 毫秒,当连接池中连接不够用时,服务调用会返回 503 响应码。

HTTP 连接池

HTTP 连接池对 HTTP 和 GRPC 协议均提供支持,示例如下:

 
   
   
 
  1. apiVersion: networking.istio.io/v1alpha3

  2. kind: DestinationRule

  3. metadata:

  4. name: service-go

  5. spec:

  6. host: service-go

  7. trafficPolicy:

  8. connectionPool:

  9. http:

  10. http2MaxRequests: 10

  11. http1MaxPendingRequests: 5

  12. maxRequestsPerConnection: 2

  13. maxRetries: 3

  14. subsets:

  15. - name: v1

  16. labels:

  17. version: v1

  18. - name: v2

  19. labels:

  20. version: v2


8-13 行设置 HTTP 连接池的后端实例的最大并发请求数为 10,每个目标的最大待处理请求数为 5,连接池中每个连接最多处理 2 个请求后就关闭,并根据需要重新创建连接池中的连接,请求在服务后端实例集群中失败后的最大重试次数为 3。

http2MaxRequests 对后端的最大并发请求数,默认值 1024。

maxRequestsPerConnection 设置为 1 时表示关闭 keep alive 特性,每次请求都创建一个新的请求。

http1MaxPendingRequests 为每个目标的最大待处理请求数,这里的目标指的是 virtualservice 路由规则中配置的 destination,当连接池中连接不够用时请求就处于待处理状态。默认值 1024。

maxRetries 请求后端失败后重试其他后端实例的总次数。默认值 3。

实验

启动用于并发测试的 Pod:

 
   
   
 
  1. $ kubectl apply -f kubernetes/fortio.yaml


创建 service-go 服务的 virtualservice 规则:

 
   
   
 
  1. $ kubectl apply -f istio/route/virtual-service-go.yaml


创建用于测试的连接池规则:

 
   
   
 
  1. $ kubectl apply -f istio/resilience/destination-rule-go-pool-http.yaml


访问测试:

 
   
   
 
  1. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -curl http://service-go/env

  2. HTTP/1.1 200 OK

  3. content-type: application/json; charset=utf-8

  4. date: Wed, 16 Jan 2019 10:12:35 GMT

  5. content-length: 19

  6. x-envoy-upstream-service-time: 4

  7. server: envoy


  8. {"message":"go v2"}


  9. # 10 并发

  10. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -c 10 -qps 0 -n 100 -loglevel Error http://service-go/env

  11. 09:40:38 I logger.go:97> Log level is now 4 Error (was 2 Info)

  12. Fortio 1.0.1 running at 0 queries per second, 2->2 procs, for 100 calls: http://service-go/env

  13. Aggregated Function Time : count 100 avg 0.01652562 +/- 0.013 min 0.002576677 max 0.064653438 sum 1.65256199

  14. # target 50% 0.0119375

  15. # target 75% 0.018

  16. # target 90% 0.035

  17. # target 99% 0.06

  18. # target 99.9% 0.0641881

  19. Sockets used: 15 (for perfect keepalive, would be 10)

  20. Code 200 : 95 (95.0 %)

  21. Code 503 : 5 (5.0 %)

  22. All done 100 calls (plus 0 warmup) 16.526 ms avg, 563.4 qps


  23. # 20 并发

  24. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -c 20 -qps 0 -n 200 -loglevel Error http://service-go/env

  25. 09:41:32 I logger.go:97> Log level is now 4 Error (was 2 Info)

  26. Fortio 1.0.1 running at 0 queries per second, 2->2 procs, for 200 calls: http://service-go/env

  27. Aggregated Function Time : count 200 avg 0.023987068 +/- 0.01622 min 0.001995258 max 0.067905383 sum 4.79741353

  28. # target 50% 0.0194286

  29. # target 75% 0.0357692

  30. # target 90% 0.05

  31. # target 99% 0.0626351

  32. # target 99.9% 0.0673784

  33. Sockets used: 43 (for perfect keepalive, would be 20)

  34. Code 200 : 177 (88.5 %)

  35. Code 503 : 23 (11.5 %)

  36. All done 200 calls (plus 0 warmup) 23.987 ms avg, 711.9 qps


  37. # 30 并发

  38. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -c 30 -qps 0 -n 300 -loglevel Error http://service-go/env

  39. 09:42:05 I logger.go:97> Log level is now 4 Error (was 2 Info)

  40. Fortio 1.0.1 running at 0 queries per second, 2->2 procs, for 300 calls: http://service-go/env

  41. Aggregated Function Time : count 300 avg 0.034233818 +/- 0.02268 min 0.002354402 max 0.114700368 sum 10.2701455

  42. # target 50% 0.0285417

  43. # target 75% 0.0446667

  44. # target 90% 0.0686957

  45. # target 99% 0.1

  46. # target 99.9% 0.11323

  47. Sockets used: 137 (for perfect keepalive, would be 30)

  48. Code 200 : 192 (64.0 %)

  49. Code 503 : 108 (36.0 %)

  50. All done 300 calls (plus 0 warmup) 34.234 ms avg, 702.1 qps


从压测结果可以看出,当并发逐渐增大时,服务不可用的 503 响应码所占比例逐渐升高,说明我们配置的 HTTP 连接池参数已经生效。

清理测试:

 
   
   
 
  1. $ kubectl delete -f kubernetes/fortio.yaml

  2. $ kubectl delete -f istio/route/virtual-service-go.yaml

  3. $ kubectl delete -f istio/resilience/destination-rule-go-pool-http.yaml


服务实例健康检测

借助 Istio 让服务更具弹性 | 周末送福利


Istio 可以通过设置 DestinationRule 来指定服务实例健康检测的配置,可以设置服务实例健康检测计算的时间间隔,实例移出连接池的条件,实例移出连接池时间的基础值以及服务实例移出连接池的最大比例值。设置移出连接池的最大比例,可以防止异常情况移出连接池过多的服务实例,导致服务实例不够,剩余实例承受流量过大,压跨整个服务。与负载均衡策略设置相似,服务实例健康检测的配置也支持服务默认级别的配置,服务子集的配置以及端口级别的服务实例健康检测配置。

对于 HTTP 协议的服务,当后端实例返回 5xx 的响应码时,代表后端实例出现错误,后端实例的错误计数会增加。对于 TCP 协议的服务,连接超时、连接失败会被认为后端实例出现错误,后端实例的错误计数会增加。

配置示例:

 
   
   
 
  1. apiVersion: networking.istio.io/v1alpha3

  2. kind: DestinationRule

  3. metadata:

  4. name: service-go

  5. spec:

  6. host: service-go

  7. trafficPolicy:

  8. outlierDetection:

  9. consecutiveErrors: 3

  10. interval: 10s

  11. baseEjectionTime: 30s

  12. maxEjectionPercent: 10

  13. subsets:

  14. - name: v1

  15. labels:

  16. version: v1

  17. - name: v2

  18. labels:

  19. version: v2


8-12为后端实例健康检测配置的定义,配置最大实例移出比例(maxEjectionPercent)不超过 10%,基础移出时间(baseEjectionTime)为 30s,当实例恢复健康加入集群中后,再次出现故障被移出时,移出时间会根据此值增加移出时间,每隔 10s 检测一次后端实例是否应该被移出连接池,当在 10s 内出现3次错误时,实例会被移出连接池。

consecutiveErrors 表示服务实例移出连接池之前的最大出错数阀值,当连接池中实例在指定的时间间隔内出错次数到达该值时,会被移出连接池。

interval 表示两次后端实例健康检查时间间隔,默认值为 10s。

baseEjectionTime 表示移出连接池的基础时间,默认值为 30s。

maxEjectionPercent 表示后端实例最大移出百分比,默认值为 10。

服务熔断

借助 Istio 让服务更具弹性 | 周末送福利


后端服务调用偶尔出现失败是正常情况,但是当对后端服务的调用出现大比例的调用失败,此时可能由于后端服务已经无法承受当前压力,如果我们还是继续调用后端服务,我们不仅不能得到响应,还有可能会把后端服务整个压跨。如果当服务出现大比例的调用失败的时候,我们停止调用后端服务,经过短暂时间间隔后,我们尝试让部分请求调用后端服务,如果服务返回正常,我们就可以让更多的请求调用后端服务,直到恢复到正常情况,如果仍然出现无法响应的情况,我们将再次停止调用服务,这种处理后端服务调用的机制就叫做熔断。

Istio 通过结合连接池和实例健康检测机制,可以实现熔断机制。当后端实例出现故障时移出连接池,当连接池中无可用健康实例时,服务请求会立即得到服务不可用的响应,此时服务就处于熔断状态了。当服务实例被移出的时间结束时,服务实例会被再次加入连接池中,等待下一轮的服务健康检测。

配置示例:

 
   
   
 
  1. apiVersion: networking.istio.io/v1alpha3

  2. kind: DestinationRule

  3. metadata:

  4. name: service-go

  5. spec:

  6. host: service-go

  7. trafficPolicy:

  8. connectionPool:

  9. tcp:

  10. maxConnections: 10

  11. http:

  12. http2MaxRequests: 10

  13. maxRequestsPerConnection: 10

  14. outlierDetection:

  15. consecutiveErrors: 3

  16. interval: 3s

  17. baseEjectionTime: 3m

  18. maxEjectionPercent: 100

  19. subsets:

  20. - name: v1

  21. labels:

  22. version: v1

  23. - name: v2

  24. labels:

  25. version: v2



8-13 定义连接池配置,并发请求设置为 10。

14-18 定义后端实例健康检测配置,允许全部实例移出连接池。

实验

启动用于并发测试的 Pod:

 
   
   
 
  1. $ kubectl apply -f kubernetes/fortio.yaml


创建 service-go 服务的 virtualservice 规则:

 
   
   
 
  1. $ kubectl apply -f istio/route/virtual-service-go.yaml


创建用于测试的熔断规则:

 
   
   
 
  1. $ kubectl apply -f istio/resilience/destination-rule-go-cb.yaml


访问测试:

 
   
   
 
  1. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -curl http://service-go/env

  2. HTTP/1.1 200 OK

  3. content-type: application/json; charset=utf-8

  4. date: Wed, 16 Jan 2019 10:22:35 GMT

  5. content-length: 19

  6. x-envoy-upstream-service-time: 3

  7. server: envoy


  8. {"message":"go v2"}


  9. # 20 并发

  10. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -c 20 -qps 0 -n 200 -loglevel Error http://service-go/env

  11. 10:25:21 I logger.go:97> Log level is now 4 Error (was 2 Info)

  12. Fortio 1.0.1 running at 0 queries per second, 2->2 procs, for 200 calls: http://service-go/env

  13. Aggregated Function Time : count 200 avg 0.023687933 +/- 0.01781 min 0.002302379 max 0.082312522 sum 4.73758658

  14. # target 50% 0.0175385

  15. # target 75% 0.029375

  16. # target 90% 0.0533333

  17. # target 99% 0.0766667

  18. # target 99.9% 0.08185

  19. Sockets used: 22 (for perfect keepalive, would be 20)

  20. Code 200 : 198 (99.0 %)

  21. Code 503 : 2 (1.0 %)

  22. All done 200 calls (plus 0 warmup) 23.688 ms avg, 631.3 qps


  23. # 30 并发

  24. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -c 30 -qps 0 -n 300 -loglevel Error http://service-go/env

  25. 10:25:49 I logger.go:97> Log level is now 4 Error (was 2 Info)

  26. Fortio 1.0.1 running at 0 queries per second, 2->2 procs, for 300 calls: http://service-go/env

  27. Aggregated Function Time : count 300 avg 0.055940327 +/- 0.04215 min 0.001836339 max 0.207798702 sum 16.782098

  28. # target 50% 0.0394737

  29. # target 75% 0.0776471

  30. # target 90% 0.123333

  31. # target 99% 0.18

  32. # target 99.9% 0.205459

  33. Sockets used: 94 (for perfect keepalive, would be 30)

  34. Code 200 : 236 (78.7 %)

  35. Code 503 : 64 (21.3 %)

  36. All done 300 calls (plus 0 warmup) 55.940 ms avg, 486.3 qps


  37. # 40 并发

  38. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -c 40 -qps 0 -n 400 -loglevel Error http://service-go/env

  39. 10:26:17 I logger.go:97> Log level is now 4 Error (was 2 Info)

  40. Fortio 1.0.1 running at 0 queries per second, 2->2 procs, for 400 calls: http://service-go/env

  41. Aggregated Function Time : count 400 avg 0.034048003 +/- 0.02541 min 0.001808212 max 0.144268023 sum 13.6192011

  42. # target 50% 0.028587

  43. # target 75% 0.0415789

  44. # target 90% 0.0588889

  45. # target 99% 0.132

  46. # target 99.9% 0.143414

  47. Sockets used: 203 (for perfect keepalive, would be 40)

  48. Code 200 : 225 (56.2 %)

  49. Code 503 : 175 (43.8 %)

  50. All done 400 calls (plus 0 warmup) 34.048 ms avg, 951.0 qps


  51. # 查看 istio-proxy 状态

  52. $ kubectl exec fortio -c istio-proxy -- curl -s localhost:15000/stats | grep service-go | grep pending

  53. cluster.outbound|80|v1|service-go.default.svc.cluster.local.upstream_rq_pending_active: 0

  54. cluster.outbound|80|v1|service-go.default.svc.cluster.local.upstream_rq_pending_failure_eject: 0

  55. cluster.outbound|80|v1|service-go.default.svc.cluster.local.upstream_rq_pending_overflow: 0

  56. cluster.outbound|80|v1|service-go.default.svc.cluster.local.upstream_rq_pending_total: 0

  57. cluster.outbound|80|v2|service-go.default.svc.cluster.local.upstream_rq_pending_active: 0

  58. cluster.outbound|80|v2|service-go.default.svc.cluster.local.upstream_rq_pending_failure_eject: 0

  59. cluster.outbound|80|v2|service-go.default.svc.cluster.local.upstream_rq_pending_overflow: 0

  60. cluster.outbound|80|v2|service-go.default.svc.cluster.local.upstream_rq_pending_total: 0

  61. cluster.outbound|80||service-go.default.svc.cluster.local.upstream_rq_pending_active: 0cluster.outbound|80||service-go.default.svc.cluster.local.upstream_rq_pending_failure_eject: 0

  62. cluster.outbound|80||service-go.default.svc.cluster.local.upstream_rq_pending_overflow: 551

  63. cluster.outbound|80||service-go.default.svc.cluster.local.upstream_rq_pending_total: 1282



实验部署了两个版本的 service-go 实例,每个版本一个 Pod,每个 Pod 的并发为 10,所有设置的总并发就为 20。从压测结果可以看出,当并发逐渐增大时,服务不可用的 503 响应码所占比例逐渐升高。但是从结果看熔断器并不是非常准确的拦截了高于设置并发值的请求,Istio 允许有部分请求遗漏。

清理测试:

 
   
   
 
  1. $ kubectl delete -f kubernetes/fortio.yaml

  2. $ kubectl delete -f istio/route/virtual-service-go.yaml

  3. $ kubectl delete -f istio/resilience/destination-rule-go-cb.yaml


服务超时

借助 Istio 让服务更具弹性 | 周末送福利


设置服务调用的超时时间,当服务没有在规定的时间内返回数据,就直接取消此次请求,返回服务超时的响应。给服务设置超时时间可以防止服务提供方拖垮服务调用方,防止请求的总耗时时间过长。

使用示例:

 
   
   
 
  1. apiVersion: networking.istio.io/v1alpha3

  2. kind: VirtualService

  3. metadata:

  4. name: service-node

  5. spec:

  6. hosts:

  7. - service-node

  8. http:

  9. - route:

  10. - destination:

  11. host: service-node

  12. timeout: 500ms



12 行指定服务的调用时间不能超过 500 毫秒,当调用 service-node 服务时如果超过 500 毫秒请求没有完成就直接返回超时错误给调用方。

实验

部署 service-node 服务:

 
   
   
 
  1. $ kubectl apply -f service/node/service-node.yaml


  2. $ kubectl get pod

  3. NAME READY STATUS RESTARTS AGE

  4. service-go-v1-7cc5c6f574-lrp2h 2/2 Running 0 4m

  5. service-go-v2-7656dcc478-svn5c 2/2 Running 0 4m

  6. service-node-v1-d44b9bf7b-ppn26 2/2 Running 0 24s

  7. service-node-v2-86545d9796-rgmb7 2/2 Running 0 24s


启动用于并发测试的 Pod:

 
   
   
 
  1. $ kubectl apply -f kubernetes/fortio.yaml


创建 service-node 服务的超时 virtualservice 规则:

 
   
   
 
  1. $ kubectl apply -f istio/resilience/virtual-service-node-timeout.yaml


访问测试:

 
   
   
 
  1. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -curl http://service-node/env

  2. HTTP/1.1 200 OK

  3. content-type: application/json; charset=utf-8

  4. content-length: 77

  5. date: Wed, 16 Jan 2019 10:33:57 GMT

  6. x-envoy-upstream-service-time: 18

  7. server: envoy


  8. {"message":"node v1","upstream":[{"message":"go v1","response_time":"0.01"}]}


  9. # 10 并发

  10. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -c 10 -qps 0 -n 100 -loglevel Error http://service-node/env

  11. 11:08:24 I logger.go:97> Log level is now 4 Error (was 2 Info)

  12. Fortio 1.0.1 running at 0 queries per second, 2->2 procs, for 100 calls: http://service-node/env

  13. Aggregated Function Time : count 100 avg 0.19270902 +/- 0.1403 min 0.009657651 max 0.506141264 sum 19.2709017

  14. # target 50% 0.173333

  15. # target 75% 0.3

  16. # target 90% 0.421429

  17. # target 99% 0.505118

  18. # target 99.9% 0.506039

  19. Sockets used: 15 (for perfect keepalive, would be 10)

  20. Code 200 : 94 (94.0 %)

  21. Code 504 : 6 (6.0 %)

  22. All done 100 calls (plus 0 warmup) 192.709 ms avg, 45.4 qps


  23. # 20 并发

  24. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -c 20 -qps 0 -n 200 -loglevel Error http://service-node/env

  25. 11:08:47 I logger.go:97> Log level is now 4 Error (was 2 Info)

  26. Fortio 1.0.1 running at 0 queries per second, 2->2 procs, for 200 calls: http://service-node/env

  27. Aggregated Function Time : count 200 avg 0.44961158 +/- 0.122 min 0.006904922 max 0.524347684 sum 89.9223153

  28. # target 50% 0.50864

  29. # target 75% 0.516494

  30. # target 90% 0.521206

  31. # target 99% 0.524034

  32. # target 99.9% 0.524316

  33. Sockets used: 163 (for perfect keepalive, would be 20)

  34. Code 200 : 46 (23.0 %)

  35. Code 504 : 154 (77.0 %)

  36. All done 200 calls (plus 0 warmup) 449.612 ms avg, 39.2 qps


当并发逐渐增大时,服务的响应时间逐渐增大,服务响应超时的 504 响应码所占比例逐渐升高。这说明我们配置的服务超时时间已经生效。

清理:

 
   
   
 
  1. $ kubectl delete -f kubernetes/fortio.yaml

  2. $ kubectl delete -f service/node/service-node.yaml

  3. $ kubectl delete -f istio/resilience/virtual-service-node-timeout.yaml


服务重试

借助 Istio 让服务更具弹性 | 周末送福利


当网格出现抖动,或者被调用的服务出现瞬时故障,由于这种问题都是偶发瞬时的,只需要再次调用后端服务,可能请求就会正常,这种情况下我就需要服务的重试功能,防止由于被调用方的服务偶发瞬时故障,导致调用后端服务实现出现不可用的情况,影响整体的应用稳定性。

使用示例:

 
   
   
 
  1. apiVersion: networking.istio.io/v1alpha3

  2. kind: VirtualService

  3. metadata:

  4. name: service-node

  5. spec:

  6. hosts:

  7. - service-node

  8. http:

  9. - route:

  10. - destination:

  11. host: service-node

  12. retries:

  13. attempts: 3

  14. perTryTimeout: 2s


12-14 行定义了重试规则,当调用 service-node 服务时,如果服务出错可以进行重试,最多可以重试 3 次,每次调用超时为 2s,每次重试的时间间隔由 Istio 决定,重试时间间隔一般大于 25 毫秒。

实验

创建测试 Pod:

 
   
   
 
  1. $ kubectl apply -f kubernetes/fortio.yaml


部署 httpbin 服务:

 
   
   
 
  1. $ kubectl apply -f kubernetes/httpbin.yaml


  2. $ kubectl get pod -l app=httpbin

  3. NAME READY STATUS RESTARTS AGE

  4. httpbin-b67975b8f-vmbtv 2/2 Running 0 49s


创建 httpbin 服务路由规则:

 
   
   
 
  1. $ kubectl apply -f istio/route/virtual-service-httpbin.yaml


访问测试:

 
   
   
 
  1. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -curl http://httpbin:8000/status/200

  2. HTTP/1.1 200 OK

  3. server: envoy

  4. date: Wed, 16 Jan 2019 14:03:00 GMT

  5. content-type: text/html; charset=utf-8

  6. access-control-allow-origin: *

  7. access-control-allow-credentials: true

  8. content-length: 0

  9. x-envoy-upstream-service-time: 33


  10. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -c 10 -qps 0 -n 100 -loglevel Error http://httpbin:8000/status/200%2C200%2C200%2C200%2C500

  11. 14:18:37 I logger.go:97> Log level is now 4 Error (was 2 Info)

  12. Fortio 1.0.1 running at 0 queries per second, 2->2 procs, for 100 calls: http://httpbin:8000/status/200%2C200%2C200%2C200%2C500

  13. Aggregated Function Time : count 100 avg 0.24802899 +/- 0.06426 min 0.016759858 max 0.390472066 sum 24.8028985

  14. # target 50% 0.252941

  15. # target 75% 0.289706

  16. # target 90% 0.326667

  17. # target 99% 0.376981

  18. # target 99.9% 0.389123

  19. Sockets used: 30 (for perfect keepalive, would be 10)

  20. Code 200 : 78 (78.0 %)

  21. Code 500 : 22 (22.0 %)

  22. All done 100 calls (plus 0 warmup) 248.029 ms avg, 38.5 qps


创建 httpbin 服务重试路由规则:

 
   
   
 
  1. $ kubectl apply -f istio/resilience/virtual-service-httpbin-retry.yaml


访问测试:

 
   
   
 
  1. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -curl http://httpbin:8000/status/200

  2. HTTP/1.1 200 OK

  3. server: envoy

  4. date: Wed, 16 Jan 2019 14:19:14 GMT

  5. content-type: text/html; charset=utf-8

  6. access-control-allow-origin: *

  7. access-control-allow-credentials: true

  8. content-length: 0

  9. x-envoy-upstream-service-time: 5


  10. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -c 10 -qps 0 -n 100 -loglevel Error http://httpbin:8000/status/200%2C200%2C200%2C200%2C500

  11. 14:19:32 I logger.go:97> Log level is now 4 Error (was 2 Info)

  12. Fortio 1.0.1 running at 0 queries per second, 2->2 procs, for 100 calls: http://httpbin:8000/status/200%2C200%2C200%2C200%2C500

  13. Aggregated Function Time : count 100 avg 0.23708609 +/- 0.1323 min 0.017537636 max 0.793965189 sum 23.7086086

  14. # target 50% 0.226471

  15. # target 75% 0.275

  16. # target 90% 0.383333

  17. # target 99% 0.7

  18. # target 99.9% 0.784569

  19. Sockets used: 13 (for perfect keepalive, would be 10)

  20. Code 200 : 97 (97.0 %)

  21. Code 500 : 3 (3.0 %)

  22. All done 100 calls (plus 0 warmup) 237.086 ms avg, 35.5 qps


从上面的测试结果可以看出来,当没有开启服务重试时,服务有大概 1/4 的请求失败,当开启服务重试之后,服务只有少部分请求失败。

清理:

 
   
   
 
  1. $ kubectl delete -f kubernetes/fortio.yaml

  2. $ kubectl delete -f kubernetes/httpbin.yaml

  3. $ kubectl delete -f istio/resilience/virtual-service-httpbin-retry.yaml


服务限流

借助 Istio 让服务更具弹性 | 周末送福利


当服务并发请求激增,流量增大,如果此时我们没有使用任何限流措施,这很有可能导致我们的服务无法承受如此多的请求,进而导致服务崩溃,还可能会影响整个应用的稳定性。如果我们的服务有限流功能,当请求数过多时,可以直接丢掉过多的流量,防止服务被压垮,保证服务稳定。Istio 提供两种限流的实现,基于内存存储限流数据和使用 Redis 存储限流数据的方式,基于内存存储限流数据的方式只能适用于只部署一个 Mixer 的集群,而且由于使用内存存储限流数据,Mixer 重启后限流数据会丢失,生产环境建议使用 Redis 存储限流数据的限流实现。在 Istio 中服务被限流的请求会得到 429(Too Many Requests)的响应码。

限流的配置分为客户端和 Mixer 端两个部分:

客户端配置:

  • QuotaSpec 定义了 quota 实例名称和对应的每次请求消耗的配额数。

  • QuotaSpecBinding 将 QuotaSpec 与一个或多个服务相关联绑定,只有被关联绑定的服务限流才会生效。


Mixer 端配置:

  • quota 实例定义了 Mixer 如何区别度量一个请求的限流配额,用来描述请求数据收集的维度。

  • memquota/redisquota 适配器定义了 memquota/redisquota 的配置,根据 quota 实例定义的请求数据收集维度来区分并定义一个或多个限流配额数量。

  • rule 规则定义了 quota 实例应该何时分发给 memquota/redisquota 适配器处理。


基于内存的限流使用示例如下:

 
   
   
 
  1. apiVersion: "config.istio.io/v1alpha2"

  2. kind: quota

  3. metadata:

  4. name: requestcount

  5. namespace: istio-system

  6. spec:

  7. dimensions:

  8. source: request.headers["x-forwarded-for"] | "unknown"

  9. destination: destination.labels["app"] | destination.service.name | "unknown"

  10. destinationVersion: destination.labels["version"] | "unknown"---

  11. apiVersion: "config.istio.io/v1alpha2"

  12. kind: memquota

  13. metadata:

  14. name: handler

  15. namespace: istio-system

  16. spec:

  17. quotas:

  18. - name: requestcount.quota.istio-system

  19. maxAmount: 500

  20. validDuration: 1s

  21. overrides:

  22. - dimensions:

  23. destination: service-go

  24. maxAmount: 50

  25. validDuration: 1s

  26. - dimensions:

  27. destination: service-node

  28. source: "10.28.11.20"

  29. maxAmount: 50

  30. validDuration: 1s

  31. - dimensions:

  32. destination: service-node

  33. maxAmount: 20

  34. validDuration: 1s

  35. - dimensions:

  36. destination: service-python

  37. maxAmount: 2

  38. validDuration: 5s

  39. ---

  40. apiVersion: config.istio.io/v1alpha2

  41. kind: rule

  42. metadata:

  43. name: quota

  44. namespace: istio-system

  45. spec:

  46. actions:

  47. - handler: handler.memquota

  48. instances:

  49. - requestcount.quota

  50. ---

  51. apiVersion: config.istio.io/v1alpha2

  52. kind: QuotaSpec

  53. metadata:

  54. name: request-count

  55. namespace: istio-system

  56. spec:

  57. rules:

  58. - quotas:

  59. - charge: 1

  60. quota: requestcount

  61. ---

  62. apiVersion: config.istio.io/v1alpha2

  63. kind: QuotaSpecBinding

  64. metadata:

  65. name: request-count

  66. namespace: istio-system

  67. spec:

  68. quotaSpecs:

  69. - name: request-count

  70. namespace: istio-system

  71. services:

  72. - name: service-go

  73. namespace: default

  74. - name: service-node

  75. namespace: default

  76. - name: service-python

  77. namespace: default



1-10 定义了名为 requestcount 的 quota 实例,获取请求的 source、destination、destinationVersion 的值供 memquota 适配器来区分请求的限流配额。取值规则如下:

  • source 获取请求的 x-forwarded-for 请求头的值作为 source 的取值,不存在时 source 取值 "unknown"。

  • destination 获取请求的目标服务标签中的 app 标签的值,不存在时取目标服务的 service.name 字段的值,否则 destination 取值 "unknown"。

  • destinationVersion 获取请求目标服务标签中的 version 标签的值,不存在时 destinationVersion 取值 "unknown"。


12-39 行定义了名为 handler 的 memquota 适配器,19 行中的 name 字段值为上面定义的 quota 实例名称。20 行定义了默认的限流配额为500,21 行定义默认的限流计算周期为1s,即默认情况下每秒最高 500 个请求。23-39行为具体的限流配置,23-26 行定义了当 destination 是 service-go 时,每秒不能高于 50 个请求。27-31 定义了当 destination 是 service-node 且 source 是 "10.28.11.20" 时,每秒不能高于 50 个请求。32-35 行定义了当 destination 是 service-node 时,每秒不能高于 20 个请求。36-39 行定义了当 destination 是 service-python 时,每 5 秒内不能高于 2 个请求。

41-50 行定义了名为 quota 的 rule 规则,由于没有指定条件,会把所有相关联的服务请求都分发给 memquota 适配器处理。

52-61 行定义了名为 request-count 的 QuotaSpec ,指定了名为 requestcount 的 quota 实例每次消耗一个配额。

63-78 行定义了名为 request-count 的 QuotaSpecBinding ,把 default 命名空间的 service-go、service-node、service-python 服务与名为 request-count 的 QuotaSpec 关联起来。

在 memquota 适配器配置的所有限流规则中,执行限流时会从第一条限流规则开始匹配,当遇到第一条匹配的规则后,后面的规则不再匹配,如果没有匹配到任何具体的规则,则使用默认的规则。所以 27-31 行的规则不能与 32-35 规则交换位置,如果交换位置就会导致 27-31 行的规则永远不会被匹配到,所以配置限流规则的时候应该把越具体的匹配规则放在越靠前的位置,否则可能会出现达不到预期的限流效果。

quota 实例具体可以使用获取哪些值用于区分请求,可以参考官方文档,链接如下:https://istio.io/docs/reference/config/policy-and-telemetry/attribute-vocabulary/。

基于 Redis 的限流使用示例如下:

 
   
   
 
  1. apiVersion: "config.istio.io/v1alpha2"

  2. kind: quota

  3. metadata:

  4. name: requestcount

  5. namespace: istio-system

  6. spec:

  7. dimensions:

  8. source: request.headers["x-forwarded-for"] | "unknown"

  9. destination: destination.labels["app"] | destination.workload.name | "unknown"

  10. destinationVersion: destination.labels["version"] | "unknown"

  11. ---

  12. apiVersion: "config.istio.io/v1alpha2"

  13. kind: redisquota

  14. metadata:

  15. name: handler

  16. namespace: istio-system

  17. spec:

  18. redisServerUrl: redis-ratelimit.istio-system:6379

  19. connectionPoolSize: 10

  20. quotas:

  21. - name: requestcount.quota.istio-system

  22. maxAmount: 500

  23. validDuration: 1s

  24. bucketDuration: 500ms

  25. rateLimitAlgorithm: ROLLING_WINDOW

  26. overrides:

  27. - dimensions:

  28. destination: service-go

  29. maxAmount: 50

  30. - dimensions:

  31. destination: service-node

  32. source: "10.28.11.20"

  33. maxAmount: 50

  34. - dimensions:

  35. destination: service-node

  36. maxAmount: 20

  37. - dimensions:

  38. destination: service-python

  39. maxAmount: 2

  40. ---

  41. apiVersion: config.istio.io/v1alpha2

  42. kind: rule

  43. metadata:

  44. name: quota

  45. namespace: istio-system

  46. spec:

  47. actions:

  48. - handler: handler.redisquota

  49. instances:

  50. - requestcount.quota

  51. ---

  52. apiVersion: config.istio.io/v1alpha2

  53. kind: QuotaSpec

  54. metadata:

  55. name: request-count

  56. namespace: istio-system

  57. spec:

  58. rules:

  59. - quotas:

  60. - charge: 1

  61. quota: requestcount

  62. ---

  63. apiVersion: config.istio.io/v1alpha2

  64. kind: QuotaSpecBinding

  65. metadata:

  66. name: request-count

  67. namespace: istio-system

  68. spec:

  69. quotaSpecs:

  70. - name: request-count

  71. namespace: istio-system

  72. services:

  73. - name: service-go

  74. namespace: default

  75. - name: service-node

  76. namespace: default

  77. - name: service-python

  78. namespace: default


12-39 行定义了名为 handler 的 redisquota 适配器,18 行定义了 Redis 的连接地址,19 行定义了 Redis 的连接池大小。

22 行定义了默认配额为 500,23 行定义了默认限流周期为 1s,即默认情况下每秒最高 500 个请求。

25 行定义了使用的限流算法有 FIXEDWINDOW 和 ROLLINGWINDOW 两种,FIXED_WINDOW 为默认的算法。

  • FIXED_WINDOW 算法可以允许2倍的设置的请求速率峰值。

  • ROLLING_WINDOW 算法可以提高更高的精确度,这也会额外消耗 Redis 的资源。


27-39 行定义了具体的限流规则,与 memquota 不同,这里不允许再单独为限流规则设置限流周期,只能使用默认的限流周期。

其余部分的配置与 memquota 的配置保持一致。

基于条件的限流

如下配置只对 cookie 中不存在 user 的请求做限流。

 
   
   
 
  1. apiVersion: config.istio.io/v1alpha2

  2. kind: rule

  3. metadata:

  4. name: quota

  5. namespace: istio-system

  6. spec:

  7. match: match(request.headers["cookie"], "user=*") == false

  8. actions:

  9. - handler: handler.memquota

  10. instances:

  11. - requestcount.quota


对所有服务限流:

 
   
   
 
  1. apiVersion: config.istio.io/v1alpha2

  2. kind: QuotaSpecBinding

  3. metadata:

  4. name: request-count

  5. namespace: istio-system

  6. spec:

  7. quotaSpecs:

  8. - name: request-count

  9. namespace: istio-system

  10. services:

  11. - service: '*'


实验

本次实验使用基于内存的 memquota 适配器来进行服务限流测试。如果使用基于 Redis 的 redisquota 适配器进行实验,可能会由于实验环境机器性能问题,导致 Mixer 访问 Redis 出现错误,进而导致 qps 还没有到达设置值时就出现被限流有情况,影响实验结果。

部署其他服务:

 
   
   
 
  1. $ kubectl apply -f service/node/service-node.yaml

  2. $ kubectl apply -f service/lua/service-lua.yaml

  3. $ kubectl apply -f service/python/service-python.yaml


  4. $ kubectl get pod

  5. NAME READY STATUS RESTARTS AGE

  6. service-go-v1-7cc5c6f574-488rs 2/2 Running 0 15m

  7. service-go-v2-7656dcc478-bfq5x 2/2 Running 0 15m

  8. service-lua-v1-5c9bcb7778-d7qwp 2/2 Running 0 3m12s

  9. service-lua-v2-75cb5cdf8-g9vht 2/2 Running 0 3m12s

  10. service-node-v1-d44b9bf7b-z7vbr 2/2 Running 0 3m11s

  11. service-node-v2-86545d9796-rgtxw 2/2 Running 0 3m10s

  12. service-python-v1-79fc5849fd-xgfkn 2/2 Running 0 3m9s

  13. service-python-v2-7b6864b96b-5w6cj 2/2 Running 0 3m15s


启动用于并发测试的 Pod:

 
   
   
 
  1. $ kubectl apply -f kubernetes/fortio.yaml


创建限流规则:

 
   
   
 
  1. $ kubectl apply -f istio/resilience/quota-mem-ratelimit.yaml


测试 service-go 服务的限流是否生效:

 
   
   
 
  1. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -curl http://service-go/env

  2. HTTP/1.1 200 OK

  3. content-type: application/json; charset=utf-8

  4. date: Wed, 16 Jan 2019 15:33:02 GMT

  5. content-length: 19

  6. x-envoy-upstream-service-time: 226

  7. server: envoy


  8. {"message":"go v1"}


  9. # 30 qps

  10. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -qps 30 -n 300 -loglevel Error http://service-go/env

  11. 15:33:36 I logger.go:97> Log level is now 4 Error (was 2 Info)

  12. Fortio 1.0.1 running at 30 queries per second, 2->2 procs, for 300 calls: http://service-go/env

  13. Aggregated Function Time : count 300 avg 0.0086544419 +/- 0.005944 min 0.002929143 max 0.065596074 sum 2.59633258

  14. # target 50% 0.007375

  15. # target 75% 0.00938095

  16. # target 90% 0.0115

  17. # target 99% 0.0325

  18. # target 99.9% 0.0647567

  19. Sockets used: 4 (for perfect keepalive, would be 4)

  20. Code 200 : 300 (100.0 %)

  21. All done 300 calls (plus 0 warmup) 8.654 ms avg, 30.0 qps


  22. # 50 qps

  23. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -qps 50 -n 500 -loglevel Error http://service-go/env

  24. 15:34:17 I logger.go:97> Log level is now 4 Error (was 2 Info)

  25. Fortio 1.0.1 running at 50 queries per second, 2->2 procs, for 500 calls: http://service-go/env

  26. Aggregated Function Time : count 500 avg 0.0086848862 +/- 0.005076 min 0.00307391 max 0.05419281 sum 4.34244311

  27. # target 50% 0.0075

  28. # target 75% 0.00959459

  29. # target 90% 0.0132857

  30. # target 99% 0.03

  31. # target 99.9% 0.0531446

  32. Sockets used: 4 (for perfect keepalive, would be 4)

  33. Code 200 : 500 (100.0 %)

  34. All done 500 calls (plus 0 warmup) 8.685 ms avg, 50.0 qps


  35. # 60 qps

  36. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -qps 60 -n 600 -loglevel Error http://service-go/env

  37. 15:35:28 I logger.go:97> Log level is now 4 Error (was 2 Info)

  38. Fortio 1.0.1 running at 60 queries per second, 2->2 procs, for 600 calls: http://service-go/env

  39. Aggregated Function Time : count 600 avg 0.0090870522 +/- 0.008314 min 0.002537502 max 0.169680378 sum 5.45223134

  40. # target 50% 0.00748529

  41. # target 75% 0.0101538

  42. # target 90% 0.0153548

  43. # target 99% 0.029375

  44. # target 99.9% 0.163872

  45. Sockets used: 23 (for perfect keepalive, would be 4)

  46. Code 200 : 580 (96.7 %)

  47. Code 429 : 20 (3.3 %)

  48. All done 600 calls (plus 0 warmup) 9.087 ms avg, 59.9 qps


测试 service-node 服务的限流是否生效:

 
   
   
 
  1. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -curl http://service-node/env

  2. HTTP/1.1 200 OK

  3. content-type: application/json; charset=utf-8

  4. content-length: 77

  5. date: Wed, 16 Jan 2019 15:36:13 GMT

  6. x-envoy-upstream-service-time: 1187

  7. server: envoy


  8. {"message":"node v2","upstream":[{"message":"go v1","response_time":"0.51"}]}


  9. # 20 qps

  10. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -qps 20 -n 200 -loglevel Error http://service-node/env

  11. 15:37:51 I logger.go:97> Log level is now 4 Error (was 2 Info)

  12. Fortio 1.0.1 running at 20 queries per second, 2->2 procs, for 200 calls: http://service-node/env

  13. Aggregated Sleep Time : count 196 avg -0.21285915 +/- 1.055 min -4.8433788589999995 max 0.190438028 sum -41.7203939

  14. # range, mid point, percentile, count

  15. >= -4.84338 <= -0.001 , -2.42219 , 18.37, 36

  16. > 0.003 <= 0.004 , 0.0035 , 20.41, 4

  17. > 0.011 <= 0.013 , 0.012 , 20.92, 1

  18. > 0.015 <= 0.017 , 0.016 , 21.43, 1

  19. > 0.069 <= 0.079 , 0.074 , 21.94, 1

  20. > 0.089 <= 0.099 , 0.094 , 24.49, 5

  21. > 0.099 <= 0.119 , 0.109 , 28.57, 8

  22. > 0.119 <= 0.139 , 0.129 , 33.67, 10

  23. > 0.139 <= 0.159 , 0.149 , 38.27, 9

  24. > 0.159 <= 0.179 , 0.169 , 68.37, 59

  25. > 0.179 <= 0.190438 , 0.184719 , 100.00, 62

  26. # target 50% 0.166797

  27. WARNING 18.37% of sleep were falling behind

  28. Aggregated Function Time : count 200 avg 0.07655831 +/- 0.3601 min 0.007514854 max 5.046878744 sum 15.311662

  29. # target 50% 0.0258696

  30. # target 75% 0.045

  31. # target 90% 0.104

  32. # target 99% 0.55

  33. # target 99.9% 5.0375

  34. Sockets used: 4 (for perfect keepalive, would be 4)

  35. Code 200 : 200 (100.0 %)

  36. All done 200 calls (plus 0 warmup) 76.558 ms avg, 18.1 qps


  37. # 30 qps

  38. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -qps 30 -n 300 -loglevel Error http://service-node/env

  39. 15:38:36 I logger.go:97> Log level is now 4 Error (was 2 Info)

  40. Fortio 1.0.1 running at 30 queries per second, 2->2 procs, for 300 calls: http://service-node/env

  41. Aggregated Sleep Time : count 296 avg 0.035638851 +/- 0.1206 min -0.420611573 max 0.132597685 sum 10.5491

  42. # range, mid point, percentile, count

  43. >= -0.420612 <= -0.001 , -0.210806 , 24.66, 73

  44. > -0.001 <= 0 , -0.0005 , 25.00, 1

  45. ...

  46. # target 50% 0.0934

  47. WARNING 24.66% of sleep were falling behind

  48. Aggregated Function Time : count 300 avg 0.06131494 +/- 0.08193 min 0.001977589 max 0.42055696 sum 18.3944819

  49. # target 50% 0.03

  50. # target 75% 0.0628571

  51. # target 90% 0.175

  52. # target 99% 0.4

  53. # target 99.9% 0.418501

  54. Sockets used: 55 (for perfect keepalive, would be 4)

  55. Code 200 : 249 (83.0 %)

  56. Code 429 : 51 (17.0 %)

  57. All done 300 calls (plus 0 warmup) 61.315 ms avg, 29.9 qps


  58. # 30 qps

  59. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -qps 30 -n 300 -loglevel Error -H "x-forwarded-for: 10.28.11.20" http://service-node/env

  60. 15:40:34 I logger.go:97> Log level is now 4 Error (was 2 Info)

  61. Fortio 1.0.1 running at 30 queries per second, 2->2 procs, for 300 calls: http://service-node/env

  62. Aggregated Sleep Time : count 296 avg -1.4901022 +/- 1.952 min -6.08576837 max 0.123485559 sum -441.070241

  63. # range, mid point, percentile, count

  64. >= -6.08577 <= -0.001 , -3.04338 , 69.59, 206

  65. ...

  66. # target 50% -1.72254

  67. WARNING 69.59% of sleep were falling behind

  68. Aggregated Function Time : count 300 avg 0.1177745 +/- 0.4236 min 0.008494289 max 5.14910151 sum 35.332351

  69. # target 50% 0.0346875

  70. # target 75% 0.0985714

  71. # target 90% 0.25

  72. # target 99% 0.55

  73. # target 99.9% 5.12674

  74. Sockets used: 4 (for perfect keepalive, would be 4)

  75. Code 200 : 300 (100.0 %)

  76. All done 300 calls (plus 0 warmup) 117.775 ms avg, 24.7 qps


  77. # 50 qps

  78. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -qps 50 -n 500 -loglevel Error -H "x-forwarded-for: 10.28.11.20" http://service-node/env

  79. 15:45:31 I logger.go:97> Log level is now 4 Error (was 2 Info)

  80. Fortio 1.0.1 running at 50 queries per second, 2->2 procs, for 500 calls: http://service-node/env

  81. Aggregated Sleep Time : count 496 avg 0.0015264793 +/- 0.1077 min -0.382731569 max 0.078526418 sum 0.757133711

  82. # range, mid point, percentile, count

  83. >= -0.382732 <= -0.001 , -0.191866 , 25.40, 126

  84. > -0.001 <= 0 , -0.0005 , 25.60, 1

  85. ...

  86. > 0.069 <= 0.0785264 , 0.0737632 , 100.00, 34

  87. # target 50% 0.0566056

  88. WARNING 25.40% of sleep were falling behind

  89. Aggregated Function Time : count 500 avg 0.039103632 +/- 0.05723 min 0.001972061 max 0.450959277 sum 19.5518159

  90. target 50% 0.0175385

  91. # target 75% 0.0323529

  92. # target 90% 0.0975

  93. # target 99% 0.3

  94. # target 99.9% 0.450719

  95. Sockets used: 7 (for perfect keepalive, would be 4)

  96. Code 200 : 497 (99.4 %)

  97. Code 429 : 3 (0.6 %)

  98. All done 500 calls (plus 0 warmup) 39.104 ms avg, 48.4 qps


  99. # 60 qps

  100. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -qps 60 -n 600 -loglevel Error -H "x-forwarded-for: 10.28.11.20" http://service-node/env

  101. 15:50:24 I logger.go:97> Log level is now 4 Error (was 2 Info)

  102. Fortio 1.0.1 running at 60 queries per second, 2->2 procs, for 600 calls: http://service-node/env

  103. Aggregated Sleep Time : count 596 avg -0.081667759 +/- 0.1592 min -0.626635518 max 0.064876123 sum -48.6739846

  104. # range, mid point, percentile, count

  105. >= -0.626636 <= -0.001 , -0.313818 , 51.01, 304

  106. > 0 <= 0.001 , 0.0005 , 51.34, 2

  107. ...

  108. > 0.059 <= 0.0648761 , 0.0619381 , 100.00, 14

  109. # target 50% -0.0133888

  110. WARNING 51.01% of sleep were falling behind

  111. Aggregated Function Time : count 600 avg 0.04532505 +/- 0.04985 min 0.001904423 max 0.304644243 sum 27.1950299

  112. # target 50% 0.0208163

  113. # target 75% 0.07

  114. # target 90% 0.1025

  115. # target 99% 0.233333

  116. # target 99.9% 0.303251

  117. Sockets used: 19 (for perfect keepalive, would be 4)

  118. Code 200 : 585 (97.5 %)

  119. Code 429 : 15 (2.5 %)

  120. All done 600 calls (plus 0 warmup) 45.325 ms avg, 59.9 qps


测试 service-python 服务的限流是否生效:

 
   
   
 
  1. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -curl http://service-python/env

  2. HTTP/1.1 200 OK

  3. content-type: application/json

  4. content-length: 178

  5. server: envoy

  6. date: Wed, 16 Jan 2019 15:47:30 GMT

  7. x-envoy-upstream-service-time: 366


  8. {"message":"python v2","upstream":[{"message":"lua v2","response_time":0.19},{"message":"node v2","response_time":0.18,"upstream":[{"message":"go v1","response_time":"0.02"}]}]}


  9. $ kubectl exec fortio -c fortio /usr/local/bin/fortio -- load -qps 1 -n 10 -loglevel Error http://service-python/env

  10. 15:48:02 I logger.go:97> Log level is now 4 Error (was 2 Info)

  11. Fortio 1.0.1 running at 1 queries per second, 2->2 procs, for 10 calls: http://service-python/env

  12. Aggregated Function Time : count 10 avg 0.45553668 +/- 0.5547 min 0.003725253 max 1.4107851249999999 sum 4.55536678

  13. # target 50% 0.18

  14. # target 75% 1.06846

  15. # target 90% 1.27386

  16. # target 99% 1.39709

  17. # target 99.9% 1.40942

  18. Sockets used: 6 (for perfect keepalive, would be 4)

  19. Code 200 : 5 (50.0 %)

  20. Code 429 : 5 (50.0 %)

  21. All done 10 calls (plus 0 warmup) 455.537 ms avg, 0.6 qps


从上面的实验结果,可以得出如下的结论:

对于 service-go 服务,当 qps 低于50时请求几乎全部正常通过,当 qps 大于50时会有部分于请求得到 429 的响应码,这说明我们配置的限流规则已经生效。

对于 service-node 服务,对于普通调用,当 qps 大于20时就会出现部分于请求得到 429 响应码,但是当添加 "x-forwarded-for: 10.28.11.20" 请求头时,只有 qps 大于50时才会出现部分于请求得到 429 响应码,说明我们配置的关于 service-node 的两条限流规则都已经生效。

对于 service-python 服务,我们限定每 5s 只允许 2 次请求的限制,当以每秒 1qps 请求时,10 个请求只有 3 个请求通过,其他请求均得到 429 响应码。这说明我们对于 service-python 配置的限流规则也已经生效。

Istio 通过 quota 实现限流,但是限流并不是完全准确的,可能会存在部分误差,使用时需要注意。

清理:

 
   
   
 
  1. $ kubectl delete -f kubernetes/fortio.yaml

  2. $ kubectl delete -f istio/resilience/quota-mem-ratelimit.yaml

  3. $ kubectl delete -f service/node/service-node.yaml

  4. $ kubectl delete -f service/lua/service-lua.yaml

  5. $ kubectl delete -f service/python/service-python.yaml


总结

借助 Istio 让服务更具弹性 | 周末送福利


借助 Istio 提供的负载均衡,连接池,熔断和限流机制,我们可以使得我们的服务更具弹性,在遇到故障能更好的应对,以及快速的从故障中恢复过来,而这些几乎不需要人来参与其中。

以上内容摘自《Istio入门与实战》一书,经出版方授权发布。
周末福利


本次联合【机械工业出版社华章公司】为大家送上5 本正版《Istio入门与实战》图书。

留言谈谈你对Istio的认识或需要这本书的理由,截止9月24日12时, 点赞前3名的读者和 留言经典的2名读者可获赠正版图书1本!

没有获得的小伙伴,可以点击下面链接当当购书哦!