1. 概述

Docker 是一个方便的容器化工具。它如此有用,以至于有时我们希望在项目中拥有不止一个 Dockerfile。不幸的是,这与命名所有 Dockerfile 只为“Dockerfile”的简单约定相冲突。

在这篇教程中,我们将探讨如何绕过这一限制,并保持项目结构的整洁。

2. Dockerfile

Dockerfile 是一个包含构建 Docker 映像所需的所有指令的文件。Docker 可以自动使用它来构建映像,而无需任何额外的命令或参数。由于命名约定,我们甚至不需要(直到版本 1.8.0,实际上我们无法)指定文件路径。

我们可以从 Dockerfile 所在目录调用 Docker 的 build 命令:

$ docker build .

3. 指定 Dockerfile 名称

从版本 1.8.0 开始,我们可以通过使用 -f 参数将 Dockerfile 的名称更改并传递给 build 命令。假设我们有两个 Dockerfile,一个用于构建后端,另一个用于构建前端。

我们可以适当地命名它们,并分别调用两次 build 命令,每次传递一个 Dockerfile 的名称:

$ docker build -f Dockerfile.frontend .
...
$ docker build -f Dockerfile.backend .

这种解决方案工作良好,但有一些不方便的缺点。第一个是需要单独为每个文件调用 build 命令。对于两个文件来说这并不是太糟糕,但如果我们的组件更多,它可能会迅速产生需要额外构建脚本的需求。第二个缺点是,一些集成开发环境可能因为偏离了约定而感到困惑,并停止提供语法高亮。

4. 使用 docker-compose

与其更改 Dockerfile 的名称,我们可以将它们放在不同的文件夹中。然后,我们可以使用 docker-compose 来触发所有文件的构建。假设我们有一个类似于以下的目录结构:

docker-compose.yml
docker
├── frontend
│   └── Dockerfile
└── backend
    └── Dockerfile

通常,docker-compose 文件最基本的用途意味着从仓库使用图像,我们也可以提供目录路径来用于 build

version: '3'
services:
  frontend:
    build: ./docker/frontend
    ports:
     - "8081:8081"
  backend:
    build: ./docker/backend
    ports:
      - "8080:8080"

现在运行 docker-compose 将会从 Dockerfile 构建图像:

$ docker-compose up

此外,在一个 docker-compose 文件中,我们可以放入使用不同方式创建图像的服务。有些可以从我们的源代码构建,其他则可以由外部注册表提供。例如,我们可能想要构建后端图像,但获取公共注册表中的数据库图像:

services:
  backend:
    build: ./docker/backend
    ports:
      - "8080:8080"
  postgres:
    image: 'postgres:latest'

现在,docker-compose 不仅会构建我们的服务并运行它们,还会为我们提供数据库。

5. 结论

在这篇教程中,我们学习了处理一个项目中多个 Dockerfile 的两种不同策略。

第一种基于更改不同 Dockerfile 的名称,提供了快速且优雅地解决问题的方法。第二种依赖于 docker-compose 并提供了构建过程的结构化和自动化选项。