Istio 流量治理
引言
流量治理是Istio的核心功能,通过使用Istio可以管理服务网格的服务发现、流量路由、负载均衡,简化服务级属性的配置,例如超时和重试等。
目标规则(DestinationRule)
在东西向流量管理的文章中我们简单的了解过
DestinationRule的subsets的作用,配合VirtrualService可以实现基于不同比例和通过header来控制流量导向。
这些规则还可以指定负载均衡的配置、来自Sidecar代理的连接池大小或异常检测的设置,以实现从负载均衡池中检测和驱逐不健康的主机。
DestinationRule定义了在路由发生后,应用于服务(对应host)流量的策略。
流量策略(TrafficPolicy)
在DestinationRule中我们可以配置TrafficPolicy,虽然它不是必须的。
tls (ClientTLSSettings)
我们在南北向流量管理中,也有一个tls设置,还记得吗?不过那是在Gateway网关上的配置,其意义在于从集群外部到服务网格内部的流量设置,这和网格内的流量策略要区分开。
tls的mode有这几种:
栗子:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: db-mtls
spec:
host: mydbserver.prod.svc.cluster.local
trafficPolicy:
tls:
mode: MUTUAL
clientCertificate: /etc/certs/myclientcert.pem
privateKey: /etc/certs/client_private_key.pem
caCertificates: /etc/certs/rootcacerts.pem
subsets(服务版本 子集)
我们在东西向流量管理那篇文章中没有写trafficPolicy,我们可以看见这个字段并不是必须写的,因为当时我们只是想简单的分配比例和通过http headers导向不同版本,所以我们只使用了subsets。
其实我们可以根据subsets也可以定义不同的trafficPolicy,像是这样:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: bookinfo-ratings
spec:
host: ratings.prod.svc.cluster.local
trafficPolicy: # 如果没有匹配subsets默认的流策
loadBalancer:
simple: LEAST_CONN
subsets:
- name: testversion
labels:
version: v3
trafficPolicy: # 看这里哦
loadBalancer:
simple: ROUND_ROBIN
portLevelSettings(PortTrafficPolicy[])
针对某一具体目标我们可以定义流量策略,这些策略默认对目标服务端的所有端口有效。如果针对某一端口需要定义特定的策略,那么可以通过使用portLevelSettings(PortTrafficPolicy[]类型)来定义,像是这样:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: bookinfo-ratings-port
spec:
host: ratings.prod.svc.cluster.local
trafficPolicy: # Apply to all ports
portLevelSettings:
- port:
number: 80
loadBalancer:
simple: LEAST_CONN
- port:
number: 9080
loadBalancer:
simple: ROUND_ROBIN
负载均衡配置(LoadBalancerSettings)
在trafficPolicy中我们看见了loadBalancer,上面2个例子都是使用simple方式的负载均衡算法,Istio其实提供了3种方式。
simple方式
我们先来看看simple提供的4种负载均衡算法:
- 轮训模式
- 最少连接
- 随机
- 透传

hash一致性方式(consistentHash)

consistentHash的httpCookie栗子:
描述一个HTTP cookie,它将用作Consistent Hash负载平衡器的哈希键。如果不存在Cookie,则会生成它。
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: bookinfo-ratings
spec:
host: ratings.prod.svc.cluster.local
trafficPolicy:
loadBalancer:
consistentHash:
httpCookie:
name: user
ttl: 0s # Cookie的生命周期 必须写
区域加权负载均衡(LocalityLoadBalancer)

熔断降级
Istio提供了一种非常实用且经过良好测试的熔断模式,实现3个状态机,这和Hystrix类似
ClosedHalf-OpenOpen
关于这3个状态机,这里就不过多解释了,应该都懂。
连接池管理
熔断是分布式系统的重要组成部分,在整个微服务系统中,如果没有熔断,那么极大可能会因为某一服务挂掉导致整个服务雪崩。
Istio采用连接池管理Envoy代理在网络级别实现强制断路限制,而不必独立配置和编写对应每个应用程序的熔断代码。
我们可以通过trafficPolicy.connectionPoll来限制最大连接数和超时时间:
trafficPolicy:
connectionPoll:
tcp:
maxConnections: 100
connectTimeout: 30ms
http:
http2MaxRequests: 1000
maxRequestsPerConnection: 10
关于connectionPoll.tcp:
关于connectionPoll.http:
异常检测(OutlierDetection 也叫离群检测)
异常检测OutlierDetection是Istio中熔断具体实现,英文直译离群检测,是因为一旦达到需要移除的阈值就会被移出连接池。用于跟踪上游服务中每个主机的状态,适用于HTTP和TCP服务。对于HTTP服务来说,在预定时间段内,持续返回API调用的5XX错误的主机将在连接池中被移除。对于TCP服务来说,连接超时或连接失败将认为异常。
白话的理解就是连接池负责熔断降级,异常检测负责将被降级或错误的服务移出连接池。
在不使用Istio的情况下我们只能做到熔断降级,快速返回熔断后被降级服务的响应。
优点:以前Hystrix只是针对单个服务实例的熔断降级,而在微服务集群下,也许我们想要的是相同的服务均摊限流策略(有时候我们并不是用轮训规则选择服务),而不是针对单个实例。所以Sentinel加入了集群限流模式,但是有时候我们的这些服务实例也许机器配置不同,集群限流模式下也许会让比较差的服务器跟不上更高配置的机器。Istio的这种基础架构上的异常离群检测可以将单个服务实例暂时移出集群,比起集群限流来说更稳定。

我们可以通过trafficPolicy.outlierDetection来配置异常检测:
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: test-policy
spec:
host: test.prod.svc.cluster.local
trafficPolicy:
connectionPool:
tcp:
maxConnections: 1 # TCP最大连接数
http:
http1MaxPendingRequests: 1 # HTTP1.1 等待就绪连接池连接时排队的最大请求数
http2MaxRequests: 1 # HTTP2最大请求数
maxRequestsPerConnection: 1 # 一个连接内能够发出的最大请求数,如果为1那么就不会保持keepalive特性,因为这个连接只能发一次请求,也就不需要长连接了。
outlierDetection: # 异常检测
consecutiveErrors: 1 # 连续错误数量
interval: 1s # 移除检测的时间间隔
baseEjectionTime: 1m # 最小的移除时间长度,主机每次被移除后的隔离时间=移除次数*最小的移除时间长度
maxEjectionPercent: 100 # 上游服务的负载均衡池中允许被移除主机的最大百分比,默认是10%
我们测试后,可以使用下面的命令进入pod中的sidecar代理查看关于upstream_rq_pending_xx日志。
upstream_rq_pending_overflow记录的是熔断次数。
kubectl exec it -n 命名空间 pod名 -c istio-proxy -- sh -c ‘curl localhost:15000/status’ | grep 服务名 | grep pending
服务重试
注意:这里开始是在VirtualService中了哦,不是DestinationRule.
Istio的VirtualService提供了用于定义HTTP请求失败时的重试策略。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: test-vs
spec:
hosts:
- test.prod.svc.cluster.local
http:
- route:
- destination:
host: test.prod.svc.cluster.local
subset: v1
retries:
attempts: 3 # 给定请求的重试次数。重试之间的时间间隔将自动确定(25毫秒以上)
perTryTimeout: 2s # 给定请求的每次重试尝试超时
retryOn: 5xx,gateway-error,connect-failure,refused-stream # 指定重试发生的条件。可以使用“,”分隔列表指定一个或多个策略
故障注入(HTTPFaultInjection)
故障注入HTTPFaultInjection用于在将HTTP请求转发到路由中指定的目标时,指定要注入的一个或多个故障。故障规范是虚拟服务规则的一部分,包括从下游服务终止HTTP请求、延迟请求的代理等。
故障注入是混沌工程(说白话就是模拟故障)中经常用的一种手段。Istio支持2种故障注入手段(中断abort和延迟delay)。
混沌工程,是一种提高技术架构弹性能力的复杂技术手段。Chaos工程经过实验可以确保系统的可用性。混沌工程旨在将故障扼杀在襁褓之中,也就是在故障造成中断之前将它们识别出来。通过主动制造故障,测试系统在各种压力下的行为,识别并修复故障问题,避免造成严重后果。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: test-vs
spec:
hosts:
- test.prod.svc.cluster.local
http:
- route:
- destination:
host: test.prod.svc.cluster.local
fault:
#abort: # 表示将50%的请求直接返回502错误
# httpStatus: 502
# percentage:
# value: 0.5
delay: # 表示将50%的请求延迟5秒 如果是abort就是直接返回定义的错误
fixedDelay: 5s
percentage:
value: 0.5
Sidecar配置
默认情况下,
Istio将使用到达网格中每个工作负载实例所需的必要配置对网格中的所有Sidecar代理进行编程,并在与工作负载相关的所有端口上接受流量。Sidecar配置提供了一种方法,可以微调代理在与工作负载之间来回通信时,代理将接受的端口集,协议。此外,可以限制从工作负载实例转发出站流量时,代理可以访问的服务集。
注意:每个命名空间下只允许存在一个没有workloadSelector的Sidecar配置。
默认情况下如果该命名空间下没有Sidecar配置将使用根命名空间istio-config中的Sidecar配置。
注意:这里说的是Sidecar是资源,不是之前所说的Sidecar代理模式。
Sidecar资源包含了4个部分
IstioIngressListener在附加到工作负载实例的Sidecar代理上指定入站流量侦听器的属性。
IstioEgressListener在附加到工作负载实例的Sidecar代理上指定出站流量侦听器的属性。
栗子:
这个栗子在根命名空间
istio-config下声明了一个全局默认的Sidecar配置,它将所有命名空间中的Sidecar配置为出口流量只允许到同命名空间(并不是istio-config,而是指用了全局默认配置的命名空间本身)或istio-system命名空间中的工作负载服务。
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
name: default
namespace: istio-config
spec:
egress:
- hosts:
- "./*"
- "istio-system/*"
针对性的栗子:
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
name: ratings
namespace: prod-us1
spec:
workloadSelector:
labels:
app: ratings
ingress:
- port:
number: 9080
protocol: HTTP
name: somename
defaultEndpoint: unix:///var/run/someuds.sock
egress:
- hosts:
- "prod-us1/*"
port:
number: 9080
protocol: HTTP
name: egresshttp
- hosts:
- "istio-system/*"
上面的栗子是说,在prod-us1命名空间下,配置pod或VM标签为app:ratings的工作负载实例使用此Sidecar配置进行限制,入口限制为仅允许9080端口(HTTP协议),并将它们转发到附加的工作负载实例的unix套接字上。
对于出口流量只允许访问prod-us1命名空间下9080端口的服务,或istio-system命名空间下的所有服务。
注意:因为我们写了workloadSelector,所以prod-us1命名空间下还可以创建其他的Sidecar配置。