Files
Havline/docs/system-upgrade.md
T
mrlee 2a0740a60f
CI / verify (push) Canceled after 0s
Docker Release / build-and-push (amd64, linux/amd64) (push) Canceled after 0s
Docker Release / build-and-push (arm64, linux/arm64) (push) Canceled after 0s
GitHub Release / release (push) Canceled after 0s
Docker Release / create-manifest (push) Canceled after 0s
chore(release): 发布 v1.9.2
- 「公网服务端」新增 Nginx 启停控制(Agent 0.15.2,停止带二次确认)
- frps 启动失败不再误报成功,并增强启动诊断
- frps vhost 端口禁止 80/443,避免与 VPS 上 Nginx 冲突
- 旧服务端配置的 tcp_mux / login_fail_exit 默认值补齐
- 新增安全审查、页面布局方案与场景操作文档
2026-10-07 19:26:02 +08:00

9.0 KiB
Raw Permalink Blame History

Havline Web 一键升级方案

实施版本:v1.3.0
适用范围:docker-compose.yml、docker-compose.hub.yml
不适用范围:docker-compose.host.yml、原生 systemd 安装、Windows 直接运行

1. 设计目标

在管理后台检测到新版本后,用户点击「一键升级」即可完成:

  1. 读取远端最新版本;
  2. 构建新的 havline 镜像;
  3. 只重建 havline 容器;
  4. 等待 /api/version 返回目标版本;
  5. 失败时尝试回滚到升级前的镜像 tag;
  6. 把升级过程和结果返回给前端。

2. 安全边界

主程序容器不挂载 /var/run/docker.sock。

只有 havline-updater sidecar 持有:

  • Docker Socket;
  • Docker CLI 与 Compose;
  • 项目目录;
  • Git。

主程序与 sidecar 通过一个共享 Unix Socket 通信:

浏览器
  → havline 后端 /api/system/update/*
  → /run/havline-updater/updater.sock
  → havline-updater
  → docker compose build/up

这表示:

  • 管理后台被攻破时,攻击者仍需通过 sidecar 的固定接口才能触发升级;
  • sidecar 只接受固定的 Compose 文件名和固定的 havline 服务名;
  • 不提供任意命令执行接口;
  • 不支持自定义 shell 命令。

3. 两种升级模式

Compose 文件 模式 行为
docker-compose.yml local 在项目目录检查 Git 工作区,干净时执行 git pull --ff-only,再构建
docker-compose.hub.yml git Compose 的 build context 直接从 Git 仓库拉取,updater 只负责构建与重建

两种模式都会:

  • 读取目标版本;
  • 构建 havline 镜像;
  • 执行 docker compose up -d --no-deps --force-recreate havline;
  • 轮询 /api/version 验证升级结果。

4. 首次部署

docker-compose.yml 和 docker-compose.hub.yml 会由主程序在共享卷中自动生成 updater Token 文件,sidecar 等待并读取该文件。HAVLINE_SESSION_SECRET 可以留空,由应用生成并写入数据目录;已有数据时必须沿用原值。

一键升级依赖 havline-updater 已经运行。

旧版本升级到 v1.3.0 时,需要先手动执行一次:

# 本地源码部署
docker compose up -d --build

# 单文件 Git 构建部署
docker compose -f docker-compose.hub.yml up -d --build

完成这一轮后,后续版本即可在 Web 页面一键升级。

5. 环境变量

主程序

变量 默认值 说明
HAVLINE_UPDATER_SOCKET /run/havline-updater/updater.sock 与 sidecar 通信的 Unix Socket

updater sidecar

变量 说明
HAVLINE_UPDATER_TOKEN 可选覆盖值;显式设置时优先于 Token 文件
HAVLINE_UPDATER_TOKEN_FILE 主程序与 sidecar 共享的 Token 文件,默认 /run/havline-updater/updater.token
HAVLINE_UPDATER_SOCKET sidecar 监听的 Unix Socket

| 变量 | 说明 | | HAVLINE_UPDATER_SOCKET | sidecar 监听的 Unix Socket | | HAVLINE_UPDATE_PROJECT_DIR | Compose 项目目录,容器内为 /workspace | | HAVLINE_UPDATE_COMPOSE_FILE | 只允许 docker-compose.yml、docker-compose.hub.yml、docker-compose.host.yml | | HAVLINE_UPDATE_SERVICE | 要重建的服务,默认 havline | | HAVLINE_UPDATE_MODE | local 或 git | | HAVLINE_UPDATE_REPO | git 模式下的仓库地址 | | HAVLINE_UPDATE_BRANCH | git 模式下的分支,默认 main | | HAVLINE_UPDATE_APP_URL | 健康检查地址,默认 http://havline:6893 | | DOCKER_SOCK | 宿主机 Docker Socket,默认 /var/run/docker.sock | | HAVLINE_UPDATE_HOST_DIR | 宿主机上 Compose 文件所在目录;留空时自动从 Compose 容器标签发现,设置后作为显式覆盖 |

6. 前端行为

侧栏版本区在满足以下条件时显示「一键升级」:

  • sidecar 可用;
  • 远端版本高于当前版本;
  • 当前没有正在执行的升级任务。

点击后弹窗显示:

  • 当前版本;
  • 目标版本;
  • 升级方式;
  • 当前阶段;
  • 最近日志。

升级开始后,页面会持续轮询升级状态。容器重建期间请求会短暂失败,前端会显示「服务正在重启,等待恢复」,恢复成功后自动刷新页面。

7. 回滚

升级前会记录当前容器镜像。升级后健康检查超时,sidecar 会尝试:

HAVLINE_VERSION=<上一个版本> docker compose up -d --no-deps --force-recreate havline

回滚是否成功会写入升级日志。

8. 常见问题

侧栏没有「一键升级」

检查:

  1. 当前是否使用 docker-compose.yml 或 docker-compose.hub.yml;
  2. havline-updater 容器是否在运行;
  3. HAVLINE_UPDATER_SOCKET 是否在两侧指向同一路径;
  4. 共享卷 havline-updater-sock 是否存在。

如果状态接口返回:

读取远端版本失败:git clone ... signal: killed ... fatal: early EOF

说明运行中的 updater 仍是旧版本,仍在用完整克隆读取 VERSION。升级到 v1.3.2 后,updater 会改用 Gitea API / raw 地址读取版本文件。由于 sidecar 不能升级自身,需要先手动重建一次:

docker compose -f docker-compose.hub.yml up -d --build updater

# 本地源码部署
docker compose up -d --build updater

docker-compose.yml 模式提示存在未提交改动

升级前的 Git 工作区检查要求干净,避免覆盖用户修改。先处理工作区,或手动执行:

docker compose up -d --build

升级时报 /workspace/docker-compose.hub.yml: no such file or directory

v1.3.6 起 updater 会自动从 havline 容器的 Compose 标签读取宿主机目录,并通过 helper 容器执行 Compose。如果自动发现仍失败,可以显式设置目录:

先查询运行中 havline 容器的 Compose 工作目录:

docker inspect havline --format '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}'

把输出目录写入 Compose 同层 .env 作为覆盖:

HAVLINE_UPDATE_HOST_DIR=/实际/的/Compose/目录

然后只重建 updater:

docker compose -f /实际/的/Compose/目录/docker-compose.hub.yml up -d --build updater

状态接口会明确提示 Compose 文件缺失,修复挂载或设置覆盖后按钮才会重新显示。

报 container name "/havline" is already in use

这表示 Compose 没有识别到当前容器所属的原项目,可能把升级当成新项目并尝试新建网络与数据卷。v1.3.7 的更新版 updater 会读取现有容器的 com.docker.compose.project 标签,并显式复用原项目名。

如果现有 havline 容器不是由 Compose 管理,updater 会拒绝升级,避免新建 havline_havline-data 等空数据卷。应先按 Compose 方式重新部署,再使用一键升级。

helper 命令里出现 -v /workspace:/workspace

说明 Compose 标签只返回了容器内路径。更新后的 updater 会优先从 project.config_files 标签推导宿主机目录,并忽略 /workspace 这类容器路径。若标签仍无法提供真实宿主机路径,应检查部署工具的 Compose 标签是否完整;正常情况下无需手工设置目录。

docker-compose.hub.yml 模式无法读取远端版本

当前 hub 发布源固定为 https://jayhub.top/mrlee/Havline.git 的 main 分支;检查 Compose 文件中的 build.context、HAVLINE_UPDATE_REPO 和 HAVLINE_UPDATE_BRANCH 三者是否一致。

检查 sidecar 容器内是否可以访问 Git 仓库:

docker exec -it havline-updater sh -lc 'git ls-remote https://jayhub.top/mrlee/Havline.git main'

升级卡在 running

查看 sidecar 日志:

docker logs -f havline-updater

以及 Compose 输出:

docker compose ps
docker compose logs --tail 100 havline

回滚失败

确认升级前的镜像 tag 仍然存在:

docker images havline

然后用明确的版本 tag 手动切回:

HAVLINE_VERSION=<旧版本> docker compose up -d --no-deps --force-recreate havline

9. 暂不支持

  • docker-compose.host.yml:需要确认 host 网络与端口占用后再设计;
  • 原生 systemd 安装:需要单独的受控升级 helper;
  • 回滚到远端不存在的镜像:需要额外发布流程;
  • 升级 havline-updater 自身:当前只重建 havline 服务,sidecar 更新仍需一次手动 Compose 操作。

10. 在线升级验证

「关于项目」页面可以用于确认升级后的版本信息。v1.9.0 的验证流程:

  1. 确认当前线上实例为 v1.3.0,且 havline-updater sidecar 已运行。
  2. 确认远端 main 分支的 VERSION 为 1.9.0。
  3. 在侧栏看到「一键升级」后点击并确认。
  4. 等待升级弹窗显示成功,页面自动刷新。
  5. 打开侧栏「关于项目」,确认:
    • 页面路由为 /about;
    • 页面显示的版本为 1.9.0;
    • 「源项目」显示为 O96u/Fonu;
    • 开源协议显示为 GPL-3.0。

如果「关于项目」页面可见且版本正确,说明镜像构建、容器重建、后端版本注入和前端静态资源更新均已生效。