Istio服务网格的可观察性

Istio 可观察性

前面我们学习了 Istio 中的流量管理功能,本节我们来学习如何配置 Istio来自动收集网格中的服务遥测。Istio为网格内所有的服务通信生成详细的遥测数据,这种遥测技术提供了服务的可观察性,使运维人员能够排查故障、维护和优化应用程序,而不会给服务的开发人员带来任何额外的负担。通过 Istio,运维人员可以全面了解到受监控的服务如何与其他服务以及Istio组件进行交互。

网站会自动生成以下类型的遥测数据,以提供对整个服务网格的可观察性:

  • 指标:Istio 基于 4 个监控的黄金标识(延迟、流量、错误、饱和)生成了一系列服务指标,Isti 还为网格控制平面提供了更详细的指标,除此以外还提供了一组默认的基于这些指标的网格监控仪表板。
    • 延迟表示服务一个请求所需的时间。这个指标应该分成成功请求(如 HTTP 200)和失败请求(如 HTTP 500)的延迟。
    • 流量是衡量对系统的需求有多大,它是以系统的具体指标来衡量的。例如,每秒的 HTTP 请求,或并发会话,每秒的检索量,等等。
    • 错误用来衡量请求失败的比率(例如 HTTP 500)。
    • 饱和度衡量一个服务中最紧张的资源有多满。例如,线程池的利用率。
  • 分布式追踪:Istio 为每个服务生成分布式追踪 span,运维人员能够直观的看到网格内服务的依赖和调用关系。
  • 拜访日志:当流量流入网格中的服务时,Istio 能够生成每个申请的残缺记录,包含源和指标的元数据,该信息能够让运维人员可能将服务行为的审查管制到单个工作负载实例的级别

环境准备

主机名IP角色
k8s-mastereth0:10.1.1.100、docker:172.17.100.0/24K8S-master
k8s-node1eth0:10.1.1.120、docker:172.17.120.0/24K8S-node
k8s-node2eth0:10.1.1.130、docker:172.17.130.0/24K8S-node
nginx-proxyeth0:10.1.1.11代理节点

使用 Kiali 观测微服务

由于微服务之间的调用关系错综复杂,排查问题就更加困难了,为了使服务之间的关系更加清晰明了,了解应用的行为和状态,我们有必要使用一些可视化的方案来观测我们的微服务应用,其中 Kiali 就是这样的一个工具。**Kiali** 是一个基于 Istio 的服务网格的管理控制台。它提供了仪表盘、可观察性,并让我们通过强大的配置和验证能力来操作网格。它通过推断流量拓扑来显示服务网格,并显示网格的健康状况。Kiali 提供了详细的指标,强大的验证,Grafana 访问,以及与 Jaeger 的分布式追踪的强大集成
在安装 Kiali时,需要使用Istio的addons文件夹中的 kiali.yaml 文件。

1
2
3
$ cd /opt/istio/samples/addons/
# 创建kiali
$ kubectl apply -f /opt/istio/samples/addons/kiali.yaml

注意,如果你看到任何错误,例如在版本 monitoringkiali.io/v1alpha 中没有匹配的 MonitoringDashboard,请重新运行 kubectl apply 命令。问题是,在安装 CRD(自定义资源定义)和由该 CRD 定义的资源时,可能存在一个匹配条件。

我们可以用 istioctl dashboard kiali 打开 Kiali。

本次使用另外一种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#可以看到kiali这个service的类型为ClusterIP,外部环境访问不了
$ kubectl get svc -n istio-system kiali
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kiali ClusterIP 192.168.97.86 <none> 20001/TCP,9090/TCP 25s

#修改kiali这个service的类型为NodePort,这样外部环境就可以访问kiali了
#type: ClusterIP 修改为 type: NodePort即可
# kubectl edit service kiali -n istio-system

# 或者通过Ingress实现
cat > /opt/istio/samples/addons/kiali-ingress.yaml <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kiali
namespace: istio-system
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: kiali.od.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kiali
port:
number: 20001
EOF

# 应用Ingress配置清单
$ kubectl -f /opt/istio/samples/addons/kiali-ingress.yaml

100.1.1.11绑定本地hosts文件,访问 http://kiali.od.com/

使用 Prometheus 监控指标

Istio 使用 Prometheus 来监控指标,跟踪 Istio 和网格中的应用程序的健康状况

要安装 Prometheus,我们可以使用 Istio 安装包中 /samples/addons 文件夹中的示例安装。

1
2
3
4
$ cd /opt/istio/samples/addons/
# 创建Prometheus
$ kubectl apply -f /opt/istio/samples/addons/prometheus.yaml
$ kubectl get deploy -n istio-system

要打开 Prometheus 仪表板,我们可以使用 Istio CLI 中的 dashboard 命令:

1
2
$ istioctl dashboard prometheus
http://localhost:9090

这里我们使用另外一种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#修改prometheus这个service的类型为NodePort,这样外部环境就可以访问prometheus了
#type: ClusterIP 修改为 type: NodePort即可
#$ kubectl edit service prometheus -n istio-system
# 或者通过Ingress访问
$ cat > /opt/istio/samples/addons/prometheus-ingress.yaml <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: prometheus-ui
namespace: istio-system
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: prometheus.od.com
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: prometheus
port:
number: 9090
EOF

$ kubectl apply -f /opt/istio/samples/addons/prometheus-ingress.yaml

100.1.1.11绑定本地hosts文件,访问 http://prometheus.od.com

指标的监测

我们也可以来筛选一些关于 productpage 服务的指标数据,比如查询请求 productpage 服务的总次数:

1
2
3
istio_requests_total{destination_service="productpage.bookinfo.svc.cluster.local"}
#
istio_requests_total{destination_service_name="productpage"}

请求 reviews 服务 V3 版本的总次数,可以使用下面的 proml 语句,该语句返回所有请求 reviews 服务 V3 版本的当前总次数:

1
2
3
istio_requests_total{destination_service="reviews.bookinfo.svc.cluster.local",destination_version="v3"}
#
istio_requests_total{destination_service_name="reviews",destination_version="v3"}

过去 5 分钟 productpage 服务所有实例的正常请求频率

1
rate(istio_requests_total{destination_service_name=~"productpage.*",response_code="200"}[5m])

使用 Grafana 出图

Grafana 是一个用于分析和监控的开放平台。Grafana 可以连接到各种数据源,并使用图形、表格、热图等将数据可视化。通过强大的查询语言,你可以定制现有的仪表盘并创建更高级的可视化。

通过 Grafana,我们可以监控 Istio 安装和服务网格中运行的应用程序的健康状况

我们可以使用 grafana.yaml 来部署带有预配置仪表盘的 Grafana 示例安装。该 YAML 文件在 Istio 安装包的 /samples/addons 下。

确保在部署 Grafana 之前部署 Promeheus 插件,因为 Grafana 使用 Prometheus 作为其数据源

运行下面的命令来部署 Grafana 和预配置的仪表盘:

1
2
3
4
$ cd /opt/istio/samples/addons/
# 创建Grafana
$ kubectl apply -f /opt/istio/samples/addons/grafana.yaml
$ kubectl get deploy -n istio-system

这个 Grafana 安装不打算在生产中运行,因为它没有经过性能或安全方面的调整。

Kubernetes 将 Grafana 部署在 istio-system 命名空间。要访问 Grafana,我们可以使用 istioctl dashboard 命令。

1
istioctl dashboard grafana

我们使用另外一种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#修改prometheus这个service的类型为NodePort,这样外部环境就可以访问prometheus了
#type: ClusterIP 修改为 type: NodePort即可
#$ kubectl edit service grafana -n istio-system
# 或者通过Ingress访问
$ cat > /opt/istio/samples/addons/grafana-ing.yaml <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: grafana
namespace: istio-system
spec:
rules:
- host: grafana.od.com
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: grafana
port:
number: 3000
EOF

100.1.1.11绑定本地hosts文件,访问 http://grafana.od.com

Istio Grafana 安装时预配置了以下仪表盘:

点击搜索框和 istio 文件夹,查看已安装的仪表盘,如下图所示。

Istio 控制平面仪表盘(Istio Control Plane Dashboard)

从 Istio 控制平面仪表盘,我们可以监控 Istio 控制平面的健康和性能。

Istio Control Plane Dashboard 仪表盘将向我们展示控制平面的资源使用情况(内存、CPU、磁盘、Go routines),以及关于 Pilot、Envoy 和 Webhook 的信息。

Istio 网格仪表盘(Istio Mesh Dashboard)

网格仪表盘为我们提供了在网格中运行的所有服务的概览。仪表盘包括全局请求量、成功率以及 4xx 和 5xx 响应的数量。

Istio 性能仪表盘(Istio Performance Dashboard)

性能仪表盘向我们展示了 Istio 主要组件在稳定负载下的资源利用率。

Istio 服务仪表盘(Istio Service Dashboard)

服务仪表盘允许我们在网格中查看服务的细节。

我们可以获得关于请求量、成功率、持续时间的信息,以及显示按来源和响应代码、持续时间和大小的传入请求的详细图表。

Istio Wasm 扩展仪表盘(Istio Wasm Extension Dashboard)

Istio Wasm 扩展仪表盘显示与 WebAssembly 模块有关的指标。从这个仪表盘,我们可以监控活动的和创建的 Wasm 虚拟机,关于获取删除 Wasm 模块和代理资源使用的数据。

Istio 工作负载仪表盘(Istio Workload Dashboard)

这个仪表盘为我们提供了一个工作负载的详细指标分类。

获取Envoy的日志并进行调试

日志也是应用可观测性中一个非常重要的方式,也是我们传统调试应用非常重要的手段,在 Istio 中使用 Sidecar 容器对请求进行了拦截,无疑也增大了调试难度,但是同时 Istio 也可以监测到网格内的服务通信的流转情况,并生成详细的遥测日志数据,任何请求与事件的元信息都可以获取到,所以我们也非常有必要来查看下 Istio中的代理日志数据。Istio 最简单的日志类型是 Envoy 的访问日志,Envoy 代理打印访问日志信息到标准输出,然后我们就可以通过 kubectl logs 命令打印出来查看了
默认情况下 Istio 已经开启了 Envoy 访问日志,我们也可以通过 istioctl install 命令来配置日志:

1
2
3
4
5
$ kubectl get istiooperators -n istio-system installed-state -oyaml
....
meshConfig:
accessLogFile: /dev/stdout
....

默认情况下日志就是输出到stdout 上的 TEXT 文本格式,为了方便显示,这里我们将其设置为 JSON 格式,如果要想修改访问日志的格式可以设置 accessLogFormat 属性,具体的访问日志格式可以查看 Envoy 官方文档了解配置规则。

配置项说明
accessLogFile访问日志的日志文件路径,例如/dev/stdout,空值表示禁用该日志;
accessLogFormat访问日志的日志格式,空值表示使用默认的日志格式;
accessLogEncoding访问日志编码格式,支持TEXT和JSON两种,默认为TEXT;
level访问日志级别,默认warning;可选trace|debug|info|warning|error|critical|off

将日志格式设置为JSON格式

1
2
3
4
5
6
7
8
$ istioctl install --set profile=demo --set meshConfig.accessLogFile="/dev/stdout" --set meshConfig.accessLogEncoding="JSON"

$ kubectl get istiooperators -n istio-system installed-state -oyaml
....
meshConfig:
accessLogEncoding: JSON
accessLogFile: /dev/stdout
....

将日志格式设置为JSON后,此时输出的日志内容进行格式化如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"upstream_cluster": "outbound|9080||reviews.bookinfo.svc.cluster.local",
"start_time": "2023-03-03T04:59:48.441Z",
"upstream_service_time": "949",
"response_code_details": "via_upstream",
"request_id": "0071de46-2937-97bf-aaa7-4ab9dacc363d",
"method": "GET",
"requested_server_name": null,
"protocol": "HTTP/1.1",
"route_name": "default",
"downstream_local_address": "192.168.100.231:9080",
"response_flags": "-",
"bytes_received": 0,
"x_forwarded_for": null,
"downstream_remote_address": "172.7.130.7:33286",
"bytes_sent": 442,
"response_code": 200,
"upstream_local_address": "172.7.130.7:43992",
"upstream_transport_failure_reason": null,
"path": "/reviews/0",
"authority": "reviews:9080",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36",
"duration": 949,
"connection_termination_details": null,
"upstream_host": "172.7.100.7:9080"
}

Envoy 流量五元组

日志信息中有五个重要的字段,也就是所谓的Envoy 流量五元组:

  • 在 Envoy 中接受(用户A)请求流量叫做 Downstream (下游),Envoy 发出请求流量到B用户叫做 Upstream (上游),在处理DownstreamUpstream 过程中,分别会涉及2个流量端点,即请求的发起端和接收端;
  • 在这个过程中,Envoy 会根据用户规则,计算出符合条件的转发目的主机集合,这个集合叫做 UPSTREAM_CLUSTER,并根据负载均衡规则,从这个集合中选择一个 host 作为流量转发的接收端点,这个 host 就是 UPSTREAMLHOST。这就是 Envoy请求处理的流量五元组信息,这是 Envoy 日志里最重要的部分,通过Envoy 流量五元组我们可以准确的观测流量从哪里来到哪里去

还有一个非常重要的属性:response_flags,一些请求的错误标志会被赋值给该字段,用于调试请求的时候非常有用。常见错误标志有如下几种:

  • UH:upstream cluster 中没有健康的 host,503
  • UF:upstream 连接失败,503
  • UO:upstream overflow(熔断)
  • NR:没有路由配置,404
  • URX:请求被拒绝因为限流或最大连接次数
  • 更多信息可参考:官方文档

使用 Jaeger 进行分布式追踪

相比传统的单体应用,微服务的一个主要变化是将应用中的不同模块拆分为了独立的服务,在微服务架构下,原来进程内的方法调用成为了跨进程的远程方法调用。相对于单一进程内的方法调用而言,跨进程调用的调试和故障分析是非常困难的,难以使用传统的代码调试程序或者日志打印来对分布式的调用过程进行查看和分析。一个来自客户端的请求在其业务处理过程中很有可能需要经过多个微服务,我们如果想要对该请求的端到端调用过程进行完整的分析,则必须将该请求经过的所有进程的相关信息都收集起来并关联在一起,这就是分布式追踪,也是应用可观测性中非常重要的手段。

Istio支持通过 Envoy 代理进行分布式追踪。代理会代表其代理的应用程序自动生成跟踪范围,只需要应用程序转发适当的请求上下文。

随着分布式追踪技术的发展,社区推出了 OpenTracing 这个规范,提供了标准的 API 规范、框架以及一些公共的库。目前比较知名的追踪工具基本上都通过 OpenTracing 进行实现,比如 JaegerZipkin
我们这里主要来给大家介绍比较流行的 Jaeger。Jaeger 是由 Uber 开源的分布式追踪系统,采用 Go 语言编写,主要借鉴了 Google Dapper 论文和 Zipkin 的设计兼容 OpenTracing 以及 Zipkin 踪格式,目前已成为 CNCF 基金会的开源项目。

什么是 Jaeger

  • 开源、端到端的分布式追踪系统

  • 针对复杂的分布式系统,对业务链路进行监控和问题排查

    image.png

分布式追踪的两个重要术语

  • Span:

    • 逻辑单元
    • 有操作名、执行时间
    • 嵌套、有序、因果关系
  • Trace:

    • 数据/执行路径

    • 由一个或多个 Span 的组合

Jaeger 的架构:

核心组件:

  • Client libraries:客户端公共库,支持不同语言
  • Agent:用于从应用抓取Span,并发送给Collector
  • Collector:收集器,从应用层收集Span数据,并发送给存储系统(DB)
  • Query:相当于查询引擎,用于提供在页面上查询数据的能力
  • Ingester:与Kafka配合使用,相当于Kafka的一个Consumer,消费数据存储到DB中

部署Jaeger

接下来我们把 Jaeger 集成到 Istio。首先,确认 Istio 开启了追踪选项,可以使用如下命令开启:

1
2
# 默认就是开启的
$ istioctl install <flags-you-used-to-install-Istio> --set values.tracing.enabled=true

我们可以使用官方提供的配置文件快速将 Jaeger 集成到 Istio 中:

1
2
3
4
$ cd /opt/istio/samples/addons/
# 创建Grafana
$ kubectl apply -f /opt/istio/samples/addons/jaeger.yaml
$ kubectl get deploy -n istio-system

然后使用如下命令启动 Jaeger 的Web UI:

1
istioctl dashboard jaeger

这里我们使用另外一种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#修改jaeger这个service的类型为NodePort,这样外部环境就可以访问jaeger了
#type: ClusterIP 修改为 type: NodePort即可
#$ kubectl edit service jaeger-collector -n istio-system
# 或者通过Ingress访问
$ cat > /opt/istio/samples/addons/jaeger-ingress.yaml <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: jaeger
namespace: istio-system
spec:
rules:
- host: jaeger.od.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: tracing
port:
number: 80
EOF

$ kubectl apply -f /opt/istio/samples/addons/jaeger-ingress.yaml

100.1.1.11绑定本地hosts文件,访问 http://jaeger.od.com

然后访问 Bookinfo 应用页面生成一些 trace 数据后,到 Jaeger 查询 trace 数据:

点击任意 Span 可以查看其详细 trace 数据:

通过System Architecture查看调用视图