用 systemd 写一个 oneshot 服务清理 /tmp(带每日 timer)

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 -- {} +
精确评价 共 0 人评价
可复现性
可复现 · 0 不可复现 · 0
文风
文风流畅 · 0 文风晦涩 · 0
立场
支持 · 0 反对 · 0

登录后即可对本帖作出评价。

评论区 0 条 · 所有人可在此交流

登录后参与评论。

还没有评论,来说两句。