k8s进阶:Istio流量管理

基础教程

tetrate发行版有一个简单的istio入门教程,感觉还不错,视频+文字,中文字幕.
传送门,懒得在官方文档里面跳来跳去的可以直接看这个.

本文为基于该教程作的一些记录,基于istioctl+operator的部署方式,版本为1.10.3

发现选择器: Discovery selectors

Istio:1.10引入的新功能.

默认情况下,istio会watch和update集群中所有的服务,也就是所有集群中的服务都会被记录并维护在istiod的服务发现表中.

默认情况下所有envoy代理的配置方式是,他们可以到达服务网格中的每个工作负载,并接受与其相关的所有端口的流量.

例如有两个服务,foo和bar,尽管他们不需要相互通信,但是他们的端点都会被记录在对方的已发现端点列表中.

当服务数量越来越大,将不可避免地导致某程度的slow down.

discovery selectors就是为了解决这个问题存在的,配置在mesh层,可以限制被istio观察和处理的项目数量.

同样有类似功能的还有一个叫sidecar resource的解决方案.

一些命令

1
2
#列出某foo命名空间的foo工作负载的服务发现表
istioctl proxy-config endpoints deploy/foo.foo

开启功能

1
kubectl edit istiooperators.install.istio.io -n istio-system installed-state
1
2
3
4
5
6
7
8
9
10
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
namespace: istio-system
name: istio-demo
spec:
meshConfig:
discoverySelectors:
- matchLabels:
env: test #只watch和update匹配这个标签的命名空间的工作负载

可观测性:遥测和日志

  • 监控: prometheus

  • 追踪: zipkin

  • 数据可视化: grafana

为了使grafana和kiali工作,需要先安装prometheus addon

istio会生成三种类型的遥测数据:指标度量(metric), 分布式追踪, 访问日志.

envoy代理负责收集指标.

Metric:指标

istio基于四个黄金信号生成指标:

  • 延迟: 分为成功和失败请求的延迟

  • 流量: 用于衡量系统需求,如每秒http请求数,并发会话,每秒检索量等

  • 错误: 衡量失败请求的比率

  • 饱和度: 衡量一个服务中最紧张的资源有多满.例如线程池利用率

代理级指标

可以从每个envoy代理实例的/stats查看代理级指标

服务级指标

涵盖前面提到的四个黄金信号.这些指标可以让我们能够监控服务于服务之间的通讯.此外,istio还提供了一组仪表盘,我们可以根据这些指标监控服务行为.

默认情况下,istio的标准指标集会被导出到Prometheus.

控制平面度量

istio的控制平面也会有指标,用于监控istio的控制平面,[指标列表](Istio / pilot-discovery).

包括: 入站/出站监听器的数量,没有实例的集群数量,被拒绝或被忽略的配置等指标

Prometheus

1
2
3
4
5
6
7
#使用安装包的示例配置文件安装
kubectl apply -f samples/addons/prometheus.yaml
#打开dashboard,默认监听locahost
istioctl dashboard prometheus --address 11.0.1.121
#暴露先前创建的deployment my-nginx并请求
kubectl expose deployment my-nginx --port 80 --target-port 80 --name my-nginx-svc --type=ClusterIP
curl http://<cluster-ip>

然后在prometheus的dashboard中搜索istio_requests_total就能看到刚刚的请求产生了1个请求数

Grafana

1
2
3
4
#使用安装包的示例配置文件安装
kubectl apply -f samples/addons/grafana.yaml
#打开dashboard,默认监听locahost
istioctl dashboard grafana --address 11.0.1.121

注意: 这里的配置不建议在生产中运行,因为它没有经过性能和安全方面的调整

默认已经在grafna配置了一些仪表盘:

  • istio控制平面仪表盘:control plane dashboard,展示控制平面的资源使用情况(mem,cpu,disk,go goutines),以及ilot,envoy和webhook信息.

  • 网格仪表盘: mesh dashboard,提供网格中运行的所有服务的概览,包括全局请求数,成功率以及错误响应数.

  • 性能仪表盘: performence,展示istio主要组件在稳定负载下的资源利用率

  • 服务仪表盘: service,展示网格中的服务的细节,比如请求量,成功率,持续时间,以及显示按来源和响应代码,持续时间和大小的传入请求的详细图表

  • wash扩展: wash extension:显示于webassembly模块有关的指标,通过这个仪表盘,我们可以监控活动的和创建的wash虚拟机和获取删除wash模块和代理资源使用的数据.

  • istio工作负载: workload:关于工作负载的指标

分布式追踪

是一种检测微服务apps的方法.

当一个请求进入服务网格时,Envoy都会生成一个唯一的请求ID和追踪信息,并存储在http请求头中,任何apps都可以将这些header转发给它缩调用的其他服务,这样就可以在震哥哥系统中创建一个完整的追踪.

span

分布式追踪是一个跨度(span)的集合.

每当有请求流经不同的系统组件时,每个组件都会产生一个跨度.每个跨度包含:

  • 一个名称

  • 一组<标签tag>:<日志log>的键值对

  • 以及一个span context,跨度上下文

单个跨度,识别跨度,父跨度和追踪ID的上下文头一起被发送到一个叫做采集器的组件,采集器负责对数据进行验证,索引和存储.

当请求流经Envoy代理时,Envoy代理会自动生成各个跨度.但是,envoy只能在边缘收集跨度,任何如果需要的额外的跨度,由我们的app需要负责生成.

我们的app还要确保我们在调用其他服务时转发追踪header.这样,各个跨度才能正确地关联到一个单一的追踪中.

Zipkin

Zipkin是一款开源的分布式实时数据追踪系统(Distributed Tracking System),基于 Google Dapper的论文设计而来,由 Twitter 公司开发贡献。其主要功能是聚集来自各个异构系统的实时监控数据。分布式跟踪系统还有其他比较成熟的实现,例如:Naver的Pinpoint、Apache的HTrace、阿里的鹰眼Tracing、京东的Hydra、新浪的Watchman,美团点评的CAT,skywalking等。

上面说到,为了让我们的服务参与分布式追踪,我们需要在进行任何下游服务调用时传播http header.

但是尽管所有请求都经过istio sidecar,istio没有办法将出站和入站请求关联起来,而zipkin可以利用在服务之间传播的header,将这些请求拼接起来,形成完整的追踪.

istio依赖B3跟踪头(以x-b3开头的header)和envoy生成的请求id(x-request-id),通常做法是我们的应用程序需要捕获传入请求的以下header并将他们包含在所有从你的app发送出去的请求中:

  • x-request-id

  • x-b3-traceid

  • x-b3-spanid

  • x-b3-parentspanid

  • x-b3-sampled

  • x-b3-flags

  • b3

  • 如果使用lightstep,还需要转发x-ot-span-context

zipkin的安装运行方式于上面的addon类似,在samples/addons/extras中有示例配置yaml文件.

Jaeger

也是一个分布式追踪平台,它与zipkin的对比可以看看这篇文章: Jaeger与Zipkin:分布式跟踪平台该选谁_Kubernetes中文社区

1
2
kubectl apply - samples/addons/jaeger.yaml
istioctl dashboard jaeger --address 11.0.1.121

Kiali

Kiali是一个基于istio的service mesh管理控制台.提供dashboard,可视化,允许我们通过web界面配置,验证,操作网格.它通过推断流量拓扑来显示服务网格,并显示网格的健康状况.

它提供强大的验证,详细的指标,grafana访问,以及于jaeger的分布式追踪的强大集成.

安装部署与上面其他addon类似,示例配置文件在/spamles/addon/kiali.yaml

如果遇到报错:unable to recognize "samples/addons/kiali.yaml": no matches for kind "MonitoringDashboard" in version "monitoring.kiali.io/v1alpha1",重新执行一次apply即可,原因是在安装CRD(自定义资源定义)和该CRD定义的资源时,会存在一个匹配条件.

流量管理

k8s进阶:Istio - Any & Nothing 一文中我们已经根据官方文档操作了一遍流量管理的功能,这里再进行一些复习或查漏补缺.

gateway

默认安装了两个gateway: ingressgateway和engressgateway

入口网关接受入站连接,出口网关接受从集群出去的连接.网关都运行一个envoy代理,他们再网格的边缘作为负载均衡器运行.

默认使用的是loadbalance类型的svc,私有环境没有外置的负载均衡器可以通过以下方式获取它的nodeIP和nodePort:

1
2
3
4
5
6
export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')
export GATEWAY_URL=$INGRESS_HOST:$INGRESS_PORT
#这就是你的gateway访问地址
echo "$GATEWAY_URL"

有了网关,我们还需要配置网关资源(Gateway)来配置网关.

这两个东西叫法上容易叫混:

  • 网关,ingressgateway和engressgateway部署在网格边缘,可以叫做网关控制器或者叫网关代理

  • 网关资源,Gateway,是用来配置网关控制器的,也可以叫网关配置

下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: my-gateway
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- dev.example.com
- test.example.com
- "*" #支持通配符

网关控制器部署时默认有个istio=ingressgateway的标签,网关配置根据这个标签来选择它使用的网关控制器.

hosts是一个过滤器,只有以符合它配置规则为目的地的流量才能允许通过.

简单路由

virtual service

使用virtual service实现路由,通过virtual service我们可以定义流量路由规则,并在客户端连接服务时应用这些规则.例如上一篇文章配置的将流量路由到不同version的pod.下面再举一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: customers-route
spec:
hosts:
- customers.default.svc.cluster.local
gateway:
- my-gateway
http:
- name: customers-v1-route
route:
- destination:
host: customers.default.svc.cluster.local
subset: v1
weight: 70
- name: customers-v2-route
route:
- destination:
host: customers.default.svc.cluster.local
subset: v2
weight: 30

在这个例子中,我们定义流量发送到customers.default.svc.cluster.local这个kubernetes的SVC,这个SVC下绑定了两个版本的pod,通过virtual service定义70%流量去到v1,30%去到v2.

http是一个包含http流量的路由规则的有序列表.

destination是指服务注册表中的一个服务,也是路由规则处理后请求将被发送的目的地.istio的服务注册表上记录的都是合法值,也包括通过serviceEntry手动添加的合法服务.

gateway字段则用来绑定对应的网关资源,有点类似ingressclass和ingress的关系

virtual service和gateway的hosts匹配关系

gateway hostsvirtualservice hostsaction
*customers.default.svc.cluster.local流量会直接通过virtual service发送到目的地customers,因为gateway允许了所有
customers.default.svc.cluster.localcustomers.default.svc.cluster.localhost匹配,流量将被发送
hello.comcustomers.default.svc.cluster.localhosts不匹配,无效
hello.com[“hello.com”, “customers.default.svc.cluster.local”]只会允许hello.com,这依然是一个有效配置,因为virtual service可以连接到第二个网关,该网关的hosts包含*.default.svc.cluster.local即可

subset 和 destination rule

目的地指的是不同子集(subset)或服务版本.通过subset,我们可以识别应用程序的不同变体.比如version1和version2.对应于我们对应服务的两个不同版本.

每个子集都使用label去确定那些pod包含在子集中.

subset在destination rule中声明,这里前文就有例子.

另外destination rule还可以配置流量策略.

destination rule的流量策略

负载均衡策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: customer-destination
spec:
host: customers.default.svc.cluster.local
trafficPolicy:
loadBalancer:
simple: ROUND_ROBIN
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2

还可以基于哈希的负载均衡,并基于http头,cookies或其他请求属性实现亲和性,例如:

1
2
3
4
5
6
trafficPolicy:
loadBalancer:
consistentHash: #基于hash的负载均衡
httpCookie:
name: location #使用cookie中的location实现会话亲和
ttl: 4s

连接池配置

应用于上游服务的每个主机,用它来控制连接量:

1
2
3
4
5
6
spec:
host: myredissrv.prod.svc.cluster.local
trafficPolicy:
connectionPool:
http:
http2MaxRequests: 50

异常点检测/断路器/熔断

跟踪上游服务中每个主机(pod)的状态,如果一个主机开始返回错误,它就会在预定的时间内被负载均衡池中弹出.

对于TCP服务,envoy将连接超时或失败计算判定为错误.

1
2
3
4
5
6
7
8
9
trafficPolicy:
connectionPool:
http:
http2MaxRequests: 500 #限制500并发http2请求
maxRequestsPerConnection: 10 #每个连接不超过10个请求
outlierDetection:
interval: 5m #每5分钟扫描一次上游主机(pod)
consecutiveError: 10 #如果任何一个主机连续失败10次
bashEjectionTime: 10m #就把它弹出10分钟

客户端TLS设置

1
2
3
4
5
6
trafficPolicy:
tls:
mode: MUTUAL #mTLS
clientCertificate: /etc/certs/cert.pem
privateKey: /etc/certs/key.pem
caCertificates: /etc/certs/ca.pem

其他支持的TLS模式有:

  • DISABLE: 没有TLS连接

  • SIMPLE: 在上游端点发起TLS连接

  • ISTIO_MUTUAL: 类似MUTUAL但使用的istio的mTLS证书

端口流量策略

可以根据不同的端口配置不同的流量策略

1
2
3
4
5
6
7
8
9
10
trafficPolicy:
portLevelSettings:
- port:
number: 80
loadBanlancer:
simple: LEAST_CONN
- port:
number: 8000
loadBalancer:
simple: ROUND_ROBIN

弹性:Resiliency

弹性的目的是故障发生后将服务恢复到一个完全正常的状态.也就是failover.

服务的可用关键是在提出服务请求时使用超时(timeout)和重试(retry)策略.可以在istio的virtual service上配置这两者.下面是两个例子.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
- route:
- destination: customers.default.svc.cluster.local
host:
subset: v1
timeout: 10s #设置10s超时
...
- route:
- destination:
host:
subset: v1
retries:
attempts: 10 #尝试次数为10
perTryTimeout: 2s #每次超时时间为2s
#重试任何连接超时(connect-failure)或服务不响应(reset)的失败请求
retryOn: connect-failure,reset
...

注意: 如果同时设置超时和重试,超时值是请求等待的最长时间,比如如果我们在上面的第二个例子中设置了超时10s,那么10s一过,即使重试策略还剩下一些尝试,也会终止直接判断服务不可用.

重试策略可以更加细化:

例如我们可以只在上游服务器返回5xx错误或者网关错误(502,503,504)时重试,或者在请求头中指定可重试的状态代码.

重试和超时都发生在客户端.

当envoy重试请求时,最初失败并导致重试的端点就不再包含在负载均衡池中,例如:

假设kubernetes有3个端点(pod),其中一个失败了,并出现了可重试的错误代码,Envoy重试请求时,将不会向原来的端点重新发送,而只发送给另外两个没有失败的端点的一个.

故障注入: failure injection


k8s进阶:Istio流量管理
http://example.com/2023/05/21/k8s-istio-2/
作者
Peter Pan
发布于
2023年5月21日
许可协议