diff --git a/.gitea/workflows/docker-image.yml b/.gitea/workflows/docker-image.yml index 215cb1a8..771239c9 100644 --- a/.gitea/workflows/docker-image.yml +++ b/.gitea/workflows/docker-image.yml @@ -12,7 +12,7 @@ jobs: name: Build and Push Docker Image runs-on: gitea-label container: - image: node:24-bookworm + image: cozsweet-act-runner-node24-docker:latest env: NEXT_TELEMETRY_DISABLED: "1" SENTRY_UPLOAD_SOURCEMAPS: "0" @@ -21,19 +21,10 @@ jobs: - name: Checkout 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 run: | node --version + pnpm --version docker version docker info diff --git a/Dockerfile b/Dockerfile index 4a0ed96e..3f35e541 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,19 +3,23 @@ WORKDIR /app ENV NEXT_TELEMETRY_DISABLED=1 ENV PNPM_HOME=/pnpm +ENV PNPM_STORE_PATH=/pnpm/store ENV PATH=$PNPM_HOME:$PATH RUN apk add --no-cache libc6-compat \ && 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 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 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 ARG NEXT_ENV_FILE=.env.local @@ -26,6 +30,7 @@ COPY --from=deps /app/node_modules ./node_modules COPY . . 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; \ pnpm run build; \ rm -f .env .env.local .env.development .env.production diff --git a/docs/auto-build-restart.md b/docs/auto-build-restart.md index 07993a35..2850a125 100644 --- a/docs/auto-build-restart.md +++ b/docs/auto-build-restart.md @@ -186,11 +186,13 @@ Docker 构建使用 Next.js standalone 输出: 1. `next.config.ts` 设置 `output: "standalone"`。 2. `Dockerfile` 使用 multi-stage build。 -3. build 阶段通过 Docker BuildKit secret 挂载 `.env.local` 或 `.env.production`。 -4. build 完成后删除临时 env 文件,避免 env 文件被复制到最终镜像。 -5. runtime 阶段复制 `.next/standalone`、`.next/static`、`public` 和生产依赖 `node_modules`。 -6. 生产依赖用于支持 Serwist 在运行时生成 `/serwist/sw.js`,避免手动修复 pnpm symlink。 -7. 容器通过 `node server.js` 启动,不再使用 `next start`。 +3. pnpm 依赖安装使用 BuildKit cache mount 复用 `/pnpm/store`。 +4. Next.js 构建使用 BuildKit cache mount 复用 `.next/cache`。 +5. build 阶段通过 Docker BuildKit secret 挂载 `.env.local` 或 `.env.production`。 +6. build 完成后删除临时 env 文件,避免 env 文件被复制到最终镜像。 +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,因此测试环境和生产环境必须分别构建镜像,不能共用同一个镜像。 diff --git a/docs/docker-image-ci.md b/docs/docker-image-ci.md index dab99c7e..fd049472 100644 --- a/docs/docker-image-ci.md +++ b/docs/docker-image-ci.md @@ -56,19 +56,20 @@ runs-on: gitea-label 自托管 runner 需要满足: -1. 安装 Node.js,确保 `node` 在 runner 执行环境的 `PATH` 中。 -2. 安装 Docker CLI。 -3. 当前 runner 用户可以访问 Docker daemon。 -4. 能访问配置的镜像仓库。 -5. 支持 Docker BuildKit secret,即 `DOCKER_BUILDKIT=1 docker build --secret ...`。 +1. 可以拉取并运行 job 容器镜像。 +2. 当前 runner 用户可以访问 Docker daemon。 +3. 能访问配置的镜像仓库。 +4. 支持 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 volumes: - /var/run/docker.sock:/var/run/docker.sock ``` +自定义 runner job 镜像的构建方式见 [Gitea Runner Job 镜像](./gitea-runner-image.md)。 + ## 验证方式 推送到 `test` 分支后,检查 Actions 日志中是否出现: diff --git a/docs/gitea-runner-image.md b/docs/gitea-runner-image.md new file mode 100644 index 00000000..340419f3 --- /dev/null +++ b/docs/gitea-runner-image.md @@ -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 +``` diff --git a/infra/gitea-runner/node24-docker/Dockerfile b/infra/gitea-runner/node24-docker/Dockerfile new file mode 100644 index 00000000..5a73aa35 --- /dev/null +++ b/infra/gitea-runner/node24-docker/Dockerfile @@ -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"]