Black lives matter.
We stand in solidarity with the Black community.
Racism is unacceptable.
It conflicts with the core values of the Kubernetes project and our community does not tolerate it.
We stand in solidarity with the Black community.
Racism is unacceptable.
It conflicts with the core values of the Kubernetes project and our community does not tolerate it.
应用和系统日志可以让您了解集群内部的运行状况。日志对调试问题和监控集群活动非常有用。大部分现代化应用都有某种日志记录机制;同样地,大多数容器引擎也被设计成支持某种日志记录机制。针对容器化应用,最简单且受欢迎的日志记录方式就是写入标准输出和标准错误流。
但是,由容器引擎或 runtime 提供的原生功能通常不足以满足完整的日志记录方案。例如,如果发生容器崩溃、pod 被逐出或节点宕机等情况,您仍然想访问到应用日志。因此,日志应该具有独立的存储和生命周期,与节点、pod 或容器的生命周期相独立。这个概念叫 集群级的日志 。集群级日志方案需要一个独立的后台来存储、分析和查询日志。Kubernetes 没有为日志数据提供原生存储方案,但是您可以集成许多现有的日志解决方案到 Kubernetes 集群中。
集群级日志架构假定在集群内部或者外部有一个日志后台。如果您对集群级日志不感兴趣,您仍会发现关于如何在节点上存储和处理日志的描述对您是有用的。
本节,您会看到一个kubernetes 中生成基本日志的例子,该例子中数据被写入到标准输出。 这里通过一个特定的 pod 规约 演示创建一个容器,并令该容器每秒钟向标准输出写入数据。
debug/counter-pod.yaml
|
---|
|
用下面的命令运行 pod:
kubectl apply -f https://k8s.io/examples/debug/counter-pod.yaml
输出结果为:
pod/counter created
使用 kubectl logs
命令获取日志:
kubectl logs counter
输出结果为:
0: Mon Jan 1 00:00:00 UTC 2001
1: Mon Jan 1 00:00:01 UTC 2001
2: Mon Jan 1 00:00:02 UTC 2001
...
一旦发生容器崩溃,您可以使用命令 kubectl logs
和参数 --previous
检索之前的容器日志。
如果 pod 中有多个容器,您应该向该命令附加一个容器名以访问对应容器的日志。
详见 kubectl logs
文档。
容器化应用写入 stdout
和 stderr
的任何数据,都会被容器引擎捕获并被重定向到某个位置。
例如,Docker 容器引擎将这两个输出流重定向到某个 日志驱动 ,
该日志驱动在 Kubernetes 中配置为以 json 格式写入文件。
注意: Docker json 日志驱动将日志的每一行当作一条独立的消息。该日志驱动不直接支持多行消息。您需要在日志代理级别或更高级别处理多行消息。
默认情况下,如果容器重启,kubelet 会保留被终止的容器日志。 如果 pod 在工作节点被驱逐,该 pod 中所有的容器也会被驱逐,包括容器日志。
节点级日志记录中,需要重点考虑实现日志的轮转,以此来保证日志不会消耗节点上所有的可用空间。
Kubernetes 当前并不负责轮转日志,而是通过部署工具建立一个解决问题的方案。
例如,在 Kubernetes 集群中,用 kube-up.sh
部署一个每小时运行的工具 logrotate
。
您也可以设置容器 runtime 来自动地轮转应用日志,比如使用 Docker 的 log-opt
选项。
在 kube-up.sh
脚本中,使用后一种方式来处理 GCP 上的 COS 镜像,而使用前一种方式来处理其他环境。
这两种方式,默认日志超过 10MB 大小时都会触发日志轮转。
例如,您可以找到关于 kube-up.sh
为 GCP 环境的 COS 镜像设置日志的详细信息,
相应的脚本在 这里。
当运行 kubectl logs
时,
节点上的 kubelet 处理该请求并直接读取日志文件,同时在响应中返回日志文件内容。
注意: 当前,如果有其他系统机制执行日志轮转,那么 `kubectl logs` 仅可查询到最新的日志内容。 比如,一个 10MB 大小的文件,通过`logrotate` 执行轮转后生成两个文件,一个 10MB 大小,一个为空,所以 `kubectl logs` 将返回空。
系统组件有两种类型:在容器中运行的和不在容器中运行的。例如:
在使用 systemd 机制的服务器上,kubelet 和容器 runtime 写入日志到 journald。
如果没有 systemd,他们写入日志到 /var/log
目录的 .log
文件。
容器中的系统组件通常将日志写到 /var/log
目录,绕过了默认的日志机制。他们使用 klog 日志库。
您可以在日志开发文档找到这些组件的日志告警级别协议。
和容器日志类似,/var/log
目录中的系统组件日志也应该被轮转。
通过脚本 kube-up.sh
启动的 Kubernetes 集群中,日志被工具 logrotate
执行每日轮转,或者日志大小超过 100MB 时触发轮转。
虽然Kubernetes没有为集群级日志记录提供原生的解决方案,但您可以考虑几种常见的方法。以下是一些选项:
您可以通过在每个节点上使用 节点级的日志记录代理 来实现群集级日志记录。日志记录代理是一种用于暴露日志或将日志推送到后端的专用工具。通常,日志记录代理程序是一个容器,它可以访问包含该节点上所有应用程序容器的日志文件的目录。
由于日志记录代理必须在每个节点上运行,它可以用 DaemonSet 副本,Pod 或 本机进程来实现。然而,后两种方法被弃用并且非常不别推荐。
对于 Kubernetes 集群来说,使用节点级的日志代理是最常用和被推荐的方式,因为在每个节点上仅创建一个代理,并且不需要对节点上的应用做修改。 但是,节点级的日志 仅适用于应用程序的标准输出和标准错误输出。
Kubernetes 并不指定日志代理,但是有两个可选的日志代理与 Kubernetes 发行版一起发布。 Stackdriver 日志 适用于 Google Cloud Platform,和 Elasticsearch。 您可以在专门的文档中找到更多的信息和说明。两者都使用 fluentd 与自定义配置作为节点上的代理。
您可以通过以下方式之一使用 sidecar 容器:
利用 sidecar 容器向自己的 stdout
和 stderr
传输流的方式,您就可以利用每个节点上的 kubelet 和日志代理来处理日志。
sidecar 容器从文件,socket 或 journald 读取日志。每个 sidecar 容器打印其自己的 stdout
和 stderr
流。
这种方法允许您将日志流从应用程序的不同部分分离开,其中一些可能缺乏对写入 stdout
或 stderr
的支持。重定向日志背后的逻辑是最小的,因此它的开销几乎可以忽略不计。
另外,因为 stdout
、stderr
由 kubelet 处理,你可以使用内置的工具 kubectl logs
。
考虑接下来的例子。pod 的容器向两个文件写不同格式的日志,下面是这个 pod 的配置文件:
admin/logging/two-files-counter-pod.yaml
|
---|
|
在同一个日志流中有两种不同格式的日志条目,这有点混乱,即使您试图重定向它们到容器的 stdout
流。
取而代之的是,您可以引入两个 sidecar 容器。
每一个 sidecar 容器可以从共享卷跟踪特定的日志文件,并重定向文件内容到各自的 stdout
流。
这是运行两个 sidecar 容器的 pod 文件。
admin/logging/two-files-counter-pod-streaming-sidecar.yaml
|
---|
|
现在当您运行这个 pod 时,您可以分别地访问每一个日志流,运行如下命令:
kubectl logs counter count-log-1
0: Mon Jan 1 00:00:00 UTC 2001
1: Mon Jan 1 00:00:01 UTC 2001
2: Mon Jan 1 00:00:02 UTC 2001
...
kubectl logs counter count-log-2
Mon Jan 1 00:00:00 UTC 2001 INFO 0
Mon Jan 1 00:00:01 UTC 2001 INFO 1
Mon Jan 1 00:00:02 UTC 2001 INFO 2
...
集群中安装的节点级代理会自动获取这些日志流,而无需进一步配置。如果您愿意,您可以配置代理程序来解析源容器的日志行。
注意,尽管 CPU 和内存使用率都很低(以多个 cpu millicores 指标排序或者按内存的兆字节排序),
向文件写日志然后输出到 stdout
流仍然会成倍地增加磁盘使用率。
如果您的应用向单一文件写日志,通常最好设置 /dev/stdout
作为目标路径,而不是使用流式的 sidecar 容器方式。
应用本身如果不具备轮转日志文件的功能,可以通过 sidecar 容器实现。
该方式的一个例子是运行一个定期轮转日志的容器。
然而,还是推荐直接使用 stdout
和 stderr
,将日志的轮转和保留策略交给 kubelet。
如果节点级日志记录代理程序对于你的场景来说不够灵活,您可以创建一个带有单独日志记录代理程序的 sidecar 容器,将代理程序专门配置为与您的应用程序一起运行。
注意: 在 sidecar 容器中使用日志代理会导致严重的资源损耗。此外,您不能使用kubectl logs
命令访问日志,因为日志并没有被 kubelet 管理。
例如,您可以使用 Stackdriver,它使用fluentd作为日志记录代理。 以下是两个可用于实现此方法的配置文件。 第一个文件包含配置 fluentd 的ConfigMap。
admin/logging/fluentd-sidecar-config.yaml
|
---|
|
注意: 配置fluentd超出了本文的范围。要知道更多的关于如何配置fluentd,请参考fluentd 官方文档.
第二个文件描述了运行 fluentd sidecar 容器的 pod 。flutend 通过 pod 的挂载卷获取它的配置数据。
admin/logging/two-files-counter-pod-agent-sidecar.yaml
|
---|
|
一段时间后,您可以在 Stackdriver 界面看到日志消息。
记住,这只是一个例子,事实上您可以用任何一个日志代理替换 fluentd ,并从应用容器中读取任何资源。
通过暴露或推送每个应用的日志,您可以实现集群级日志记录;然而,这种日志记录机制的实现已超出 Kubernetes 的范围。