0

我有一个 Azure Devops 管道,它构建将在不同的 IoT-Edge 设备上运行的 docker 映像。这些设备的互联网连接非常糟糕,因此较小的 docker diff 大小至关重要。

代码库由一个打字稿 nodejs 服务器(因此yarn build)组成,它需要需要使用 make gcc 等构建的 node_modules。

当我在我的机器上本地运行 docker 构建过程时,docker 使用以前构建的中间层,因此使用时的差异docker history <image_id>只有 700kb。(实际的代码库)。我在yarn install从缓存中获取的构建日志中看到,因此层哈希变得相同。

在 azure 中构建图像时,差异变为 90MB。(复制了整个 node_modules)我从每个图像中提取了 node_modules,并比较了每个文件夹中所有文件的哈希值,并HashMyFiles.exe比较了 SHA-1 和 SHA-256。

但是未压缩的 tar 散列并不相同,请参阅这篇关于 docker 层如何散列的帖子:Docker 如何计算每个层的散列?它是确定性的吗?


所以问题是,在 azure 中构建图像时,如何避免每次代码更改都拉取整个 node_modules。


我们讨论过的一种解决方案是使用我们想要的 node_modules 构建一个 docker node-image preinstall。但这不是首选,并且在更改模块时需要额外的工作。

来自 azure 中构建的两个相同代码库的 Docker 历史记录: 1

PS C:\temp\cby> docker history a8f3453f4c1c
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
a8f3453f4c1c        2 hours ago         /bin/sh -c #(nop)  CMD ["node" "./dist/index…   0B
<missing>           2 hours ago         /bin/sh -c #(nop)  ENV NODE_ENV=production      0B
<missing>           2 hours ago         /bin/sh -c mkdir ./logs/                        0B
<missing>           2 hours ago         /bin/sh -c yarn run build                       3.34MB
<missing>           2 hours ago         /bin/sh -c #(nop) COPY dir:31a5b4423ce7e6928…   323kB
<missing>           2 hours ago         /bin/sh -c #(nop) COPY dir:a234dce19106582d9…   93.7MB
<missing>           2 hours ago         /bin/sh -c #(nop) WORKDIR /app                  0B
<missing>           2 hours ago         /bin/sh -c apk add --no-cache udev              1.83MB
<missing>           2 days ago                                                          70.2MB              merge sha256:eef5dfda7c2565cba57f222376d551426487839af67cf659bb3bb4fa51ef688a to sha256:6d1ef012b5674ad8a127ecfa9b5e6f5178d171b90ee462846974177fd9bdd39f
<missing>           2 days ago          /bin/sh -c rm -rf latest.tar.gz* /tmp/*     …   0B
<missing>           2 days ago          /bin/sh -c apk del curl gnupg                   0B
<missing>           2 days ago          /bin/sh -c curl -sfSL -O https://yarnpkg.com…   0B
<missing>           2 days ago          /bin/sh -c for server in ipv4.pool.sks-keyse…   0B
<missing>           2 days ago          /bin/sh -c /usr/lib/node_modules/npm/bin/npm…   0B
<missing>           2 days ago          /bin/sh -c apk upgrade --no-cache -U &&   ap…   0B
<missing>           2 days ago          /bin/sh -c #(nop) COPY file:fc6fb2d3d0d591f8…   0B
<missing>           2 days ago          /bin/sh -c #(nop) COPY dir:3d23406cd5b322399…   0B
<missing>           2 days ago          /bin/sh -c #(nop) COPY dir:857b32a43b41ef438…   0B
<missing>           3 days ago          /bin/sh -c #(nop) COPY file:20cc2cc5b0ae7508…   0B
<missing>           10 months ago       /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>           10 months ago       /bin/sh -c #(nop) ADD file:aa17928040e31624c…   4.21MB

2

PS C:\temp\cby> docker history 2fc80525d55e
IMAGE               CREATED              CREATED BY                                      SIZE                COMMENT
2fc80525d55e        45 seconds ago       /bin/sh -c #(nop)  CMD ["node" "./dist/index…   0B
<missing>           46 seconds ago       /bin/sh -c #(nop)  ENV NODE_ENV=production      0B
<missing>           46 seconds ago       /bin/sh -c mkdir ./logs/                        0B
<missing>           48 seconds ago       /bin/sh -c yarn run build                       3.34MB
<missing>           57 seconds ago       /bin/sh -c #(nop) COPY dir:31a5b4423ce7e6928…   323kB
<missing>           About a minute ago   /bin/sh -c #(nop) COPY dir:a234dce19106582d9…   93.7MB
<missing>           About a minute ago   /bin/sh -c #(nop) WORKDIR /app                  0B
<missing>           About a minute ago   /bin/sh -c apk add --no-cache udev              1.83MB
<missing>           2 days ago                                                           70.2MB              merge sha256:eef5dfda7c2565cba57f222376d551426487839af67cf659bb3bb4fa51ef688a to sha256:6d1ef012b5674ad8a127ecfa9b5e6f5178d171b90ee462846974177fd9bdd39f
<missing>           2 days ago           /bin/sh -c rm -rf latest.tar.gz* /tmp/*     …   0B
<missing>           2 days ago           /bin/sh -c apk del curl gnupg                   0B
<missing>           2 days ago           /bin/sh -c curl -sfSL -O https://yarnpkg.com…   0B
<missing>           2 days ago           /bin/sh -c for server in ipv4.pool.sks-keyse…   0B
<missing>           2 days ago           /bin/sh -c /usr/lib/node_modules/npm/bin/npm…   0B
<missing>           2 days ago           /bin/sh -c apk upgrade --no-cache -U &&   ap…   0B
<missing>           2 days ago           /bin/sh -c #(nop) COPY file:fc6fb2d3d0d591f8…   0B
<missing>           2 days ago           /bin/sh -c #(nop) COPY dir:3d23406cd5b322399…   0B
<missing>           2 days ago           /bin/sh -c #(nop) COPY dir:857b32a43b41ef438…   0B
<missing>           3 days ago           /bin/sh -c #(nop) COPY file:20cc2cc5b0ae7508…   0B
<missing>           10 months ago        /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>           10 months ago        /bin/sh -c #(nop) ADD file:aa17928040e31624c…   4.21MB

我的 Dockerfile,我尝试了多个 dockerfile,有和没有多阶段构建,结果相同。Azure 在下载图像时给出了很大的差异:

FROM mhart/alpine-node:10
RUN apk add --no-cache make gcc g++ python linux-headers udev
WORKDIR /app

# Install node modules first (avoids reinstalling for every source code change).
COPY package.json yarn.lock ./
RUN yarn install


FROM mhart/alpine-node:10
RUN apk add --no-cache udev
WORKDIR /app
COPY --from=0 /app/node_modules ./node_modules
COPY . .
RUN yarn run build

RUN mkdir ./logs/

ENV NODE_ENV production

CMD ["node", "./dist/index.js"]

.dockerignore

node_modules
/build
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

/logs
/tests
/testlogs
/dist
/ota

.vscode
.git

编辑+临时解决方案

我们最终做了一个自托管的构建代理,它有点贵,但我们获得了更快的构建时间和每个操作的正确缓存。最重要的是,我们获得了更小的下载大小。

我不确定为什么每次我们在每个操作上运行构建时 docker build 都会给出一个新的哈希值。

如果哈希相同,构建时间仍然会很慢,因为 azure 构建代理每次都从干净的机器开始。

4

2 回答 2

0

Docker 在 Dockerfile 中缓存各个步骤的结果。这是全有或全无。如果上一步被缓存了,并且您正在执行的步骤与您之前所做的相同,那么docker build将使用缓存的结果,但如果它不完全相同,那么从那时起缓存将不再有任何内容。

特别是在您的构建阶段,当您

COPY . ./

如果任何文件更改,这会使缓存无效;那么当你yarn install在下一行运行时,它几乎总是会重复。此时您实际上只需要包元数据文件,因此您可以改为

COPY package.json yarn.lock ./
RUN yarn install

并且在重建时不会重复。


如果图像大小是一个问题,你也yarn install --production可以不安装devDependencies你的package.json. 在典型使用中,您可以在最终的运行时映像中执行此操作,但在您的情况下,您需要一个 C 工具链来构建这些依赖项。这意味着您的 Dockerfile 中将包含三个阶段:

  1. 基于 Node,加上一个 C 工具链,安装所有依赖项并运行yarn build
  2. 基于 Node,加上一个 C 工具链,只运行yarn install --production
  3. 基于Node,只有COPY --from=...第一阶段构建的应用程序和node_modules第二阶段的运行时,然后具有通常EXPOSECMD元数据
于 2020-01-17T10:51:19.147 回答
0

对于您正在做的事情,您实际上并不需要 2 个阶段的构建。一个就够了。但是您的方法存在一些一般性问题。

在运行之前,yarn build您只需要复制 package*.json,而不是上下文中的所有内容(请记住,您的本地上下文已经包含 node_modules,但您的远程服务器没有)。

当您执行以下操作时:

COPY --from=0 /app/node_modules ./node_modules
COPY . .

您实际上用上下文中的任何内容覆盖了文件夹 node_modules ,因此基本上您之前的步骤是无用的。

我建议你尝试这样的事情:

FROM mhart/alpine-node:10
RUN apk add --no-cache udev
WORKDIR /app

COPY package*.json ./
RUN yarn install

COPY index.js ./
# also copy here any other project files if any

RUN yarn run build

RUN mkdir ./logs/

ENV NODE_ENV production

CMD ["node", "./dist/index.js"]
于 2020-01-17T09:51:05.353 回答