Skip to main content
Version: 9.x

使用 Docker

注意

在构建期间不可能在 Docker 容器和主机文件系统之间创建引用链接或硬链接。你可以做的下一个最好的事情是使用 BuildKit 缓存挂载在构建之间共享缓存。或者,你可以使用 podman,因为它可以在构建时挂载 Btrfs 卷。

¥It is impossible to create reflinks or hardlinks between a Docker container and the host filesystem during build time. The next best thing you can do is using BuildKit cache mount to share cache between builds. Alternatively, you may use podman because it can mount Btrfs volumes during build time.

最小化 Docker 镜像大小和构建时间

¥Minimizing Docker image size and build time

  • 使用小图片,例如 node:XX-slim

    ¥Use a small image, e.g. node:XX-slim.

  • 如果可能的话,利用多阶段并且有意义。

    ¥Leverage multi-stage if possible and makes sense.

  • 利用 BuildKit 缓存安装。

    ¥Leverage BuildKit cache mounts.

示例 1:在 Docker 容器中构建打包包

¥Example 1: Build a bundle in a Docker container

由于 devDependencies 仅是构建打包包所必需的,因此 pnpm install --prod 将是与 pnpm installpnpm run build 不同的单独阶段,允许最后阶段仅复制早期阶段中的必要文件,从而最大限度地减少最终映像的大小。

¥Since devDependencies is only necessary for building the bundle, pnpm install --prod will be a separate stage from pnpm install and pnpm run build, allowing the final stage to copy only necessary files from the earlier stages, minimizing the size of the final image.

.dockerignore
node_modules
.git
.gitignore
*.md
dist
Dockerfile
FROM node:20-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
COPY . /app
WORKDIR /app

FROM base AS prod-deps
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile

FROM base AS build
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN pnpm run build

FROM base
COPY --from=prod-deps /app/node_modules /app/node_modules
COPY --from=build /app/dist /app/dist
EXPOSE 8000
CMD [ "pnpm", "start" ]

示例 2:在 monorepo 中构建多个 Docker 镜像

¥Example 2: Build multiple Docker images in a monorepo

假设你有一个包含 3 个包的 monorepo:app1、app2 和通用;app1 和 app2 相互依赖但不相互依赖。

¥Assuming you have a monorepo with 3 packages: app1, app2, and common; app1 and app2 depend on common but not each other.

你只想保存每个包的必要依赖,pnpm deploy 应该帮助你仅复制必要的文件和包。

¥You want to save only necessary dependencies for each package, pnpm deploy should help you with copying only necessary files and packages.

Structure of the monorepo
./
├── Dockerfile
├── .dockerignore
├── .gitignore
├── packages/
│   ├── app1/
│   │   ├── dist/
│   │   ├── package.json
│   │   ├── src/
│   │   └── tsconfig.json
│   ├── app2/
│   │   ├── dist/
│   │   ├── package.json
│   │   ├── src/
│   │   └── tsconfig.json
│   └── common/
│   ├── dist/
│   ├── package.json
│   ├── src/
│   └── tsconfig.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
└── tsconfig.json
pnpm-workspace.yaml
packages:
- 'packages/*'
.dockerignore
node_modules
.git
.gitignore
*.md
dist
Dockerfile
FROM node:20-slim AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable

FROM base AS build
COPY . /usr/src/app
WORKDIR /usr/src/app
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN pnpm run -r build
RUN pnpm deploy --filter=app1 --prod /prod/app1
RUN pnpm deploy --filter=app2 --prod /prod/app2

FROM base AS app1
COPY --from=build /prod/app1 /prod/app1
WORKDIR /prod/app1
EXPOSE 8000
CMD [ "pnpm", "start" ]

FROM base AS app2
COPY --from=build /prod/app2 /prod/app2
WORKDIR /prod/app2
EXPOSE 8001
CMD [ "pnpm", "start" ]

运行以下命令为 app1 和 app2 构建镜像:

¥Run the following commands to build images for app1 and app2:

docker build . --target app1 --tag app1:latest
docker build . --target app2 --tag app2:latest

示例 3:在 CI/CD 上构建

¥Example 3: Build on CI/CD

在 CI 或 CD 环境中,BuildKit 缓存挂载可能不可用,因为 VM 或容器是短暂的,只有正常的 docker 缓存才能工作。

¥On CI or CD environments, the BuildKit cache mounts might not be available, because the VM or container is ephemeral and only normal docker cache will work.

因此,另一种方法是使用典型的 Dockerfile 和增量构建的层,对于这种情况,pnpm fetch 是最佳选择,因为它只需要 pnpm-lock.yaml 文件,并且只有在更改依赖时才会丢失层缓存。

¥So an alternative is to use a typical Dockerfile with layers that are built incrementally, for this scenario, pnpm fetch is the best option, as it only needs the pnpm-lock.yaml file and the layer cache will only be lost when you change the dependencies.

Dockerfile
FROM node:20-slim AS base

ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable

FROM base AS prod

COPY pnpm-lock.yaml /app
WORKDIR /app
RUN pnpm fetch --prod

COPY . /app
RUN pnpm run build

FROM base
COPY --from=prod /app/node_modules /app/node_modules
COPY --from=prod /app/dist /app/dist
EXPOSE 8000
CMD [ "pnpm", "start" ]