这篇文章已超过一年。旧文章可能包含过时的内容。请检查页面中的信息自发布以来是否已变得不准确。

使用 Stern 跟踪 Kubernetes 日志

我们 Wercker 非常喜欢 Kubernetes,并在此基础上构建了我们所有的基础设施。部署任何东西时,你需要对正在发生的事情有良好的可见性,而日志是了解应用内部运作的第一个途径。好用的 tail -f 命令行工具已经存在了很长时间,Kubernetes 也内置了它,就在 kubectl 中。

我应该说,tail 绝不是用于调试问题的首选工具,相反,你应该将日志馈送到更持久的地方,比如 Elasticsearch。然而,当你需要快速调试某些内容,或者可能尚未设置持久化日志(例如在 Minikube 中开发应用时),tail 仍然有用武之地。

多个 Pod

Kubernetes 有 Replication Controllers 的概念,它确保同时运行 n 个 Pod。这使得滚动更新和冗余成为可能。考虑到它们设置起来非常容易,实在没有理由不这样做。

然而现在有多个 Pod 正在运行,并且它们都有唯一的 ID。这里的一个问题是你需要知道确切的 Pod ID(kubectl get pods),但每次创建 Pod 时它都会改变,所以你每次都需要重新获取。另一个需要考虑的事实是 Kubernetes 会进行流量负载均衡,所以你不知道请求最终到达哪个 Pod。如果你正在跟踪 Pod A 的日志,但流量最终到达 Pod B,你就会错过发生的事情。

假设我们有一个名为 service 的 Pod,有 3 个副本。看起来像这样:

$ kubectl get pods                         # get pods to find pod ids

$ kubectl log -f service-1786497219-2rbt1  # pod 1

$ kubectl log -f service-1786497219-8kfbp  # pod 2

$ kubectl log -f service-1786497219-lttxd  # pod 3

多个容器

我们是内部服务 gRPC 的重度用户,并使用 gRPC Gateway 将 gRPC 端点通过 REST 公开。通常,我们将服务器和网关作为两个容器放在同一个 Pod 中(同一个二进制文件通过命令行标志设置模式)。网关与同一个 Pod 中的服务器通信,并且这两个端口都暴露给 Kubernetes。对于内部服务,我们可以直接与 gRPC 端点通信,而我们的网站则使用标准 REST 与网关通信。

然而这带来了一个问题;我们不仅有多个 Pod,而且 Pod 内也有多个容器。在这种情况下,kubectl 内置的日志功能要求你指定要获取哪些容器的日志。

如果一个 Pod 有 3 个副本,并且 Pod 中有 2 个容器,你需要运行 6 个 kubectl log -f <pod id> <container id> 命令。我们使用大显示器,但这很快就会失控…

如果我们的 service Pod 有一个服务器容器和一个网关容器,看起来会像这样:

$ kubectl get pods                                 # get pods to find pod ids

$ kubectl describe pod service-1786497219-2rbt1    # get containers in pod

$ kubectl log -f service-1786497219-2rbt1 server   # pod 1

$ kubectl log -f service-1786497219-2rbt1 gateway  # pod 1

$ kubectl log -f service-1786497219-8kfbp server   # pod 2

$ kubectl log -f service-1786497219-8kfbp gateway  # pod 2

$ kubectl log -f service-1786497219-lttxd server   # pod 3

$ kubectl log -f service-1786497219-lttxd gateway  # pod 3

Stern

为了解决这个问题,我们构建了 Stern。这是一个超级简单的工具,它允许你将 Pod ID 和容器 ID 都指定为正则表达式。任何匹配项都会被跟踪,输出会被多路复用在一起,并以 Pod 和容器 ID 为前缀,且进行颜色编码以便人类阅读(如果输出到文件则会去除颜色)。

这是 service 示例的样子:

$ stern service

这将匹配包含单词 service 的任何 Pod,并监听其中的所有容器。如果你只想看到流向服务器容器的流量,可以运行 stern --container server service,它将流式输出所有 3 个 Pod 中服务器容器的日志。

输出看起来像这样:

$ stern service

+ service-1786497219-2rbt1 › server

+ service-1786497219-2rbt1 › gateway

+ service-1786497219-8kfbp › server

+ service-1786497219-8kfbp › gateway

+ service-1786497219-lttxd › server

+ service-1786497219-lttxd › gateway

+ service-1786497219-8kfbp server Log message from server

+ service-1786497219-2rbt1 gateway Log message from gateway

+ service-1786497219-8kfbp gateway Log message from gateway

+ service-1786497219-lttxd gateway Log message from gateway

+ service-1786497219-lttxd server Log message from server

+ service-1786497219-2rbt1 server Log message from server

此外,如果在部署过程中 Pod 被杀死并重新创建,Stern 将停止监听旧 Pod,并自动连接到新 Pod。不再需要找出新创建的 Pod 的 ID 是什么。

配置选项

Stern 被有意设计得非常简洁,所以功能不多。然而,这里仍然有几个可以重点介绍的配置选项。它们与 kubectl 内置的选项非常相似,所以如果你熟悉 kubectl,你会感觉很自在。

  • timestamps 为每一行添加时间戳
  • since 显示某个时间点之后的日志条目(例如 --since 15min)
  • kube-config 允许你指定另一个 Kubernetes 配置。默认为 ~/.kube/config
  • namespace 允许你将搜索限制在特定的命名空间。运行 stern --help 查看所有选项。

示例

跟踪 staging 环境下 envvars Pod 内运行的 gateway 容器日志

 + stern --context staging --container gateway envvars

显示 15 分钟前的 auth 活动日志并带时间戳

+ stern -t --since 15m auth

在 minikube 中跟踪 some-new-feature 的开发日志

+ stern --context minikube some-new-feature

查看其他命名空间中的 Pod

+ stern --namespace kube-system kubernetes-dashboard

获取 Stern

Stern 是开源的,在 GitHub 上可用,我们欢迎您的贡献或想法。如果你不想从源代码构建,也可以从 GitHub releases 下载预编译的二进制文件。