菜单
开源 RSS

Promtail 和日志轮换

注意

Promtail 已被弃用,并将持续获得长期支持 (LTS) 直至 2026 年 2 月 28 日。Promtail 将于 2026 年 3 月 2 日到达生命周期结束 (EOL)。您可以在此处找到迁移资源。

日志轮换为何重要?

在任何时间点,可能都有三个进程在处理日志文件,如下图所示。

block diagram showing three processes
  1. Appender - 一个持续向日志文件追加内容的写入器。这可以是您的应用程序或一些系统守护进程,如 Syslog、Docker 日志驱动程序或 Kubelet 等。
  2. Tailer - 一个随着日志行被追加而读取它们的读取器,例如 Promtail 等代理。
  3. 日志轮换器 - 一个根据时间(例如,每天定时)或大小(例如,日志文件达到最大大小)来轮换日志文件的进程。

注意

这里的 fd 定义了一个文件描述符。一旦文件打开用于读或写,操作系统会为每个进程返回一个唯一的文件描述符(通常是一个整数),并且所有的读写操作都通过该文件描述符进行。换句话说,文件一旦成功打开,文件描述符比文件名更重要。

这里的关键组件之一是日志轮换器。让我们了解它如何影响 Appender 和 Tailer 等其他组件。

通常,当软件程序轮换日志时,内部可以通过两种方式完成。

假设有一个名为 error.log 的日志文件

  1. 将日志文件复制到另一个名称,例如 error.log.1,并截断原始日志文件 error.log
  2. 将日志文件重命名为另一个名称,例如 error.log.1,然后创建一个使用原始名称 error.log 的新日志文件。

在这两种情况下,日志轮换后,所有新的日志行都会写入原始的 error.log 文件。

以下图片展示了这两种日志轮换方法。

复制并截断

Copy and truncate log rotation

重命名并创建

Rename and create log rotation

这两种日志轮换类型产生相同的结果。但是,它们有一些细微的差别。

复制并截断 (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 进程来管理日志轮换。

  1. containerLogMaxSize - 它定义了容器日志文件轮换前的最大大小。例如:“5Mi”或“256Ki”。默认值:“10Mi”。
  2. containerLogMaxFiles - 它指定每个容器可以存在的最大容器日志文件数量。默认值:5

两者都应该作为 kubelet 配置的一部分。如果您在云中运行托管版本的 Kubernetes,请参考您的云提供商文档来配置 kubelet。例如 GKEAKSEKS

注意

kubelet 管理的日志轮换仅支持重命名 + 创建,不支持复制 + 截断。

如果 kubelet 未配置日志轮换,则由集群使用的容器运行时接口 (CRI) 负责。或者,也可以通过 Kubernetes 节点上的 logrotate 工具管理日志轮换。

通过运行以下命令检查节点上的容器运行时 (CRI):

bash
$ kubectl get nodes -o wide

两种常用的 CRI 实现是 containerddocker

containerd CRI

在撰写本指南时,containerd 不支持任何日志轮换方法。在这种情况下,轮换通常由 kubelet 本身处理。GKE 和 AKS 等托管 Kubernetes 集群使用 containerd 作为运行时,日志轮换由 kubelet 处理。1.24 版本后的 EKS 也使用 containerd 作为默认容器运行时。

docker CRI

当使用 docker 作为运行时(1.24 版本前的 EKS 默认使用),日志轮换由其日志驱动程序管理(如果支持)。Docker 支持多种日志驱动程序

您可以通过运行以下命令确定 docker 正在使用哪个日志驱动程序:

bash
 docker info --format '{{.LoggingDriver}}'

在所有这些日志驱动程序中,只有 local(默认)和 json-file 驱动程序支持日志轮换。您可以在 /etc/docker/daemon.json 下配置以下 log-opts

  1. max-size - 日志文件轮换前的最大大小。一个正整数加上表示度量单位(k、m 或 g)的修饰符。默认为 20m(20 兆字节)。
  2. max-file - 可存在的最大日志文件数量。如果轮换日志创建了多余的文件,最旧的文件将被移除。一个正整数。默认为 5。

示例 /etc/docker/daemon.json

json
{
  "log-driver": "local",
  "log-opts": {
    "max-size": "10m",
    "max-file": "10"
  }
}

如果 kubeletCRI 都未配置日志轮换,则可以在 Kubernetes 节点上使用之前解释过的 logrotate 工具。

注意

我们推荐使用 kubelet 进行日志轮换。

配置 Promtail

Promtail 使用**轮询**来监视文件更改。轮询机制与**复制并截断**日志轮换相结合可能会导致一些日志丢失。如本主题前面所解释,这发生在 Promtail 从文件中读取完所有日志行之前,文件就被截断的情况下。

因此,作为长期解决方案,我们强烈建议将日志轮换策略更改为**重命名并创建**。另外,作为短期权宜之计,您可以调整 Promtail 客户端的 batchsize 配置,设置更高值(例如 5M 或 8M)。这让 Promtail 有更多空间读取日志行,而无需频繁等待 Loki 服务器的推送响应。