LabHub
学习 学习路径 课程

日志流水线设计

亲眼看轮转到底丢了什么

在 LabHub 中继续学习

目标

亲手构造日志轮转的两种方式,观察它们各自会丢失什么。 阅读说明与亲眼看到文件仍为 0 字节,效果完全不同。

为什么重要

错误配置日志轮转会导致以下两种情况之一。

无论哪一种,如果在调查事故时才发现,都已经太晚。

步骤

  1. 持续写入的进程 → /root/log/01-pid.txt
  2. 删除后空间未释放 → /root/log/02-deleted.txt
  3. 通过重启清理 → /root/log/03-restart.txt
  4. 模拟 rename 方式 → /root/log/04-rename.txt
  5. 模拟 copytruncate 方式 → /root/log/05-truncate.txt
  6. 编写 logrotate 配置 → /root/log/06-app.conf
  7. 说明会丢失什么 → /root/log/07-tradeoff.md
  8. 清理 → /root/log/08-clean.txt

参考

创建持续写入的进程

持续写入的进程 → /root/log/01-pid.txt

在后台启动一个持续写日志的进程。它必须始终保持文件打开并写入,因为本实验展示的现象全部由“打开的文件”引起。

mkdir -p /root/log
( i=0; while true; do i=$((i+1)); echo "line $i"; sleep 0.05; done ) >> /root/log/app.log &
echo $! > /root/log/01-pid.txt

关键在于将重定向放在循环外部。如果像 echo ... >> 파일 一样放在内部,每次都会打开再关闭;这样即使删除文件也不会留下 handle,rename 后也会写入新文件。真实 daemon 会打开文件一次后持续写入。

观察删除后空间仍未释放

删除后空间未释放 → /root/log/02-deleted.txt

执行 rm /root/log/app.log 后,查看 ls -l /proc/<PID>/fd | grep deleted。将结果保存到 /root/log/02-deleted.txt。文件虽然消失,进程却仍在写入,对应 block 也不会释放。

重新启动以完成清理

通过重启清理 → /root/log/03-restart.txt

终止该进程(kill <PID>)并重新启动。在 /root/log/03-restart.txt 中写两行:终止后 ls -l /proc/*/fd 2>/dev/null | grep -c deleted 的结果,以及说明重启为什么能解决问题的一行文字。

模拟 rename 方式

模拟 rename 方式 → /root/log/04-rename.txt

启动新的 writer 后,执行 mv /root/log/app.log /root/log/app.log.1,等待 5 秒,再用 wc -l 查看两个文件的行数。在 /root/log/04-rename.txt 中记录两者的行数。新的 app.log 可能根本没有创建,这正是需要 SIGHUP 的原因。

模拟 copytruncate 方式

模拟 copytruncate 方式 → /root/log/05-truncate.txt

使用 cp /root/log/app.log.1 /root/log/app.log.2 && : > /root/log/app.log.1 先复制后截断。5 秒后查看 wc -l /root/log/app.log.1。在 /root/log/05-truncate.txt 中记录刚截断后和 5 秒后的行数。目的是确认进程仍在继续写入。

编写 logrotate 配置

编写 logrotate 配置 → /root/log/06-app.conf

在 /root/log/06-app.conf 中编写 logrotate 配置。需要包含:path pattern、daily、rotate 14、compress、delaycompress、missingok、notifempty,以及 create 或 copytruncate 二选一。即使没有 logrotate 命令也没关系,目标是能够读写配置。

写明会丢失什么

说明会丢失什么 → /root/log/07-tradeoff.md

在 /root/log/07-tradeoff.md 中写四行:create 会丢失什么、copytruncate 会丢失什么、你选择的方式及其理由。本实验的结论是两者都并非没有代价。

清理

清理 → /root/log/08-clean.txt

使用记录的 PID 终止进程:kill $(cat /root/log/01-pid.txt)。然后使用 rm -f /root/log/app.log* 删除文件,并用 pgrep -f sleep | wc -l 之类的方法确认没有残留,将结果写入 /root/log/08-clean.txt。

⚠️ 不要使用 pkill -f 'while true'。pkill 的 -f 会把自身命令行也纳入检查对象,因此会杀死包含该字符串的 shell 自身。实际工作中,使用 pkill -f 断开自己 session 的事故也很常见。始终记录 PID 并据此终止,才更安全。