4、日志采集进阶:使用 journalctl、logrotate、fluentd 进行日志管理与采集

日志这东西,平时没人管,一出事它就是唯一的救命稻草。我见过太多升级失败的案例,最后能定位到根因,靠的就是日志里那几行不起眼的记录。但前提是——你得会管它,会采它。

这一章我们聊三个工具:journalctl、logrotate、fluentd。它们分别解决日志的查看、轮转和采集问题。说白了,一个帮你查,一个帮你省磁盘,一个帮你把日志送到该去的地方。

4.1 journalctl:systemd 日志的瑞士军刀

现在大部分 Linux 发行版都用 systemd,日志自然也就归 journald 管。journalctl 就是它的命令行客户端。我个人习惯,排查问题时第一件事就是跑 journalctl -xe,看最后几行有没有明显报错。

4.1.1 基本用法

命令 作用
journalctl -u nginx.service 查看指定服务的日志
journalctl --since "10 min ago" 只看最近 10 分钟的日志
journalctl -p err -b 只看本次启动以来的错误级别日志
journalctl -o json-pretty 以 JSON 格式输出,方便程序解析
我的习惯:排查问题时先用 -p err -b 缩小范围,再根据时间戳用 --since 精确锁定。别一上来就全量输出,几千行日志看得眼晕。

4.1.2 日志持久化

默认情况下,journald 的日志存在内存里,重启就没了。这显然不行。我建议你改一下配置:

# /etc/systemd/journald.conf
Storage=persistent
Compress=yes
SystemMaxUse=1G
RuntimeMaxUse=200M

改完重启服务:systemctl restart systemd-journald。这样日志会写到 /var/log/journal/ 目录下,重启也不会丢。

注意:如果 /var/log/journal/ 目录不存在,需要手动创建并设置权限:mkdir -p /var/log/journal && systemd-tmpfiles --create --prefix /var/log/journal。否则配置不生效。

4.2 logrotate:日志轮转的老司机

日志如果不轮转,磁盘迟早会被撑爆。我曾经接手过一个系统,日志文件 50GB,直接导致根分区满了,服务全挂。嗯,从那以后我每次部署都会检查 logrotate 配置。

4.2.1 核心配置

logrotate 的配置文件在 /etc/logrotate.conf 和 /etc/logrotate.d/ 目录下。每个服务一个配置文件,清晰好管理。

# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    postrotate
        /usr/sbin/nginx -s reopen
    endscript
}
参数 说明
daily 每天轮转一次
rotate 7 保留最近 7 个归档文件
compress 轮转后压缩为 .gz
delaycompress 延迟一次再压缩,方便查最近日志
postrotate 轮转后执行的命令,通知 nginx 重新打开日志文件
关键点:很多服务在日志轮转后需要发送信号才能继续写新日志。比如 nginx 用 nginx -s reopen,rsyslog 用 kill -HUP。忘了这一步,日志就写不进去了。

4.2.2 手动测试

配置完别直接等 cron 执行,先手动跑一下验证:

logrotate -d /etc/logrotate.d/nginx   # 调试模式,只模拟不执行
logrotate -f /etc/logrotate.d/nginx   # 强制轮转

我每次改完配置都会跑 -d 看看有没有语法错误,确认无误再用 -f 执行一次。这个习惯帮我避免过好几次线上事故。

4.3 Fluentd:统一日志采集层

当服务器多了,日志就不能只留在本地了。你需要一个统一的采集层,把日志集中起来。Fluentd 就是干这个的。它用 Input → Filter → Buffer → Output 的管道模型,灵活又稳定。

4.3.1 架构概览

Fluentd 日志采集管道 Input tail / http / syslog Filter grep / record_transformer Buffer 内存 / 文件 Output ES / S3 / Kafka 管道说明: • Input:从文件尾、HTTP、Syslog 等来源读取日志 • Filter:对日志进行过滤、解析、字段变换 • Buffer:暂存数据,防止突发流量导致下游过载 • Output:写入 Elasticsearch、S3、Kafka 等目标

4.3.2 实战配置:采集 Nginx 日志到 Elasticsearch

下面是一个我在生产环境用过的配置。它从 Nginx 的 access.log 采集日志,解析成结构化数据,然后写入 Elasticsearch。

# /etc/fluentd/fluent.conf
<source>
  @type tail
  path /var/log/nginx/access.log
  pos_file /var/log/fluentd/nginx-access.pos
  tag nginx.access
  <parse>
    @type nginx
  </parse>
</source>

<filter nginx.access>
  @type record_transformer
  <record>
    hostname "#{Socket.gethostname}"
  </record>
</filter>

<match nginx.access>
  @type elasticsearch
  host localhost
  port 9200
  index_name fluentd.nginx.${tag}
  flush_interval 5s
</match>
避坑指南:我曾经因为忘了配 pos_file,导致 Fluentd 每次重启都从头读日志,ES 里重复数据一大堆。pos_file 记录读取位置,保证重启后从断点续传,这个一定要配。

4.3.3 Buffer 参数调优

Buffer 是 Fluentd 的稳定器。参数调不好,要么丢数据,要么内存爆炸。我一般这样配:

<buffer>
  @type file
  path /var/log/fluentd/buffer
  flush_mode interval
  flush_interval 5s
  retry_max_times 3
  retry_wait 2s
  chunk_limit_size 8MB
  total_limit_size 1GB
</buffer>
参数 推荐值 说明
flush_interval 5s 每 5 秒刷一次,平衡实时性和吞吐
chunk_limit_size 8MB 单个 chunk 大小,避免单次写入过大
total_limit_size 1GB 总 buffer 上限,防止磁盘写满
retry_max_times 3 失败重试次数,避免无限重试阻塞管道

4.4 三剑客配合使用

实际生产环境里,这三个工具是配合着用的:

  • journald 负责系统级日志的收集和存储
  • logrotate 负责本地日志文件的轮转和压缩
  • Fluentd 负责把日志从各个节点采集到中央存储

举个例子:Fluentd 的 @type tail 读取 Nginx 日志时,logrotate 正好在轮转文件。如果 Fluentd 没处理好文件切换,就会丢日志。解决办法是让 logrotate 的 postrotate 通知 Fluentd 重新打开文件,或者用 copytruncate 模式避免文件句柄变化。

我的建议:日志采集链路越简单越好。能用 journald 直接转发就别用 tail 读文件。能用 Fluentd 自带插件就别自己写脚本。每多一层,就多一个故障点。