切换主题
构建、配置与发布
问题:CI 成功是否证明线上运行了新版本
流水线可能成功构建,却没有更新集群;部署成功可能只说明清单被接受,Pod 还在拉旧标签。看到网页能打开也不证明 API 或 Worker 已升级。
发布是一条证据链:源码提交 → 可追溯产物 → 明确部署引用 → 实际运行实例 → 就绪与行为验证。每一步需要自己的证据。
原理:代码、配置和数据分别管理
锁文件固定依赖,构建元数据记录 revision/version,镜像 digest 标识实际内容。版本标签用于人的发布协作,但可变标签需要读取 digest 审计。
配置应有明确来源与优先级,启动时校验必填依赖和有效范围。凭据通过专门配置途径提供,不进入镜像、网页 bundle 或发布记录。数据库、Worker SQLite 和身份是运行数据,不应当作可替换构建产物。
最小例子:一份发布检查表
text
教学记录:
source_revision = 完整 Git SHA
version = 明确语义版本
artifact = 镜像 digest 或二进制 hash
target = 集群 / namespace / deployment / 主机
running = 实际 Pod imageID / 进程版本
acceptance = readiness + 关键 API / 工作流行为记录不必都存在同一文件,但应能相互关联。静态前端的版本元数据在构建时注入,不能假设容器启动后环境变量会改变已经编译的 JavaScript。
方案比较
| 方式 | 优势 | 主要风险 |
|---|---|---|
| Compose 本地栈 | 快速联调、隔离服务 | 开发挂载与生产镜像行为不同 |
| K3s 滚动部署 | 就绪管理与实例替换 | 迁移、共享依赖和兼容性 |
| Worker 包发布 | 覆盖多 OS/架构 | 本地身份、状态、进程生命周期 |
| 独立子仓库版本 | 模块自主发布 | 父 gitlink 与远端可达性需核对 |
服务端、前端和 Worker 独立版本不应强行相同;接口兼容比字符串一致更关键。发布多个组件需根据协议兼容安排顺序。
真实案例:发布仓库独立维护
Workbench 的普通 init 初始化代码与设计子模块,deploy 独立可选。发布仓库分别管理 K3s 应用和 Worker 分发;业务源代码仍位于各源码仓库。根 Makefile 保留本地 Compose 与隔离 E2E 命令。
已核对的实现 · 本地代码快照
发布仓库与运维职责 deploy · cfb60638
README.md · 第 1–15 行
符号:部署仓库说明 · 核对日期 2026-10-02
来源与提交版本一致
# TDP 发布
本仓库 [deploy-k3s](https://cnb.cool/iosoon/tdp/deploy-k3s) 是 Workbench 的可选 `deploy/` 子模块,独立维护两条并列发布流程:
| 目录 | 范围 | 手册 |
| --- | --- | --- |
| `k3s/` | Server、管理前端、App Demo 的镜像、K3s 清单与部署 | [K3s 发布](k3s/README.md) |
| `worker/` | Worker 五平台包、MinIO 发行与目标机升级 | [Worker 发布](worker/README.md) |
版本审计及最近生产基线见 [DEPLOYMENT.md](DEPLOYMENT.md)。共享基础设施由 `io-k3s` 管理;本仓库不管理 Workbench 业务源码,也不为日常代码开发增加部署步骤。
在 Workbench 根目录运行 `make deploy-init`,部署维护者随后在 `deploy/` 中切换到 `main` 并单独提交、推送。普通开发只运行 Workbench 的 `make init`,不会初始化本仓库。独立检出时将 `WORKBENCH_ROOT` 指向有 `backend/`、`frontend/`、`app-demo/` 的源码目录。
本地检查:`make check`。任何实际发布都需要用户明确确认版本、组件和目标;改动发布工具不授权构建推送镜像、公开 Worker 包或修改集群。
片段展示核对时的源码;完整文件指纹用于检测后续变化。这里的路径用于定位,不要求手机访问源码仓库。
本地栈与验证入口 workbench · 514f5a00
Makefile · 第 19–55 行
符号:check / up / e2e · 核对日期 2026-10-02
来源与提交版本一致
check:
$(COMPOSE) -f compose.yaml config --quiet
$(COMPOSE) -f compose.yaml -f compose.dev.yaml config --quiet
$(COMPOSE) -f compose.yaml -f compose.dev.yaml -f compose.demo.yaml config --quiet
$(COMPOSE) -f compose.yaml -f compose.e2e.yaml config --quiet
$(COMPOSE) -f compose.yaml -f compose.demo.yaml config --quiet
python3 scripts/check-doc-links.py
docs-check:
python3 scripts/check-doc-links.py
ps:
$(LOCAL_COMPOSE) ps -a
up:
$(LOCAL_COMPOSE) up -d --build
down:
$(LOCAL_COMPOSE) down
logs:
$(LOCAL_COMPOSE) logs -f
e2e:
TDP_E2E_PROJECT=$(E2E_PROJECT) ./scripts/e2e.sh
e2e-down:
$(COMPOSE) -p $(E2E_PROJECT) -f compose.yaml -f compose.e2e.yaml $(E2E_EXTRA_COMPOSE) --profile test down
fault-test:
TDP_E2E_PROJECT=$(E2E_PROJECT) ./scripts/fault-test.sh
demo-up:
$(LOCAL_COMPOSE) -f compose.demo.yaml up -d --build
demo-down:
$(LOCAL_COMPOSE) -f compose.demo.yaml down片段展示核对时的源码;完整文件指纹用于检测后续变化。这里的路径用于定位,不要求手机访问源码仓库。
生成与工程门禁 backend · c6dbe05c
Makefile · 第 37–83 行
符号:generate / check · 核对日期 2026-10-02
来源与提交版本一致
generate:
$(GO) tool oapi-codegen -config contracts/oapi-codegen/public.yaml contracts/openapi/public/v1/openapi.yaml
$(GO) tool oapi-codegen -config contracts/oapi-codegen/worker.yaml contracts/openapi/worker/v1/openapi.yaml
$(GO) tool oapi-codegen -config contracts/oapi-codegen/management.yaml contracts/openapi/management/v1/openapi.yaml
$(GO) tool oapi-codegen -config contracts/oapi-codegen/callback.yaml contracts/openapi/callback/v1/openapi.yaml
$(GO) tool oapi-codegen -config contracts/oapi-codegen/resource-provider.yaml contracts/openapi/resource-provider/v1/openapi.yaml
$(GO) tool sqlc generate
$(GO) run ./tools/sqlcpostprocess
migration-validate:
$(GO) tool goose -dir server/db/migrations validate
$(GO) tool goose -dir worker/db/migrations validate
generate-check: generate
git diff --exit-code -- contracts/gen/go server/internal/adapters/outbound/postgres/pgqueries worker/internal/adapters/outbound/sqlite/sqlitequeries
schema-check:
$(GO) test ./contracts/...
config-check:
TDP_SERVER_OIDC_CLIENT_SECRET=validation TDP_SERVER_DATABASE_URL=postgres://tdp:validation@localhost/tdp TDP_SERVER_NATS_URL=nats://localhost:4222 TDP_SERVER_DATA_ENCRYPTION_KEY=BwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwc= $(GO) run ./server/cmd/server config validate --config configs/examples/server.yaml
$(GO) run ./worker/cmd/worker config validate --config configs/examples/worker.yaml
architecture-check:
$(GO) run ./tools/archcheck
api-compat:
@test -n "$(BASE_OPENAPI_DIR)" || (echo "BASE_OPENAPI_DIR is required"; exit 2)
$(GO) tool oasdiff breaking $(BASE_OPENAPI_DIR)/public/v1/openapi.yaml contracts/openapi/public/v1/openapi.yaml
$(GO) tool oasdiff breaking $(BASE_OPENAPI_DIR)/worker/v1/openapi.yaml contracts/openapi/worker/v1/openapi.yaml
vuln:
$(GO) tool govulncheck ./...
license-check:
$(GO) tool go-licenses check ./server/cmd/server ./worker/cmd/worker \
--ignore=tdp \
--ignore=github.com/nexus-rpc/nexus-proto-annotations/go/nexusannotations/v1
check: fmt-check vet test schema-check config-check architecture-check migration-validate
clean:
$(GO) clean ./...
rm -f $(BIN_DIR)/server $(BIN_DIR)/worker
rm -f $(BIN_DIR)/server-linux-amd64 $(BIN_DIR)/worker-linux-amd64
rm -f $(BIN_DIR)/worker-linux-arm64 $(BIN_DIR)/worker-windows-amd64.exe
片段展示核对时的源码;完整文件指纹用于检测后续变化。这里的路径用于定位,不要求手机访问源码仓库。
当前案例展示这些职责,不复述历史生产版本或声明线上状态。部署运行事实需要当次查询,这个静态学习站不替代生产审计。
失败与边界:回滚代码不等于回滚数据
破坏性迁移后旧应用可能不能读取新结构。常见策略是先扩展、兼容读写、迁移数据、再收缩;实际选择需要考虑业务和维护窗口。备份也必须确认可恢复,不能只生成一个文件。
重启 Worker 时保留 SQLite、身份、日志与尚未同步事件。容器 down 保留卷与 down -v 删除数据是不同动作,教程不能让初学者把删除数据当作通用修复步骤。
迁移练习与参考答案
练习:部署新字段并重命名旧数据库列,如何让滚动部署中旧实例继续工作?
参考答案:先新增兼容结构,部署能同时处理旧新结构的应用,再迁移和验证数据,最后移除旧结构。发布记录关联精确源码与产物,验证每个实例版本和 readiness。回滚计划说明哪些数据变化可逆,不能只写“换回镜像”。