Ansible清理历史临时文件避免磁盘占满的定时任务?

文章导读
服务器跑一段时间后,磁盘使用率会在某个时间点突然变得扎眼。如果监控迟迟没有预警,登录服务器时被 No space left on device 打断,第一步通常不是急着清理,而是确认空间到底被什么吃掉了。Ansible 适合用来做这件事,前提是先把边界想清楚。
📋 目录
  1. A 先判断:是不是历史临时文件在占盘
  2. B 用 cron 模块下发定时清理任务
  3. C 容易踩中的配置细节
  4. D 清理动作的风险边界
  5. E 执行后确认效果
A A

服务器跑一段时间后,磁盘使用率会在某个时间点突然变得扎眼。如果监控迟迟没有预警,登录服务器时被 No space left on device 打断,第一步通常不是急着清理,而是确认空间到底被什么吃掉了。Ansible 适合用来做这件事,前提是先把边界想清楚。

先判断:是不是历史临时文件在占盘

在服务器日常运行中,临时文件常由程序崩溃残留、日志轮转失效或任务处理中间态产生。它们分散在 /tmp、/var/tmp 或应用自定义的工作目录里,单个体积不大,但日积月累会悄悄占用大量磁盘空间。建议先用 df -h 观察磁盘使用率,再结合 du --max-depth=1 定位增长最快的目录。如果发现某个分区的使用率持续超过 75% 且没有对应的业务数据增长,就应当考虑引入定时清理机制。判断时注意区分留存期短的有效临时文件和已经失去价值的垃圾数据,不能简单按目录一刀切。

这个 75% 只是一个参考阈值,具体要看业务写入速度和分区大小。如果分区是 2T,75% 可能还有很大余量;如果分区只有 20G,75% 就接近告警。重点在于“持续增长”和“无法对应到业务数据”这两个信号。Ansible 在这里的作用是帮你在多台服务器上统一执行同样的诊断命令,比如用 ad-hoc 批量跑 df 和 du,而不是一台一台手动登录。

用 cron 模块下发定时清理任务

使用 Ansible 的 cron 模块创建系统定时任务,例如每天凌晨 3 点执行一次清理脚本。核心操作可以用 find 命令按文件修改时间过滤,再用 -delete 删除。一个保守的写法是先收集超过 7 天的 *.tmp 和 *.log 文件,打印列表确认后,再实际执行删除。结合 Ansible 的 shell 或 command 模块时,要确保命令在目标主机上可执行,并且避免使用可能被 shell 解释的通配符。建议将清理逻辑写成独立脚本,由 cron 调用,方便后续维护和扩展。

Ansible清理历史临时文件避免磁盘占满的定时任务?

上面这段说的是从 playbook 角度如何组织。实际项目中我建议把清理逻辑放在一个独立脚本或 include_tasks 中,不要直接在 cron 命令里塞一长串 find 和 rm。原因有两个:一是后续增加白名单或排除规则时,脚本更容易维护;二是 cron 模块每一次重新执行都会改写 crontab 条目,如果命令太长,容易在引号转义上出问题。用 find 命令时,-mtime +7-delete 组合要放在同一行,但如果交给 Ansible 的 shell 模块执行,建议写成多行脚本,并确保目标主机上 find 命令的路径可用。

---
- name: 清理历史临时文件
  hosts: all
  vars:
    tmp_clean_paths:
      - /tmp
      - /var/tmp
  tasks:
    - name: 收集 7 天前的临时文件
      ansible.builtin.find:
        paths: "{{ tmp_clean_paths }}"
        patterns:
          - '*.tmp'
          - '*.part'
        age: '7d'
        hidden: yes
      register: tmp_files

    - name: 预览待清理文件
      ansible.builtin.debug:
        var: tmp_files.files

    - name: 删除匹配文件
      ansible.builtin.file:
        path: "{{ item.path }}"
        state: absent
      loop: "{{ tmp_files.files }}"
      when: tmp_files.files | length > 0

这段 playbook 先用 find 模块收集文件,再逐个删除,比直接 find -delete 更安全。正式执行之前,可以在命令行加 --check 试运行,Ansible 会以 check_mode 执行并输出将要做的改动,但不会真正删除文件。等确认列表正确后,再关闭 check_mode 跑一遍。如果你是第一次部署,建议先加一个 when 条件,限定在测试主机组上。

容易踩中的配置细节

使用 Ansible 的 find 模块时,age 参数的单位默认是秒,需要显式写成 age=7d 表示 7 天。另一个坑是 find 的 patterns 参数默认不匹配隐藏文件,如果临时文件以点号开头,需要额外指定 hidden=yes。cron 模块创建的任务默认使用 crond 的分钟、小时字段,容易忽略系统时区与 UTC 的差异,导致计划执行时间与预期不符。此外,如果 Ansible 的控制节点与目标主机之间网络不稳定,cron 模块可能因连接超时而失败,但任务可能已经部分写入,建议在任务前加入 force=no 避免重复创建。最后,清理后记得用 df 输出前后对比,确认释放了预期空间,而不是依赖直觉判断。

Ansible清理历史临时文件避免磁盘占满的定时任务?

这里展开说一下。age 参数默认单位是秒,意味着如果你只写 age: 7,它会认为是 7 秒,而不是 7 天。写成 age: 7d 才代表 7 天,这个行为在不同 Ansible 版本里没有变化,但还是建议在目标主机上先跑一条 ansible all -m find -a "paths=/tmp age=7d" 做验证。patterns 默认不匹配隐藏文件,这在清理临时目录时很容易漏掉,比如一些工具生成的 .cache 文件。你可以加上 hidden: yes,也可以直接把点号开头文件写到 patterns 里,但后者要小心,可能把隐藏目录也匹配进去。时区问题比较隐蔽:cron 模块按目标主机的系统时区写入 crontab,如果 Ansible 控制节点和服务器时区不同,你看到的计划时间可能和实际执行时间差几个小时。最好先确认服务器用的是 UTC 还是本地时间,再决定 cron 字段。

清理动作的风险边界

清理临时文件最怕误删,所以必须设置明确的边界。只删除匹配特定模式的文件,比如 *.tmp、*.part,不要对 /tmp 做全量通配。对某些目录做白名单排除,例如在 find 命令里用 -path 排除正在运行的进程目录,或者先通过 lsof 检查文件是否被占用。Ansible 的 find 模块本身没有 lsof 调用能力,但你可以先用 shell 模块收集被占用的文件列表,再在删除任务中排除这些路径。这个步骤比较繁琐,但值得做,尤其是涉及多实例应用时。

另一个更保守的做法是:把不确定价值的文件先移动到 /tmp/archive 或应用自己的归档目录,保留 24 小时再从归档目录删除。这样即使误判,也还有恢复的机会。在 Ansible 中可以用 command 模块执行 mv,或者用 file 模块的 state=absent 配合 copy 模块实现移动,但涉及大目录时不如先打包再用 cron 删除。要避免在业务高峰时段执行清理,因为文件可能正在写入。建议把清理时间安排在凌晨,并且加上随机延时,防止所有服务器同时执行造成磁盘 IO 波动。

Ansible清理历史临时文件避免磁盘占满的定时任务?

执行后确认效果

每次清理后,用 df -h 对比任务执行前后的输出,确认释放了空间。如果任务跑完发现磁盘使用率没有明显变化,先检查是否还有别的服务在写入,或者 find 条件没有匹配到文件。可以把 Ansible 的 cron 任务输出重定向到日志文件,比如在 cron 命令中追加 >> /var/log/tmp_clean.log,然后定期看日志里删除了多少文件。需要说明的是,这个日志文件本身也会增长,别忘了把它也纳入日志轮转或排除在清理范围之外。

如果发现误删,立即停止清理任务。恢复策略取决于文件是否还有备份。所以不建议一开始就在全公司服务器上跑清理,先挑几台测试机验证效果。Ansible 的 cron 模块支持指定 hosts 或限制运行范围,比如先用 -l 一台服务器,再逐步扩大。任务本身要保持幂等,每次执行重新收集文件列表,这样即使上一次中断,下一次运行也会重新计算,不会依赖内存里的旧结果。

临时文件清理不是一劳永逸的事。Ansible 能帮你把判断、删除、记录变成可重复执行的流程,前提是边界和预期验证都清楚。如果你的服务器当前还没有出现磁盘告警,先建一个只统计不删除的任务跑一段观察,比直接上清理动作更稳妥。