Skip to content

K8s 持久化 Pod 控制台日志到节点本地:使用 Fluent Bit

约 2701 字大约 9 分钟

计算机技术云原生

2025-08-30

K8s 持久化 Pod 控制台日志到节点本地:一个 Fluent Bit 实战指南

动机

在 Kubernetes 的日常使用中,我们通常会把日志接入云服务商或自建的中心化日志系统(如 ELK、Loki)。但在某些场景下,这并不是最佳选择,甚至不可行。

比如,你可能遇到了以下情况:

  • 环境限制:无法将日志上报到外部网络,或者压根就不想依赖外部服务。
  • 简单持久化需求:不需要复杂的搜索和分析,只希望 Pod 销毁后,日志能作为文件留在节点上,方便以后排查。
  • 快速调试需求:在开发测试时,只想快速、直接地看到某个应用的日志,不想经过层层转发和复杂的平台。

针对这些需求,我们可以采用一个更直接的方案:在 K8s 集群里部署一个日志代理,精确抓取特定应用的控制台日志,并把它写入每个节点主机的特定目录下。

本文将通过一个完整的实战案例,介绍如何使用 Fluent Bit(一个轻量级、高性能的日志处理工具)作为 DaemonSet 来实现这个目标。

解决方案架构

我们的方案由三个核心的 K8s 资源组成,它们协同工作,构成了一条完整的日志采集流水线:

  1. RBAC 权限 (ServiceAccount, ClusterRole, ClusterRoleBinding):这是“通行证”。Fluent Bit 需要权限查询 K8s API,以获取 Pod 的标签等元数据,否则它就无法分辨日志的来源。
  2. ConfigMap (配置中心):这是“大脑”。它包含了 Fluent Bit 的所有配置,告诉它该做什么、怎么做。我们在这里定义日志的输入、解析、过滤和输出规则。
  3. DaemonSet (执行者):这是“工人”。通过 DaemonSet,我们确保每个节点上都运行一个 Fluent Bit Pod,负责采集该节点上所有容器的日志。

原理

为了让我们的 Fluent Bit 配置更加清晰,我们有必要了解一下 K8s 节点上日志文件的命名规范。当你 SSH 登录到任何一个 K8s 工作节点并查看 /var/log/containers/ 目录时,你会看到一系列遵循严格格式的文件。

这个目录存放着节点上所有容器日志的实际物理文件

标准格式

每个日志文件的名称都由几个部分拼接而成,格式如下:

<pod_name>_<namespace>_<container_name>-<container_id>.log

让我们来逐一拆解:

  • <pod_name>: 产生这条日志的 Pod 的完整名称。例如,my-app-7b5d8f9c6c-xyz
  • <namespace>: 该 Pod所在的 Kubernetes 命名空间。例如,my-app-ns
  • <container_name>: Pod 中产生日志的具体容器的名称。这对于一个 Pod 内有多个容器(例如,一个主应用容器和一个 sidecar 容器)的场景非常重要。
  • <container_id>: 由容器运行时(如 containerd 或 Docker)分配给该容器的一个唯一哈希值(ID)。这个 ID 确保了即使容器重启(Pod 不变,但容器实例是新的),日志文件也不会发生冲突。

举个例子

假设我们有一个 Pod,信息如下:

  • Pod 名称: my-app-7b5d8f9c6c-xyz
  • 命名空间: my-app-ns
  • 容器名称: my-app-container

那么,它在节点上对应的日志文件名称就会是这样的: my-app-7b5d8f9c6c-xyz_my-app-ns_my-app-container-a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2.log

一个有用的快捷方式:/var/log/pods 目录

除了 /var/log/containers/,你可能还会注意到一个名为 /var/log/pods/ 的目录。这个目录提供了一套更具可读性的符号链接(Symbolic Links),方便我们手动查找日志。

它的目录结构是这样的: /var/log/pods/<namespace>_<pod_name>_<pod_uid>/<container_name>/0.log

这里的 0.log 文件就是一个符号链接,它最终指向了 /var/log/containers/ 目录中对应的那个长长的、带有哈希值的实际日志文件。

理解了这个文件结构,我们 Fluent Bit 配置中的 Path /var/log/containers/*.log 就变得非常清晰了:它告诉 Fluent Bit 去监控这个目录下所有符合命名规范的日志文件,从而捕获节点上所有容器的输出。

完整配置清单

要实现我们的目标,你需要将以下所有 K8s 资源清单整合到一个 YAML 文件中(例如 fluent-bit-daemonset.yaml)。

这个配置文件会创建一个 my-app-ns 命名空间,并部署所有需要的资源。它经过精心设计,能够:

  • 监控节点上所有容器的控制台输出。
  • 为每条日志附加其来源 Pod 的 K8s 标签。
  • 只保留那些带有 app: my-app 标签的 Pod 的日志。
  • 将这些日志的纯净、原始内容(不含任何 JSON 包装或时间戳前缀)写入到每个节点主机的 /var/log/my-app/app.log 文件中。

部署与验证

  1. 应用配置: 将上面的配置保存为 fluent-bit-daemonset.yaml,然后执行:

    kubectl apply -f fluent-bit-daemonset.yaml
  2. 检查 DaemonSet 状态: 确认 Fluent Bit 的 Pod 已经在你的节点上成功运行。

    kubectl get ds -n my-app-ns
    # 期望输出中 DESIRED, CURRENT, READY 的数量应该一致
  3. 验证日志文件: SSH 登录到运行你目标应用 (my-app) 的任一节点,然后查看目标文件:

    tail -f /var/log/my-app/app.log

    此时,当你与 my-app 应用交互时,应该能在这里看到实时输出的原始日志行。

常见问题排查 (Troubleshooting)

如果你发现日志文件为空,别慌,这很常见。我们可以按以下步骤排查:

  1. 确认源头有日志:你的应用真的在向控制台输出日志吗?我们可以绕过 Fluent Bit,直接用 kubectl 检查。

    # 确保你的应用正在运行并处理流量
    kubectl logs deployment/my-app -n my-app-ns -f

    如果这里没输出,那问题就在你的应用本身,而不是日志采集。

  2. 确认标签完全匹配:我们的过滤规则依赖精确的标签。检查一下 Pod 是否真的拥有我们指定的标签。

    kubectl get pods -n my-app-ns --show-labels

    仔细核对 LABELS 列,确保目标 Pod 拥有 app: my-app。任何拼写错误都会导致过滤失败。

  3. 检查 Fluent Bit 自身日志:Fluent Bit Pod 在启动或运行时报错了吗?

    kubectl logs -n my-app-ns -l k8s-app=fluent-bit -f

    常见的错误包括权限不足(RBAC 问题)、配置文件语法错误或无法访问挂载目录。

总结与扩展

通过上面的步骤,我们就成功构建了一个轻量级、高针对性的 K8s 日志持久化系统。它不依赖任何外部服务,将指定应用的控制台日志以最纯粹的形式保存在了节点本地,完美解决了特定场景下的日志采集需求。

这个方案的真正强大之处在于它的扩展性。通过简单修改 ConfigMap,你可以:

  • 更改过滤规则:采集不同应用或整个命名空间的日志。
  • 更改输出目标:将日志发送到 Elasticsearch、S3、Loki 或其他任何 Fluent Bit 支持的后端,轻松从本地持久化方案升级为中心化日志系统。