使用COPY时Dockerfile位于子目录中的方法

46

我正在使用多个Dockerfile(每个服务一个)构建一个应用程序。我的应用程序目录结构如下:

app
├── dockerfiles
│   ├── webserver
│   │   └── Dockerfile
│   └── database
│       └── Dockerfile
├── public
    └── <frontend>
├── db
    └── <data>
  [...]
├── LICENSE
├── README.md
└── docker-compose.yml

在我的Web服务器的Dockerfile中,我想使用COPY命令将现有代码复制进来:
# Dockerfile
COPY ./public /var/www/html

我希望使用我的docker-compose.yml文件部署该应用程序:

# docker-compose.yml
version: "3"
services:
   webserver:
      build: ./dockerfiles/webserver
      image: webserver:php-apache

然而,当我从工作目录(app)运行docker-compose时,我会收到以下错误:

Building webserver
Step 1/2 : FROM php:7.1.11-apache-jessie
 ---> cb6a5015ad72
Step 2/2 : COPY ./public /var/www/html
Service 'webserver' failed to build: COPY failed: stat /var/lib/docker/tmp/docker-builder193736188/public: no such file or directory

如果我将Web服务器的Dockerfile移动到应用程序的根目录下,这个错误就会消失,因此我知道它是由路径或构建上下文问题引起的。

有了这个信息,我们可以通过以下两种方式之一来解决问题:

(1) 在整个应用程序中使用一个Dockerfile(在应用程序的根目录下),或者

app
└── Dockerfile

(2) 对于每个服务,使用多个Dockerfiles(在应用的根目录下)。

app
├── Dockerfile.webserver
└── Dockerfile.database

这些解决方案不好,因为使用一个 Dockerfile/容器来处理所有内容不是最佳实践(1),而且以这种方式组织多个 Dockerfile 只会显得杂乱无序(2)。


所以,我的问题是:

如何在不更改原始目录结构的情况下解决这个问题?

  • 需要对dockerfiles、docker-compose.yml或基本运行命令进行哪些更改?
  • 有没有更好的组织方式?
  • 那么WORKDIR命令呢?

理想情况下,最好的解决方案应该适用于开发(本地)和生产(远程)环境,因此让我们暂时避免使用卷...

4个回答

42

在这里你需要做的就是在你的docker-compose.yml文件的构建部分内添加context: .dockerfile,以便你的服务了解完整的目录结构。

# docker-compose.yml
version: "3"
services:
  webserver:
    build:
      context: .
      dockerfile: ./dockerfiles/webserver/Dockerfile
    image: webserver:php-apache

1
这个完美地解决了!我知道这一定是构建上下文的问题,但是我在寻找解决方案时遇到了很多麻烦...希望这能帮助到未来有类似情况的人! - CodyEakins

25

虽然不完全符合OP的情况,但如果你没有使用 docker-compose,你也可以分别指定上下文和 Dockerfile:

docker build . -f app/dockerfiles/webserver/Dockerfile

这个构建过程以当前目录下的.作为上下文,但使用了子目录下的Dockerfile


7

就像在其他地方已经澄清的那样,contextdockerfile的组合可以解决问题。我只想补充一点,即使你不需要将docker-compose.yml文件本身定位在应用程序的根目录中也可以工作。

例如,在我的一个项目中,为了保持整洁,我有多个组合配置来支持几个环境(开发,暂存,生产等),并且我决定将所有内容放在自己的子目录中。我的目录树如下:

.
+-- README.md
+-- scripts
|   +-- docker
|       +-- compose.yml
|       +-- compose-stage.yml
|       +-- compose-prod.yml
|       +-- dockerfile
|       +-- ...
|   +-- ...

其中 compose.yml 的内容应该如下所示:

# compose.yml
version: "3.9"
services:
  web:
    build:
      context: ../../
      dockerfile: ./scripts/docker/dockerfile
....

注意context,它指向应用程序的根目录——比compose.yml文件所在的目录上两级,以及相对于contextdockerfile
有了以上的目录树和compose.yml文件,我们只需从应用程序的根目录(即README.md所在的目录)执行以下命令即可:
> docker compose -f scripts/docker/compose.yml up

一切都像魔法一样正常工作!

我知道这听起来有点复杂,而且可能确实如此,但在部署应用程序到三个(或多个!)不同环境时,花费的时间将得到倍数回报。


这正是我想要做的。感谢分享! - Michael McGuire

1
COPY 指令的作用范围是 Dockerfile 所在的文件夹。我的建议是将您的 Dockerfile 更改为公共文件夹,并将指令更改为 COPY . /var/www/html。请注意,保留原文中的 HTML 标签。

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