文档/ 部署/ 蓝绿部署与回滚

蓝绿部署与回滚

sh0 如何在不中断服务的情况下替换正在运行的版本、部署历史记录了什么,以及 1.10.2 版本中回滚按钮实际做了什么。

部署如何替换正在运行的版本

每次部署都会在已上线版本旁边启动新版本,只有在新容器证明自己可用之后才切换流量,无需任何设置。对于从 Git 部署的应用,顺序如下:

  1. 从克隆的仓库构建镜像(前后分别运行 pre_build 和 post_build 钩子)。
  2. 启动新容器,同时旧容器继续服务。重叠期间两者挂载相同的卷。
  3. 对新容器进行健康检查(详见下文)。
  4. pre_deploy 钩子,若应用有多个副本,再启动其余副本。
  5. 切换路由:Caddy 反向代理指向新容器。
  6. 停止旧容器:它收到停止信号,有 30 秒时间退出,然后被删除。
  7. post_deploy 钩子,最后确认新容器仍在运行且没有循环重启。

通过上传归档、Docker 镜像或粘贴 Dockerfile 部署的应用遵循相同顺序,只是没有钩子阶段。如果在路由切换之前任何一步失败,新容器会被删除,旧版本继续服务;日志中会写明:A failed deploy leaves nothing running.

健康检查

sh0 最多等待 300 秒,直到新容器被判定为健康:

  • 镜像声明了 HEALTHCHECK:sh0 等待 Docker 报告其健康,同时检查端口能否从容器外部访问。
  • 没有 HEALTHCHECK:sh0 向应用端口发起 TCP 连接,不请求任何 HTTP 路径。
  • 无端口的 worker:容器必须连续运行 15 秒且不重启。

循环崩溃或因内存不足被杀会立即导致部署失败,无需等满 300 秒。

“零停机”不包括什么

  • 旧容器停止时仍未关闭的连接(WebSocket、长时间下载、流式响应)会在 30 秒宽限期后被切断,不会转移到新容器。
  • 在重叠期间,同一应用的两个容器同时使用相同的卷。无法同时运行两份的应用(独占锁定的 SQLite 文件、必须唯一的任务)需要能容忍这几秒钟。
  • 由 pre_deploy 钩子执行的数据库迁移会在旧版本仍在提供服务时生效。编写迁移时要保证旧代码在新结构下依然可用。

部署历史

每个应用的 Deployments 选项卡按时间倒序列出其部署,包括触发方式(upload、cli-push、cli、rollback…)、Git 应用的提交、耗时和完整构建日志。同样的列表也可通过 GET /api/v1/apps/<app-id>/deployments 获取。

成功的部署即使被更新的部署替换后,状态仍保持 running:线上版本是最新的那条 running 记录,而不是唯一的一条。

回滚

在 Deployments 选项卡中,每个状态为 running 的部署都有一个 Rollback to this deployment 按钮,点击后需确认(Confirm Rollback)。对应的 API 是 POST /api/v1/deployments/<deployment-id>/rollback,需要该应用的开发者角色,返回 202 以及新排队的部署。1.10.2 版本的 CLI 没有回滚命令。

回滚部署的是什么

Warning
自 1.10.3 版本起,回滚会运行所选部署上记录的镜像:不会克隆或构建任何内容。1.10.2 及更早版本则会重新部署当前版本——在依赖回滚撤销错误发布之前,请先升级。

在 1.10.3 版本上,对一个通过归档上传部署的应用观察到:先部署 v1,再部署 v2,然后回滚到 v1 的部署。回滚部署携带 v1 的镜像,日志表明没有构建任何内容,应用返回 v1:

Terminal
$ curl -s https://panel.example.com/api/v1/apps/<app-id>/deployments \
    -H "Authorization: Bearer $SH0_TOKEN" \
  | jq -c '.data[] | {id: .id[0:8], status, triggered_by, commit_msg, image_id: .image_id[7:19]}'
{"id":"c97f6268","status":"running","triggered_by":"rollback","commit_msg":"Rollback to deployment 0c5abcdc","image_id":"44da57c73080"}
{"id":"a36c7932","status":"running","triggered_by":"cli-push","commit_msg":"Push update","image_id":"916d35592fa5"}
{"id":"0c5abcdc","status":"running","triggered_by":"upload","commit_msg":"File upload","image_id":"44da57c73080"}

$ curl -s https://my-site.example.com
version v1

当所选部署未成功时,回滚会以 400 拒绝;当其镜像已无法运行时,以 409 拒绝:由 sh0 构建、之后被下文所述清理删除的镜像,或者对于从 Docker 镜像部署的应用,当前在线部署也在使用的可变标签(如 nginx:latest)。设置了多个副本的应用回滚后只有一个容器。

回滚恢复的是镜像,而不是配置:环境变量、卷和域名仍保持当前状态。若要回到镜像已不存在的版本,请重新部署:用 sh0 push 推送之前的归档,或对 Git 应用回退提交后再推送。

镜像保留

sh0 每 6 小时删除一次旧的应用镜像,每个应用保留最近的 3 个。这两个值是 sh0 serve 的选项,而不是控制面板设置:--cleanup-interval-hours(0 表示关闭清理)和 --cleanup-keep-images。