build(docker): optimize image build workflow

This commit is contained in:
2026-07-07 10:59:38 +08:00
parent c0d20e650b
commit ed0ed99450
6 changed files with 74 additions and 25 deletions
+2 -11
View File
@@ -12,7 +12,7 @@ jobs:
name: Build and Push Docker Image name: Build and Push Docker Image
runs-on: gitea-label runs-on: gitea-label
container: container:
image: node:24-bookworm image: cozsweet-act-runner-node24-docker:latest
env: env:
NEXT_TELEMETRY_DISABLED: "1" NEXT_TELEMETRY_DISABLED: "1"
SENTRY_UPLOAD_SOURCEMAPS: "0" SENTRY_UPLOAD_SOURCEMAPS: "0"
@@ -21,19 +21,10 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Setup Docker CLI
run: |
set -eu
if ! command -v docker >/dev/null 2>&1; then
apt-get update
apt-get install -y --no-install-recommends docker.io
rm -rf /var/lib/apt/lists/*
fi
- name: Verify Docker - name: Verify Docker
run: | run: |
node --version node --version
pnpm --version
docker version docker version
docker info docker info
+8 -3
View File
@@ -3,19 +3,23 @@ WORKDIR /app
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
ENV PNPM_HOME=/pnpm ENV PNPM_HOME=/pnpm
ENV PNPM_STORE_PATH=/pnpm/store
ENV PATH=$PNPM_HOME:$PATH ENV PATH=$PNPM_HOME:$PATH
RUN apk add --no-cache libc6-compat \ RUN apk add --no-cache libc6-compat \
&& corepack enable \ && corepack enable \
&& corepack prepare pnpm@10.30.3 --activate && corepack prepare pnpm@10.30.3 --activate \
&& pnpm config set store-dir "$PNPM_STORE_PATH"
FROM base AS deps FROM base AS deps
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile RUN --mount=type=cache,id=cozsweet-pnpm-store,target=/pnpm/store,sharing=locked \
pnpm install --frozen-lockfile --prefer-offline
FROM base AS prod-deps FROM base AS prod-deps
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN pnpm install --prod --frozen-lockfile RUN --mount=type=cache,id=cozsweet-pnpm-store,target=/pnpm/store,sharing=locked \
pnpm install --prod --frozen-lockfile --prefer-offline
FROM base AS builder FROM base AS builder
ARG NEXT_ENV_FILE=.env.local ARG NEXT_ENV_FILE=.env.local
@@ -26,6 +30,7 @@ COPY --from=deps /app/node_modules ./node_modules
COPY . . COPY . .
RUN --mount=type=secret,id=next_env,target=/tmp/next_env,required=false \ RUN --mount=type=secret,id=next_env,target=/tmp/next_env,required=false \
--mount=type=cache,id=cozsweet-next-cache,target=/app/.next/cache,sharing=locked \
if [ -f /tmp/next_env ]; then cp /tmp/next_env "$NEXT_ENV_FILE"; fi; \ if [ -f /tmp/next_env ]; then cp /tmp/next_env "$NEXT_ENV_FILE"; fi; \
pnpm run build; \ pnpm run build; \
rm -f .env .env.local .env.development .env.production rm -f .env .env.local .env.development .env.production
+7 -5
View File
@@ -186,11 +186,13 @@ Docker 构建使用 Next.js standalone 输出:
1. `next.config.ts` 设置 `output: "standalone"` 1. `next.config.ts` 设置 `output: "standalone"`
2. `Dockerfile` 使用 multi-stage build。 2. `Dockerfile` 使用 multi-stage build。
3. build 阶段通过 Docker BuildKit secret 挂载 `.env.local``.env.production` 3. pnpm 依赖安装使用 BuildKit cache mount 复用 `/pnpm/store`
4. build 完成后删除临时 env 文件,避免 env 文件被复制到最终镜像 4. Next.js 构建使用 BuildKit cache mount 复用 `.next/cache`
5. runtime 阶段复制 `.next/standalone``.next/static``public` 和生产依赖 `node_modules` 5. build 阶段通过 Docker BuildKit secret 挂载 `.env.local``.env.production`
6. 生产依赖用于支持 Serwist 在运行时生成 `/serwist/sw.js`,避免手动修复 pnpm symlink 6. build 完成后删除临时 env 文件,避免 env 文件被复制到最终镜像
7. 容器通过 `node server.js` 启动,不再使用 `next start` 7. runtime 阶段复制 `.next/standalone``.next/static``public` 和生产依赖 `node_modules`
8. 生产依赖用于支持 Serwist 在运行时生成 `/serwist/sw.js`,避免手动修复 pnpm symlink。
9. 容器通过 `node server.js` 启动,不再使用 `next start`
注意:`NEXT_PUBLIC_*` 变量会在 `next build` 时固化到前端 bundle,因此测试环境和生产环境必须分别构建镜像,不能共用同一个镜像。 注意:`NEXT_PUBLIC_*` 变量会在 `next build` 时固化到前端 bundle,因此测试环境和生产环境必须分别构建镜像,不能共用同一个镜像。
+7 -6
View File
@@ -56,19 +56,20 @@ runs-on: gitea-label
自托管 runner 需要满足: 自托管 runner 需要满足:
1. 安装 Node.js,确保 `node` 在 runner 执行环境的 `PATH` 1. 可以拉取并运行 job 容器镜像
2. 安装 Docker CLI 2. 当前 runner 用户可以访问 Docker daemon
3. 当前 runner 用户可以访问 Docker daemon 3. 能访问配置的镜像仓库
4. 能访问配置的镜像仓库 4. 支持 Docker BuildKit secret,即 `DOCKER_BUILDKIT=1 docker build --secret ...`
5. 支持 Docker BuildKit secret,即 `DOCKER_BUILDKIT=1 docker build --secret ...`
当前 Docker 镜像发布 workflow 会使用 `node:24-bookworm` 作为 job 容器,确保 `actions/checkout@v4` 执行时已经存在 Node.js。runner 仍然需要挂载宿主机 Docker socket 当前 Docker 镜像发布 workflow 会使用 `cozsweet-act-runner-node24-docker:latest` 作为 job 容器,确保 `actions/checkout@v4` 执行时已经存在 Node.js,并且容器内已经包含 Docker CLI。runner 仍然需要挂载宿主机 Docker socket
```yaml ```yaml
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
``` ```
自定义 runner job 镜像的构建方式见 [Gitea Runner Job 镜像](./gitea-runner-image.md)。
## 验证方式 ## 验证方式
推送到 `test` 分支后,检查 Actions 日志中是否出现: 推送到 `test` 分支后,检查 Actions 日志中是否出现:
+34
View File
@@ -0,0 +1,34 @@
# Gitea Runner Job 镜像
当前 Docker 镜像发布 workflow 使用项目专用 job 容器:
```yaml
container:
image: cozsweet-act-runner-node24-docker:latest
```
这个镜像内置 Node.js 24、pnpm、Git 和 Docker CLI,可以避免每次构建时在 job 容器内重复 `apt-get install docker.io`
如果要进一步加速,可以在 runner 服务器上预构建一个项目专用 job 镜像:
```bash
docker build \
-f infra/gitea-runner/node24-docker/Dockerfile \
-t cozsweet-act-runner-node24-docker:latest \
infra/gitea-runner/node24-docker
```
验证镜像:
```bash
docker run --rm cozsweet-act-runner-node24-docker:latest node --version
docker run --rm cozsweet-act-runner-node24-docker:latest pnpm --version
docker run --rm cozsweet-act-runner-node24-docker:latest docker --version
```
runner 编排中仍需要保留 Docker socket 挂载:
```yaml
volumes:
- /var/run/docker.sock:/var/run/docker.sock
```
@@ -0,0 +1,16 @@
FROM node:24-bookworm
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
bash \
ca-certificates \
curl \
docker.io \
git \
openssh-client \
&& rm -rf /var/lib/apt/lists/*
RUN corepack enable \
&& corepack prepare pnpm@10.30.3 --activate
CMD ["bash"]