使用 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 install
和 pnpm 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.
node_modules
.git
.gitignore
*.md
dist
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.
./
├── 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
packages:
- 'packages/*'
node_modules
.git
.gitignore
*.md
dist
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.
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" ]