使用缓存层优化 Azure DevOps docker 管道

Øys*_*sen 5 docker dockerfile azure-devops azure-pipelines

我正在尝试优化我的 azure devops 管道中的构建时间,但npm install我的 dockerfile 中的阶段不会缓存。为什么?

这是我的 dockerfile。在复制我的其余源代码之前,我已经将 package*.json 文件和 npm install 分开复制到它自己的层中,因为这是最佳实践,并且应该使 npm install 层可以在构建之间缓存。

FROM node:12-alpine3.12 AS builder
WORKDIR /app
ARG VERSION

COPY package.json ./
COPY package-lock.json ./
RUN npm install

COPY . .
RUN npm run build

  ...
FROM node:12-alpine3.12
COPY --from=builder /dist .
  ...
Run Code Online (Sandbox Code Playgroud)

这是我的构建管道。由于 Azure 每次都构建在干净的 vm 上,因此我尝试下拉现有图像以利用以前的构建缓存(参考:如何在 Azure DevOps 中启用 Docker 层缓存)。

FROM node:12-alpine3.12 AS builder
WORKDIR /app
ARG VERSION

COPY package.json ./
COPY package-lock.json ./
RUN npm install

COPY . .
RUN npm run build

  ...
FROM node:12-alpine3.12
COPY --from=builder /dist .
  ...
Run Code Online (Sandbox Code Playgroud)

如您所见,我已将 Dockerfile 中的每个阶段与我的管道中各自的阶段分开。一个用于构建“构建器”阶段,另一个用于构建生成的图像。来自每个阶段的 docker 镜像被推送到我的容器注册表。在重建或 package.json 未更改的构建中,我希望npm install层输出---> Using cache,但在运行“构建器”阶段时它永远不会输出。

Step 1/8 : FROM node:12-alpine3.12 AS builder
12-alpine3.12: Pulling from library/node
188c0c94c7c5: Already exists
c4e63f2c1114: Already exists
74bf6ceff101: Already exists
1f6472fc624b: Already exists
Digest: sha256:f2e453020045d7d93790777bc3ce2c992f097ce9a6d577d73490093df93b0702
Status: Downloaded newer image for node:12-alpine3.12
 ---> ccd680d0b809
Step 2/8 : WORKDIR /app
 ---> Using cache
 ---> 9f88e2fda996
Step 3/8 : ARG VERSION
 ---> Using cache
 ---> 707e936abbc5
Step 4/8 : COPY package.json ./
 ---> Using cache
 ---> 034785fd08a7
Step 5/8 : COPY package-lock.json ./
 ---> Using cache
 ---> ab778dbabb01
Step 6/8 : RUN npm install
 ---> Running in df1dc4b5bf91
    ...
Removing intermediate container df1dc4b5bf91
 ---> 4ee43e4f6095
Step 7/8 : COPY . .
 ---> 9ea6540727f2
Step 8/8 : RUN npm run build
 ---> Running in bd65f90191a5
Run Code Online (Sandbox Code Playgroud)

请注意Removing intermediate container df1dc4b5bf91上述事项。它可能与问题有关吗?尽管如此,我确实尝试过docker build --rm=false,但它仍然没有在重建时使用缓存层。但是,在构建管道的最后阶段时,它确实从缓存中运行:

Step 1/16 : FROM node:12-alpine3.12 AS builder
 ---> ccd680d0b809
Step 2/16 : WORKDIR /app
 ---> Using cache
 ---> 9f88e2fda996
Step 3/16 : ARG VERSION
 ---> Using cache
 ---> 707e936abbc5
Step 4/16 : COPY package.json ./
 ---> Using cache
 ---> 034785fd08a7
Step 5/16 : COPY package-lock.json ./
 ---> Using cache
 ---> ab778dbabb01
Step 6/16 : RUN npm install
 ---> Using cache
 ---> 4ee43e4f6095
Run Code Online (Sandbox Code Playgroud)

我错过了什么?

Øys*_*sen 6

解决了!

这里的问题是ARGDockerfile 中的关键字。它总是会发生变化,从而创建一个无法缓存的层,从而更改下面其他层的哈希值。

来自 Docker 文档:https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact

ARG 是 Dockerfile 中唯一可以位于 FROM 之前的指令

ARG VERSION

FROM node:12-alpine3.12 AS builder
WORKDIR /app

COPY package.json ./
COPY package-lock.json ./
RUN npm install

COPY . .
RUN npm run build

  ...
FROM node:12-alpine3.12
COPY --from=builder /dist .
RUN if [ "x$VERSION" = "x" ] ; then echo "VERSION not set" ; else echo "$VERSION" > ./assets/version.txt ; fi
  ...
Run Code Online (Sandbox Code Playgroud)

通过将第一个放入ARGDockerfile 中,docker 构建上下文仍然会接收它,但它不会成为一个层并破坏缓存。