Files
labmem 86f6eb39b2 docs(polaris): 同步设计与部署文档现状
对齐深色玻璃样式、响应式布局、数据规模与配置生效范围。
明确仅由 polaris-v* 标签触发发布,并保留历史规格说明。
2026-10-14 16:38:07 +08:00
..

Polaris 部署(第一阶段:CI 出镜像,宿主机手动部署 digest)

本目录提供在服务器上以独立 Compose 项目运行 Polaris 的最小配置。

第一阶段流程:仅当推送 polaris-v* 标签时才运行 CI(Gitea Actions)整条验证+发布 工作流——verify job 通过校验(lint / typecheck / test / build / standalone 冒烟)后, publish job 构建镜像 git.labmem.icu/<repo-owner>/polaris:<full-commit-sha>(完整 commit SHA 作 tag)并自动推送到 Gitea registry,从 push 日志解析输出 digest; 分支/路径推送不触发 CI。宿主机(服务器)首次部署时用只读凭据 docker login, 在 .env 中以 digest 固定该镜像,再 pull + up 单服务运行。

文件说明

文件 用途
compose.yaml 独立项目 polaris 的服务定义(单服务,仅外部网络 net-public)
.env.example 环境变量示例,复制为同目录 .env 并填入真实镜像 digest

设计约束:

  • 镜像来源:CI 仅在 polaris-v* 标签触发的发布中,以完整 commit SHA 为 tag 推送; 部署侧约定在 .env 写 ...@sha256:<digest> 固定镜像,杜绝 latest 漂移; compose.yaml 的 image: ${POLARIS_IMAGE:?...} 只校验变量非空,不校验 digest 格式
  • 只加入外部网络 net-public(与 nginx 同网);不连 net-db
  • 不映射宿主机端口(无 ports:),仅通过 nginx 反代访问
  • 无 DB/Redis、无 secrets、无源码/数据卷挂载
  • 应用在镜像内已是 node 非 root 用户,监听 3000(见仓库 Dockerfile,本目录不改镜像与代码)
  • HEALTHCHECK 用运行时 Node 原生 fetch 探测 /,不要求 curl 等额外工具
  • 顶层 name: polaris 固定项目名,避免项目路径/命名歧义

<repo-owner> 为 Gitea 用户/组织名(compose.yaml 报错提示文本中的 <owner> 同义)。

前置条件(须预先就绪)

一次性、由 Gitea 仓库/实例管理员完成:

  1. registry 已启用且支持推送(否则 CI push 会失败)。
  2. Gitea Actions secrets 已配置:POLARIS_REGISTRY_USER、POLARIS_REGISTRY_TOKEN, 对应具备镜像推送(写)权限的账号/token。secrets 必须预先在仓库(或组织)设置中 填好——CI 不会也不能替你创建。
  3. 宿主机另备一个只读(拉取)token:与 CI 写权限分离,只在服务器 docker login 使用。

以上三项均为目标要求,现状尚未验证;不满足时先补齐再走首次发布。

待现场验证(不要假设,逐项落实)

  • workflow 的 build/push 与 digest 输出逻辑已定稿:推送步骤从 push 日志解析 digest: sha256:<64 位十六进制>,回显「镜像摘要」与手动部署引用,解析失败即 job 失败; 待现场验证的是真实 Gitea registry / runner 下 push 输出能否匹配该解析
  • registry / runner 实际行为:登录、推送、按 digest 拉取
  • 生产 Nginx 行为(见「Nginx 反代」)
  • 服务器上 net-public 网络名、既有 Compose 项目与目录布局 (以 docker network ls、docker compose ls 实际输出为准)
  • Nginx 配置未提供:不要在本目录猜测域名,也不要改动服务器 Nginx 文件

首次发布步骤

1) 推送标签触发 CI,记录镜像与 digest

# 推送 polaris-v* 标签触发整条工作流(分支/路径推送不触发 CI),例如:
git tag polaris-v0.1.0 && git push origin polaris-v0.1.0
# 在 Gitea 网页打开 Actions 运行页,等待 verify 与 publish job 全绿
  • 确认全部 job 通过后,从该次运行日志的推送步骤记录:
    • 镜像:git.labmem.icu/<repo-owner>/polaris:<full-commit-sha>
    • digest:推送步骤解析成功时直接回显「镜像摘要:sha256:」与 docker pull ...@sha256:<digest> 手动部署引用;实际输出以日志为准, 若未给出,用下文「如何确认真实 digest」的方法在部署前核对
  • 记下对应 commit sha 与 run 编号,便于追溯。

2) 拷贝示例到服务器,并确认现状与项目路径

# 仓库机器上(主机/路径按实际调整):
rsync -av ts/web/nextjs/polaris/deploy/ admin@<server>:/home/admin/docker/lab/polaris/
# 服务器上:
docker compose version   # 需 Compose v2(docker compose 子命令)
docker network ls        # 确认 net-public 存在(名字以实际为准)
docker compose ls        # 查看既有项目,避免目录/文件名冲突
ls -la /home/admin/docker/lab

Compose 项目路径注意事项:

  • 推荐把文件放进 /home/admin/docker/lab/polaris/ 子目录,与既有栈完全隔离; 若 /home/admin/docker/lab 下已存在 compose.yaml / docker-compose.yaml, 切勿覆盖,必须使用子目录。
  • .env 必须与 compose.yaml 同目录(Compose 从项目目录自动加载它)。
  • compose.yaml 已用顶层 name: polaris 固定项目名;仍建议 cd 到该目录再执行命令, 保证 .env 加载与 down/up 指向同一项目。

3) 宿主机登录 registry(只读 token)

docker login git.labmem.icu -u <registry用户名>
# 交互输入只读 token;不要把 token 写进命令行参数/历史

或非交互(输入不回显、不进历史):

read -rs REGISTRY_TOKEN   # 粘贴只读 token 后回车
printf '%s' "$REGISTRY_TOKEN" | docker login git.labmem.icu -u <registry用户名> --password-stdin
unset REGISTRY_TOKEN

凭据会存于当前用户 ~/.docker/config.json(base64 明文),见「安全提醒」。

4) 准备 .env 并按 digest 部署(单服务)

cd /home/admin/docker/lab/polaris
cp .env.example .env
# 编辑 .env:POLARIS_IMAGE=git.labmem.icu/<repo-owner>/polaris@sha256:<digest>
# 注释行保留来源 tag/commit 与上一版 digest,便于追溯与回退

docker compose pull polaris
docker compose up -d polaris
docker compose ps
docker inspect -f '{{.State.Health.Status}}' polaris   # 等待变为 healthy

5) 健康与反代验证

# 容器内自测(镜像内无 curl,用 node):
docker exec polaris node -e "fetch('http://127.0.0.1:3000/').then(r=>{console.log(r.status);process.exit(r.ok?0:1)})"

# 若 nginx 容器内带 wget/curl,可从 net-public 内验证服务名连通性:
docker exec <nginx容器名> wget -qO- http://polaris:3000/ >/dev/null && echo ok

Nginx 反代(配置在服务器,本仓库不改)

  • 上游地址:http://polaris:3000 —— polaris 是其在 net-public 内的 服务/DNS 名(compose 已设 container_name: polaris 保证名字稳定)。
  • 域名、server 块、TLS 以服务器实际配置为准;本目录不猜测、不代改 Nginx 文件。
  • 重建后注意 DNS/IP:Nginx 对静态 proxy_pass http://polaris:3000; 只在载入配置时 解析一次;polaris 容器重建后其在 net-public 内的 IP 可能变化, 需要重启/reload Nginx(在其所属 Compose 项目内操作),或在其配置中改用 resolver 127.0.0.11 valid=10s; 的变量式动态上游——是否改造由服务器 Nginx 配置决定。
  • 验证:从外部按实际域名请求一次,确认经反代返回 Polaris 页面。
  • 生产 Nginx 的上述行为未经实测,待现场验证。

升级与回退

升级(无零停机承诺):为要发布的新 commit 打新的 polaris-v* 标签触发 CI, 运行全绿并取得新 digest 后,把新 digest 写入 .env 的 POLARIS_IMAGE, 旧值保留为注释行,然后:

docker compose pull polaris && docker compose up -d polaris

up -d 是停旧建新,存在秒级中断;健康检查只表示容器自检通过, 不会自动把坏版本隔离在流量之外。

回退:

  • 有旧版本:把 .env 中 POLARIS_IMAGE 换回上一个 digest(注释行),再次 pull + up -d。
  • 首发失败且无旧版本可回退:在 polaris 项目目录执行 docker compose down (只移除本项目的容器与网络引用,不影响 nginx / db 等既有项目)。

如何确认真实 digest

按可靠程度排序,最终以「按 digest 拉取成功」为准:

  • CI 运行日志:workflow 已实现 digest 输出——推送步骤解析 push 日志后回显 「镜像摘要」与 docker pull 手动部署引用;真实 registry / runner 下输出是否符合预期 待现场验证,以实际日志为准。
  • 部署前交叉核对(任一已登录该 registry、装有 buildx 的机器): docker buildx imagetools inspect git.labmem.icu/<repo-owner>/polaris:<full-commit-sha> 查看该 tag 当前指向的 manifest(列表)摘要。
  • 服务器最终校验:docker pull git.labmem.icu/<repo-owner>/polaris@sha256:<digest> 输出 Digest: sha256:... —— 按 digest 拉取本身就是内容寻址校验, 两者不一致不可能拉取成功。
  • 已拉取过 tag 的机器可查本地记录的远端摘要: docker image inspect --format '{{index .RepoDigests 0}}' git.labmem.icu/<repo-owner>/polaris:<full-commit-sha>。
  • 注意:.env 固定的是 manifest(列表)digest;多架构镜像按 digest 拉取时由运行时 选择本平台对应的 manifest。workflow 用单条 docker build 构建单平台镜像 (平台取决于 runner 架构),是否覆盖服务器架构待现场验证。
  • commit sha tag 正常不应被重复推送;若发现同名 tag 的 digest 变化, 先排查 CI 再部署,不要直接沿用旧记录。

安全提醒(registry token 读写分离)

  • CI 写:POLARIS_REGISTRY_USER / POLARIS_REGISTRY_TOKEN 只存在于 Gitea Actions secrets,具备推送(写)权限;绝不写进仓库、README、命令或日志。
  • 宿主读:服务器 docker login 只用只读(拉取)token;尽量范围最小、时效最短。 token 实际权限粒度以 Gitea registry 实现为准(待现场验证)。
  • 登录凭据以 base64 明文存于该用户 ~/.docker/config.json,注意宿主机账号权限; 拉取完成后可 docker logout git.labmem.icu 清除。
  • 本目录所有命令均为占位示例,不含任何真实 secret;.env 只放镜像引用,不放凭据。
  • 若在仓库中发现疑似真实凭据:先停止操作,只报告路径、行号与类型(不复述值), 随后脱敏/移除并轮换该凭据。