现代 Linux 生产服务通常由 systemd 统一管理。真正可靠的服务管理不仅是会执行 start 和 restart,还要处理启动依赖、运行身份、环境变量、日志、重启策略、资源限制、优雅停机、健康验证和失败回滚。
核心原则:同一个进程只能有一个主管理器。不要同时让 systemd、Supervisord、脚本和定时任务重复拉起同一服务,否则会产生多实例、端口冲突和无限重启。
一、适用环境与目标
本文适用于使用 systemd 的 Ubuntu、Debian、Rocky Linux、AlmaLinux、CentOS Stream 和 RHEL。示例服务名为 app.service,程序目录为 /opt/app,实际使用时请替换。
systemctl --version
systemd-analyze --version
cat /etc/os-release
ps -p 1 -o comm=目标是让服务具备可预测的启动、明确的运行权限、可查询的日志、有限的自动恢复能力和经过验证的停止与回滚流程。
二、先理解 Unit 文件的位置和优先级
/usr/lib/systemd/system或/lib/systemd/system:软件包提供的 Unit,不建议直接修改。/etc/systemd/system:管理员创建或覆盖的 Unit,优先级更高。/run/systemd/system:运行期临时 Unit,重启后消失。
systemctl cat app.service
systemctl show app.service -p FragmentPath -p DropInPaths
systemd-delta修改软件包自带 Unit 时优先使用 systemctl edit app.service 创建 Drop-in,避免升级软件包时覆盖本地修改。
三、常用操作及其差异
systemctl status app.service
systemctl start app.service
systemctl stop app.service
systemctl restart app.service
systemctl reload app.service
systemctl reload-or-restart app.service
systemctl enable --now app.service
systemctl disable app.service
systemctl is-active app.service
systemctl is-enabled app.service
systemctl is-failed app.servicereload 只有在 Unit 定义了 ExecReload 且应用支持热加载时才有效。enable 只创建开机启动关系,不代表服务当前已经启动。
四、生产可用的 Service Unit 示例
[Unit]
Description=Example Application
Documentation=https://docs.example.com/app
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=300
StartLimitBurst=5
[Service]
Type=simple
User=app
Group=app
WorkingDirectory=/opt/app
EnvironmentFile=-/etc/app/app.env
ExecStartPre=/opt/app/bin/check-config
ExecStart=/opt/app/bin/server --config /etc/app/app.yaml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=10s
TimeoutStartSec=60s
TimeoutStopSec=45s
KillSignal=SIGTERM
SuccessExitStatus=143
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/app /var/log/app
LimitNOFILE=65536
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target保存为 /etc/systemd/system/app.service 后先验证语法:
systemd-analyze verify /etc/systemd/system/app.service
systemctl daemon-reload
systemctl start app.service
systemctl status app.service --no-pager五、Type 必须匹配程序行为
simple:ExecStart 启动的前台进程就是主进程,最常用。
exec:与 simple 类似,但只有可执行文件成功启动后才认为启动成功。
forking:传统程序会自行 fork 到后台,通常还要配置 PIDFile。
oneshot:执行一次任务后退出,可配合 RemainAfterExit。
notify:应用通过 sd_notify 主动告知 systemd 已就绪。
容器化和现代服务通常应以前台方式运行,避免程序自行 daemonize 后让 systemd 追踪错误的 PID。
六、启动依赖和“网络已就绪”
After=network.target 只表示网络管理服务已经启动,不保证地址、路由或 DNS 可用。确实依赖网络时通常使用:
Wants=network-online.target
After=network-online.target但网络依赖仍可能在运行过程中故障。应用必须实现连接超时、重试和退避,而不是把所有可靠性寄托在启动顺序上。
七、环境变量和 Secret
# /etc/app/app.env
APP_ENV=production
APP_PORT=8080install -o root -g app -m 0640 /tmp/app.env /etc/app/app.env
systemctl show app.service -p Environment -p EnvironmentFiles不要把数据库密码、Token 或私钥直接写在公开的 Unit 文件和命令行参数中。敏感信息优先使用 Vault、云 Secret Manager、systemd credentials 或权限严格控制的文件。即使使用 EnvironmentFile,也要评估进程环境是否会被有权限的用户读取。
八、日志和退出码诊断
systemctl status app.service --no-pager -l
journalctl -u app.service -b --no-pager
journalctl -u app.service --since '-30 minutes' -p warning
journalctl -u app.service -f
systemctl show app.service -p ExecMainCode -p ExecMainStatus -p Result -p NRestarts
coredumpctl list app
coredumpctl info app常见结果包括:
status=203/EXEC:ExecStart 路径不存在、不可执行或解释器错误。status=217/USER:用户或组不存在。status=1/FAILURE:应用自行返回失败,需要查看应用日志。Result=timeout:启动或停止超过 Unit 设置的超时时间。Result=signal:进程被信号终止,继续检查 OOM、崩溃和人工操作。
九、服务反复重启的排查顺序
确认故障开始时间、业务影响和最近变更。
查看
status、退出码、NRestarts和最近日志。核对 ExecStart、WorkingDirectory、User、Group、环境变量和权限。
检查配置语法、端口占用、依赖服务、磁盘和内存。
确认是否触发启动频率限制或被 OOM Killer 终止。
以服务账号在前台运行同一命令复现。
修复后重置失败状态并验证启动、停止和重启。
systemctl show app.service -p NRestarts -p Result
journalctl -k --since '-30 minutes' | grep -Ei 'oom|killed process'
ss -lntp | grep ':8080'
sudo -u app -- /opt/app/bin/check-config
systemctl reset-failed app.service
systemctl start app.service不要简单把 Restart=always 和很短的 RestartSec 当作修复。持续崩溃会形成重启风暴,增加日志、CPU 和下游压力。
十、优雅停止与健康验证
收到 SIGTERM 后,服务应停止接收新请求、完成或转移在途任务、关闭连接并在 TimeoutStopSec 内退出。超时后 systemd 可能发送 SIGKILL,可能造成事务中断或数据损坏。
systemctl stop app.service
systemctl show app.service -p ActiveState -p SubState
systemctl start app.service
curl -fsS http://127.0.0.1:8080/health
ss -lntp | grep ':8080'
journalctl -u app.service --since '-5 minutes' --no-pager验证不能只看 active (running),还应检查监听端口、健康接口、依赖连接、核心业务请求和监控指标。
十一、资源限制与安全加固
systemctl show app.service -p LimitNOFILE -p MemoryMax -p CPUQuota
systemd-analyze security app.service可以根据压测结果设置 MemoryMax、CPUQuota、TasksMax 和 LimitNOFILE,但不要照抄过小的限制。使用 NoNewPrivileges、PrivateTmp、ProtectSystem 等沙箱选项时,要明确应用实际需要访问的路径和内核能力。
十二、Supervisord 的使用边界
Supervisord 适用于遗留应用或同一受控环境中的多个前台进程:
[program:worker]
command=/opt/app/bin/worker
directory=/opt/app
user=app
autostart=true
autorestart=unexpected
startsecs=5
stopwaitsecs=30
stopasgroup=true
killasgroup=true
stdout_logfile=/var/log/app/worker.log
stderr_logfile=/var/log/app/worker.err.logsupervisorctl status
supervisorctl reread
supervisorctl update
supervisorctl restart worker顶层可以由 systemd 管理 supervisord,但不要同时让 systemd 直接管理同一个 worker。容器环境中也不要再叠加不必要的进程管理层。
十三、变更、回滚和上线检查
备份 Unit、环境文件和应用配置。
用
systemd-analyze verify和应用检查命令验证。执行
daemon-reload,先在单节点灰度重启。检查日志、端口、健康接口、错误率和延迟。
观察一个完整业务周期,再扩大范围。
异常时恢复备份文件、重新加载并启动旧版本。
cp -a /etc/systemd/system/app.service /etc/systemd/system/app.service.bak.$(date +%F-%H%M%S)
systemd-analyze verify /etc/systemd/system/app.service
systemctl daemon-reload
systemctl restart app.service
systemctl is-active app.service
curl -fsS http://127.0.0.1:8080/health十四、交付检查清单
Unit 文件路径、Drop-in 和最终合并配置已核对。
Type、运行用户、工作目录和 ExecStart 与程序行为一致。
Secret 未出现在 Unit、命令行和公开日志中。
重启次数有限制,不会形成重启风暴。
优雅停止、超时和信号处理已经验证。
服务状态、端口、健康接口、日志和业务链路全部正常。
备份和回滚命令可用,监控与告警已经恢复。
总结
可靠的服务管理是一套完整生命周期:明确依赖和权限,正确启动,持续记录,有限恢复,优雅停止,验证业务,并能安全回滚。systemd 是执行工具,真正决定生产质量的是围绕它建立的变更和验证流程。