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

201 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```bash
# 推送 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:<digest>」与
`docker pull ...@sha256:<digest>` 手动部署引用;实际输出以日志为准,
若未给出,用下文「如何确认真实 digest」的方法在部署前核对
- 记下对应 commit sha 与 run 编号,便于追溯。
### 2) 拷贝示例到服务器,并确认现状与项目路径
```bash
# 仓库机器上(主机/路径按实际调整):
rsync -av ts/web/nextjs/polaris/deploy/ admin@<server>:/home/admin/docker/lab/polaris/
```
```bash
# 服务器上:
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)
```bash
docker login git.labmem.icu -u <registry用户名>
# 交互输入只读 token;不要把 token 写进命令行参数/历史
```
或非交互(输入不回显、不进历史):
```bash
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 部署(单服务)
```bash
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) 健康与反代验证
```bash
# 容器内自测(镜像内无 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`,
旧值保留为注释行,然后:
```bash
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` 只放镜像引用,不放凭据。
- 若在仓库中发现疑似真实凭据:先停止操作,只报告路径、行号与类型(不复述值),
随后脱敏/移除并轮换该凭据。