Files
lab/ts/web/nextjs/polaris/deploy/README.md
T
labmem 144b8abe49
Polaris CI / verify (push) Successful in 2m10s
Polaris CI / publish (push) Successful in 1m56s
ci(polaris): 增加镜像发布工作流
校验通过后构建、冒烟测试并推送镜像。
提供按摘要手动部署与回滚的 Compose 模板和说明。
2026-10-12 23:00:34 +08:00

192 lines
9.5 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 的最小配置。
第一阶段流程:CI(Gitea Actions)在校验(lint / typecheck / test / build / standalone
冒烟)通过后,**自动构建并推送**镜像 `git.labmem.icu/<repo-owner>/polaris:<full-commit-sha>`
到 Gitea registry;宿主机(服务器)首次部署时用**只读**凭据 `docker login`,在 `.env`
中固定该镜像的 digest,再 `pull + up` 单服务运行。
## 文件说明
| 文件 | 用途 |
|---|---|
| `compose.yaml` | 独立项目 `polaris` 的服务定义(单服务,仅外部网络 `net-public`) |
| `.env.example` | 环境变量示例,复制为同目录 `.env` 并填入真实镜像 digest |
设计约束:
- 镜像来源:CI 按 commit sha tag 推送;部署侧用 `image: ${POLARIS_IMAGE:?...}`
必填 + `sha256:<digest>` 固定,杜绝 latest 漂移
- 只加入外部网络 `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 在 CI 运行日志中的输出形式**
(workflow 正在另行更新,本 lane 不改它;下文涉及处均以实际运行日志为准)
- registry / runner 实际行为:登录、推送、按 digest 拉取
- 生产 Nginx 行为(见「Nginx 反代」)
- 服务器上 `net-public` 网络名、既有 Compose 项目与目录布局
(以 `docker network ls`、`docker compose ls` 实际输出为准)
- Nginx 配置未提供:不要在本目录猜测域名,也不要改动服务器 Nginx 文件
## 首次发布步骤
### 1) 触发并等待 CI,记录镜像与 digest
```bash
# 推送到触发分支/路径后,在 Gitea 网页打开 Actions 运行页,等待 job 全绿
```
- 确认校验/构建/冒烟全部通过后,从该次运行日志的**推送步骤**记录:
- 镜像:`git.labmem.icu/<repo-owner>/polaris:<full-commit-sha>`
- digest:`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 的 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 运行日志**:推送步骤的输出通常包含 digest;具体展示形式取决于 workflow 写法,
**待现场验证**,以实际日志为准。
- **部署前交叉核对**(任一已登录该 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。CI 采用单平台还是多平台构建取决于 workflow
(**待现场验证**),构建平台需覆盖服务器架构。
- 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` 只放镜像引用,不放凭据。
- 若在仓库中发现疑似真实凭据:先停止操作,只报告路径、行号与类型(不复述值),
随后脱敏/移除并轮换该凭据。