系统组件日志记录了集群中发生的事件,这对调试非常有用。你可以配置日志的详细程度,以获取更多或更少的详细信息。日志的粒度可以从粗略地显示组件内的错误,到细致地显示事件的分步跟踪(如 HTTP 访问日志、Pod 状态变更、控制器动作或调度器决策)。
klog 是 Kubernetes 的日志库。klog 为 Kubernetes 系统组件生成日志消息。
Kubernetes 正致力于简化其组件的日志记录。从 Kubernetes v1.23 开始,以下 klog 命令行标志已被弃用,并已在 Kubernetes v1.26 中移除。
--add-dir-header--alsologtostderr--log-backtrace-at--log-dir--log-file--log-file-max-size--logtostderr--one-output--skip-headers--skip-log-headers--stderrthreshold无论采用何种输出格式,输出内容始终会写入 stderr。输出重定向预期由调用 Kubernetes 组件的程序来处理,这可以是一个 POSIX shell 或像 systemd 这样的工具。
在某些情况下,例如 distroless 容器或 Windows 系统服务,这些选项不可用。此时,可以使用 kube-log-runner 二进制文件作为 Kubernetes 组件的包装器来重定向输出。预构建的二进制文件以其传统名称 /go-runner 包含在多个 Kubernetes 基础镜像中,并在服务器和节点发布包中以 kube-log-runner 的名称提供。
此表显示了 kube-log-runner 的调用如何对应于 shell 重定向:
| 用法 | POSIX shell(例如 bash) | kube-log-runner <options> <cmd> |
|---|---|---|
| 合并 stderr 和 stdout,写入 stdout | 2>&1 | kube-log-runner(默认行为) |
| 将两者重定向到日志文件 | 1>>/tmp/log 2>&1 | kube-log-runner -log-file=/tmp/log |
| 复制到日志文件并输出到 stdout | 2>&1 | tee -a /tmp/log | kube-log-runner -log-file=/tmp/log -also-stdout |
| 仅将 stdout 重定向到日志文件 | >/tmp/log | kube-log-runner -log-file=/tmp/log -redirect-stderr=false |
传统 klog 原生格式示例
I1025 00:15:15.525108 1 httplog.go:79] GET /api/v1/namespaces/kube-system/pods/metrics-server-v0.3.1-57c75779f-9p8wg: (1.512ms) 200 [pod_nanny/v0.0.0 (linux/amd64) kubernetes/$Format 10.56.1.19:51756]
消息字符串可能包含换行符
I1025 00:15:15.525108 1 example.go:79] This is a message
which has a line break.
Kubernetes v1.23 [beta]迁移到结构化日志消息是一个持续的过程。并非此版本中的所有日志消息都是结构化的。解析日志文件时,必须同时处理非结构化的日志消息。
日志格式和值序列化可能会发生变化。
结构化日志记录在日志消息中引入了统一的结构,允许以编程方式提取信息。你可以用更少的成本和精力存储和处理结构化日志。生成日志消息的代码决定了它是使用传统的非结构化 klog 输出还是结构化日志。
结构化日志消息的默认格式为文本,其格式与传统的 klog 向后兼容。
<klog header> "<message>" <key1>="<value1>" <key2>="<value2>" ...
示例
I1025 00:15:15.525108 1 controller_utils.go:116] "Pod status updated" pod="kube-system/kubedns" status="ready"
字符串会被加引号。其他值使用 %+v 格式化,这可能会导致日志消息 根据数据情况 换行显示。
I1025 00:15:15.525108 1 example.go:116] "Example" data="This is text with a line break\nand \"quotation marks\"." someInt=1 someFloat=0.1 someStruct={StringField: First line,
second line.}
Kubernetes v1.30 [beta]上下文日志记录构建在结构化日志记录之上。它主要涉及开发人员如何使用日志记录调用:基于该概念的代码更灵活,并支持上下文日志记录 KEP 中描述的其他用例。
如果开发人员在其组件中使用了 WithValues 或 WithName 等附加函数,则日志条目将包含由调用者传递给函数的额外信息。
对于 Kubernetes 1.36,此功能受 ContextualLogging 特性门控 控制,并默认启用。其基础设施已于 1.24 版本添加,无需修改组件。component-base/logs/example 命令演示了如何使用新的日志调用以及支持上下文日志记录的组件的行为。
$ cd $GOPATH/src/k8s.io/kubernetes/staging/src/k8s.io/component-base/logs/example/cmd/
$ go run . --help
...
--feature-gates mapStringBool A set of key=value pairs that describe feature gates for alpha/experimental features. Options are:
AllAlpha=true|false (ALPHA - default=false)
AllBeta=true|false (BETA - default=false)
ContextualLogging=true|false (BETA - default=true)
$ go run . --feature-gates ContextualLogging=true
...
I0222 15:13:31.645988 197901 example.go:54] "runtime" logger="example.myname" foo="bar" duration="1m0s"
I0222 15:13:31.646007 197901 example.go:55] "another runtime" logger="example" foo="bar" duration="1h0m0s" duration="1m0s"
logger 键和 foo="bar" 是由记录 runtime 消息和 duration="1m0s" 值的函数调用者添加的,无需修改该函数。
当上下文日志记录被禁用时,WithValues 和 WithName 不起作用,日志调用将通过全局 klog 记录器。因此,这些额外信息将不再出现在日志输出中。
$ go run . --feature-gates ContextualLogging=false
...
I0222 15:14:40.497333 198174 example.go:54] "runtime" duration="1m0s"
I0222 15:14:40.497346 198174 example.go:55] "another runtime" duration="1h0m0s" duration="1m0s"
Kubernetes v1.19 [alpha]JSON 输出不支持许多标准的 klog 标志。有关不支持的 klog 标志列表,请参阅命令行工具参考。
不能保证所有日志都以 JSON 格式编写(例如,在进程启动期间)。如果打算解析日志,请确保也能处理非 JSON 的日志行。
字段名称和 JSON 序列化可能会发生变化。
--logging-format=json 标志将日志格式从 klog 原生格式更改为 JSON 格式。JSON 日志格式示例(美化打印):
{
"ts": 1580306777.04728,
"v": 4,
"msg": "Pod status updated",
"pod":{
"name": "nginx-1",
"namespace": "default"
},
"status": "ready"
}
具有特殊含义的键:
ts - 作为 Unix 时间的时间戳(必需,浮点数)v - 详细程度(仅用于信息日志,不用于错误消息,整数)err - 错误字符串(可选,字符串)msg - 消息(必需,字符串)当前支持 JSON 格式的组件列表:
-v 标志控制日志详细程度。增加该值会增加记录的事件数量,减少该值则减少记录的事件数量。提高详细程度设置会记录严重程度越来越低的事件。详细程度设置为 0 时,仅记录严重事件。
有两种类型的系统组件:在容器中运行的和不在容器中运行的。例如:
在装有 systemd 的机器上,kubelet 和容器运行时写入 journald。否则,它们会写入 /var/log 目录中的 .log 文件。容器内的系统组件始终写入 /var/log 目录中的 .log 文件,绕过默认日志记录机制。与容器日志类似,你应该轮换 /var/log 目录中的系统组件日志。在使用 kube-up.sh 脚本创建的 Kubernetes 集群中,日志轮换由 logrotate 工具配置。logrotate 工具每天轮换一次日志,或者在日志大小超过 100MB 时轮换。
Kubernetes v1.36 [stable](默认启用)日志查询功能有助于调试 Linux 和 Windows 节点中的问题。该功能于 Kubernetes v1.27 引入,允许查看节点上运行的服务的日志。要使用此功能,请确保目标节点的 kubelet 配置选项 enableSystemLogHandler 和 enableSystemLogQuery 均设置为 true。
在 Kubernetes v1.36 中,此功能已升级为稳定版,NodeLogQuery 特性门控 现在锁定为 true,因此该特性门控默认启用,只需设置 enableSystemLogHandler 即可启用或禁用日志查询功能。
enableSystemLogHandler 默认为 false,建议仅在主动调试时才启用。
nodes/proxy 权限(即使只是 get 权限)也会授权对强大的 kubelet API 的访问,这些 API 可用于在节点上运行的任何容器中执行命令,因此请谨慎管理。有关更多信息,请参见 Kubelet 认证/授权。在 Linux 上,假设服务日志可通过 journald 获取。在 Windows 上,假设服务日志可在应用程序日志提供程序中获取。在这两个操作系统上,也可以通过读取 /var/log/ 中的文件来获取日志。
只要你有权与节点对象交互,就可以在所有节点或部分节点上试用此功能。以下是从节点检索 kubelet 服务日志的示例:
# Fetch kubelet logs from a node named node-1.example
kubectl get --raw "/api/v1/nodes/node-1.example/proxy/logs/?query=kubelet"
如果文件位于 kubelet 允许获取日志的目录中,你还可以获取这些文件。例如,你可以获取 Linux 节点上 /var/log 中的日志:
kubectl get --raw "/api/v1/nodes/<insert-node-name-here>/proxy/logs/?query=/<insert-log-file-name-here>"
kubelet 使用启发式方法来检索日志。这在你不知道某个系统服务是将其日志写入操作系统的原生记录器(如 journald)还是写入 /var/log/ 中的日志文件时很有帮助。启发式方法首先检查原生记录器;如果不可用,则尝试从 /var/log/<servicename>、/var/log/<servicename>.log 或 /var/log/<servicename>/<servicename>.log 获取第一批日志。
可使用的完整选项列表如下:
| 选项 | 描述 |
|---|---|
boot | 显示特定系统启动引导的日志消息 |
pattern | 根据提供的 PERL 兼容正则表达式过滤日志条目 |
query | 指定要从中返回日志的服务或文件(必需) |
sinceTime | 显示日志的起始时间(RFC3339 时间戳,包含该时间点) |
untilTime | 显示日志的截止时间(RFC3339 时间戳,包含该时间点) |
tailLines | 指定检索日志末尾的多少行;默认获取全部日志 |
更复杂查询的示例:
# Fetch kubelet logs from a node named node-1.example that have the word "error"
kubectl get --raw "/api/v1/nodes/node-1.example/proxy/logs/?query=kubelet&pattern=error"