Dockerfile能否扩展另一个Dockerfile?

127

我有一个 PHP 的 Dockerfile,内容如下:

FROM php:7-fpm
ENV DEBIAN_FRONTEND noninteractive

RUN apt-get update && \
    apt-get install -y git libicu-dev libmagickwand-dev libmcrypt-dev libcurl3-dev jpegoptim
RUN pecl install imagick && \
    docker-php-ext-enable imagick

RUN docker-php-ext-install intl
RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install opcache
RUN docker-php-ext-install mcrypt
RUN docker-php-ext-install curl
RUN docker-php-ext-install zip

我想创建另一个Dockerfile,基于第一个Dockerfile,并添加一些PHP扩展(供开发目的使用):Xdebug和其他东西。

我能否创建一个“dev” Dockerfile,扩展我的主要Dockerfile(而无需重写它)?


3
在文档中没有找到。 - Sylvain
4
你可以基于你从第一个镜像创建的镜像来制作新的Dockerfile。 - Jimmy
5
Dockerfile是否可以继承另一个文件,而不必构建中间镜像? - Frank Robert Anderson
4
在有关在Dockerfiles中添加INCLUDE指令的巨大讨论中,这位评论者建议使用make从各个部分组合一个Dockerfile。 - asciimo
实际上,使用Docker和Docker Compose,你可以创建"2"个镜像,但第二个镜像将与第一个镜像不同,因此你只需要创建2个目标和2个Dockerfile,其中开头是相同的,或者使用FROM语法。另外,对于你的问题,如果不需要运行命令来获取参数,你可以使用ARG命令并在那里传递你的附加包。 - undefined
5个回答

101

使用多阶段构建绝对是此处答案的一部分。

docker-compose v3.4 target 是第二个也是最后一个。

这里是一个例子,有2个容器(1个普通容器和1个安装了xdebug的容器)共存:

Dockerfile

FROM php:7-fpm AS php_base 
ENV DEBIAN_FRONTEND noninteractive

RUN apt-get update && \
    apt-get install -y git libicu-dev libmagickwand-dev libmcrypt-dev libcurl3-dev jpegoptim
RUN pecl install imagick && \
    docker-php-ext-enable imagick

RUN docker-php-ext-install intl
RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install opcache
RUN docker-php-ext-install mcrypt
RUN docker-php-ext-install curl
RUN docker-php-ext-install zip

FROM php_base AS php_test

RUN pecl install xdebug 
RUN docker-php-ext-enable xdebug

docker-compose.yml

-->

docker-compose.yml

version: '3.4'

services:
  php:
    build:
      context: ./
      target: php_base

  php_test:
    build:
      context: ./
      target: php_test
  
# ...

3
是的!这个似乎解决了我的问题。 - Josef Sábl
所以,每个 FROM 都与其他阶段分离? - ssi-anik
@ssi-anik 不确定是否回答了你的问题,但 FROM 初始化 一个新的构建阶段。更多信息请参见文档:https://docs.docker.com/engine/reference/builder/#from - Cethy
是的,这很有帮助。FROM 开始一个新阶段。谢谢。@Cethy - ssi-anik
1
如果两个阶段在不同的Dockerfile中怎么办?docker-compose如何知道先构建哪个阶段? - Simon Tran

62
如果您不想标记第一个Dockerfile以便在下一个Dockerfile中使用FROM指令,且您正在使用Docker 20.10+,那么您也可以这样做:
# syntax = edrevo/dockerfile-plus

INCLUDE+ Dockerfile.base

RUN whatever

INCLUDE+ 指令在 Dockerfile 的第一行被导入。你可以在https://github.com/edrevo/dockerfile-plus阅读更多有关 dockerfile-plus 的信息。


1
没错!这基本上只是用包含的文件替换INCLUDE+指令,因此Docker构建器(buildkit)将接收完全展开和标准化的Dockerfile,这意味着它可以很好地与构建层缓存配合使用。 - edrevo
1
这太棒了。这似乎是一种简单而明显的分层Dockerfiles的方法。感谢您! - Joel Mellon
我不知道为什么无法运行,我做错了什么吗?我已经将注释行添加为Dockerfile的第一行,然后添加INCLUDE+ ../Dockerfile(我在测试目录中),但是我得到了以下错误:failed to solve with frontend dockerfile.v0: failed to solve with frontend gateway.v0: rpc error: code = Unknown desc = failed to create LLB definition: dockerfile parse error line 5: unknown instruction: INCLUDE+ - Guus
1
@Guus 请按照 README 中的说明设置环境变量 DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1(否则,在 docker 20.10.12 上它将无法工作)。简而言之,在 Linux 上,以下命令适用于我:DOCKER_BUILDKIT=1 COMPOSE_DOCKER_CLI_BUILD=1 docker build -f Dockerfile.test -t demo . - koppor
1
很遗憾,看起来它与dockerignore不兼容,所以并不是一个很好的解决方案。 - Nathan Appere
显示剩余4条评论

15

这正是你的FROM php:7-fpm所做的:使用你的Dockerfile内容扩展了带有7-fpm标签的php镜像的Dockerfile。

因此,在从你的Dockerfile构建镜像之后:

docker build -t my-php-base-image .

你可以通过创建一个以以下内容开头的新的Dockerfile来扩展它:
FROM my-php-base-image

64
如果你有一个docker-compose设置,想要在Dockerfile级别上的基础上将一个容器建立在另一个容器之上,那该怎么办呢? 如果要在一台机器上运行多个版本的环境,则无法使用镜像。 - Niksac
73
我认为这个答案不太好,因为问题是如何导入一个 dockerfile 而不是已构建的镜像。我认为基本问题是“如何在多个相似的 Dockerfile 中保存重复的代码”? - WebQube
12
没问题。我想要一个调试版本的dockerfile,它扩展了主要的dockerfile并添加了gdb。这样,我就必须在主要的和扩展的dockerfile上始终执行docker build... - Martin Kosicky
3
例如,我有完全相同的Dockerfile用于两个不同版本的Ubuntu,唯一不同的是开头的FROM行——一个是基于ubuntu:trusty镜像,另一个是基于ubuntu:bionic镜像。我希望有一种方法可以将FROM行后面的所有内容作为Dockerfile包含进来,而不是在两个不同的文件中维护完全相同的Dockerfile代码。 - CivFan
4
答案中使用了“确切地”这个词,但实际上并不是完全“确切地”。 - Gherman
显示剩余7条评论

1
大家好,为了满足在Dockerfile中进行因式分解的需求,我开发了这个buildkit自定义语法前端插件:devthefuture/dockerfile-x
只需要在文件顶部加上一行代码# syntax = devthefuture/dockerfile-x,就完成了!
# syntax = devthefuture/dockerfile-x

FROM ./base/dockerfile

COPY --from=./build/dockerfile#build-stage /app /app

INCLUDE ./other/dockerfile

这是受到@edrevo作品的启发。

0

Sand是我编写的一个小型开源工具,用于解决这个需求(以及许多其他需求),它允许您使用类似Python语法的方式编写dockerfiles,从而使您能够在它们之间共享代码。

假设有以下目录结构:

my-monorepo/
│
├── tweet-service/
|   ├── src/
|   ├── ...
│   └── Sandfile
│
├── home-timeline/
|   ├── src/
|   ├── ...
│   └── Sandfile
│
└── Sandfile

你可以这样写你的Sandfile

# ./my-monorepo/Sandfile
from sand import *

def MyService(name):
    From("ubuntu", "20.04")
    Run("apt-get install python3")
    Copy(Src="src", Dst="/app")
    Entrypoint(f"python3 /app/{name}.py")

Sand("tweet-service")
Sand("home-timeline")

# ./my-monorepo/tweet-service/Sandfile
from sand import *

MyService("tweet-service") # Defined in ../Sandfile

# ./my-monorepo/home-timeline/Sandfile
from sand import *

MyService("home-timeline") # Defined in ../Sandfile

这使您能够在Dockerfile之间共享代码,并保持它们DRY

这类似于CMakeadd_subdirectory的工作方式。


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