升级后自动重启:systemd 与脚本托管配置

配好 systemd 单元或 deploy.restart_command,升级后自动切换版本并拉起新进程,附脚本写法与排查方法。

DJM CMS 的在线升级分成两段:更新器负责下载、校验、切换 current 并做健康检查;新进程由谁来拉起,取决于站点是怎么启动的。

新安装的站点默认不会自动重启:install.sh 只负责铺目录和启动器,进程通常由宝塔 Go 项目或手工命令启动,既没有 systemd 单元,也没有重启命令,所以升级后会停在“请重启站点服务”。

下面按部署方式给出两种配置,配一次之后升级会自动完成。前提是当前运行的版本不低于 1.0.157:更新器在“当前正在运行的版本”里判断部署方式,所以 1.0.157 这一次升级仍然需要人工重启一次。

1. systemd 托管(推荐)

按安装文档创建单元,使用默认的 Restart=on-failure 和 KillMode=process,不需要额外配置,升级会自动完成。用 Restart=always 也可以,1.0.157 起同样能自动完成。

[Unit]
Description=DJM CMS
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=www
Group=www
WorkingDirectory=/www/wwwroot/djm-cms
Environment=DJM_CONFIG=/www/wwwroot/djm-cms/shared/configs/app.yaml
ExecStart=/www/wwwroot/djm-cms/current/bin/cms serve
Restart=on-failure
RestartSec=3
KillMode=process

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now djm-cms
systemctl status djm-cms

升级时更新器先切换 current 并写好结果,旧进程随后退出,systemd 拉起的就是新版本,不需要手工执行 systemctl restart。

2. 宝塔 Go 项目、supervisor 或自建脚本

在 shared/configs/app.yaml 增加重启命令。新安装生成的配置里已经有空的 deploy: 注释段,照着填即可:

deploy:
  restart_command: "/www/wwwroot/djm-cms/restart.sh"

更新器切换完成后会在独立会话里执行这条命令,所以脚本要自己完成“停旧进程 + 起新进程”:

#!/bin/sh
INSTALL_ROOT="/www/wwwroot/djm-cms"

# 先停掉本安装目录下的 CMS 进程
for pid in $(pgrep -f "bin/cms serve"); do
  if [ "$(readlink "/proc/$pid/cwd")" = "$INSTALL_ROOT" ]; then
    kill "$pid"
  fi
done
sleep 3

# 确认没有残留进程再启动,避免端口被占用
if ! pgrep -f "$INSTALL_ROOT/.*serve$" >/dev/null 2>&1; then
  cd "$INSTALL_ROOT" || exit 1
  DJM_CONFIG="$INSTALL_ROOT/shared/configs/app.yaml" \
    nohup "$INSTALL_ROOT/current/bin/cms" serve >> shared/logs/cms.log 2>&1 &
fi
chown www:www /www/wwwroot/djm-cms/restart.sh
chmod +x /www/wwwroot/djm-cms/restart.sh

注意脚本里按进程的工作目录匹配,而不是只写一个绝对路径。pkill -f 匹配的是完整命令行:如果旧进程是用相对路径 ./current/bin/cms serve 启动的,绝对路径模式匹配不到,旧进程会继续占用端口,新进程启动时报 address already in use。按 /proc/<pid>/cwd 判断还能避免误伤同机其它站点。

3. 确认配置生效

后台「系统更新」在升级完成后不会再停在“请重启站点服务”。也可以直接查状态接口:

curl -s https://cms.example.com/api/admin/v1/core-updates/status

返回里的 deployment 字段会说明当前识别到的托管方式:

  • kind:systemd,或者 external(面板、supervisor、自建脚本)。
  • auto_restart_command:配置了 deploy.restart_command 时显示实际执行的命令。
  • exit_restart:为 true 表示由 systemd 在旧进程退出后拉起新版本。

4. 失败时看哪里

  • update-state/updater.log:更新器与重启命令的执行记录,重启命令失败的原因也会写在这里。
  • update-state/result.json:本次升级的最终状态。
  • shared/logs/:重启脚本重定向的进程日志。
  • systemd 托管时用 journalctl -u djm-cms -n 100 查看。

后台「系统更新」页面会保留兜底命令,不会静默失败。

5. 常见问题

  • 升级一直停在“等待重启”:既没有 systemd 单元,也没有配置 deploy.restart_command。
  • 重启后版本没变:新进程没有起来,先看 updater.log 和端口占用。
  • 日志里出现 bind: address already in use:旧进程没停掉,检查脚本里的匹配条件。
  • restart.sh 执行了但没效果:确认脚本有执行权限、路径正确,并且用运行 CMS 的同一用户执行。

升级前的备份与回滚操作,见《版本升级、回滚与备份》。

继续阅读

安装部署 让 AI 帮你部署 DJM CMS 安装部署 SQLite 部署与备份:安装时选择单文件数据库 安装部署 宝塔面板部署 DJM CMS:数据库可选、Nginx 与进程守护 安装部署 反向代理、HTTPS 与健康检查 安装部署 版本升级、回滚与备份

讨论