Files
zt-closet-manage-backend/.codebuddy/plans/管理后台_CI_CD_流水线_1e4d6d0a.md
T
2026-09-13 23:44:36 +08:00

8.7 KiB
Raw Blame History

name, overview, todos
name overview todos
管理后台 CI/CD 流水线 参照业务服务 /root/zt-closet-backend 的 Gitea Actions CI/CD(.gitea/workflows/deploy.yaml + 多阶段 Dockerfile + .dockerignore),为本服务 /root/zt-closet-manage-backend 建立等价的构建部署流水线:tag 推送或手动触发 → 构建镜像推送到 Gitea 仓库 → SSH 部署生产机 → 健康检查失败自动回滚。
id content status
create-deploy-workflow 新增 .gitea/workflows/deploy.yaml 构建部署流水线(tag v* 与手动触发、镜像推送、SSH 部署、健康检查失败回滚) completed
id content status
create-dockerfile 新增 scripts/dockerfile/Dockerfile 多阶段构建(golang:1.26-alpine 静态编译、alpine:3.22 非 root 运行、TZ 与 /ping 健康检查) completed
id content status
create-dockerignore 新增 .dockerignore(排除 configs/storage/文档/scripts 等,防密钥进入镜像层) completed
id content status dependencies
update-readme 更新 README 新增「CI/CD 与部署」章节(流水线说明、Gitea Secrets/Vars 清单、生产机目录与配置准备) completed
create-deploy-workflow
id content status dependencies
verify-cicd 验证:pyyaml 校验 workflow 语法,docker build 本地构建确认 Dockerfile 可产出镜像 completed
create-deploy-workflow
create-dockerfile
create-dockerignore

产品概述

为管理后台服务(zt-closet-manage-backend)建立与业务服务一致的 CI/CD 体系。参照业务服务已有的 Gitea Actions 流水线与 Docker 构建方案,实现「打 tag 自动触发 + 手动触发」的镜像构建、推送与生产机部署,并带健康检查与失败自动回滚能力。

核心功能

  • 构建部署流水线(Gitea Actions):推送 v* 标签或手动触发后,构建 Docker 镜像推送到 Gitea 镜像仓库(git.lzcwy.cn),再通过 SSH 部署到生产机。
  • 多阶段 Dockerfile:Go 静态编译(golang:1.26-alpine)→ 精简运行镜像(alpine:3.22),非 root 用户运行,内置 /ping 健康检查,配置与日志目录以卷挂载方式接入(密钥不进镜像层)。
  • 部署安全机制:部署前记录旧镜像 ID,新容器启动 5 秒后健康检查,失败自动输出日志并回滚到旧镜像。
  • .dockerignore:剔除配置密钥、日志、文档等非构建资产,防止密钥进入镜像层。
  • 文档:README 增加 CI/CD 章节,说明流水线触发方式、Gitea Secrets/Vars 配置清单、生产机目录准备要求。
  • 不包含数据库迁移流水线:管理后台无独立迁移命令(admin_users 表由服务启动时 GORM AutoMigrate 幂等创建),故无需业务服务的 mysqlup/mysqldown 工作流。

与业务服务的差异适配

业务服务(zt-closet-backend)CI/CD 的调研结论:Gitea Actions 三套工作流(deploy.yaml、mysqlup.yaml、mysqldown.yaml)+ scripts/dockerfile 下两个 Dockerfile + .dockerignore。管理后台按自身特性适配:Go 版本 1.26(业务 1.27)、无上传功能(挂载仅 configs + storage/logs)、独立部署目录与 Secrets 命名(ZT_CLOSET_MANAGE_*)。

技术栈

  • CI/CD 平台:Gitea Actions(与业务服务同一 Gitea 实例 git.lzcwy.cn,runner 支持 docker)
  • 容器:Docker(多阶段构建,golang:1.26-alpine → alpine:3.22)
  • 部署:SSH 免密部署到生产机 + docker run(--network host)+ curl/wget 健康检查 + 旧镜像回滚
  • 验证:pyyaml 语法校验 + 本机 docker build 构建验证(本机 docker 26.1.3 可用)

实现方案

1. 流水线文件 .gitea/workflows/deploy.yaml

结构复刻业务 deploy.yaml,参数按管理后台调整:

name: 构建并部署
on:
  push:
    tags: ['v*']
  workflow_dispatch:
concurrency:
  group: deploy
  cancel-in-progress: false
env:
  REGISTRY: git.lzcwy.cn
  IMAGE: git.lzcwy.cn/zhengchiliu/zt-closet-manage-backend

Job 步骤(runs-on: ubuntu-latest):

  1. actions/checkout@v4 拉取代码。
  2. 确认 docker 可用(docker version)。
  3. 计算镜像标签:refs/tags/* 取 tag 名,否则 sha-<7位SHA>。
  4. docker login 登录 Gitea 镜像仓库(secrets:ZT_CLOSET_MANAGE_REGISTRY_USER / ZT_CLOSET_MANAGE_REGISTRY_TOKEN)。
  5. docker build --build-arg GOPROXY=https://goproxy.cn,direct -f scripts/dockerfile/Dockerfile -t <IMAGE>:<tag> -t <IMAGE>:latest . 并推送 tag + latest。
  6. SSH 部署生产机(secrets:ZT_CLOSET_MANAGE_PROD_SSH_KEY;vars:ZT_CLOSET_MANAGE_PROD_SSH_HOST / ZT_CLOSET_MANAGE_PROD_SSH_USER),远端脚本:
  • DIR=/opt/zt-closet-manage、APP=zt-closet-manage-backend;
  • mkdir -p $DIR/configs $DIR/storage/logs 并 chown -R 10001:10001 $DIR/storage(容器内非 root 用户 uid 10001 需日志写权限);
  • 记录旧镜像 ID → docker pull → docker rm -f → docker run -d --name $APP --network host --restart unless-stopped -v $DIR/configs:/app/configs:ro -v $DIR/storage/logs:/app/storage/logs -e TZ=Asia/Shanghai --log-opt max-size=50m --log-opt max-file=5 $IMAGE:latest;
  • sleep 5 后 curl/wget http://127.0.0.1:8080/ping 健康检查(端口须与生产 config.yaml 的 Server.HttpPort 一致,注释说明);
  • 失败:docker logs --tail 50 → 移除新容器 → 若有旧镜像则以相同参数启动旧镜像回滚 → exit 1;
  • 成功:docker image prune -f 清理旧镜像。
  1. 部署结果步骤(if: always())输出镜像标签。

2. 多阶段 Dockerfile scripts/dockerfile/Dockerfile

复刻业务 Dockerfile,差异:ARG GO_VERSION=1.26(管理后台 go.mod 为 go 1.26.1)、二进制名 zt-closet-manage-backend、runtime 仅建 configs + storage/logs 两个挂载点(无 uploads)。

  • 构建阶段:FROM golang:${GO_VERSION}-alpine AS builder;ARG TARGETOS/TARGETARCH(默认 linux/amd64,兼容未启用 buildx 场景);ARG GOPROXY 默认 https://proxy.golang.org,direct(流水线内以 build-arg 覆盖为 goproxy.cn);先 COPY go.mod go.sum + go mod download 复用缓存层,再 COPY . .;ENV CGO_ENABLED=0,GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -trimpath -ldflags="-s -w" -o /out/zt-closet-manage-backend . 静态编译。
  • 运行阶段:FROM alpine:${ALPINE_VERSION}(3.22);apk add --no-cache ca-certificates tzdata(HTTPS 依赖根证书、本地时区计算每日额度展示);addgroup -g 10001 -S app && adduser -u 10001 -S -G app app 非 root;WORKDIR /app;COPY 二进制;mkdir -p /app/configs /app/storage/logs && chown -R app:app /app;ENV TZ=Asia/Shanghai、ENV APP_PORT=8080;USER app;EXPOSE ${APP_PORT};HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 CMD wget -q -O - "http://127.0.0.1:${APP_PORT}/ping" || exit 1;CMD ["/app/zt-closet-manage-backend"]。

3. .dockerignore

复刻业务按实际目录裁剪:.git、.gitignore、*.md、docs/、design/、storage/、configs/(密钥不进构建上下文)、scripts/、Dockerfile、.dockerignore。.gitea/ 与业务一致不排除。

4. README 新增「CI/CD 与部署」章节

  • 流水线说明:打 v* 标签或 Gitea 页面手动触发;触发后构建推送镜像并部署到生产机,健康检查失败自动回滚。
  • Gitea 仓库需创建的 Secrets:ZT_CLOSET_MANAGE_REGISTRY_USER、ZT_CLOSET_MANAGE_REGISTRY_TOKEN、ZT_CLOSET_MANAGE_PROD_SSH_KEY;Variables:ZT_CLOSET_MANAGE_PROD_SSH_HOST、ZT_CLOSET_MANAGE_PROD_SSH_USER。
  • 生产机准备:目录 /opt/zt-closet-manage(configs/config.yaml 必填、storage/logs 自动创建并授权 uid 10001);config.yaml 的 Server.HttpPort 须为 8080(与健康检查一致)。
  • 说明无迁移流水线:admin_users 表由服务启动时 AutoMigrate 自动创建。

5. 验证

  • python3 -c "import yaml; yaml.safe_load(...)" 校验 deploy.yaml 语法。
  • 本机 docker build -f scripts/dockerfile/Dockerfile --build-arg GOPROXY=https://goproxy.cn,direct -t zt-closet-manage-backend:test . 验证 Dockerfile 可正常构建出镜像(不推送、不部署)。

目录结构

/root/zt-closet-manage-backend/
├── .gitea/
│   └── workflows/
│       └── deploy.yaml          # [NEW] 构建部署流水线(tag v* / 手动触发,SSH 部署 + 健康检查回滚)
├── scripts/
│   └── dockerfile/
│       └── Dockerfile           # [NEW] 多阶段构建(golang:1.26-alpine → alpine:3.22,非 root,HEALTHCHECK /ping)
├── .dockerignore                # [NEW] 排除配置密钥/日志/文档等非构建资产
└── README.md                    # [MODIFY] 新增「CI/CD 与部署」章节(Secrets/Vars 清单、生产机准备、触发方式)