跳到正文
systemd:从「能用」到「能信赖」,中间差一个守护
systemd:从「能用」到「能信赖」,中间差一个守护

systemd:从「能用」到「能信赖」,中间差一个守护

一开始我用 nohup 裸跑程序,关掉终端还能跑,但进程崩了不会自己起来。这篇不讲 unit 大全,讲我为什么判定「裸跑不算上线」,以及守护这一面站的位置。

速读#

nohup 只解决关终端不退出,systemd 才解决崩溃恢复、开机自启、统一日志和服务管理。一个你睡觉时挂了不会自己回来的进程,不算真正上线;但 systemd 只拉进程级故障,整机卡死还要另看。

裸跑不算上线#

我的判定标准:一个我睡觉时挂了、不会自己回来的进程,不叫服务,叫「还没跑稳的程序」。从能用到能信赖,中间差的就是守护

nohup 裸跑systemd
崩溃恢复不会可自动重启
开机自启不会支持
日志自己重定向journalctl 统一收
管理靠 killstart / stop / restart

nohup 只解决「关终端别杀我」;systemd 解决「崩了能回来、重启能起来、日志有人收、管理有命令」一整面。不是同一量级。

一份最小 unit,核心就那两行#

完整的最小 unit 长这样(/etc/systemd/system/myapp.service):

[Unit]
Description=myapp
After=network.target
[Service]
User=s1oopx
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/python3 main.py
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target

其中真正承担「守护」语义的是两行:

保什么
Restart=always崩了回来
WantedBy=multi-user.target重启了回来

其余是交代清楚身份和位置:用哪个用户跑(别用 root)、在哪个目录跑、执行什么。ExecStart 要写绝对路径——systemd 不继承你 shell 的 PATH,这是从裸跑迁移过来最容易撞的一下。还有一处容易望文生义:After=network.target 只保证启动顺序排在网络配置之后,不保证网络真的通了;真要「等到网络可用」,得用 network-online.target。练手服务差别不大,但排查「开机后第一次请求失败」这类问题时,知道这层区别就不会走错方向。

Terminal window
systemctl enable --now myapp # 开机自启 + 立即启动
systemctl status myapp # 第一眼:活着吗、重启过几次
journalctl -u myapp -f # 看日志

一个容易误解的点:Restart=always 拉的是异常退出systemctl stop 停的进程不会被拉起来。手动停是意图,崩溃才是故障,systemd 分得清这两件事。另外崩得太密会触发 start limit,systemd 直接放弃重启——这其实是对的:三秒一崩的服务,无限重启只是把故障刷成日志噪音,该做的是去查根因。

改完要 reload#

改完 unit 一定 systemctl daemon-reload,否则 systemd 还在用旧配置。踩过:改了 ExecStart 没 reload,重启发现跑的还是旧命令。

配置改了 ≠ 生效,要触发加载。 Nginx 改完要 reload、fail2ban 改完要 restart——「改」和「生效」之间那一步,最容易漏。

边界:它拉的是进程退出#

Restart=always 拉的是进程崩了、被杀了。拉不起更彻底的挂:系统卡死、硬件故障、内核 panic——那是 watchdog 这一层的事。

我不假装 systemd 万能,也不假装已经上了 watchdog。守护是分层的,每层挡什么级别的挂,照实写。

守护要配可观测#

systemd 负责拉回来,探针负责通知我它挂过。自愈是好事,自愈了还不知道,就少了一次复盘机会。 我用哪吒探针补这一环。

事后翻 journalctl -u myapp --since yesterday,能看到它什么时候退、退出码多少、几秒后被拉起——自愈的每一次现场都留了底,这是裸跑时代想都不敢想的。

守护是「上线」的分水岭,且它是分层的:systemd 挡进程级,watchdog 挡更底层。

版权许可

CC BY-NC-SA 4.0 本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

相关文章

s1oopX

登录 s1oopX