tmpfiles.d(5) 能解决大多数自动清理需求,但偶尔我们需要更"剧本化"的清理逻辑:
按业务规则保留某些目录、清完后通知 Prometheus pushgateway,或者顺便压缩归档。
这时候手写一个 oneshot 服务比改 tmpfiles.d 干净得多。
1. 单元文件
# /etc/systemd/system/tmp-purge.service
[Unit]
Description=Purge stale /tmp entries older than 24h
After=network.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/tmp-purge.sh
Nice=10
IOSchedulingClass=idle
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/tmp
NoNewPrivileges=true
ReadWritePaths=/tmp 是关键 —— ProtectSystem=strict 之后整个 / 默认只读,
不显式开白名单会让脚本写不了任何东西。
2. 脚本
#!/usr/bin/env bash
# /usr/local/sbin/tmp-purge.sh
set -euo pipefail
PROTECTED='^/tmp/(systemd-private-|\.X11-unix|\.ICE-unix)'
find /tmp -mindepth 1 -maxdepth 1 \
-mmin +1440 \
! -regex "$PROTECTED" \
-exec rm -rf -- {} +
logger -t tmp-purge "completed at $(date -Iseconds)"
-mmin +1440 是 24 小时;用 +10080 改为一周。-regex 那条把
systemd-private-* 和 X session 套接字目录排除掉,否则容易踩坑。
3. timer
# /etc/systemd/system/tmp-purge.timer
[Unit]
Description=Daily /tmp purge
[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true
RandomizedDelaySec=15m
[Install]
WantedBy=timers.target
Persistent=true 在机器关机过那一时刻后会补跑一次。RandomizedDelaySec 让多
台机器错峰,避免 NFS / 备份目标在同一时刻被打爆。
4. 启用 + 校验
chmod +x /usr/local/sbin/tmp-purge.sh
systemctl daemon-reload
systemctl enable --now tmp-purge.timer
# 手动跑一次确认
systemctl start tmp-purge.service
journalctl -u tmp-purge.service -n 20 --no-pager
# 看下一次触发时间
systemctl list-timers tmp-purge.timer
踩过的坑
- 早期版本忘记
Type=oneshot,systemd 默认 simple 模式会把"脚本退出"判为
"服务挂了",反复重启把磁盘 I/O 拉爆。一定要写 oneshot。 - 在很老的 systemd(< 235)上
ReadWritePaths=/tmp会被PrivateTmp=true
覆盖;如果你的 base unit 继承了PrivateTmp,记得显式写PrivateTmp=false。 find -delete不会递归删除非空目录,所以这里用-exec rm -rf -- {} +。
登录后参与评论。