如何防止 docker-compose 多次构建相同的镜像?

66

我的 docker-compose.yml 文件指定了多个镜像,其中两个镜像使用相同的本地 Dockerfile 构建。它们共享相同的镜像名称,但每个镜像有不同的命令。

在开发过程中,我经常使用 docker-compose up --build 命令来重新构建镜像。问题是 docker 会重复构建相同的 myimage 镜像 - 导致构建时间比必要的长。

有没有一种方法可以表达只需要构建一次此镜像呢?

version: '2'
services:

  abc:
    image: myimage
    command: abc
    build:
      context: .
      dockerfile: Dockerfile

  xyz:
    image: myimage
    command: xyz
    build:
      context: .
      dockerfile: Dockerfile

除非您在Dockerfiles的顶部放置CMD指令,否则构建缓存应该意味着第二次运行只需要几秒钟。第二次运行需要多少时间? - jwodder
1
无论哪种构建方式,都需要根据机器的性能大约花费10秒钟的时间。 - Nick
也许在Compose之外构建? docker build -t myimage . && docker-compose up - Matt
5个回答

84
根据docker-compose文件文档,对于构建,请为第一个服务指定build:image:,然后对于后续的服务,只需使用image:
以下是您示例的修改版本,仅在构建abc服务时构建图像,并重复使用该图像来构建xyz服务。
version: '2'
services:

  abc:
    image: myimage
    command: abc
    build:
      context: .
      dockerfile: Dockerfile

  xyz:
    image: myimage
    command: xyz

这(还是)最好的做法吗?它非常丑陋,如果您经常添加和删除此图像的实例,则绝对容易出错,必须始终确保其中1个实例包含构建说明。 - Hubro
4
再次阅读答案中链接的文档后,仍然认为这是最小化“docker-compose up --build”时间的最佳方法。 - amath
8
请在xyz服务中添加 depends_on: [abc] - DrSensor
3
使用这种方法,docker-compose pull现在会尝试从Docker Hub拉取“myimage”...但很明显它不在那里。除此之外,这种方法可以节省我构建时间一分钟或更多。 - matt
1
好的,但在这种情况下,如果我仅运行docker-compose up xyz而不先运行docker-compose build abc,则无法为xyz构建容器,这意味着如果我只运行docker-compose up xyz,那么镜像myimage将不会被构建,会出现错误。假设我不想为服务xyz添加depends_on: abc,因为我不关心其他服务是否已启动。我只需要它的镜像。 - Baco
1
为了解决上述描述的docker compose pull问题,可以在所有使用"myimage"的地方添加pull_policy: never属性。这样做可能会有点啰嗦,但可以解决这个问题。 - Alex Povar

15
你可以伪造一个构建服务来构建镜像,然后将依赖项用于实际的工作程序。这样做的好处是,如果你想启动其他服务,则不会自动启动其中一个服务。
你需要添加一个映像名称来指定build:image:用于构建服务,并确保它终止且永不自动重启。 然后在你的服务中使用image:depends_on: first_service
如下所示:
version: '2'
services:
  _myimage_build:
    image: myimage
    command: ['echo', 'build completed']  # any linux command which directly terminates.
    build:
      context: .
      dockerfile: Dockerfile

  first_service:
    image: myimage
    depends_on:
    - _myimage_build
    command: abc
    
  second_service:
    image: myimage
    depends_on:
    - _myimage_build
    command: xyz

感谢@amath的回答。

在尝试使用其他镜像之前,一定要先构建myimage镜像(_myimage_build): docker compose build _myimage_build


1
我按照您的步骤操作,但是出现了“无法拉取myimage,仓库不存在或可能需要'docker login':拒绝访问所请求的资源”的错误提示。 - Alexander Suraphel
在尝试使用其他内容之前,请确保首先构建“myimage”:docker compose build _myimage_build - luckydonald

5
为了让@amath的好答案更加清晰,你需要添加一个图像名称来指定第一个服务的build:image:,然后对于后续的服务,使用image:depends_on: first_service
如下所示:
version: '2'
services:

  first_service:
    image: myimage
    command: abc
    build:
      context: .
      dockerfile: Dockerfile

  second_service:
    image: myimage
    command: xyz
    depends_on:
    - first_service

感谢 @DrSensor 的评论。


4
我按照你的步骤操作了,但是出现了以下错误信息:"pull access denied for myimage, repository does not exist or may require 'docker login': denied: requested access to the resource is denied"。 - Alexander Suraphel
看起来你的问题与登录到你的私有Docker有关,请查看以下链接: https://docs.docker.com/engine/reference/commandline/login/ 或搜索<你的私有Docker名称> Docker登录。 - Oded BD

2
我遇到了类似的问题。
我有两个基于相同镜像的服务,只是配置不同。 @luckydonald 提供的解决方案对我有效,但在你只想启动项目的情况下不适用。
docker compose up -d 

在那里,你会遇到一个服务的错误,即图像不存在,而第一个则会构建。 'depends_on'标志只是“运行”依赖关系,而不是构建。

因此,为了避免两次构建图像并在第一次运行docker compose up时使其运行,您可以在服务中使用“extend”。

所以基本上我们复制并改进了luckydonald的答案:

services:
  _myimage_build:
    image: myimage
    command: ['echo', 'build completed']  # any linux command which directly terminates.
    build:
      context: .
      dockerfile: Dockerfile

  first_service:
    image: myimage
    #here's the magic
    extends:
      service: _myimage_build
    command: abc

  second_service:
    image: myimage
    #here's the magic
    extends:
      service: _myimage_build
    command: xyz

还有一种可能是将第一个构建任务丢弃,在第一个服务中完成,并且第二个服务将扩展第一个服务... 但是一旦你有了像'ports'这样的配置,扩展的服务将会!扩展!它。

所以举个例子:

first_service:
  ....
  ports:
  - 80:80

second_service: 
  extends:
    service: first_service
  ports: 
  - 81:80

这将以以下错误结束:

端口已被分配

因为第一个服务注册了80:80,但第二个服务会使用原始的“扩展”端口并添加自己的端口。所以是80:80和81:80。

对于多值选项ports、expose、external_links、dns、dns_search和tmpfs,Compose会连接两组值

在这里查看更多关于"extends"的内容


0
看起来行为已更改,现在docker compose每个Dockerfile只构建一次。
现在这样可以正常工作:
services:
  abc:
    build: .
  xyz:
    build: .

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接