用 nohup 或 screen 跑服务,机器重启后就没了,进程崩溃也没人拉起。把应用交给 systemd 管理,可以获得开机自启、崩溃重启、统一日志和资源限制。本文用一个例子说明 service 单元的常用配置,以及最常见的几个坑。
保存为 /etc/systemd/system/myapp.service:
[Unit]
Description=My Application
After=network-online.target
Wants=network-online.target
StartLimitIntervalSec=60
StartLimitBurst=5
[Service]
Type=simple
User=app
Group=app
WorkingDirectory=/opt/myapp
EnvironmentFile=-/etc/myapp/env
ExecStart=/opt/myapp/bin/server --config /etc/myapp/config.yaml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=3
LimitNOFILE=65536
TimeoutStopSec=30
[Install]
WantedBy=multi-user.target加载并启动:
systemctl daemon-reload
systemctl enable --now myapp
systemctl status myapp修改单元文件后必须 daemon-reload,否则 systemd 仍使用旧配置。
User 和 Group:不要用 root 运行应用,按最小权限原则使用专用账号。WorkingDirectory:设置工作目录,避免相对路径出错。EnvironmentFile:从文件读取环境变量,路径前的 - 表示文件不存在时不报错。systemd 不会加载 ~/.bashrc,应用依赖的环境变量要显式提供。ExecStart:必须写绝对路径,且不是通过 shell 解释的,管道和重定向不会生效。需要 shell 特性时写成 /bin/bash -c '...'。Restart:常用取值有 on-failure(异常退出时重启)和 always(无论如何都重启)。手动 systemctl stop 不会触发重启。RestartSec:重启前等待的时间,避免快速循环重启。StartLimitIntervalSec 与 StartLimitBurst:在时间窗口内启动次数超过上限,单元会进入 failed 状态不再重启,防止无限崩溃循环。这两项属于 [Unit] 段。LimitNOFILE:提高文件描述符上限,高并发服务常用。注意在 systemd 下,/etc/security/limits.conf 对服务不生效。TimeoutStopSec:停止时等待进程优雅退出的时间,超时后会被强制杀掉。After= 只控制顺序,不表示依赖;Wants= 和 Requires= 才表达依赖关系。常见写法:
After=network-online.target 加 Wants=network-online.target。After=mysql.service 加 Wants=mysql.service(若数据库在本机)。After=network.target 只表示网络栈已启动,不保证已获得 IP 地址;需要网络可用时用 network-online.target。
即使设置了顺序,也不要假设依赖服务“已经可用”。应用本身应当对数据库、缓存的连接做重试,这比依赖启动顺序更可靠。
simple(默认):ExecStart 启动的进程就是主进程,适合前台运行的程序。forking:程序自己 fork 到后台,需要配合 PIDFile。oneshot:执行完就退出,适合一次性任务。notify:程序启动完成后通过 sd_notify 通知 systemd,适合需要精确判断启动完成的服务。常见错误:程序本身做了守护化(后台运行),却用 simple 类型,systemd 看到主进程退出就认为服务结束,随后清理掉子进程。解决方式是让程序在前台运行,或使用 forking。
# 查看该服务日志,实时跟踪
journalctl -u myapp -f
# 查看本次启动以来的日志
journalctl -u myapp -b
# 按时间过滤
journalctl -u myapp --since "1 hour ago"
# 检查单元文件语法
systemd-analyze verify /etc/systemd/system/myapp.service应用把日志输出到标准输出和标准错误,journald 会自动收集。需要长期保存时,检查 /etc/systemd/journald.conf 中的持久化和大小限制,磁盘占用可以用 journalctl --disk-usage 查看。
ExecStart 路径不存在或没有执行权限。User 指定的用户不存在。systemctl reset-failed myapp 清除状态,并先解决崩溃原因。EnvironmentFile 后需要重启服务,修改单元文件还需要 daemon-reload。AmbientCapabilities=CAP_NET_BIND_SERVICE,或者让反向代理监听 80/443。在不影响功能的前提下,可以逐项尝试:NoNewPrivileges=true、ProtectSystem=full、PrivateTmp=true、ProtectHome=true。每开启一项都要测试应用是否仍能正常读写所需路径。可用 systemd-analyze security myapp 查看当前的暴露评分。
Restart=on-failure 加 RestartSec,并理解启动频率限制。After 管顺序,Wants 管依赖,应用自身仍要做连接重试。journalctl -u 排查,改完单元文件记得 daemon-reload。原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。