一般来说,我应该如何调试这种问题?我怎么知道为什么docker-compose认为事情已经改变了(尽管我非常确定没有任何改变),哪些文件/命令/结果是不同的。通常可以按照
此处所示的步骤进行操作:
我有点失望,似乎找不到任何方法使Docker构建更详细
但是当涉及到
docker-compose时,它取决于您使用的版本和选项。
moby/moby
issue 30081 (由
Sebastiaan van Stijn (thaJeztah
)解释)
当前版本的
docker-compose
和
docker build
在许多(或所有)情况下都不会共享构建缓存,或者至少不会产生相同的摘要。
原因是使用
docker-compose
发送构建上下文时,它将使用稍微不同的压缩方式(
docker-compose
用Python编写,而
docker
cli用Go编写)。
由于它们是不同的实现(和语言),可能会有其他差异。
(这也在docker/compose issue 883中讨论过)
下一个版本的
docker compose
将具有一项(目前是选择性的)功能,使其使用实际的
docker
cli执行构建(通过设置
COMPOSE_DOCKER_CLI_BUILD=1
环境变量)。这是在
docker/compose#6865中实现的(1.25.0-rc3+,2019年10月)。使用该功能时,docker compose也可以使用
BuildKit来构建镜像(设置
DOCKER_BUILDKIT=1
环境变量)。如果可能的话,我强烈建议您使用buildkit进行构建。当使用BuildKit(需要Docker 18.09或更高版本,并且此时不支持构建Windows容器)时,您将看到构建速度大大提高,并且在重复构建中将构建上下文发送到守护程序所需的时间也会缩短(buildkit使用交互式会话仅发送构建期间需要的文件,而不是上传整个构建上下文)。
因此,首先仔细检查您的docker-compose是否使用了BuildKit,如果问题(缓存未被重用)仍然存在,则:
COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose build
Sebastiaan在问题4012中添加了以下内容:
BuildKit仍然是自选的(因为目前还没有Windows支持),但其质量已经达到生产级别,可以作为构建Linux映像的默认选项。
最后,我意识到对于Azure流水线,您(可能)无法控制安装的Docker和Docker Compose的版本,但对于您的本地机器,请确保升级到最新的19.03补丁版本;例如,Docker 19.03.3及以上版本在BuildKit缓存机制方面有各种改进和修复(请参见,例如,docker#373)。
注意,在您的特定情况下,即使这不是您问题的主要问题,了解以下内容是否有帮助会很有趣:
yarnpkg/yarn/issue 749建议:
您不应该挂载Yarn缓存目录。相反,您应该确保充分利用Docker的镜像层缓存。
这些是我使用的命令:
COPY package.json yarn.lock ./
RUN yarn --pure-lockfile
然后尝试执行yarn install
命令,查看是否docker仍然不使用它的缓存。
RUN yarn install --frozen-lockfile --production && yarn cache clean
不要忘记执行
yarn cache clean
以防止 yarn 缓存进入 docker 层。
如果问题仍然存在,请直接切换到 buildkit(进行测试),使用
buildctl build --progress=plain
查看更详细的输出,并调试缓存情况。
通常,
多阶段方法,如下所示,是有用的:
FROM node:alpine
WORKDIR /usr/src/app
COPY . /usr/src/app/
RUN set -ex; \
yarn install --frozen-lockfile --production; \
yarn cache clean; \
yarn run build
FROM nginx:alpine
WORKDIR /usr/share/nginx/html
COPY --from=0 /usr/src/app/build/ /usr/share/nginx/html
正如nairum在评论中所指出的:
我刚发现,当我使用多阶段Dockerfile和Docker Compose时,必须使用cache_from
才能使缓存起作用。
根据文档:
cache_from
defines a list of sources the Image builder SHOULD use for cache resolution.
Cache location syntax MUST follow the global format [NAME|type=TYPE[,KEY=VALUE]]
.
Simple NAME
is actually a shortcut notation for type=registry,ref=NAME
.
build:
context: .
cache_from:
- alpine:latest
- type=local,src=path/to/cache
- type=gha