许多组织正在实施内部开发平台(Internal Development Platform,IDP),将其作为一个集中式自助门户,让工程师能够完成日常任务,而不必依赖那些需要按请求履行任务的中心化专业团队。在很多方面,一个平台就像公司的循环系统,支撑着服务于各种用途的应用,无论是内部应用还是面向客户的应用。在本章中,我们将深入探讨:可观测性数据如何成为自助式平台的一部分——无论这些平台是通过 Backstage、商业化方案,还是自研方案来实现的。我们将重点说明:如何把可观测性集成进这些平台,平台如何帮助确保采集到正确的数据,以及可观测性数据应当如何通过这些平台提供给用户,而不是让用户自己在一整套可观测性工具中手动查找相关数据。
本章将涵盖以下几个主要主题:
- 什么是自助式平台?
- 如何把可观测性最佳实践与上下文“烘焙”进模板
- 如何把相关的可观测性数据带给最终用户
- 如何利用并监控 AI
- 超越 IDP:还有哪些地方值得推送可观测性数据
技术要求
在本章中,我们将讨论模型准确率测量。
要跟着本章中的示例一起操作,你需要预先安装好 Python 3 和 pip3:
www.python.org/downloads/
请参阅 Python 官方文档获取安装说明。
在 Linux 和 macOS 系统上,Homebrew 是一种很方便的安装方式:
brew.sh/
本章中 Python 脚本所需的一切内容,都位于我们的 GitHub 仓库中:
github.com/PacktPublis…
我们建议你在安装依赖或运行这些脚本之前,先使用 venv 或其他虚拟环境。
本章中我们将使用 Python 中的 ROUGE 和 BERT 评分库。要安装这些依赖,请在 GitHub 项目目录下运行以下命令:
pip install -r requirements.txt
如果你还没有配置好 Ollama 来运行 DeepSeek,那么现在正是时候:
ollama.com/download
要使用 DeepSeek 模型,请运行以下命令:
ollama run deepseek-r1
完成这些依赖安装之后,我们就开始吧。
什么是自助式平台?
自助式平台是一种 IDP,它使使用该平台的开发团队能够直接利用各种功能或增加能力,而无需等待平台工程团队替他们完成这些工作。
IDP 往往也被简称为“平台”,它为整个组织的软件开发、测试、可观测性、安全与运维提供一种集中化的“黄金路径(golden-path)”式自助服务,用来标准化这些工作方式。当一个 IDP 既具有明确倾向性(opinionated),又文档完善时,它就被视为黄金路径。黄金路径不仅仅定义组织标准;它还会去执行这些标准,并在最终用户偏离这些标准时,快速而有意义地给出反馈。
举例来说,如果我们看平台工程领域中的一项技术——Backstage,那么平台开发者拥有一项名为 plugin support 的能力,它允许工程师通过预先写好的社区插件,或者自己编写插件,来扩展平台能力。
backstage.io/plugins/
上面就有一系列由社区创建的插件,覆盖 API、日志等多个领域的能力。
还必须强调的一点是:一个门户本身,例如 Backstage,本身并不等于自助式平台。门户只是起点,它提供了对模板、软件目录以及类似资源的便捷访问。
采用 IDP 的软件交付方式,通常是一项业务层面的决策,目的是提升工程交付速度,同时解决若干业务问题,例如部署标准化、支持安全与合规,以及让可扩展性成为所开发软件的核心特性。当软件应用开发者需要高速推进时,自助式平台不仅能为他们提供快速开始交付产品所需的能力,也能为他们提供应对自身运维需求的灵活性。
IDP 的目标,是统一整个组织中那些高度重复性任务的完成方式。技术可以替代人,把原本分散在各个开发团队中的、重复且专职的发布团队与运维团队工作,整合到一个中心化平台中。这使得开发团队可以把时间集中在写软件本身,而不是交付机制上。
IDP 往往是在组织试图识别“如何让开发者更高效”这一过程中逐步形成的。通往 IDP 的旅程,第一步通常是把许多开发团队都在重复执行的功能,集中到一个中心团队中。
图 5.1:重复劳动和重复职能被迁移到一个中心化团队中
然而,这会引发可扩展性挑战,于是中心团队开始转型,最终,一个 IDP 诞生了。
图 5.2:平台工程团队创建了一个 IDP
IDP 会成为组织内部软件开发的中心枢纽。这也使得项目规范、代码风格指南的制定与执行成为可能,并可以利用模板技术与 linter 来支持这些质量关卡。
类似地,IDP 也能够支持并规定集中式日志系统、指标采集系统,以及可观测性数据应通过什么方式提供给平台用户。这些用户不仅包括高度技术化的工程人员,也可能代表业务和客户利益。
在第 3 章中,我们讨论过,可观测性的三大支柱——日志、指标和追踪——除了用于应用健康与运行时监控之外,还有更多用途。安全、合规或成本管理等业务洞察,同样依赖于可观测性数据的收集与分析。然而,若组织想真正实现这些收益,其部署必须在某种程度上具备一致性。对许多组织而言,标准化资源部署是一项关键策略,它有助于验证、安全与合规、成本控制,以及可观测性建设。在第 3 章中,我们还讨论了:为采集到的可观测性数据补充额外元数据有多重要。模板与样板代码不仅有助于确保这些数据存在,也能帮助标准化日志级别,并统一这些数据应在何处、以何种方式被发送出去。
所谓“可观测性数据的民主化(democratizing observability data)”,就是要让所有需要它的人都能获得它。这是通过确保软件系统能够产出必要的数据来实现的,同时还要把正确的数据推送到那些既可访问、又有访问控制的位置。
你可能会问:“IDP 怎么执行这些标准?”答案一如既往地是:“这要看情况。”当我们审视平台能力时,其中一项能力,就是对平台上工作负载预定义成熟度模型(maturity model)的执行。成熟度模型,就是一个工作负载要成为平台正式组成部分所必须满足的“完成定义(definition of done)”。它包括对黄金路径的遵循,而这又可能包含遵守已知最佳实践,例如日志级别、功能开关(feature flags)的使用,以及在可观测性栈中配置得当并具有正确路由的告警。平台及其平台工程团队可以通过若干技术性和社会技术性的流程来达成这一目标。其中一种确保采集到正确数据的流程,就是利用模板和样板代码,创建统一的数据采集与数据组织方式——换句话说,就是把围绕可观测性的最佳实践编码化。
首先,我们来看看用来实现标准化的技术有哪些。我们会从 Kubernetes、无服务器部署和传统 LAMP 栈部署的角度来看,按照从较简单到更复杂的顺序依次说明。
Kubernetes 标准化
Kubernetes API 允许通过 label 和 annotation 对对象进行增强。Label 允许根据 label 的值来选择对象,因此它实际上是一种识别相关 Kubernetes 对象的方式。使用 app.kubernetes.io 前缀时,label 可供集群所有用户使用;如果不带这个前缀,则可以只供私有用户使用。另一方面,annotation 用于存储非标识性元数据,任何有权限读取该对象的用户都能看到。在这里,“标识性”意味着该数据可用于搜索和对象分组。通常被认为是“标识性”的数据,也可以出现在 annotation 中,例如用来标识某个工作负载的 pager 升级路径;但由于这并不是 Kubernetes API 本身提供的功能,因此需要由额外的自动化工具来解析它。
一个 label 的例子是:
app.kubernetes.io/environment: staging
它可以用来过滤或搜索所有具有相同 label 的 Kubernetes 对象。
一个 annotation 则可能存放关于所属团队或其 Slack 频道的信息。额外的自动化可以使用这个 annotation,把数据或告警发送给相应工作负载,而 staging 这个 label 则帮助团队判断这份数据属于哪个环境。当然,还有许多其他最佳实践示例。下面这些表格应当被视为灵感来源,而不是完整清单:
| 示例 Label | 示例值 |
|---|---|
| Environment | production、staging、test、dev |
| Application | nginx、load、front-end |
| Version | 1.2.3、alpha、beta |
表 5.1:Kubernetes label 示例
| Annotation Key 示例 | 示例值 |
|---|---|
| Owning team | 一个 Slack handle 或频道 |
| Cost center | dev、sales、finance、LOB |
| App group | public facing websites、business apps、back office、dev tools |
| Priority | tier 1、tier 2、tier 3 |
| Alerting mode | off、daily、instant |
表 5.2:Kubernetes annotation 示例
正如我们在第 3 章“Context is king(上下文为王)”一节中所讨论的,这些 label 和 annotation 也可以被可观测性平台利用。例如,在日志被摄取时,可以为其补充 environment、team 或 priority 等信息。这样,开发团队就可以提出这样的请求:“给我看所有由我团队负责的 Tier 1 应用的日志。”再往前想一步,当可观测性平台检测到带有 Tier 1 标签的新错误日志时,它还可以根据 alerting mode 这个 annotation 采取行动。如果这个 annotation 被设置为 instant,就可以立刻向设为 owner 的团队发送消息。
接下来的问题是:我们如何在整个组织中一致地应用这些元数据?
对于基于 Kubernetes 的平台团队来说,一种简单的强制执行方式,是通过 CI/CD 流水线检查、准入控制器(admission controllers),或者在 Git 仓库中针对 Pull Request(PR)合并执行自定义检查。然而,这仍然要求人手工编写这些 label 和 annotation。这给人为错误留下了令人不安的空间,而且如果直到部署尝试时才暴露问题,还会引发挫败感并浪费大量时间。
因此,平台团队必须识别用户需求。从可观测性的角度看,开发者会受益于若干关键能力:
- 直接获取与其软件组件相关的关键可观测性仪表板访问链接,而不是每次都要在各类工具中重新学习如何找到这些仪表板
- 直接看到由可观测性工具捕获到的最新关键日志,而不是自己去寻找这些日志
- 看到该组件所有 SLO 及其当前值的总览,而不是自己去查找这些信息
- 获得自动化的性能热点分析,例如哪些请求最慢、哪些数据库调用最糟糕,而不是自己去分析日志、指标和追踪来找出这些模式。这里我们也经常谈到 自动化可观测性评分卡(automated observability scorecarding)
这正是模板技术能够产生重大影响的地方。通过提供模板或样板代码示例,平台能够在开发周期中支持数据质量与一致性。虽然模板与样板代码的用途远不止于确保可观测性最佳实践被一致采用,但这正是本章要聚焦的使用场景。
既然我们已经理解了为何要使用模板、其业务动机是什么,以及它为平台用户解决了哪些问题,那么接下来我们深入看看今天可用的技术。
Kustomize
Kustomize 是 Kubernetes 默认内置的一种配置管理工具,它允许用户自动生成配置。
例如,使用 Kustomize,你可以通过 annotation transformer 自动把 annotation 应用到 Kubernetes 资源上。这样做可以防止错误或因配置缺失而阻塞资源部署,因为配置应用逻辑就和它要修改的工作负载一起存在于集群中。在 Kustomize YAML 文件里,会定义用于识别和修改哪些工作负载的方法。
Kustomize 的强项,在于它能够遍历一个目录,并且以一种“外科手术式精确度”修改其中的内容。
样板代码与模板
样板代码通常会作为软件开发工具包(SDK)的一部分提供,通常表现为通用库或函数,开发者可以利用它们完成任务。用于构建 Kubernetes Operator 的 Operator SDK 就会生成相当多的样板代码,以帮助补齐一个 Operator 的基础功能。它还支持通过样板代码生成可观测性配置,暴露那些被视为最佳实践的指标。更多关于这些指标的信息,可参见:
book.kubebuilder.io/reference/m…
另一个样板代码的例子来自 OpenTelemetry(OTel)SDK。这个 SDK 提供了帮助开发者快速利用核心功能的能力。它支持诸如采样和把数据导出到长期存储方案等最佳实践,也包含了一些能力,可以为导出的可观测性数据附加元数据,例如主机名或某种层级信息。
如果你用 Backstage 作为你的 IDP,那么它原生就支持 software templates 这一核心能力:
backstage.io/docs/featur…
当你创建一个模板时,它会被存储到软件目录(software catalog)中,然后供 IDP 用户使用。
Prometheus 模板
在第 1 章中,我们已经讨论过 OTel 和 Prometheus 在指标采集方面的作用,因此这里不再深入展开。对 Prometheus 来说,一个值得注意的点是:与 OTel 通过 SDK 来落地最佳实践不同,Prometheus 利用模板把最佳实践编码进可观测性栈中:
prometheus.io/docs/promet…
Prometheus 的模板能力基于 Golang 的 Go templates 包。模板可用的位置不止一个,但最常见的用途很可能是在告警规则中。这样,你就可以为集群中的每一种 Kubernetes 资源类型写一次告警规则。例如,如果你想对 Pod 崩溃进行告警,你可以写出类似这样的规则:
annotations:
summary: "Pod {{ $labels.pod }} is crashing"
description: >
Pod {{ $labels.pod }} in namespace {{ $labels.namespace }}
is restarting frequently on node {{ $labels.node }}.
Prometheus 模板还可以用于把 label 和 annotation 元数据加入到指标与告警中,确保在查询和导出过程中完成合适的数据增强。通常,这会通过 Helm 与 Prometheus Operator 的结合来完成,这也把我们带到了最后一种 Kubernetes 模板技术。
Helm 模板
Helm 被称作 Kubernetes 的包管理器,它为管理 Kubernetes 部署提供了强大的工具能力。Helm 的一个核心特性就是模板引擎,它与 Prometheus 一样使用 Golang 模板,并额外在引擎中扩展了一些功能。
Helm 能够在模板中管理继承关系,这使得 Chart 既可以拥有自己的定制化内容,又能从父 Chart 中继承特性,让开发者既能利用模板的力量,又能在需要时保留灵活性。
非 Kubernetes 架构中的模板
虽然 Kubernetes 和云原生架构在可观测性领域占据很大比重,但其他软件架构同样能从相同原则以及部分相同技术中受益。例如,Prometheus 和各类 SDK 也可用于其他架构,而不只限于 Kubernetes。下面给出一个关于架构类型与支持模板或提供参考 / 样板代码的可观测性方案的简要参考表:
| 架构 | 平台与虚拟化层 | 可观测性工具 |
|---|---|---|
| Serverless | AWS、GCP、Azure、Red Hat | Prometheus、Grafana、CloudWatch、Azure Monitor |
| 虚拟机 | 云(AWS、GCP、Azure、Oracle、DigitalOcean、Heroku)与本地环境 | Dynatrace、Datadog、SolarWinds、Graphite、Nagios、Grafana、Prometheus、Sensu、Telegraf |
表 5.3:非 Kubernetes 架构中的可观测性技术
当你不在 Kubernetes 环境中时,这些可观测性工具的配置方式看起来也大致相同。你仍然会有一个持续集成与持续交付(CI/CD)流水线,例如 GitHub Actions,用来执行基础设施即代码工具以应用这些配置。主要云服务提供商都有带模板能力的基础设施即代码工具,例如 AWS 的 CloudFormation、GCP 的 Deployment Manager,以及 Azure 的 Azure Resource Manager。不过,这些工具都是厂商专属的,因此在多云环境或同时存在本地与云应用的环境中,缺乏灵活性。如果你的 IDP 不需要那么灵活,那么使用云厂商自家的工具并没有问题;但如果你确实需要灵活性,就得寻找更加中立的选项。
几种在混合架构中(即拥有两种或更多基础设施形态时)表现良好的常见基础设施即代码工具,包括 Ansible、OpenTofu 和 Terraform。这些工具既可以互相配合、与其他工具搭配使用,也可以独立使用。这些技术,以及那些云厂商自带的技术,同样可以支持 Kubernetes 部署,但这并不是它们的主要侧重点。
Terraform
Terraform 通常用于搭建基础设施。它管理的是底层资源,例如网络、虚拟机、存储和安全组。如果你的基础设施是通过 Terraform 管理的,那么与相应监控栈建立连接的配置,就会直接写进 Terraform 的 .tf 文件中。“基础设施即代码(infrastructure as code)”中的“代码”,意味着基础设施部署是可重复的,并且关于如何部署有一个单一事实来源。Terraform 代码按照模块(modules)进行组织,这允许代码库中出现小而独立的变更,有点类似 Helm Chart。模块支持变量,因此它们能够在同一组织中的多个团队间复用。模块并不等同于模板,因为 Terraform 会跟踪:哪些基础设施资源由哪个模块拥有。换句话说,它会维护自己管理对象的状态,并在采取任何动作之前,对比真实状态和期望状态。表面上看,这似乎和 Helm 没什么不同,但 Helm 依赖的是 Kubernetes 提供的状态管理,而并不自己维护状态记录;而这恰恰是 Terraform 的关键能力之一。和我们前面提到的许多工具一样,Terraform 周边也有庞大的社区,并且有大量社区构建的模块可以直接使用:
registry.terraform.io
OpenTofu
OpenTofu 是一个 CNCF Sandbox 项目,它源于 Terraform 在宣布许可证变更之后被 fork 出来的版本。OpenTofu 在继续演进的同时,力求保持与旧版本的向后兼容,并被设计成 Terraform 的“无缝替代品(drop-in replacement)”。当然,也可能存在某些用例,在这些情况下它们并不能做到一比一替代;随着两个项目各自独立演化,这种差异场景预计还会继续增加。不过,OpenTofu 依然是一个功能完备且生产可用的基础设施即代码工具。
Ansible
Ansible 可以用于虚拟机或 Kubernetes 的部署,但它真正的强项,在于其非常强大的配置能力。Ansible 会把任务运行在它所称的 hosts 上,也就是清单(inventory)中定义的基础设施。清单由用户提供,可以通过命令行直接传入,也可以通过 inventory 文件提供。Ansible 把工作组织成一种名为 playbook 的文件。Playbook 使用 YAML 编写,但支持 Jinja2 模板。Playbook 通过模板文件来使用模板能力。其模板功能不仅允许使用变量和 facts,还支持循环和条件判断。这让它在配置上更加灵活,也让高效的过程式操作成为可能。比如,只有当某个包尚未存在于主机上时才安装它,或者先获取主机的 IP,再在执行期间把它插入渲染后的模板中。
如何选择并使用你的模板技术
作为作者,我们能给你关于“如何决定采用哪种可观测性与模板技术”的最好建议是:做真正需要做的事;用你真正需要的,而不是你以为你需要的。 换句话说,去解决你当前真正面对的问题,而不是那些你以为将来会有、或者其实根本还不存在的问题。这并不是说你要彻底放弃灵活性,而是说:在决定要集成什么时,应优先考虑你已经明确知道自己需要的东西。
为了识别什么才是“真正需要的”,第一步要先识别你的用户是谁。记住,平台的目的,是给最终用户——也就是那些构建面向客户或面向内部应用的软件开发者——提供正确的一组能力。平台必须是为目标用途量身打造的,否则它就只不过是更多基础设施而已。关于“把平台当作产品来构建”的更多内容,可参考另一本 Packt 图书《Platform Engineering for Architects》,作者为 Max Koerbächer、Andreas Grabner 和 Hilliary Lipsig。
一旦识别出用户,你就可以开展用户访谈,并梳理关键用户旅程。这将帮助你识别:用户在哪些地方能够从模板和其他平台能力中受益,以及这些能力究竟应该是什么样子。
接下来,采取 crawl、walk、run 的方式。先从最直接的用例,以及与之匹配的技术开始。一旦它被投入使用,再逐步推进到更高级的场景。例如,你可以先从 Prometheus 模板开始,之后再转向用 Helm 来管理这些模板,最后再用 Argo CD 去管理它们的部署与变更。又或者,你也可以组合使用 Helm 与 Kustomize,以获得对平台用户来说最理想的体验。
基于我们的建议,你可以快速做出的几项决策如下:
- 如果你需要支持多架构的可观测性,选择 Prometheus + Grafana
- 如果你想在 Kubernetes 中管理具有复杂继承需求的部署,选择 Helm
- 如果你需要多云基础设施管理,选择 Terraform 或 OpenTofu
这些工具大多数并不是互斥的,而且由于存在开源选项,你并不会被供应商锁定。甚至在不同的可观测性 SaaS 供应商之间,你也可能采用混搭策略,或者在必要时从一个迁移到另一个。混搭、扩展、迁移,最终都取决于你如何管理平台所支持模板的生命周期。
如何管理模板的生命周期
如果说软件工程里有一条真理,那就是:当你以为自己已经做完了,其实还没有。总会有某个依赖需要为了安全补丁而升级,有某个缺陷需要修复,或者还有其他改动会在后面排队而来。尽管有些东西变化得非常慢,甚至极少变化,但拥有一套清晰的版本策略和变更管理仍然非常重要——换句话说,需要有一个生命周期(life cycle) 。
模板在功能上和代码没有区别;因此,模板生命周期管理其实是一个已经被解决的问题。像对待代码一样对待它们就行。像其他代码一样,它们需要有代码仓库,需要被照料:被存储、被文档化、被版本化、被跟踪。
在这个语境下,你的模板与任何其他工具类代码没有本质区别。并不存在一个“AWS Lambda 脚本博物馆”;事实上,试图复用这类高度专门化的代码通常也并没有太大价值。也许它可以在一个团队或一个社区内部共享,但它所处的环境总是在变化。一旦版本变化影响了它的可用性,它就必须被适配,或者被下线。
对于样板代码来说,这件事甚至更直接。发布一个新的 SDK 版本,然后让软件库机制的“魔法”替你和那些使用你 SDK 的工程师去完成工作就好了。
下面你会看到一份相当全面、但大概率仍不完整的清单,列出了模板生命周期管理的若干方式。
变更追踪
关于模板,最关键的一点是:当我们在行业里谈论 “* as code” 时,模板其实是这个概念的基石。模板通常被存放在 Git 这样的版本控制系统中,并像源代码一样被管理。
在日常管理层面,GitOps 的原则同样适用。如果一个模板被更新了,那么你的期望状态可能就不再等于真实状态;像 Argo CD 这样的 GitOps 工具,能够帮助确保你做出的更改被同步到正确的位置——例如,直接推送更新,或者让基于 pull 模式的机制察觉到这些变化并把它们拉进来。Push 更偏事件驱动,而 pull 更安全,因此二者在 “* as code” 的管理中都有各自的位置。
也存在一些基于自动化的方案,用于捕捉“基础模板”与“渲染后模板”之间,或者调用模板的仓库之间的差异。
其中一个这样的工具,就是 Renovate Bot。Renovate 可以用于接入 GitHub 仓库,捕捉依赖更新,并自动给项目发起 PR,提升依赖版本。需要继承这些变更的仓库,就是配置 Renovate 的目标仓库,而变更来源的仓库则会在数据源配置里列出。关于 Renovate 的工作原理细节,请参阅其官方文档:
docs.renovatebot.com/key-concept…
这种方式同样适用于模板变更、SDK 更新,或者样板代码变更。Renovate 自动化工具会检测模板版本(它们理应是有版本的)与渲染后模板当前所用版本之间的差异。一旦检测到漂移(drift),它就会发起 PR,让二者保持同步。使用 Renovate 的方式有很多,但我们建议从 GitHub Action 方式开始:
github.com/renovatebot…
如果你之前从未用过 Renovate,需要知道它支持多种数据源,最常见的一种就是 GitHub releases。假设你在发布 SDK,当一个新版本被发布且这个 release 出现在 GitHub 上时,Renovate Bot 就会为那些把它作为依赖的仓库检测到这一新版本,并在对应仓库中自动发起 PR,把依赖版本提升到新的 release。
Renovate 也是一个非常适合用来管理 Helm subchart 的工具;有关 Helm 3 兼容性以及未来 Helm 4 兼容性的最新信息,请参考官方文档:
docs.renovatebot.com/modules/man…
除了 GitOps 工具之外,如果你想在已使用的渲染模板中捕捉模板差异,还可以使用像 Kyverno 这样的策略引擎。这有助于捕捉那些试图通过非预期方式实施的更改,例如用户直接编辑资源。虽然平台及其能力的存在,本应减少人工干预或人工发起编辑的情况,但这些事情仍然会发生,而策略引擎可以帮助你彻底防止这种行为,或者根据配置,仅对这种事件发出通知。
使用情况跟踪
生命周期管理的另一关键部分,是知道何时该让某项资源进入弃用阶段。维护一个模板或样板代码片段都需要花费时间和金钱,因此你需要确认它是否仍然值得投资;或者,如果你是出于技术或业务原因想要删除某个模板,也需要确保这样做是安全的。
如果你删除的是样板代码,事情会简单一些,因为它已经被直接复制进最终环境中,可以继续独立存在,而不再需要上游维护与管理。它会变成实施团队自己的责任。但是,对于模板——尤其是 Helm Chart——来说,这就需要更谨慎的考量。
这种考量应当包括:这个 chart 是否还在被使用。但你要如何衡量使用情况,并判断模板是否仍在使用中呢?
如果你在使用 Helm,Komodor 开发了一个叫做 Helm Dashboard 的插件,它可以安装到 Kubernetes 集群上,并显示当前正在使用哪些 chart:
github.com/komodorio/h…
最后,如果你使用的是 Backstage 开发者门户作为你的 IDP,而你的模板也注册进了软件目录,那么 Backstage 本身就具备衡量这些模板使用情况的能力,从而帮助你做出数据驱动的决策。如果你发现某个模板从未被使用,或者极少被使用,那么它就是一个很适合进入弃用流程的对象。
平台的角色是什么?
由平台团队来与各类利益相关者沟通,并最终定义可观测性工具与数据采集的标准。虽然个别开发团队也可以自己使用模板,但平台作为一个统一层,负责确保一致性和易用性。通过提供模板和标准,并配合强制执行,平台能够确保最佳实践真正被遵循。模板和样板代码不仅帮助实现最佳实践的民主化;它们也能够鼓励并推动 可观测性驱动开发(observability-driven development) 。这并不是把责任“左移(shift left)”,而更像是把责任“向左扩展(expand left)”,为开发者尽可能早地在开发周期中引入可观测性提供框架。
这种强制执行与一致性,会平等地对待应用和工具的所有方面。从平台的视角看,一切都是应用(包括平台自身的一部分)。也就是说,你给面向客户应用施加的那些最佳实践,同样也必须施加到你的 AI 应用以及其他应用上。
成熟度模型的执行
在《Platform Engineering for Architects》这本书中,Hilliary 把成熟度模型定义为 IDP 的社会技术侧面。它们会非常主观,但本质上就是:一个应用想被接入到 IDP 时,所必须满足的最小标准集合。通过策略引擎和静态分析工具,平台可以塑造一个组织内部软件开发的整体面貌。
一个简单的、用于新应用接入 IDP 的成熟度模型,可能长得像一份待办清单:
- Kubernetes 对象定义已添加 label 或 annotation
- 已配置 Prometheus 告警
- 已定义 SLO
- 告警已被路由到正确的服务
- 已提交 Grafana 仪表板配置
- 已编写并审批故障排查指南
你当然还可以往这份清单里添加其他任务,但它可以作为一个起点,帮助你构建 IDP 所定义、使用并执行的标准。
平台是统一层
既然平台提供黄金路径,它也必须成为所有“真相”的中心枢纽。这包括应用定义、访问控制,以及可观测性数据。平台必须提供明确的价值,让用户愿意不断回到这里,并乐于使用它。集中式可观测性只是 IDP 所需要具备能力的一个例子;另一个例子则是访问控制。
访问控制是平台能力中最关键的部分之一。数据的民主化绝不能以牺牲安全和合规要求为代价。平台必须对它存储并向用户提供的可观测性数据,定义并执行最小权限原则(least privilege) 。
你集成到平台中的大多数可观测性工具都支持访问控制,通常称为 RBAC(基于角色的访问控制) 或 IAM(身份与访问管理) 。在构建一个具备强大可观测性能力的 IDP 场景下,你不会希望在多个地方分别管理访问控制。你必须选择那些能够与现有访问控制方案集成的技术。
Grafana 就是一个很好的例子。虽然你可以直接管理它的 RBAC,但它支持通过应用插件接入现有访问控制系统,例如通过 GitHub、GitLab、Okta、Google 等配置 OAuth。Grafana 还有额外的灵活性:它可以连接到多个数据源,例如数据库、单台机器或者对象存储,从而使其能够在一个 IDP 中充当所有数据的统一入口。它还是架构无关的,因此即便你有多种不同架构类型的系统,也无需准备多个数据可视化工具。如果你不希望平台对可观测性数据被发送到哪里、或数据在存储时如何组织持强烈意见,那么 Grafana 的灵活性会让它成为一个很好的选择,因为它允许团队自助配置自己所需的 Grafana 配置和插件。只要你能够保证 Grafana 实例本身的可用性,就能在不牺牲平台核心能力的前提下,给开发团队提供他们所需的灵活性。
Grafana 还支持使用预制模板仪表板,以及用户自定义仪表板。下面这个例子中,我们很快地用一个模板,把从开发者笔记本采集到的可观测性数据可视化了出来:
图 5.3:Grafana 仪表板及其数据
这些配置本身也是“代码形式”存储的——例如,仪表板通常是 JSON——并且能够像其他模板技术一样,被纳入软件开发生命周期中使用。
完整 JSON 太长,这里不便展示,但下面是一个众多免费模板之一中的片段:
"__inputs": [
{
"name": "DS_PROMETHEUS",
"label": "Prometheus",
"description": "",
"type": "datasource",
"pluginId": "prometheus",
"pluginName": "Prometheus"
},
{
"name": "DS_LOKI",
"label": "Loki",
"description": "",
"type": "datasource",
"pluginId": "loki",
"pluginName": "Loki"
}
],
"__requires": [
{
"type": "panel",
"id": "alertlist",
"name": "Alert list",
"version": ""
},
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "7.3.5"
}
]
使用或构建合适的工具,让正确的数据能够被提供出来——但仅提供给正确的人或系统——将是你可观测性策略的核心。任何 AIOps 的实现都必须经过适当的访问控制门禁。我们在后续章节中还会继续讨论这一点。既然我们已经建立起:平台是如何标准化可观测性数据采集的,那么接下来我们来看看 AI 如何增强平台的执行能力,以及为什么平台必须以与其他应用相同的可观测性严谨度来对待 AI 工作负载。
AI 集成到 IDP 中的用例
AIOps 在哪些地方能够帮助加速平台成长,并把任务从平台团队身上卸下来?虽然我们不可能把所有建议都列尽,但确实有几个地方,我们认为 AIOps 能提供帮助,而且不只是充当一个面向用户的聊天机器人。
第一类场景,是作为代码质量执行链条的一部分。虽然传统的 linter,甚至自定义 GitHub Action,已经能在确保样板代码与平台对可观测性处理要求方面做很多事情,但它们的灵活性终究有限。
甚至还有像 SonarQube 这样的产品,帮助平台工程团队确保质量门被满足。但任何自动化一旦开始维护,就会暴露出相同的问题:自动化的能力上限,永远取决于你为它写了什么,它能做的事情也永远受限于你投入了多少时间去实现。
而这,恰恰就是 AI 开始大放异彩的地方。正如我们在第 2 章中讨论过的,基于 LLM 的 AI 工具限制要少得多,它不仅能够遵循提供给它的一组指令,还能够进行推理。
例如,AI 可以充当多架构之间的桥梁。你几乎不可能对一个基于虚拟机的环境和一个 Kubernetes 环境施加完全相同的可观测性标准,因此 AI 可以在它们之间充当桥梁,帮助标准化数据;或者在一些标准工具不那么容易适配的地方,充当采样或脱敏的工具。
数据的时效性和质量都很重要,但没有任何工具是完美的。在某些配置下,Prometheus 可能会崩溃并丢失数据。像 CloudWatch 这样的公认数据源,也因其对日志时间有严格规则而著称,如果配置中没有正确处理时区,就可能导致摄取问题或数据缺失。Serverless、低代码和无代码环境下的指标通常覆盖面较浅,而且表达不清。更不用说,有时还会因为完全超出任何人控制范围的事件——例如自然灾害——而导致数据丢失。
在这些情况下,AI 可能非常适合处理不可靠或不完整的数据,但前提是它必须被提供数据和数据源,并配上明确的约束或指令。
指令文件:给你的 AI 方向
指令是通过 instruction files(指令文件) 提供的,而让 AI 系统获取指令文件的方式有很多。例如,Linux Foundation 内部有一个叫作 agents.md 的项目,它是一项围绕 Markdown 文件标准化的努力:把这些文件加到代码库里,用来提供对任何人都可能有用、但尤其是为了帮助 AI agent 在代码库中成功工作而设计的说明。指令文件并没有 universally expected fields(全球统一预期字段),但确实有写好它的一些最佳实践。
第一,结构化你的文件。按主题拆分内容,并利用 Markdown 格式把说明分成不同段落。例如,构建说明和测试说明就应当是不同的标题。
第二,尽可能详细且具体。就像给人类写 README 一样,细节越多,AI 需要做的猜测就越少。如果你希望 agent 扮演某个特定角色、执行某个任务或实现某个目标,一定要写进去;这种具体性会帮助 AI 给出更好的回应。如果你希望它输出某种特定格式,例如我们在“数据归一化”用例里提到的那种,那么这种输出格式也应出现在指令文件中。凡是对 AI 来说重要的信息,包括你的预期,都应当写进指令文件中。
第三,让 AI 知道它能做什么、不能做什么。如果它可以访问互联网,那就写明;如果它不可以访问互联网,那也写进说明中。你的指令文件,其实是 AI 系统治理的第一道防线。你可以通过这些文件让它意识到安全与合规要求,以及它可以访问哪些数据源。
最后,当你把指令构建出来之后,你还需要测试并调整它们。同时,随着新的信息出现,只要 AI agent 需要知道,就应该及时更新这些指令。
例如,一个面向可观测性的 agent 指令文件,大致可能像下面这样一个非常粗略的例子:
# AGENTS.md file
## Agent Persona
- You are a Site Reliability Engineer in a Kubernetes environment.
## Agent Constraints
- We are a SOC 2 Compliant Company
- You do not have external internet connectivity
## Desired Response Format
- Place investigation details into Jira
- Remove any data that could be PII
- When a solution is identified, open a pull request in GitHub following any relevant contributor guidelines.
## Data Sources
- There is a Prometheus instance on the cluster
- There are logs being collected with Fluentbit
- OpenTelemetry is also in use
## Important Cluster Information
- All software has been installed with Helm charts
- All observability data should have labels and other metadata.
有了合适的指令,AI agent 可以更高效、更快地开始工作。而且,通过预先设定它所工作的系统预期,它就能够开始在数据中识别趋势和模式。进一步说,当你处在一个可观测性数据不完整的场景中时,AI 系统还可以推断:是否有理由怀疑问题已经超出了监控栈本身。
不过,设定你自己的预期也同样重要。如果数据没有足够强的基数(cardinality),比如没有 label 和元数据,而 AI agent 或 assistant 又从未见过这个系统,那么它推断的质量就会下降;但一旦这两点都满足了,而且 AI 系统也被调优过,那么它对于识别和调查大规模问题的判断,通常就可以被信任。我们会在第 9 章更深入地讨论微调及其带来的收益。
一个经过良好调优的 AI 系统,会为用户带来洞察,而且这些洞察往往是在增强或扩展他们已有的专业知识。例如,AI 可以帮助完成以下事情:
- 识别最佳实践
- 发现配置错误
- 从预生产阶段一直到运行时进行数据采集
- 成本管理
不过,即便调优得当,我们也不能让 AI 完全无人监管地运行。这正是平台必须平等对待 AI、并像对任何其他应用一样施加相同安全与访问控制标准的原因。AI 系统必须是可观测的,并且必须接受治理;这是我们证明系统运行安全、并且满足预期的方式。既然我们已经考察了平台内部的可观测性长什么样,那么接下来就把它应用到一个 AIOps 实现场景中。
一个可靠的 AI,必须是一个可观测的 AI
一个好的 IDP,首先要对自己成为“零号客户(customer zero)”。平台所提供的黄金路径,对平台自身这个产品也必须同样可实现。AIOps 正是“零号客户模型”的一部分。它会像其他任何东西一样,被开发、被部署、被监控,其目标是成为平台提供给工程团队的另一项能力。使用并受益于 AI agent 的能力,必须成为这种集中式体验的一部分。
若一个 AIOps 实现要真正有用,它就必须是准确的。衡量 AI 的成功与准确性,是一个新挑战,但我们之前为可观测性讨论过的概念依然适用。围绕 AI 性能度量确实出现了一些新标准,也有新的工具,但归根结底,这些专门工具所复用的,仍然是最佳实践。
在 Kubernetes 环境中,AI 工作负载说到底也只是 Pod。因此,适用于 Kubernetes 工作负载的那些常规标准,同样适用。你仍然需要关注 Pod 健康,例如 CPU 和内存使用情况,也要检查崩溃、重启及其他通用健康问题。和大多数其他应用一样,你的 AI 应用也会有性能指标。其中最关键的是请求与响应时延,但准确率的测量同样重要。
对于 AI 工作负载来说,以下这些指标对于理解你的 AI 是否正在为组织带来正确价值至关重要:
-
推理速度(Inference speed)
-
首 token 时间(Time to first token,TTFT)
-
系统吞吐量:
- 每秒事务数(Transactions per second,TPS)
- 每分钟操作数(Operations per minute,OPM)
-
响应准确率(Response accuracy)
-
请求与响应 token 数
-
护栏(guardrail)执行次数(很多 AI 系统允许你指定 guardrail,例如某些特定词语被使用时如何处理;这个指标告诉你 prompt 违反 guardrail 的频率)
-
PII 泄露
知道我们需要哪些指标当然很好,但我们要如何确定这些指标应当是什么样的测量值?而一旦有了这些测量值,又该如何确保 AI 的表现符合预期呢?
一个适合用来基准测试 AI 系统的优秀开源工具,是 GuideLLM:
github.com/vllm-projec…
它是 vLLM 项目的一部分。GuideLLM 是一个用于评估语言模型在真实工作负载与配置下表现的平台。它既可以使用真实数据集,也可以使用合成生成的多模态数据集,并在把 LLM 部署进生产环境之前,为你提供关于性能、效率与可靠性的清晰洞察。GuideLLM 是一个很好的起点,可用于基准测试和优化你的 LLM,因为它同样聚焦于前面提到的关键指标:TTFT、token 间延迟(inter-token latency,ITL)、输出分布以及数据集驱动的差异。尽管行业中已经开始浮现一些关于“好的性能指标应当是什么样”的共识,但并不存在一个放之四海而皆准的度量标准。不同模型面对同一任务时表现不同,而同一个模型在面对复杂度不同的任务时表现也会不同。这是因为不同的 prompt 本来就可能合理地需要更长响应时间、更多 token,以及与最终用户更多次的交互。时间和 token 数,会成为你在模型处理 prompt 时需要监控的两个性能与可靠性维度;建立基线并捕捉偏离基线的情况,将是模型可观测性的关键。最好的做法,是用尽可能接近生产的测试去对模型进行基准测试,并用真实使用场景的数据去对照这些基准。通过定期人工评审,你会随着时间推移逐步知道:对你的系统来说,“好”究竟长什么样;最终,你将能够设置并调优告警。一种良好实践是,每周开一次运维评审会,用来帮助定义目标和告警,而不是立刻开始告警、结果制造出更多噪声信号。
无论你使用的是 GuideLLM 还是其他工具与框架,要衡量你是否持续维持了基准测试出来的性能,会更难,而且这要求你在 AI 系统设计上多动一些脑筋。技术上讲,为你的 AI 提供底层能力的 LLM,通常会通过一个 API 来被访问。从功能上说,一旦你的 AI agent 是通过 API 被使用的,它就不再是“独一无二的特殊对象”,而会开始和你环境中的其他所有工作负载一样。TTFT 也就变成了一个简单数学问题:用请求发送到 API 的时间戳,减去第一个 token 返回的时间。这完全可以通过 Prometheus 规则或 OTel 来测量。
你当然可以自己构建一套定制 API 和 AI serving engine,而 OpenAI 的开发者文档也提供了很好的参考来帮助做到这一点。但也有开源方案,能让你省去这些麻烦。如果你使用的是 vLLM 来为你的 AI agent 提供服务,那么它在框架里已经预定义了许多可直接使用的指标:
docs.vllm.ai/en/latest/u…
当然,你也始终可以根据需要自行扩展和定义新指标。
响应准确率的测量策略
准确率之所以难测,在于响应需要被持续、定期地评估;这不是一次性工作。随着训练数据或 AI 系统可访问的其他数据持续增加,系统在初始训练后与之后某个时间点给出的响应之间,合理地会出现漂移;但这种漂移应当仅仅来源于事实发生了变化,而不应来源于错误。我们总结出了以下几种测量响应准确率的实用策略。
自然语言处理评分
在自然语言处理领域,用于衡量准确率的评分体系有很多:BLEU、ROUGE、BERT、METEOR、Perplexity,仅仅列举其中几个,而且未来还可能出现更多。最广为人知的是 BLEU 和 ROUGE 体系。BLEU 评分系统通常用于那种输出必须高度精确的场景。如果你注意到某个 AI 聊天机器人会用很多词来传达一个意思,但几乎不留歧义空间,那它很可能在 BLEU 体系上表现不错。
ROUGE 更强调召回(recall),适合用在那些“总结”和“召回”比“生成输出”更关键的场景中。对于那些需要解析并提炼大量可观测性数据的 agent 来说,它是一个很好的评分体系。
在我们前面提到的其他评分体系中,与 AIOps 最相关的是 BERT。它的评分思路与 ROUGE 有点类似,但计算成本更高,而且可能给出更准确的分数。关于 BLEU、ROUGE 和 BERT 三种体系评分计算方式的深入文章链接,我们放在本章的“延伸阅读”部分,这里就不展开数学细节了。
出于可观测性和 AIOps 的目的,我们建议在你愿意采用某种评分体系时,优先考虑使用 ROUGE 或 BERT 来评估模型表现。这两种评分模型的区间都是 0 到 1。对于生产系统来说,大致而言:
- BERT 评分在 0.8 左右可视为达标
- ROUGE 评分在 0.4 左右可视为达标
接下来,我们用 Python 脚本,对自第 2 章以来一直在使用的 DeepSeek R1 模型做一次评分。
在理想环境中,我们会有一个完全自动化的评估闭环。这个闭环大致会包括如下步骤:
- 一个告警生成系统,例如 Prometheus 或 Alertmanager,把一个 prompt 发给 LLM 的 API,让它去调查相关日志。LLM 把日志文件存储到一个评分自动化可访问的位置。
- LLM 把调查结果及其响应存储到评分自动化可访问的某个地方。
- 通过 webhook 触发器或 cron job 运行评分脚本文件。
- 评分自动化把分数输出为一个指标。
为了让本章示例更易于使用,我们对示例数据和 LLM 评估做了硬编码。DeepSeek 的输出,是先把日志复制粘贴到会话中,再附上如下 prompt 获得的:
From the log, tell me as concisely as possible about the node statuses.
这就得到了接下来用于评估脚本中的文本。我们的第一个脚本用于查看 ROUGE 分数:
# Import evaluate
import evaluate
# ROUGE
rouge_calculation = evaluate.load("rouge")
# Example data to score. It should be plain text (Not tokens)
reference_log = ["level=info ts=2026-02-10T06:51:31.353816972Z caller=controller.go:195 component=kubelet_endpoints kubelet_object=kube-system/kube-prometheus-stack-1754-kubelet msg="Node Ready condition is Unknown" node=kind-worker3"]
model_summary = ["On Feb 10, 06:51:31Z, the nodes `kind-worker2` and `kind-worker3` had their "Ready" condition set to "Unknown"."]
# ROUGE expects plain text inputs
rouge_score = rouge_calculation.compute(predictions=model_summary, references=reference_log)
# Access ROUGE scores (no need for indexing into the result)
print(f"ROUGE-1 F1 Score: {rouge_score['rouge1']:.2f}")
print(f"ROUGE-L F1 Score: {rouge_score['rougeL']:.2f}")
运行该脚本后,会得到如下分数:
$ python rouge_score_example.py
ROUGE-1 F1 Score: 0.22
ROUGE-L F1 Score: 0.15
对于 ROUGE-1 和 ROUGE-L 来说,这两个分数都可以被视为偏低。理想情况下,ROUGE-1 应接近 0.40,而 ROUGE-L 应接近 0.30。不过,DeepSeek 给出的确实是 prompt 所要求的内容。这种“客观评分”与“主观满意度”之间的差异,使得 ROUGE 成为一个有用、但不能被视为权威的指标。 如果 prompt 本身是“面向评分体系”的,分数很可能会更高。因此,ROUGE 分数应与其他评估指标结合使用,以判断模型准确率;因为它的客观性无法覆盖终端用户的主观性。
接下来,我们看一个 BERT 评分脚本,看看 DeepSeek 的表现如何。我们运行如下脚本:
from bert_score import score
# Log and LLM Summary for evaluation
reference_log = ["level=info ts=2026-02-10T06:51:31.353816972Z caller=controller.go:195 component=kubelet_endpoints kubelet_object=kube-system/kube-prometheus-stack-1754-kubelet msg="Node Ready condition is Unknown" node=kind-worker3"]
model_summary = ["On Feb 10, 06:51:31Z, the nodes `kind-worker2` and `kind-worker3` had their "Ready" condition set to "Unknown"."]
P, R, F1 = score(reference_log, model_summary, model_type='distilbert-base-uncased', verbose=False)
print(f"BERTScore F1: {F1[0]:.4f}")
print(f"BERTScore Precision: {P[0]:.4f}")
print(f"BERTScore Recall: {R[0]:.4f}")
它会返回如下结果:
BERTScore F1: 0.7424
BERTScore Precision: 0.7168
BERTScore Recall: 0.7698
BERT 评分体系对 DeepSeek 的回答评估得更准确一些,但运行时间确实比 ROUGE 稍长。若要在生产场景中视为理想,这些分数通常需要达到 0.8 以上。不过,要记住,给 DeepSeek 的 prompt 强调的是“简洁”,也就是说,它是 token-aware 的,而未必是 scoring-system-aware 的。
无论你最终决定采用哪种评分体系——如果决定采用的话——只要把评分与人工审查结合起来,再为数据加入一个新的维度(例如用户满意度),就有可能更准确地理解模型表现。随着时间推移,那些被认为“好的回答”及其平均得分可以被聚合起来,而这些分数最终可以变成某种阈值。如果某次回答的分数低于用户通常感到满意的水平,那么这时候就应该触发人工复审。你可以让自动化系统把这些分数输出成指标,并利用你的可观测性栈来处理偏离。一段时间之后,你也许会发现:偶尔出现的低分其实并不是问题,但你仍希望保持原来的阈值。这种情况下,你就不一定要为每一个低分回答都叫来人工,而可以设定:只有当过去一小时内有 10% 的回答低于阈值时,才真正发告警。
下面这个简化架构图可以帮助你形成直观印象:
图 5.4:把分数转化成告警
归根结底,就像任何被观测软件系统里的任何告警一样,告警的调优和行动计划总会带有主观性。你在自己的组织中所做的事情,未必与别人一样,也未必完全符合某些新兴最佳实践,但这没有关系。只要你在定期评估并持续迭代,以确保你所做的事情确实对团队有效,那么你就做对了。
相比单纯的性能指标,测量响应准确率在执行层面上更具挑战。必须有某个东西或某个人来决定:某个响应是否准确。而这种判断方式,很大程度上取决于响应是如何被交付出去的。例如,如果一个响应是发到 Slack 团队频道里、用于提醒系统中断,而人工调查之后发现它其实是误报,那么这就是一种人工审查的例子,并且很可能随后会伴随一次对 AI 系统的缺陷修复。在那种“审查来自人类”的场景里,比如 PR 审查,任何现有的准确率测量流程,大概率都需要一定的手工环节,或者至少要搭配一些聪明的自动化,例如给那些“不准确的 PR”打标签,然后定期运行自动化统计这些标签数量。
不过,如果预期输出本身就是一个针对 GitHub 仓库的 PR,那么你就可以利用自动化质量门,帮助确保 AI 输出至少具备某种最低限度的准确性,同时也可以使用同样的“打标签”方法来衡量何时出了问题。然后你还可以利用 GitHub Actions 这类 CI 工具,把遥测数据发送到可观测性栈中,从而捕捉模型表现的趋势。说到底,在自动化世界里,想象力就是边界;真正限制你的,只会是你自己的创造力,以及你有多少时间去构建那些衡量输出准确率的解决方案。
最后,第三种选择,是让另一个 AI agent 来判断这个响应是否准确。多 agent AI 编排会引入额外的技术和财务影响。从投入程度角度看,工具体系正在演化,如今已经越来越容易支持这样一种模式:一个独立的 AI agent 充当另一个 agent 输出的验证者、评分者或评估者。你可以把它看成一位老师去批改学生作业。老师会对哪些地方准确、哪些地方不准确给出反馈。就像人类会评估别人的工作一样,这一概念同样可以应用于 AI agent,其结果就是:这些 agent 理论上应当不断变得更好。
我们会在第 9 章中更进一步探讨这一点,因为确保响应准确,将有助于建立用户对 AIOps 系统的信任。
本章中我们将讨论的最后一个 AI 可观测性指标,是 PII 泄露检测。这个指标与 guardrail 密切相关,而 guardrail 我们会在第 9 章深入讨论,但它在当前也值得单独拿出来讲一讲。
PII,即 personally identifiable information(个人可识别信息),是一类在软件系统日常运行中可能会被收集到的受保护信息。前面我们提到过,你很可能会把 AI agent 放在一个 API 后面。而实现这一点的方法,是通过一个叫做 API Gateway 的微服务。这个中间件层会负责把所有流量路由到你的 agent,或者在多 agent 场景下,路由到多个 agent。如果 agent 需要请求日志,那么 gateway 会充当中间人:它负责处理这个请求,并真正执行日志获取。API Gateway 的一个功能,就是验证来自日志系统的响应是否包含潜在 PII;如果有,就在日志正文中把它剔除掉。反向流也同样成立:如果 agent somehow 知道了 PII,并试图把它返回出来,那么 gateway 就能够在把响应发给终端用户之前,拦截并潜在地对这些 PII 进行脱敏。像 LiteLLM 就是这样一种 gateway 的例子:
docs.litellm.ai/
它是一个专为 LLM 构建的 API Gateway,并且能够与 Microsoft 的另一个开源项目 Presidio 集成:
github.com/microsoft/p…
通过这种方式,在数据进入模型之前就可以先屏蔽 PII。LiteLLM 使用声明式配置结构,因此你可以很方便地通过修改 YAML 来实现它。
下面是一个示例:
guardrails:
- guardrail_name: "presidio-block-guard"
litellm_params:
guardrail: presidio
mode: "pre_call"
pii_entities_config:
US_SSN: "BLOCK" # Block any request with SSN
CREDIT_CARD: "BLOCK" # Block credit card numbers
MEDICAL_LICENSE: "BLOCK"
pii_entities_config 应当包含所有你已知、或有理由怀疑可能出现在系统中的 PII 类型。LiteLLM 还允许你检测:这个 guardrail 是否真的被触发过。Guardrail 执行是一个重要指标,但它属于整体安全姿态的一部分,因此我们会在第 9 章中更详细讨论。现在,让我们来看看,开发者预期会如何在一个 IDP 中与 AIOps 交互。
AI 是 IDP 的一种能力
集中式平台会成为集中式数据枢纽,但它的预期定位仍然是:服务于具体团队及其具体需求。平台应当以一种便于消费的方式提供数据,而 AI 则应帮助团队尽可能高效地利用这些数据。IDP 同时还必须保护对这些数据的访问,并管理与这些数据相关的成本。在这里,AI 尤其能够帮助处理快速增长的可观测性数据量。
你有多少次真的需要查看“六个月前某个星期二凌晨 2:36 的那一份可观测性数据”?大概率一次都没有。但你可能确实需要这六个月期间的数据趋势。你并不需要保存每一个指标的每一次实例。利用 AI 去做基础数据采样,可以帮助把存储下来的指标浓缩成一段时间内的均值、中位数和众数,而这个时间粒度完全可以按照你的组织需求来定。通过只保留我们真正需要的数据,并把那些我们已经不再需要的原始数据中有价值的信息留下来,我们就可以缩小数据存储规模,而不用担心会错失某个关键的信息片段。虽然我们并不建议让 AI 自己去删除那些不再需要存储的数据,但如果确保那些需要采样的最终用户——无论是定期采样还是按需采样——都能使用它,并且能够把采样输出写入永久存储位置,那么平台团队就可以利用 TTL 或其他数据生命周期管理功能,让数据库自动清理那些过期且价值较低的旧数据。
此外,如果我们怀疑日志或指标中可能包含 PII,让一个 AI agent 去遍历这些数据、对日志进行采样,并只提取其中安全且具有统计显著性的部分,会比让一个人类去编写脚本做同样事情更快。允许 AI 访问、解析并呈现可观测性数据,从而让这些数据对开发团队变得可执行,本质上就是最适合其能力的任务之一。对于一个必须提供统一展示层、并且要加载足够快,好让最终用户愿意真正使用它的 IDP 来说,这会带来非常明确的价值。
不过,虽然通过门户提供洞察很重要,能让工程师快速获取与自己相关的全部数据,但大多数软件工程师其实并不想把大部分时间花在 Backstage 或某个自建自助门户里。软件工程团队更希望把时间花在创建新服务和新功能上。他们大部分时间会用于分析软件需求工单、讨论最佳实现方式、编写代码来实现这些功能需求、提交改动,以及审查 PR。因此,在最后一节中,我们将超越 IDP 本身,进一步探讨:哪些类型的可观测性数据,应当被推送到开发者日常真正使用的其他工具中。
超越 IDP:还有哪些地方值得推送可观测性数据
一个成功的 IDP,应当在用户所在之处与他们相遇。这意味着:要以不会打断用户工作流、而是能够融入其工作流的方式,把经过脱敏且可执行的数据交付给消费者。换句话说,平台用户把大部分时间花在哪里,数据就应该被送到哪里。
这可能意味着:通过 webhook 把数据推送到 Slack;为告警自动打开 Jira 工单,并把相关数据加入描述中;甚至把某些信息写入 Git。历史基准记录、测试通过 / 失败历史,以及类似数据,都可以写进 Git,并在未来进行对比。
下面是一些示例,说明你的可观测性工具(包括 AI)可能会把什么数据推送到哪里,以及谁可能会消费这些数据:
- 对于开发者:把新检测到的关键日志直接推送给开发者,可以是在 IDE 中、开发门户(Backstage)中、PR 上,或者通过每日一条的 Slack 消息发送,而不是要求他们再去学一个新工具,只为了查找与自己相关的数据
- 对于产品负责人:把功能采纳指标推送到当初定义该功能的 Jira 工单中,而不是要求业务方自己从各种可观测性工具或仪表板里把这些数据找出来
- 对于性能工程师:把一次最近负载测试中基于测试期间采集到的可观测性数据所提炼出的关键发现,直接提交到触发这次测试的 CI 流水线所依赖的代码库中
在你的具体行业中,可能还存在其他最佳实践;如果你使用的是某个可观测性供应商,它也可能有额外的推荐最佳实践。
理解你的安全和合规要求,对于判断哪些地方可以推送可观测性数据、哪些地方不应当推送它,至关重要。可观测性的本质,是把数据转化为信息。而这些信息往往是“按需知晓(need-to-know)”的,因此你要确保以正确方式,把正确的信息送到正确的人手中。例如,知道某个用户在与系统交互时遇到了一个 500 错误,这很重要;但这个用户是谁,对于修复问题并不重要,因此它绝不应该出现在任何被采集和存储的数据中。
你正在使用的任何 AI 系统,也必须理解并遵守这一原则,这正是为什么把这类要求写进指令文件至关重要。我们会在第 9 章进一步讨论如何实施 guardrail,以及如何保护你的 AI 系统安全。
请记住,AI 依赖于指令和输入,因此也同样受制于“垃圾进,垃圾出(GIGO)”原则。通过确保你的数据结构清晰、表达明确,你就能够避免一长串令人不愉快的后果。每当一个 AI 增强型工具泄露了用户数据时,请想想以下问题:
- 它完成工作时,真的有必要访问用户数据吗?
- 那些数据能否被混淆处理(obfuscate)?
- 那些数据是否本可以被组织得更清晰,并解释得更清楚?
合理的实现,应当从理解所呈现的信息、相关风险,以及其目标去向的性质开始。只要有可能,就应当从最小可能的访问权限与最少可能的数据量开始,只有在确有必要时才逐步增加。不要让“错失恐惧(fear of missing out)”最终演变成你组织里的一起安全事故。
小结
在本章中,我们看到,IDP 如何减少传统生产力瓶颈,使工程师能够更高效地工作。一个高效的 IDP 可以成为组织内部软件开发的枢纽,既是事实来源中心,也同时代表工程、业务和消费者的利益。
一个有 AI 加持的 IDP,能够通过推理为可观测性提供支持,而这些推理若没有 AI,就常常需要大量人工小时去解析数据和编写自动化逻辑才能完成。在下一章中,我们将深入探讨这些以及其他 AIOps 用例。
延伸阅读
自然语言处理评分: