systemd 服务配置生成

把一条启动命令配成开机自启的 systemd 服务,工作目录、运行用户、环境变量、重启策略和日志一次填完。

各段在做什么

[Unit] 只放描述和依赖顺序。After=network-online.target 保证网络真的可用之后再启动,光靠 network.target 是不够的——那个目标在网卡拿到地址之前就可能已经达成。

[Service] 里的顺序会影响环境变量的可见性:EnvironmentFile 和 Environment 都要写在 ExecStart 之前。改这些行之后记得 daemon-reload,否则跑的还是旧值。

TimeoutStopSec=20 给进程 20 秒收尾:systemd 先发 SIGTERM,超时后补 SIGKILL。Node 服务要正确处理 SIGTERM(关掉监听、把在途请求处理完),否则每次重启都是硬杀。

以 root 运行的系统服务出了漏洞就等于整台机器失守,所以默认给了 www-data 这样的普通用户。项目目录和日志文件的属主要跟着改成同一个用户,否则会出现「服务起来了但读不到文件」。

需要照着现成的部署脚本改,可以配合Nginx 配置生成和Dockerfile 生成一起用。

常见问题

为什么启动命令要写绝对路径?

systemd 不读登录 shell 的 PATH,也不加载 nvm。写 node、npm、python 这类命令时,它会在自己的默认 PATH 里找,结果往往是没有这个命令,或者找到系统自带的旧版本。先用 which node 查出真实路径再填进来。

服务起不来,报 203/EXEC 是什么意思?

ExecStart 指定的文件不存在或服务用户没有执行权限。用 nvm 时最常见:node 装在 /root 下,而 /root 是 0700,www-data 连目录都进不去。把 node 装到 /usr/local/bin 这类公共位置,或者用 NodeSource 装到 /usr/bin。

改了 .service 文件为什么不生效?

systemd 只在 daemon-reload 时重读单元文件。改完要执行 sudo systemctl daemon-reload,再 restart 服务;只 restart 用的还是内存里那份旧配置。

Environment 和 EnvironmentFile 有什么区别?

Environment= 把值直接写在单元文件里,适合不敏感的常量,比如 NODE_ENV=production;EnvironmentFile= 指向一个 KEY=VALUE 文件,适合放密钥和会变动的配置。用后者时注意文件权限,里面通常是数据库密码。

Restart=always 和 on-failure 怎么选?

常驻进程用 on-failure:正常退出(比如手动 systemctl stop)就不再拉起来。要保证进程永远在,例如崩溃后必须自愈的守护服务,才用 always。一次性任务(Type=oneshot)用 no,否则跑完会被反复执行。

加固项会不会把应用弄坏?

ProtectSystem=full 会把 /usr、/boot、/etc 挂成只读,ProtectHome 会让 /home 和 /root 不可写。应用需要往这些位置写文件时,用 ReadWritePaths= 单独放行。简单起见,先在测试机上跑一遍再上生产。