用 Telemetry API 设计信号
目标
学习如何通过配置来整理代理生成的信号。先为整个服务网格配置默认日志, 再添加只保留失败请求的条件,为单个工作负载设置采样与标签,最后在部署前 拦截会导致基数爆炸的标签。
为什么重要
服务网格提供的价值是统一性。即使服务自身没有埋点,也会生成名称和标签一致的 指标,这种统一性使各个仪表盘能够相互比较。但如果保留默认设置,会出现两个问题: 访问日志每个请求产生一行,在高流量服务中很快推高存储成本;错误的自定义标签会让 时间序列随标签值种类成倍增加,使存储系统和查询同时崩溃。
Telemetry API 可以通过声明完成这些调整。关键在于它有三层作用域:在安装
命名空间中不设置 selector 时作用于整个服务网格;在普通命名空间中不设置 selector
时作用于该命名空间;添加 selector 后则作用于相应工作负载。而且每个命名空间
只能有一个不带 selector 的资源,因为存在两个以上时,未定义哪一个优先。
环境
本 Pod 中没有真正的 istiod 或 Sidecar。由于没有请求流动,也不会生成指标或日志。
不过,这里有注册了 Telemetry CRD 的真实 apiserver 和 istioctl,因此仍能原样验证
配置是否有效以及作用域是否正确。这两点也正是实际工作中最容易出问题的地方。
工作目录是 /root/istio-obs,并使用其下的 k8s/、reject/、bin/、out/。
步骤
- 创建
mesh-obs,并在istio-system中配置服务网格的默认访问日志。 - 在
mesh-obs中配置只保留失败请求的日志过滤器。 - 为
app=reviews设置 10% 采样率和字面量标签,并观察超出范围的值被拒绝。 - 为
app=ratings配置指标调整。 - 使用
bin/tag-lint.sh在部署前拦截危险标签。 - 创建两个工作负载,并使用
istioctl analyze检查作用域规则。 - 计算实际应用于一个 Pod 的配置,并写入
out/effective.txt。 - 在
out/summary.txt中用六行总结。
参考
- 每个命名空间只能有一个不带 selector 的 Telemetry。第 3、4 步必须添加 selector。遗漏后会在第 6 步暴露出来。
- selector 查看的是 Pod 标签,而不是 Deployment 名称。
- 第 3 步中应被拒绝的文件不要放在
k8s/,而应放在reject/。最后一步要求k8s/中的所有文件都有效。 - 标签值是 CEL 表达式。若要填入常量字符串,必须用单引号包裹。
为整个服务网格配置默认访问日志
创建 mesh-obs 命名空间并添加 istio-injection=enabled 标签,再通过 /root/istio-obs/k8s/mesh-default.yaml 在 istio-system 中创建 Telemetry mesh-default。不设置 selector,只指定 accessLogging provider。
Telemetry 的作用域有三层:在 istio-system(安装命名空间)中不带 selector 时作用于整个服务网格;在普通命名空间中不带 selector 时作用于该命名空间;添加 selector 时作用于相应工作负载。不带 selector 这一事实本身决定作用域,这是该 API 的核心。provider 名称是指向网格配置中已注册名称的字符串,因此这里即使实际不存在,格式仍然有效。
只记录失败请求
通过 /root/istio-obs/k8s/failures-only.yaml 在 mesh-obs 中创建 Telemetry failures-only。不要设置 selector,并在 accessLogging 中添加只在响应码为 400 以上时保留日志的 filter.expression。
记录所有请求的成本很高。代理会为每个请求写一行日志,因此高流量服务的存储成本会快速增长。过滤器使用 CEL 表达式,在访问日志上下文中可以使用 response.code。此 Telemetry 应用于整个命名空间,因此不能添加 selector。并且每个命名空间只能有一个不带 selector 的 Telemetry,所以后续步骤中的资源务必添加 selector。
为工作负载设置采样率与自定义标签
通过 /root/istio-obs/k8s/trace-sampling.yaml 在 mesh-obs 中创建 Telemetry trace-sampling。将 selector.matchLabels.app 设为 reviews,将 tracing[0].randomSamplingPercentage 设为 10,并在 customTags 中放置一个使用字面量值的标签。然后在 /root/istio-obs/reject/bad-sampling.yaml 中写入超出范围的采样值并尝试应用,把拒绝消息保存到 /root/istio-obs/out/rejected.txt。
默认采样率为 1%。第一个代理决定是否采样并通过请求头传播,因此后续跳数会遵循该决定。只在调试时提高采样率,不要长期保持 100%。自定义标签只能使用值种类有限的内容,所以这里使用字面量。超出范围的值会被 CRD schema 阻止,对象根本不会创建。消息输出到标准错误,请使用 2>&1 接收。
关闭标准指标并添加标签
通过 /root/istio-obs/k8s/metrics-tuning.yaml 在 mesh-obs 中创建 Telemetry metrics-tuning。selector.matchLabels.app 为 ratings,provider 为 prometheus。使用 overrides 关闭 REQUEST_SIZE 并指定 mode,再在另一个条目中通过 tagOverrides 添加一个具有字面量值的标签。
关闭指标也是一种配置。关闭不用的直方图可以减少时间序列,让存储和查询都更轻量。mode 从 CLIENT、SERVER、CLIENT_AND_SERVER 中选择;同一个请求会分别在两侧代理记录,因此必须指定所指的一侧。标签值是 CEL 表达式,若要使用常量字符串,必须用单引号包裹。
在部署前拦截导致基数爆炸的标签
创建 /root/istio-obs/bin/tag-lint.sh <Telemetry파일>。如果 tagOverrides 的值指向请求路径、请求标识符、用户标识符、请求头等值种类近乎无限的内容,则输出 HIGH_CARDINALITY=<태그이름>=<값> 并以退出码 1 结束。只使用常量字符串的文件应输出 OK 并以 0 结束。
时间序列数量会按标签值组合的乘积增加。把用户标识符这类值近乎无限的内容用作标签,会同时压垮存储和查询;代理并没有自动哈希或降低采样率的保护机制。CEL 值若是用单引号包裹的常量,就是安全的;若指向 request.url_path 或 request.headers[...] 等内容,则很危险。评分器会用安全文件和危险文件实际运行该检查器,并同时确认它不会拦截第 4 步创建的文件。
通过静态分析检查作用域规则
通过 /root/istio-obs/k8s/workloads.yaml 在 mesh-obs 中创建 Deployment reviews 和 ratings。Pod 标签的 app 必须分别与名称相同。然后确认 istioctl analyze -n mesh-obs 无错误结束。
selector 查看的是 Pod 标签,而不是 Deployment 名称,因此必须准确设置 Pod 模板的标签。如果一个命名空间中存在两个以上不带 selector 的 Telemetry,优先级没有定义,istioctl analyze 会将其报告为错误。若前面步骤遗漏了 selector,就会在这里暴露。此 Pod 中没有真正的 Sidecar,因此仍会有注入相关警告;只要没有错误即可通过。
计算实际应用于一个 Pod 的配置
计算哪些 Telemetry 会应用于 app=reviews Pod,并在 /root/istio-obs/out/effective.txt 中写五行:MESH、NAMESPACE、WORKLOAD、TRACING_PERCENT、METRICS_APPLIES。
三层作用域会叠加生效:一个网格级作用域、一个命名空间级作用域,以及一个选择该 Pod 的工作负载级作用域。前三项填写名称,TRACING_PERCENT 填写工作负载级配置中的采样值。最后一行使用 yes 或 no 回答第 4 步的指标配置是否也应用于此 Pod。回顾 selector 选择的是哪个应用。不要猜名称,请通过 kubectl get telemetry -A 确认后填写。
统计所创建内容并确认全部有效
确认 /root/istio-obs/k8s 中的所有文件都能通过 istioctl validate,并在 /root/istio-obs/out/summary.txt 中用六行总结:TELEMETRY_TOTAL、MESH_SCOPED、NAMESPACE_SCOPED、WORKLOAD_SCOPED、SAMPLING_PERCENT、TRACE_HEADER_PROPAGATION。
前五行根据集群中的内容统计填写。最后一行回答本课程中最容易出错的事实:代理会创建 span、测量耗时并发送给收集器,但谁负责把入站请求的 trace header 复制到出站请求?请用一个单词作答。如果不这样做,每个服务会生成独立 trace,调用链就会断开。