1. 自定义 systemctl 服务

1. 自定义 systemctl 服务

问题:有一个脚本或后台程序,希望它能像系统服务一样被 systemctl startsystemctl stopsystemctl enable 管理。

核心systemctl 只是管理入口,真正需要自定义的是 systemd 的 unit 文件。把服务描述写进 /etc/systemd/system/*.service,再让 systemd 重新加载配置即可。

最小服务文件

假设有一个可执行程序:

/opt/my-app/bin/server

可以新建:

sudo vim /etc/systemd/system/my-app.service

内容如下:

[Unit]
Description=My App Service
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/my-app
ExecStart=/opt/my-app/bin/server
Restart=on-failure
RestartSec=3

[Install]
WantedBy=multi-user.target

这三个区块分别负责不同事情:

  • [Unit]:描述服务本身,以及它和其他系统组件的关系。
  • [Service]:定义服务怎么启动、在哪个目录运行、失败后是否重启。
  • [Install]:定义执行 enable 时要挂到哪个启动目标下。

让配置生效

写完 unit 文件后,需要重新加载 systemd 配置:

sudo systemctl daemon-reload

然后就可以像普通服务一样管理:

sudo systemctl start my-app
sudo systemctl stop my-app
sudo systemctl restart my-app
sudo systemctl status my-app

如果希望开机自启:

sudo systemctl enable my-app

取消开机自启:

sudo systemctl disable my-app

常用字段

ExecStart 是最关键的字段,它必须指向实际启动命令。最好使用绝对路径,避免环境变量和工作目录不一致带来的问题。

ExecStart=/usr/bin/node /opt/my-app/dist/server.js

如果服务需要指定运行用户,可以加上:

User=app
Group=app

如果程序依赖环境变量,可以直接写在 unit 文件中:

Environment=NODE_ENV=production
Environment=PORT=3000

也可以放到独立文件里:

EnvironmentFile=/etc/my-app.env

查看日志

服务启动失败时,先看状态:

sudo systemctl status my-app

再看日志:

journalctl -u my-app -e

实时跟踪日志:

journalctl -u my-app -f

常见错误

修改了 service 文件但没有生效

改完 /etc/systemd/system/my-app.service 后,需要重新执行:

sudo systemctl daemon-reload
sudo systemctl restart my-app

命令在终端能跑,放进 systemd 后失败

systemd 启动服务时不会继承你当前 shell 的完整环境。把命令、解释器、配置文件都写成绝对路径,必要时显式设置 WorkingDirectoryEnvironmentFile

服务启动后立刻退出

如果程序本身是常驻进程,通常用 Type=simple。如果脚本执行完就退出,systemd 会认为服务结束了。此时要么让程序保持前台运行,要么根据实际行为选择更合适的 service 类型。

自定义 systemctl 的本质不是改 systemctl 命令,而是定义一个 systemd unit,让系统知道这个服务如何启动、停止、重启和开机自启。