docker - 由于 COPY node_modules,Docker 在 azure 中构建 -> 在相同代码库上存在较大差异。本地运行没有差异
问题描述
我有一个 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 构建代理每次都从干净的机器开始。
解决方案
Docker 在 Dockerfile 中缓存各个步骤的结果。这是全有或全无。如果上一步被缓存了,并且您正在执行的步骤与您之前所做的相同,那么docker build
将使用缓存的结果,但如果它不完全相同,那么从那时起缓存将不再有任何内容。
特别是在您的构建阶段,当您
COPY . ./
如果任何文件更改,这会使缓存无效;那么当你yarn install
在下一行运行时,它几乎总是会重复。此时您实际上只需要包元数据文件,因此您可以改为
COPY package.json yarn.lock ./
RUN yarn install
并且在重建时不会重复。
如果图像大小是一个问题,你也yarn install --production
可以不安装devDependencies
你的package.json
. 在典型使用中,您可以在最终的运行时映像中执行此操作,但在您的情况下,您需要一个 C 工具链来构建这些依赖项。这意味着您的 Dockerfile 中将包含三个阶段:
- 基于 Node,加上一个 C 工具链,安装所有依赖项并运行
yarn build
- 基于 Node,加上一个 C 工具链,只运行
yarn install --production
- 基于Node,只有
COPY --from=...
第一阶段构建的应用程序和node_modules
第二阶段的运行时,然后具有通常EXPOSE
和CMD
元数据
推荐阅读
- javascript - Kendo UI 下拉列表已选中
- java - 使用 2d 数组和 for 循环的 Mailman 邮箱问题
- java - Spring Data Rest JPA rest service with hibernate:控制延迟加载
- javascript - 如何从 javascript 函数内部调用 jquery buttonclick 函数
- ant - 带有空输入字符串的 apache Ant 基本名称问题
- asp.net-mvc - Azure 应用服务上的瞬态无限登录循环 - 使用 Azure AD 进行 OpenIDConnect 身份验证
- php - 如何从字符串中添加数字
- html - Bootstrap 4 - Ubuntu Chrome 和 Firefox 中不同的渲染字体大小
- python - 使用opencv和python从具有可变帧速率的IP摄像机录制视频
- android - 如何使用 Mockito 的 doAnswer 测试 void 方法