Promtail 和日志轮换
注意
Promtail 已被弃用,并将持续获得长期支持 (LTS) 直至 2026 年 2 月 28 日。Promtail 将于 2026 年 3 月 2 日到达生命周期结束 (EOL)。您可以在此处找到迁移资源。
日志轮换为何重要?
在任何时间点,可能都有三个进程在处理日志文件,如下图所示。

- Appender - 一个持续向日志文件追加内容的写入器。这可以是您的应用程序或一些系统守护进程,如 Syslog、Docker 日志驱动程序或 Kubelet 等。
- Tailer - 一个随着日志行被追加而读取它们的读取器,例如 Promtail 等代理。
- 日志轮换器 - 一个根据时间(例如,每天定时)或大小(例如,日志文件达到最大大小)来轮换日志文件的进程。
注意
这里的
fd
定义了一个文件描述符。一旦文件打开用于读或写,操作系统会为每个进程返回一个唯一的文件描述符(通常是一个整数),并且所有的读写操作都通过该文件描述符进行。换句话说,文件一旦成功打开,文件描述符比文件名更重要。
这里的关键组件之一是日志轮换器。让我们了解它如何影响 Appender 和 Tailer 等其他组件。
通常,当软件程序轮换日志时,内部可以通过两种方式完成。
假设有一个名为 error.log
的日志文件
- 将日志文件复制到另一个名称,例如
error.log.1
,并截断原始日志文件error.log
。 - 将日志文件重命名为另一个名称,例如
error.log.1
,然后创建一个使用原始名称error.log
的新日志文件。
在这两种情况下,日志轮换后,所有新的日志行都会写入原始的 error.log
文件。
以下图片展示了这两种日志轮换方法。
复制并截断

重命名并创建

这两种日志轮换类型产生相同的结果。但是,它们有一些细微的差别。
复制并截断 (1) 对 appender
更友好,因为其指向原始日志文件 error.log
的描述符不会改变。因此,它可以继续写入相同的文件描述符。换句话说,无需重新打开文件。
然而,在考虑 tailer
时,(1) 存在一个严重问题。截断文件和 tailer
完成读取日志文件之间存在竞争。这意味着日志轮换机制很有可能在 tailer
从文件 error.log
中读取完所有内容之前就截断文件。
这就是重命名并创建 (2) 可以提供帮助的地方。在这里,当日志文件 error.log
被重命名为 error.log.1
时,tailer
仍然持有 error.log.1
的文件描述符。因此,它可以继续读取日志文件直到完成。但这带来了一个权衡:使用 (2),您必须通知 appender
重新打开 error.log
(并且 appender
应该能够重新打开它)。否则,由于文件描述符不会改变,它将继续写入 error.log.1
。好消息是,大多数流行的 appender
解决方案(例如 Syslog、Kubelet、Docker 日志驱动程序)都支持在日志文件被重命名时重新打开它们。
我们推荐使用重命名并创建 (2) 方法,因为它与 Promtail(或任何类似的日志抓取代理)配合良好,且不会丢失数据。现在,让我们准确了解如何在不同平台中配置日志轮换。
配置日志轮换
您的日志可能由不同的组件进行轮换,具体取决于您运行应用程序或服务的位置。如果您在 Linux 机器上运行它们,很可能您正在使用 logrotate
工具。然而,如果您在 Kubernetes 中运行,日志由什么进行轮换并不那么明显,而且有趣的是,它可能取决于您的 Kubernetes 集群使用了哪种容器运行时。
非 Kubernetes 环境
如上所述,在 Linux 机器中,日志轮换通常由 logrotate
工具处理。
logrotate
的配置文件通常位于 /etc/logrotate/
中。
它拥有广泛的 选项,用于压缩、邮件发送、在轮换前后运行脚本等。
它支持之前描述的两种日志轮换方法。
复制并截断
/var/log/apache2/*.log {
weekly
maxsize 1G
copytruncate
}
这里的 copytruncate
模式工作方式与上述 (1) 完全相同。
重命名并创建 (推荐)
/var/log/apache2/*.log {
weekly
maxsize 1G
create
}
这里的 create
模式工作方式与上述 (2) 中解释的相同。create
模式是可选的,因为它是 logrotate
中的默认模式。
Kubernetes
Promtail 中的 Kubernetes 服务发现 也使用基于文件的抓取。这意味着您的 Pod 中的日志存储在节点上,Promtail 从节点文件抓取 Pod 日志。
您可以通过两个配置设置来配置在每个节点上运行的 kubelet
进程来管理日志轮换。
containerLogMaxSize
- 它定义了容器日志文件轮换前的最大大小。例如:“5Mi”或“256Ki”。默认值:“10Mi”。containerLogMaxFiles
- 它指定每个容器可以存在的最大容器日志文件数量。默认值:5
两者都应该作为 kubelet
配置的一部分。如果您在云中运行托管版本的 Kubernetes,请参考您的云提供商文档来配置 kubelet
。例如 GKE、AKS 和 EKS。
注意
kubelet
管理的日志轮换仅支持重命名 + 创建,不支持复制 + 截断。
如果 kubelet
未配置日志轮换,则由集群使用的容器运行时接口 (CRI) 负责。或者,也可以通过 Kubernetes 节点上的 logrotate
工具管理日志轮换。
通过运行以下命令检查节点上的容器运行时 (CRI):
$ kubectl get nodes -o wide
两种常用的 CRI 实现是 containerd
和 docker
。
containerd CRI
在撰写本指南时,containerd
不支持任何日志轮换方法。在这种情况下,轮换通常由 kubelet
本身处理。GKE 和 AKS 等托管 Kubernetes 集群使用 containerd
作为运行时,日志轮换由 kubelet
处理。1.24 版本后的 EKS 也使用 containerd
作为默认容器运行时。
docker CRI
当使用 docker
作为运行时(1.24 版本前的 EKS 默认使用),日志轮换由其日志驱动程序管理(如果支持)。Docker 支持多种日志驱动程序。
您可以通过运行以下命令确定 docker
正在使用哪个日志驱动程序:
docker info --format '{{.LoggingDriver}}'
在所有这些日志驱动程序中,只有 local
(默认)和 json-file
驱动程序支持日志轮换。您可以在 /etc/docker/daemon.json
下配置以下 log-opts
:
max-size
- 日志文件轮换前的最大大小。一个正整数加上表示度量单位(k、m 或 g)的修饰符。默认为20m
(20 兆字节)。max-file
- 可存在的最大日志文件数量。如果轮换日志创建了多余的文件,最旧的文件将被移除。一个正整数。默认为 5。
示例 /etc/docker/daemon.json
{
"log-driver": "local",
"log-opts": {
"max-size": "10m",
"max-file": "10"
}
}
如果 kubelet
和 CRI
都未配置日志轮换,则可以在 Kubernetes 节点上使用之前解释过的 logrotate
工具。
注意
我们推荐使用
kubelet
进行日志轮换。
配置 Promtail
Promtail 使用**轮询**来监视文件更改。轮询机制与**复制并截断**日志轮换相结合可能会导致一些日志丢失。如本主题前面所解释,这发生在 Promtail 从文件中读取完所有日志行之前,文件就被截断的情况下。
因此,作为长期解决方案,我们强烈建议将日志轮换策略更改为**重命名并创建**。另外,作为短期权宜之计,您可以调整 Promtail 客户端的 batchsize
配置,设置更高值(例如 5M 或 8M)。这让 Promtail 有更多空间读取日志行,而无需频繁等待 Loki 服务器的推送响应。