本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

Kubernetes 1.29 中的上下文日志记录:更好的故障排除和增强的日志记录

我们代表结构化日志工作组SIG Instrumentation,欣然宣布在 Kubernetes v1.24 中引入的上下文日志记录功能现已成功迁移到两个组件(kube-scheduler 和 kube-controller-manager)以及一些目录中。此功能旨在提供更有用的日志,以更好地排查 Kubernetes 问题,并赋能开发者增强 Kubernetes。

什么是上下文日志记录?

上下文日志记录基于 go-logr API。其核心思想是,库从其调用者处接收一个 Logger 实例,并使用该实例进行日志记录,而不是访问全局 Logger。二进制文件决定日志记录的实现,而不是库。go-logr API 围绕结构化日志记录设计,并支持向 Logger 附加额外信息。

这使得额外的用例成为可能

  • 调用者可以向 Logger 附加额外信息

    • WithName 添加一个 "logger" 键,其值为用点连接的名称
    • WithValues 添加键/值对

    当将此扩展的 Logger 传入一个函数,并且该函数使用它而不是全局 Logger 时,附加信息就会包含在所有日志条目中,而无需修改生成日志条目的代码。这在高度并行的应用程序中非常有用,因为不同操作的输出会交错在一起,从而难以识别某个特定操作的所有日志条目。

  • 在运行单元测试时,日志输出可以与当前测试关联。然后,当测试失败时,go test 只会显示失败测试的日志输出。该输出默认也可以更详细,因为它不会在测试成功时显示。测试可以并行运行而不会使其输出交错。

上下文日志记录的一个设计决策是允许将 Logger 作为值附加到 `context.Context`。由于 Logger 封装了调用的预期日志记录的所有方面,它上下文的**一部分**,而不仅仅是**使用**它。一个实际的优势是许多 API 已经有了一个 `ctx` 参数或者可以添加一个。这提供了额外的优势,比如能够摆脱函数内部的 `context.TODO()` 调用。

如何使用它

上下文日志记录功能从 Kubernetes v1.24 开始为 Alpha 阶段,因此需要启用 `ContextualLogging` 特性门控。如果你想在 Alpha 阶段测试此功能,你需要在 `kube-controller-manager` 和 `kube-scheduler` 上启用此特性门控。

对于 `kube-scheduler`,有一点需要注意,除了启用 `ContextualLogging` 特性门控外,instrumentation 还依赖于日志详细级别。为了避免因 1.29 版本为上下文日志记录添加的日志 instrumentation 而减慢调度器,谨慎选择何时添加额外信息非常重要。

  • 在 `-v3` 或更低级别,每个调度周期只使用一次 `WithValues("pod")`。这达到了预期的效果,即该周期的所有日志消息都包含 Pod 信息。一旦上下文日志记录达到 GA 阶段,“pod” 键/值对就可以从所有日志调用中移除。
  • 在 `-v4` 或更高级别,会生成更丰富的日志条目,其中 `WithValues` 也用于节点(如果适用),`WithName` 用于当前操作和插件。

下面是一个演示效果的例子

I1113 08:43:37.029524 87144 default_binder.go:53] "Attempting to bind pod to node" logger="Bind.DefaultBinder" pod="kube-system/coredns-69cbfb9798-ms4pq" node="127.0.0.1"

直接的好处是操作和插件名称在 `logger` 中可见。`pod` 和 `node` 在 `kube-scheduler` 代码中的各个日志调用中已经作为参数被记录。一旦 `kube-scheduler` 之外的更多包支持上下文日志记录,它们也将在那里可见(例如,client-go)。一旦达到 GA 阶段,日志调用可以被简化以避免重复这些值。

在 `kube-controller-manager` 中,`WithName` 用于将用户可见的控制器名称添加到日志输出中,例如:

I1113 08:43:29.284360 87141 graph_builder.go:285] "garbage controller monitor not synced: no monitors" logger="garbage-collector-controller"

`logger=”garbage-collector-controller”` 是由 `kube-controller-manager` 核心在实例化该控制器时添加的,并出现在其所有日志条目中——至少只要它调用的代码支持上下文日志记录。需要进一步的工作来转换像 client-go 这样的共享包。

性能影响

在一个包中支持上下文日志记录,即接受来自调用者的 Logger,代价很低。对于 `kube-scheduler` 没有观察到性能影响。如上所述,添加 `WithName` 和 `WithValues` 需要更谨慎地进行。

在 Kubernetes 1.29 中,以生产环境的详细级别(`-v3` 或更低)启用上下文日志记录,并未对 `kube-scheduler` 造成可测量的减速,预计对 `kube-controller-manager` 也不会。在调试级别,某些测试用例的 28% 减速仍然是合理的,因为由此产生的日志使调试更容易。详情请参阅关于将该功能提升到 Beta 阶段的讨论

对下游用户的影响

日志输出不属于 Kubernetes API 的一部分,并且在每个版本中都会定期更改,无论是因为开发人员在处理代码,还是因为正在进行的向结构化和上下文日志记录的转换。

如果下游用户依赖于特定的日志,他们需要意识到这一变化对他们的影响。

进一步阅读

参与其中

如果你有兴趣参与进来,我们随时欢迎新的贡献者加入我们。上下文日志记录为你提供了一个为 Kubernetes 开发做出贡献并产生有意义影响的绝佳机会。通过加入结构化日志工作组,你可以积极参与 Kubernetes 的开发并做出你的第一个贡献。这是学习和与社区互动、同时获得宝贵经验的好方法。

我们鼓励你探索代码仓库,并熟悉正在进行的讨论和项目。这是一个协作的环境,你可以与其他贡献者交流思想、提出问题并共同工作。

如果你有任何问题或需要指导,请随时与我们联系,你可以在我们的公共 Slack 频道上这样做。如果你还不是该 Slack 工作区的成员,可以访问 https://slack.k8s.io/ 获取邀请。

我们想对所有提供了出色审查、分享了宝贵见解并协助实现此功能的贡献者表示感谢(按字母顺序排列):