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

使用 Stern 跟踪 Kubernetes

我们Wercker公司非常热爱Kubernetes,并将其作为我们所有基础设施的基础。部署任何东西时,您都需要对正在发生的事情有很好的可见性,而日志是了解应用程序内部工作原理的第一道窗口。老式的`tail -f`已经存在很长时间了,Kubernetes也内置了它,直接集成在kubectl中。

我应该说,`tail`绝不是用于调试问题的工具,相反,您应该将日志馈送到更持久的地方,例如Elasticsearch。然而,`tail`仍然有其用武之地,例如当您需要快速调试某些问题,或者您还没有设置持久日志记录(例如在Minikube中开发应用程序时)。

多个 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通过 REST 公开 gRPC 端点。通常,我们将服务器和网关作为两个容器放在同一个 Pod 中(通过 CLI 标志设置模式的同一个二进制文件)。网关与同一个 Pod 中的服务器通信,两个端口都暴露给 Kubernetes。对于内部服务,我们可以直接与 gRPC 端点通信,而我们的网站则使用标准 REST 与网关通信。

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

如果我们有 3 个 Pod 副本,每个 Pod 中有 2 个容器,您就需要运行 6 个 `kubectl log -f ` 命令。我们使用大显示器,但这很快就会变得难以管理……

如果我们的服务 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 和容器的输出都将被多路复用在一起,并以 Pod 和容器 ID 作为前缀,并进行颜色编码以便于阅读(如果管道输出到文件,则会去除颜色)。

服务示例将如下所示:

$ 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 分钟前的认证活动日志并带时间戳

+ 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 发布页面下载预编译的二进制文件。