Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d71f008
接入 D3 自动部署:新增 deploy.yml(第一阶段,无忙锁)
Oct 15, 2026
109e81d
compose 部署种子迁移到 ci-templates 本地 tag 形态
Oct 15, 2026
9844509
DEPLOY.md:D3 自动部署为主链路,旧脚本降为手动备用
Oct 15, 2026
1782cbd
DEPLOY.md 补写旧脚本形态耦合事实;progress 段4 记录种子双消费者冲突
Oct 15, 2026
692bdce
deploy.yml 去掉 ssh_user 显式项(public-scan 闸),落到上游默认
Oct 15, 2026
53ff847
退役旧手动部署链路:删除 pull_and_deploy.sh,pull 脚本测试随退,保留 push/compose 种子守卫
zj1123581321 Oct 15, 2026
889671d
README/DEPLOY.md:旧手动链路标注退役并指向 D3,补部署目录灾备兜底
zj1123581321 Oct 15, 2026
8040b6b
progress 段6:仲裁落地——旧链路退役清单与全绿验证证据
zj1123581321 Oct 15, 2026
fb04dce
gate major 就地修复:deploy.yml 启用忙锁门禁(服务侧持锁前空转);DEPLOY.md 灾备兜底去 compose pull
zj1123581321 Oct 15, 2026
6600351
服务侧忙锁:转录任务持共享 flock 挡部署
Oct 15, 2026
eb75e55
进度段2:红验 AssertionError 转红,make test 仅继承红
Oct 15, 2026
418e721
gate 第3轮:忙锁 inode 对账 fail-loud,README 区分本地 latest 别名
zj1123581321 Oct 15, 2026
5b35302
gate 第4轮:替换前 pre-flight inode 对账,ship 依赖事前门禁
zj1123581321 Oct 15, 2026
58114e2
忙锁 fail-closed:锁基础设施异常一律拒绝新任务(PR #246 第5轮 F1)
Oct 15, 2026
6e13cd7
部署事后对账失败自动回滚到 pre-flight 镜像锚点;SSH 身份改 repo variable(PR #246 第5轮 F2/F3)
Oct 15, 2026
ea6c076
fail-closed 测试同步:钉扎锁路径到 tmp,沙箱实验走 VTA_BUSY_LOCK_PATH 覆盖口(PR #246 第5轮)
Oct 15, 2026
15697ba
gate 第6轮:回滚 step 改 always()+运行时分支,回滚锚点按 run 隔离,DEPLOY.md 忙锁描述对齐 fail-…
zj1123581321 Oct 15, 2026
0d88e59
忙锁禁止自建锁基础设施:只读打开既有文件,缺失即拒绝(PR #246 第7轮 F2)
zj1123581321 Oct 15, 2026
969dfcf
gate 第7轮:回滚锚点重构为 last_verified_good 单一事实源,忙锁描述与沙箱预建对齐禁自建契约
zj1123581321 Oct 15, 2026
52fbbd4
部署工作流加顶层 concurrency 组:部署 run 全局串行排队(PR #246 gate 第8轮两条 major 的并发前提消解)
Oct 15, 2026
f8dc283
gate 第9轮:回滚先拿忙锁排他锁(rollback_deferred_busy fail-loud),终态写入后通知投递前提前释放忙锁
Oct 15, 2026
edce824
gate 第10轮:回滚前置对账与健康探针,灾备带锁与锁路径普通文件校验
Oct 15, 2026
dd51694
gate 第11轮:终态 CAS 成功后异常路径锁泄漏幂等兜底
Oct 15, 2026
80c6722
gate 第11轮:verify 前置校验运行容器镜像身份,防手工灾备插队污染锚点
Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
353 changes: 353 additions & 0 deletions .github/workflows/deploy.yml

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,13 @@ docker compose up -d --build

镜像内置 ffmpeg、BBDown、yt-dlp,无需额外安装。

生产部署禁止使用 `latest`。构建脚本会拒绝包含已跟踪或未跟踪修改的脏工作区,只从干净提交以 12 位 Git SHA 生成唯一 tag;部署脚本拉取该 tag 后按同一镜像仓库解析并固定 registry digest。候选镜像会先运行 `--check-config`,失败时不重启当前服务,启动后健康检查失败则恢复上一个 digest:
生产部署禁止向任何 registry 推送或使用可变 tag(含 `latest`)。构建脚本会拒绝脏工作区,只从干净提交以 12 位 Git SHA 生成唯一 tag 并推送 GHCR(公开镜像构建口,不部署)。部署机本地的 `video-transcript-api:latest` 不是 registry 可变 tag:它是 D3 部署脚本把 ACR 不可变 SHA 镜像 retag 后的本机别名,只存在于部署机,compose 与镜像对账认这个本地名,不入任何 registry。

```bash
./docker/push_to_ghcr.sh
# 在 docker/deploy_targets.json 指定的 n305:/opt/media/VideoTranscriptAPI 上执行:
./docker/pull_and_deploy.sh ghcr.io/zj1123581321/video-transcript-api:<git-sha>
```

本仓库只提供部署能力;脚本不会自行 SSH 或自动上线。服务器首次运行会从 `docker/docker-compose.deploy.yml` 生成根目录 `docker-compose.yml`,配置文件位于 `<deploy-dir>/config/config.jsonc`,成功使用的 digest 记录在 `<deploy-dir>/.deploy-image`。同一项目目录的部署由 `.deploy.lock` 串行化;所有 Compose 操作固定使用部署根目录作为 project directory,并与候选预检加载同一份根目录 `.env`。重启前还会确认现有 Compose 文件确实把服务渲染为候选 digest。旧 Compose 不兼容时会先备份为 `docker-compose.yml.pre-digest.bak`,再迁移到仓库模板;候选失败回滚时会恢复原 Compose,并叠加仅覆盖镜像的配置把旧版本固定到记录的 digest。首次切换硬化脚本时,旧容器即使由 tag 启动也会先按原仓库解析为可回滚 digest;若旧镜像还没有 Docker `HEALTHCHECK`,回滚验证会改用容器内 `/livez` 探测。候选镜像启动失败、健康检查失败、启动后脚本被中断或成功 digest 状态文件无法原子提交时,脚本都会恢复旧 digest 并确认旧版本重新健康后才退出。
生产部署由 D3 自动链路完成:push `main` 自动触发 `.github/workflows/deploy.yml`(构建 → ACR → n305 `/opt/media/VideoTranscriptAPI` → 健康探针 → 部署);workflow 自身修复后用 `workflow_dispatch` 手动补发。部署细节与灾备兜底见 [docker/DEPLOY.md](docker/DEPLOY.md);配置文件位于 `<deploy-dir>/config/config.jsonc`。

> **注意**:CapsWriter ASR Server(协议 v2)与 FunASR 需单独部署,配置中的服务地址不能用 `localhost`,需改为宿主机 IP 或 `host.docker.internal`。协议说明见 [CapsWriter ASR 协议 v2](https://github.com/zlxlabs/CapsWriter-ASR-Server/blob/master/docs/reference/protocol.md)。

Expand Down
31 changes: 27 additions & 4 deletions docker/DEPLOY.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,34 @@
# 部署流程与入口链路

镜像构建与部署由两个脚本完成:
## D3 自动部署(主链路)

- `push_to_ghcr.sh`:从干净主干构建 `ghcr.io/<owner>/video-transcript-api:<主干12位短SHA>` 并推送到 GHCR。
- `pull_and_deploy.sh`:在部署机上按**不可变 digest** 部署——先跑配置预检(`main.py --check-config`),再替换容器并等待健康检查,失败自动回滚到上一 digest;状态记录在部署目录 `.deploy-image`。
push `main` 后自动完成构建与部署:`.github/workflows/deploy.yml` 调用 `zlxlabs/ci-templates` 的 `build-deploy.yml@v2`(self-hosted runner):构建镜像(`docker/Dockerfile`,构建上下文为仓根)→ 推送 ACR(不可变 12 位短 SHA tag)→ SSH 到 n305 → 健康探针通过后在部署目录 `/opt/media/VideoTranscriptAPI` 按本地 tag `video-transcript-api:latest` 切换容器,探针失败自动回滚上一健康版本。`paths-ignore` 排除 `**.md`、`docs/**`、`.github/**`——只改文档或 CI 不扇出生产部署;workflow 自身修复后用 `workflow_dispatch` 手动补发。

部署目标(服务器别名与目录)记录在 `docker/deploy_targets.json`;入口地址、Tailscale 直连信息等基础设施细节维护在私有部署记忆中,不写入本公开仓。
- 健康探针用 `http://localhost:8200/livez`(宿主 8200 → 容器 8000),不用 `/health`:`/livez` 是纯存活探针、恒 200、不依赖下游,专为部署探针设计;`/health` 的深度检查依赖转写后端等下游服务,下游抖动会把健康的新版本误判为不健康并触发无谓回滚。
- 在飞任务保护(忙锁门禁)已在 workflow 启用(`busy_lock_file` 指向部署目录 `.deploy-state/busy.lock`,超时 600s 延期):服务侧 BusyLock 只读打开部署侧预建的锁文件并持共享 flock(服务侧永不自建锁文件——自建文件落在容器层时与宿主部署锁不同 inode,互斥静默失效),锁基础设施异常一律 fail-closed 拒绝新任务(HTTP 503),不存在「锁文件缺失自动创建后门禁空转」的降级路径——宿主锁文件缺失时 deploy.yml 的 pre-flight 对账即失败,自动部署 fail-closed 延期、人工介入(首次人工部署步骤含 `touch busy.lock`);事后对账失败还会自动回滚到 `.deploy-state/last_verified_good` 记录的最近验证好镜像。部署窗口仍建议确认无长转写任务(事故背景:2026-10-15 手动部署打断在飞转写任务)。
- 部署目录的 compose 必须与本仓种子同形态(本地 tag):ci-templates 部署脚本从 ACR 拉取不可变 SHA 镜像后在本机 retag 成 `video-transcript-api:latest`,镜像对账也认这个本地名;旧的 digest-env 形态(registry digest env 占位)与之不兼容,首次自动部署前需先确认部署目录已换成本仓种子。

## 旧手动链路(已退役)

接入 D3 前的手动部署脚本 `docker/pull_and_deploy.sh` 已于 2026-10-15 删除:它以同目录种子为模板做部署目录自迁移,要求 digest-env 形态,与 D3 的本地 tag 形态种子互斥,D3 上位后失去存在意义(锁旧脚本行为的测试随退)。`docker/push_to_ghcr.sh` 保留,仅作 ghcr 公开镜像构建口:从干净主干构建 `ghcr.io/<owner>/video-transcript-api:<主干12位短SHA>` 并推送,不部署。

自动链路不可用时的灾备兜底(在部署目录手工执行,不再依赖仓内脚本;部署目标见 `docker/deploy_targets.json`)。灾备同样尊重忙锁、使用排他锁,超时失败属预期(等在飞任务清空而非强行恢复,避免打断转写)。

日常灾备直接重建容器(compose 用宿主已有的本地 tag `video-transcript-api:latest`,该 tag 由每次 D3 部署 retag 维持;**不要用 `docker compose pull`**——本地 tag 形态下它会去 registry 找名为 `video-transcript-api` 的镜像,不存在):

```bash
flock -w 600 -x .deploy-state/busy.lock -c "docker compose up -d"
```

需要钉定版本时,从 ACR 手动拉取指定 SHA 并 retag 到本地 tag(ACR 全路径 = ci-templates 默认 registry/namespace + `image_name`,与本卡 deploy.yml 的实际值一致),同样带忙锁:

```bash
docker pull crpi-0vsre5argteykh9m.cn-guangzhou.personal.cr.aliyuncs.com/zlx-personal/video-transcript-api:<git-sha> \
&& docker tag crpi-0vsre5argteykh9m.cn-guangzhou.personal.cr.aliyuncs.com/zlx-personal/video-transcript-api:<git-sha> video-transcript-api:latest \
&& flock -w 600 -x .deploy-state/busy.lock -c "docker compose up -d"
```

入口地址、Tailscale 直连信息等基础设施细节维护在私有部署记忆中,不写入本公开仓。

## 入口分层与 Cloudflare 请求体上限

Expand Down
12 changes: 8 additions & 4 deletions docker/docker-compose.deploy.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# @generated by docker-package skill
# @skill-version 4
# Seeded to the server project root as docker-compose.yml.
# 部署种子:同步到部署目录根(n305:/opt/media/VideoTranscriptAPI)作为 docker-compose.yml。
# 2026-10-15 从 docker-package skill v4 的 digest-env 形态迁移为 ci-templates D3 的本地
# tag 形态:pull_and_deploy.sh 从 ACR 拉取 ${ACR_IMAGE}:${GIT_SHA} 后在本机 retag 成
# IMAGE_NAME:latest,compose 起容器与镜像对账都认这个本地名——故此处必须写本地名
# (ACR 私有仓,不写 registry 前缀),不能用 registry digest env 占位。
# .env 必须与 docker-compose.yml 同目录(部署目录根)。
services:
video-transcript-api:
image: ${VIDEO_TRANSCRIPT_IMAGE:?VIDEO_TRANSCRIPT_IMAGE must be an immutable digest}
image: video-transcript-api:latest
container_name: video-transcript-api
environment:
- TZ=Asia/Shanghai
Expand All @@ -18,6 +21,7 @@ services:
volumes:
- ./config:/app/config
- ./data:/app/data
- ./.deploy-state:/app/.deploy-state
mem_limit: 2g
restart: unless-stopped
logging:
Expand Down
285 changes: 0 additions & 285 deletions docker/pull_and_deploy.sh

This file was deleted.

Loading
Loading