本文发表时间已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已不正确。
使用 Stern 尾随 Kubernetes
我们 Wercker 非常喜欢 Kubernetes,并基于它构建了我们所有的基础设施。部署任何东西时,你需要很好地了解发生了什么,而日志是了解应用程序内部工作原理的第一视角。古老的 tail -f 已经存在很长时间了,Kubernetes 也内置了这个功能,就在 kubectl 中。
我应该说 tail 绝不是用于调试问题的工具,相反,你应该将日志馈送到更持久的地方,例如 Elasticsearch。但是,仍然有使用 tail 的地方,当你需要快速调试某些东西,或者你还没有设置持久日志记录时(例如在 Minikube 中开发应用程序时)。
多个 Pod
Kubernetes 具有 Replication Controller 的概念,它可以确保同时运行 n 个 pod。这允许滚动更新和冗余。考虑到它们很容易设置,所以没有理由不这样做。
但是现在有多个 pod 正在运行,并且它们都有一个唯一的 ID。这里的一个问题是你需要知道确切的 pod ID(kubectl get pods),但每次创建 pod 时,ID 都会更改,所以你需要每次都这样做。另一个需要考虑的是 Kubernetes 负载平衡流量,所以你不会知道请求最终会落在哪个 pod 上。如果你正在 tail 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 的内置日志记录要求你指定要查看哪个容器的日志。
如果我们有一个 pod 的 3 个副本和 pod 中的 2 个容器,你将需要 6 个 kubectl log -f <pod id> <container id>。我们使用大显示器,但这很快就变得难以控制…
如果我们的服务 pod 有一个 server 和 gateway 容器,它看起来像这样
$ 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 中跟踪一些新功能的开发
+ stern --context minikube some-new-feature
查看来自另一个命名空间的 pod
+ stern --namespace kube-system kubernetes-dashboard
获取 Stern
Stern 是开源的,并且 在 GitHub 上可用,我们欢迎你的贡献或想法。如果你不想从源代码构建,你也可以从 GitHub 版本下载预编译的二进制文件。