小服务器上给常驻脚本加 systemd 守护与日志轮转:一次踩坑后的整理

举报
yd_274356910 发表于 2026/10/29 09:25:43 2026/10/29
【摘要】 前段时间,我在一台 2 核 2G 的小服务器上跑了一个常驻采集脚本:定时拉取数据、写本地文件、偶尔调一次接口。刚开始图省事,直接用 nohup 加 & 扔在后台,结果一个月里出了三回问题:一次是服务器重启后脚本没起来,一次是日志把磁盘写满,还有一次是脚本明明在跑,却读不到配置文件。后来我花了一个下午把它改成 systemd 守护,才算彻底消停。这篇就把这套做法整理出来,重点不是抄配置,而是讲...

前段时间,我在一台 2 核 2G 的小服务器上跑了一个常驻采集脚本:定时拉取数据、写本地文件、偶尔调一次接口。刚开始图省事,直接用 nohup 加 & 扔在后台,结果一个月里出了三回问题:一次是服务器重启后脚本没起来,一次是日志把磁盘写满,还有一次是脚本明明在跑,却读不到配置文件。后来我花了一个下午把它改成 systemd 守护,才算彻底消停。这篇就把这套做法整理出来,重点不是抄配置,而是讲清楚每个字段为什么要这样写。

先说清楚:systemd 不是 nohup 的加强版

nohup 只能解决“终端关闭后进程不死”,它不管进程崩溃、不管开机自启、也不管日志去哪了。systemd 则是把进程变成一个受管理的服务:崩溃了能按策略重启,开机能自动拉起,日志统一进 journald,还能限制资源、指定运行用户和工作目录。对常驻脚本来说,这几项恰恰是最容易出事的地方。

一份可以直接用的 service 示例

假设脚本在 /opt/collector/collect.py,用 Python 3 运行,环境变量放在 /opt/collector/.env 里,日志希望同时进 journald。把下面内容保存为 /etc/systemd/system/collector.service:

[Unit]
Description=Collector daemon
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=collector
WorkingDirectory=/opt/collector
EnvironmentFile=/opt/collector/.env
ExecStart=/usr/bin/python3 /opt/collector/collect.py
Restart=on-failure
RestartSec=5
StartLimitIntervalSec=300
StartLimitBurst=5
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

然后执行 systemctl daemon-reload、systemctl enable --now collector,再用 systemctl status collector 和 journalctl -u collector -f 验证一次。能看到日志正常滚动、进程号稳定,才算真正跑起来。

Restart:别无脑写 always

Restart 有几个常见取值,选错了会适得其反。on-failure 只在非正常退出时重启,适合大多数采集、同步类脚本;always 则连正常退出也重启,如果脚本本身就是跑完一次就该退出的任务,用 always 会变成死循环。更关键的是要配 StartLimitIntervalSec 和 StartLimitBurst,防止脚本一启动就崩、崩了又立刻重启,把 CPU 和日志打爆。我的习惯是 5 分钟内最多重启 5 次,超过就先停下来人工看日志,而不是让它无限折腾。

WorkingDirectory:最容易被忽略的一行

这是我踩过最疼的坑。脚本里写了相对路径,比如 open(‘config.json’) 或者 open(‘data/out.csv’, ‘a’),在终端手动跑没问题,因为当前目录就是脚本所在目录;但 systemd 默认的工作目录是根目录,服务一启动就找不到文件,或者把数据写到了意想不到的地方。解决办法就是显式写 WorkingDirectory,并且在脚本里尽量用绝对路径,或者在启动时把路径算清楚。改完之后,一定要用 systemctl 的方式启动再验证一遍,不要只在终端里测。

EnvironmentFile:重启后环境变量别丢

很多人习惯在终端 export 一堆变量再启动脚本,换成 systemd 后这些变量全没了,脚本就会因为缺密钥、缺接口地址而悄悄失败。正确做法是把变量集中写进一个 EnvironmentFile,比如 /opt/collector/.env,每行一个 KEY=VALUE,并且把这个文件权限收紧到只有运行用户可读,比如 600。千万别把密钥直接写进 service 文件里随手分享,也不要把 .env 提交到代码仓库。

日志:journald 加 logrotate,别只留一种

脚本直接往文件里写日志,最怕两件事:一是没轮转,二是轮转了但进程还拿着旧文件句柄继续写。我的做法是分层:程序的标准输出和标准错误统一交给 journald,用 journalctl -u collector 查看,顺手就能按时间和级别过滤;同时在 /etc/systemd/journald.conf 里限制 SystemMaxUse,比如 500M,避免小磁盘被 journal 撑满。如果业务确实需要落盘日志,那就再配一个 logrotate,示例:

/opt/collector/logs/*.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
    copytruncate
}

copytruncate 的意思是先复制再截断原文件,适合那种不能重启、也不会重新打开日志文件的老脚本;如果脚本支持收到信号后重开日志,用 create 加 postrotate 发信号会更干净。配完之后别忘了用 logrotate -d /etc/logrotate.d/collector 做一次调试模式演练,确认它真的会轮转,而不是等磁盘满了才发现配置写错了。

上线前最后检查一遍

第一,重启机器验证一次自启,别只验证 systemctl start;第二,故意 kill 掉进程,看它是否按预期重启;第三,检查磁盘占用和日志增长速度,估算 7 天的量;第四,确认运行用户不是 root,能读到的数据刚好够用;第五,把 .env、service 文件和 logrotate 配置一起备份,下次换机器直接复用。常驻脚本最怕的不是写得多复杂,而是没人管、没日志、重启就丢。把这几处钉牢,小服务器也能跑得很稳。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。