我有一个 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 构建代理每次都从干净的机器开始。