# Polaris 部署(第一阶段:CI 出镜像,宿主机手动部署 digest) 本目录提供在服务器上以**独立 Compose 项目**运行 Polaris 的最小配置。 第一阶段流程:CI(Gitea Actions)在校验(lint / typecheck / test / build / standalone 冒烟)通过后,**自动构建并推送**镜像 `git.labmem.icu//polaris:` 到 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:` 固定,杜绝 latest 漂移 - 只加入外部网络 `net-public`(与 nginx 同网);**不连** `net-db` - 不映射宿主机端口(无 `ports:`),仅通过 nginx 反代访问 - 无 DB/Redis、无 secrets、无源码/数据卷挂载 - 应用在镜像内已是 `node` 非 root 用户,监听 3000(见仓库 `Dockerfile`,本目录不改镜像与代码) - `HEALTHCHECK` 用运行时 Node 原生 `fetch` 探测 `/`,不要求 curl 等额外工具 - 顶层 `name: polaris` 固定项目名,避免项目路径/命名歧义 `` 为 Gitea 用户/组织名(`compose.yaml` 报错提示文本中的 `` 同义)。 ## 前置条件(须预先就绪) 一次性、由 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//polaris:` - digest:`sha256:`(日志中的具体展示形式**待现场验证**; 若日志未直接给出,用下文「如何确认真实 digest」的方法在部署前核对) - 记下对应 commit sha 与 run 编号,便于追溯。 ### 2) 拷贝示例到服务器,并确认现状与项目路径 ```bash # 仓库机器上(主机/路径按实际调整): rsync -av ts/web/nextjs/polaris/deploy/ admin@:/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 # 交互输入只读 token;不要把 token 写进命令行参数/历史 ``` 或非交互(输入不回显、不进历史): ```bash read -rs REGISTRY_TOKEN # 粘贴只读 token 后回车 printf '%s' "$REGISTRY_TOKEN" | docker login git.labmem.icu -u --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//polaris@sha256: # 注释行保留来源 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 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//polaris:` 查看该 tag 当前指向的 manifest(列表)摘要。 - **服务器最终校验**:`docker pull git.labmem.icu//polaris@sha256:` 输出 `Digest: sha256:...` —— 按 digest 拉取本身就是内容寻址校验, 两者不一致不可能拉取成功。 - 已拉取过 tag 的机器可查本地记录的远端摘要: `docker image inspect --format '{{index .RepoDigests 0}}' git.labmem.icu//polaris:`。 - 注意:`.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` 只放镜像引用,不放凭据。 - 若在仓库中发现疑似真实凭据:先停止操作,只报告路径、行号与类型(不复述值), 随后脱敏/移除并轮换该凭据。