ci(polaris): 增加镜像发布工作流
校验通过后构建、冒烟测试并推送镜像。 提供按摘要手动部署与回滚的 Compose 模板和说明。
This commit is contained in:
@@ -67,3 +67,110 @@ jobs:
|
|||||||
static_asset=$(curl -fsS "$base_url/" | grep -o '/_next/static/[^"]*' | sed -n '1p')
|
static_asset=$(curl -fsS "$base_url/" | grep -o '/_next/static/[^"]*' | sed -n '1p')
|
||||||
test -n "$static_asset" || { echo "homepage references no /_next/static asset" >&2; exit 1; }
|
test -n "$static_asset" || { echo "homepage references no /_next/static asset" >&2; exit 1; }
|
||||||
curl -fsS -o /dev/null "$base_url$static_asset"
|
curl -fsS -o /dev/null "$base_url$static_asset"
|
||||||
|
|
||||||
|
publish:
|
||||||
|
needs: verify
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||||
|
runs-on: polaris-ci
|
||||||
|
container: node:22.23.2-bookworm
|
||||||
|
timeout-minutes: 30
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: https://gitea.com/actions/checkout@11d5960a326750d5838078e36cf38b85af677262
|
||||||
|
|
||||||
|
# Daemon comes from the runner (gitea/runner:3-dind-rootless) via the
|
||||||
|
# job container's /var/run/docker.sock. Only the CLI is installed here;
|
||||||
|
# DOCKER_HOST stays unset and no host socket is mounted.
|
||||||
|
- name: Install Docker CLI
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y --no-install-recommends docker.io
|
||||||
|
docker version
|
||||||
|
|
||||||
|
- name: Resolve image reference
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
repository="${GITHUB_REPOSITORY:?GITHUB_REPOSITORY is not set}"
|
||||||
|
owner="${repository%%/*}"
|
||||||
|
owner="${owner,,}"
|
||||||
|
if ! [[ "$owner" =~ ^[a-z0-9]([a-z0-9._-]*[a-z0-9])?$ ]]; then
|
||||||
|
echo "::error::owner segment '$owner' is not a valid registry path component" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
image="git.labmem.icu/${owner}/polaris:${GITHUB_SHA}"
|
||||||
|
echo "image reference: $image"
|
||||||
|
echo "IMAGE=$image" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- name: Build image
|
||||||
|
run: docker build -t "$IMAGE" ts/web/nextjs/polaris
|
||||||
|
|
||||||
|
- name: Smoke test image
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
# Use the unique container ID instead of a fixed name so
|
||||||
|
# concurrent runs on a shared runner daemon cannot collide.
|
||||||
|
container="$(docker run -d "$IMAGE")"
|
||||||
|
cleanup() { docker rm -f "$container" >/dev/null 2>&1 || true; }
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
probe() {
|
||||||
|
docker exec "$container" node -e "fetch('http://127.0.0.1:3000$1').then((r) => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
ready=0
|
||||||
|
for _ in $(seq 1 30); do
|
||||||
|
if probe /; then
|
||||||
|
ready=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if [[ "$(docker inspect -f '{{.State.Running}}' "$container")" != "true" ]]; then
|
||||||
|
echo "container exited early, logs:" >&2
|
||||||
|
docker logs "$container" >&2 || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
test "$ready" -eq 1 || { echo "server not ready within 30s" >&2; docker logs "$container" >&2 || true; exit 1; }
|
||||||
|
|
||||||
|
probe /categories/dev/ || { echo "smoke test failed: /categories/dev/" >&2; exit 1; }
|
||||||
|
probe /brand/logo.svg || { echo "smoke test failed: /brand/logo.svg" >&2; exit 1; }
|
||||||
|
echo "smoke test passed for $IMAGE"
|
||||||
|
|
||||||
|
- name: Log in and push image
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
POLARIS_REGISTRY_USER: ${{ secrets.POLARIS_REGISTRY_USER }}
|
||||||
|
POLARIS_REGISTRY_TOKEN: ${{ secrets.POLARIS_REGISTRY_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
if [[ -z "${POLARIS_REGISTRY_USER}" || -z "${POLARIS_REGISTRY_TOKEN}" ]]; then
|
||||||
|
echo "::error::缺少 secrets.POLARIS_REGISTRY_USER 或 secrets.POLARIS_REGISTRY_TOKEN,无法登录 git.labmem.icu;请在仓库 Settings -> Actions -> Secrets 配置后重试" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ephemeral docker config: credentials live only inside this step
|
||||||
|
# and are deleted when the step (and with it the job) exits.
|
||||||
|
docker_config_dir="$(mktemp -d)"
|
||||||
|
push_log_file="$(mktemp)"
|
||||||
|
trap 'rm -rf "$docker_config_dir" "$push_log_file"' EXIT
|
||||||
|
|
||||||
|
printf '%s' "$POLARIS_REGISTRY_TOKEN" | docker --config "$docker_config_dir" login git.labmem.icu --username "$POLARIS_REGISTRY_USER" --password-stdin >/dev/null
|
||||||
|
|
||||||
|
echo "pushing $IMAGE"
|
||||||
|
# tee keeps push output visible in the log; pipefail (already set
|
||||||
|
# via `set -euo pipefail`) fails the step when push itself fails.
|
||||||
|
docker --config "$docker_config_dir" push "$IMAGE" 2>&1 | tee "$push_log_file"
|
||||||
|
|
||||||
|
digest="$(sed -n 's/.*digest: \(sha256:[0-9a-f]\{64\}\).*/\1/p' "$push_log_file" | tail -n 1)"
|
||||||
|
if [[ ! "$digest" =~ ^sha256:[0-9a-f]{64}$ ]]; then
|
||||||
|
echo "::error::push 日志中未解析到合法 digest(期望 sha256:<64 位十六进制>),不输出部署引用;请在 registry 界面确认该镜像的实际 digest 后再手动部署" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "published image: $IMAGE"
|
||||||
|
echo "digest: $digest"
|
||||||
|
echo "manual deploy reference: docker pull ${IMAGE%%:*}@$digest"
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Polaris 部署环境变量示例 —— 复制为同目录 .env 后填入真实值:
|
||||||
|
# cp .env.example .env
|
||||||
|
#
|
||||||
|
# 安全约定:
|
||||||
|
# - 本文件只放镜像引用;任何 registry token、密码都不要写进这里或本示例文件
|
||||||
|
# - CI 推送用 secrets POLARIS_REGISTRY_USER / POLARIS_REGISTRY_TOKEN(写权限),
|
||||||
|
# 只配置在 Gitea Actions secrets 中;宿主机登录另用只读 token(见 README.md 安全提醒)
|
||||||
|
|
||||||
|
# 固定到具体 digest 的镜像引用(内容寻址,可精确回退)。
|
||||||
|
# 镜像由 CI 校验通过后推送:git.labmem.icu/<repo-owner>/polaris:<full-commit-sha>
|
||||||
|
# <repo-owner> 为 Gitea 用户/组织名;<64-hex-digest> 为该次推送对应的 sha256 摘要,
|
||||||
|
# 获取与校验方法见 README.md「如何确认真实 digest」。
|
||||||
|
# 建议保留来源与上一版注释行,便于追溯与回退:
|
||||||
|
# # 来源:CI run <run-id> / commit <full-commit-sha>
|
||||||
|
# POLARIS_IMAGE=git.labmem.icu/<repo-owner>/polaris@sha256:<old-64-hex-digest>
|
||||||
|
POLARIS_IMAGE=git.labmem.icu/<repo-owner>/polaris@sha256:<64-hex-digest>
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
# 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` 只放镜像引用,不放凭据。
|
||||||
|
- 若在仓库中发现疑似真实凭据:先停止操作,只报告路径、行号与类型(不复述值),
|
||||||
|
随后脱敏/移除并轮换该凭据。
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Polaris 独立 Compose 项目(第一阶段:宿主机手动发布)。
|
||||||
|
#
|
||||||
|
# 设计约束(详见 README.md):
|
||||||
|
# - 镜像由 .env 的 POLARIS_IMAGE 以 sha256 digest 固定,缺失时 compose 直接报错
|
||||||
|
# - 只加入外部网络 net-public(与 nginx 同网);不连 net-db
|
||||||
|
# - 不映射宿主机端口(无 ports:),仅经 nginx 反代访问
|
||||||
|
# - 无 DB/Redis、无 secrets、无源码/数据卷挂载
|
||||||
|
# - 应用在镜像内已是 node 非 root,监听 3000(见仓库 Dockerfile,本目录不改其行为)
|
||||||
|
# - HEALTHCHECK 用运行时 Node 原生 fetch 探测 /,不要求 curl 等额外工具
|
||||||
|
# - 顶层 name 固定项目名为 polaris,规避项目路径/命名歧义
|
||||||
|
|
||||||
|
name: polaris
|
||||||
|
|
||||||
|
services:
|
||||||
|
polaris:
|
||||||
|
# 必填:digest 固定的镜像引用;未设置时 compose 加载即失败并给出提示
|
||||||
|
image: ${POLARIS_IMAGE:?set POLARIS_IMAGE in .env, e.g. git.labmem.icu/<owner>/polaris@sha256:<digest> — see deploy/README.md}
|
||||||
|
container_name: polaris # net-public 内的 DNS 名,nginx 上游即 http://polaris:3000
|
||||||
|
restart: unless-stopped
|
||||||
|
# expose 仅作声明用途,不是端口映射;同网络容器本就可直接互访
|
||||||
|
expose:
|
||||||
|
- "3000"
|
||||||
|
healthcheck:
|
||||||
|
# 运行时镜像只有 node,不假设 curl:Node 22 自带全局 fetch
|
||||||
|
test:
|
||||||
|
- CMD
|
||||||
|
- node
|
||||||
|
- -e
|
||||||
|
- "fetch('http://127.0.0.1:3000/').then((r) => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 30s
|
||||||
|
networks:
|
||||||
|
- net-public
|
||||||
|
|
||||||
|
networks:
|
||||||
|
net-public:
|
||||||
|
external: true # 由服务器既有栈提供;名字以 docker network ls 实际输出为准,勿在本文件创建
|
||||||
Reference in New Issue
Block a user