跳到主要内容

使用 Docker Compose 让 Docker 更加强大

当我们开始使用 Docker,一般会使用 Docker 命令来运行和管理单个的容器。但是,当我们的应用变得越来越复杂,包含多个互相关联的容器时,使用 Docker 命令就变得不太方便了。这时,我们就需要使用 Docker Compose。

Docker Compose 是什么 #

Docker Compose 是用于定义和运行多容器Docker 应用程序的工具。 在Compose 中,可以使用YAML 文件来配置应用程序的服务。 然后,运行一条命令,即可从配置中创建并启动所有服务。 使用Compose 可在一台主计算机上方便地协调多个容器映像。Docker Compose 将多容器应用的管理简化了,让 Docker 变得更加强大。它在以下情况下特别有用:

  • 多容器应用:如果你的应用由多个服务组成,例如一个前端服务和一个后端服务,或者一个数据库服务和一个 web 服务,那么 Docker Compose 是管理这些服务的理想工具。你可以在一个 YAML 文件中定义所有的服务,并且只需要一条命令就可以启动所有的服务。
  • 服务关联:在复杂的应用中,不同的服务之间可能需要互相通信。例如,一个 web 服务可能需要连接到数据库服务。使用 Docker Compose,你可以很容易地在 YAML 文件中定义这些服务之间的关联。
  • 开发和测试环境:在开发和测试阶段,我们经常需要快速地启动和停止应用。Docker Compose 使得这个过程变得简单快速。你可以一次性启动你的应用的所有部分,进行开发或测试,然后一次性停止所有的服务。

总的来说,如果你的应用包含多个服务,这些服务需要互相通信,或者你需要在多个环境中运行你的应用,那么 Docker Compose 将会是你的最佳选择。

安装 Docker Compose #

在之前的 Docker 101 安装 Docker 的部分已经包含了 Docker Compose 的安装,可以快速运行下面的命令检查安装是否完成:

docker compose version

如果 terminal 能正常输出版本号则安装顺利完成,如若不是,还请根据上一篇文章重新安装一次。

理解 Compose 文件 #

Docker Compose 使用 YAML 文件来定义多容器应用的服务,这个文件通常被称为 Compose 文件。Compose 文件中定义的每一项服务都将运行在一个独立的容器中。

Compose 文件的基本结构如下:

version: "3"
services:
  service1:
    image: image1
  service2:
    build: .
    ports:
      - "5000:5000"
  service3:
    image: image3
    volumes:
      - /data

主要元素包括:

  • version:这是你使用的 Docker Compose 文件格式的版本。通常,我们应该使用最新的版本。
  • services:这里列出了你的应用中的所有服务。每个服务都运行在一个独立的容器中。
  • image:这是服务使用的 Docker 镜像。你可以使用任何在 Docker Hub 上可用的镜像,也可以使用你自己创建的镜像。
  • build:如果你的服务需要从 Dockerfile 构建镜像,你可以在这里指定 Dockerfile 所在的路径。
  • ports:这里你可以定义端口映射。格式是 :
  • volumes:你可以在这里定义数据卷。数据卷可以用于在容器之间共享数据,或者在容器和宿主机之间共享数据。

让我们通过一个简单的 Compose 文件来加深理解。假设我们有一个 web 应用,它包含一个 web 服务和一个数据库服务,对应的 Compose 文件可能如下:

version: "3"
services:
  web:
    image: my-web-app:latest
    ports:
      - "8000:8000"
    networks:
      - appnet
  db:
    image: postgres:latest
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - appnet

networks:
  appnet:

volumes:
  db-data:

以上的 Compose 文件定义了两个服务:web 和 db。web 服务使用的是我们自己创建的镜像,映射了 8000 端口。db 服务使用的是官方的 postgres 镜像,定义了一个名为 db-data 的数据卷。web 服务和 db 服务共享一个名为 appnet 的网络,因此它们可以直接通信。

运行你的第一个 Docker Compose 项目 #

为了简洁起见,我们将创建一个包含两个服务的应用:一个简单的 Node.js Web 服务和一个 Redis 服务。

首先,我们创建一个 Node.js Web 服务。在你的项目目录中创建一个名为 app.js 的文件,并添加以下内容:

const express = require('express');
const redis = require('redis');

const client = redis.createClient({host: 'redis'});
const app = express();

app.get('/', (req, res) => {
  client.incr('visits', (err, visits) => {
    res.send(`Number of visits: ${visits}`);
  });
});

app.listen(8080, () => {
  console.log('Listening on port 8080');
});

这个 Node.js 应用将会连接到 Redis 服务,并在每次接收到 HTTP GET 请求时,将 Redis 中的 ‘visits’ 键的值加一。 然后,我们需要创建一个 Dockerfile 来定义如何构建我们的 Node.js 应用的 Docker 镜像。在你的项目目录中创建一个名为 Dockerfile 的文件,并添加以下内容:

FROM node:14
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
CMD ["node", "app.js"]

接下来,我们需要创建一个 package.json 文件来定义我们的 Node.js 应用的依赖:

{
  "name": "docker-compose-example",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "dependencies": {
    "express": "^4.17.1",
    "redis": "^3.1.2"
  }
}

最后,我们创建 docker-compose.yml 文件来定义我们的服务:

version: '3'
services:
  web:
    build: .
    ports:
      - "8080:8080"
  redis:
    image: "redis:alpine"

现在,我们的项目结构应该如下:

.
├── Dockerfile
├── app.js
├── docker-compose.yml
└── package.json

现在,你可以运行 docker-compose up 来启动你的应用。你应该能在浏览器中通过访问 http://localhost:8080 来查看你的 Node.js 应用。每次刷新页面,你会看到访问次数增加,这就是我们的 Redis 服务在起作用。

Compose 命令和操作 #

Docker Compose 拥有一套丰富的命令集合,使我们可以轻松地管理和操作我们的应用。这里我们来介绍一些最常用的命令:

  • docker-compose up:这个命令是用来启动你的 Docker Compose 应用的。默认情况下,它会创建并启动定义在 docker-compose.yml 文件中的所有服务。如果你加上 -d 参数(即 docker-compose up -d),那么这个命令将会在后台运行你的应用,这在生产环境中特别有用。
  • docker-compose down:这个命令会停止并删除你的 Docker Compose 应用,包括应用的网络和卷。如果你加上 -v 参数(即 docker-compose down -v),那么它会同时删除你的应用的匿名卷。
  • docker-compose ps:这个命令会显示你的 Docker Compose 应用的状态。它会列出你的应用中所有的服务,以及它们当前的状态,如是否在运行,开放的端口等。
  • docker-compose logs:这个命令会显示你的 Docker Compose 应用的日志。默认情况下,它会显示所有服务的日志,但是你可以指定一个特定的服务来查看它的日志(如 docker-compose logs web)。
  • docker-compose build:这个命令会构建或者重新构建你的服务。如果你的服务使用了 build 参数来指定 Dockerfile 的位置,那么这个命令将会使用 Dockerfile 来构建你的服务的镜像。

以上只是 Docker Compose 命令的冰山一角。还有很多其他的命令,如 docker-compose restart(用于重启服务)、docker-compose pull(用于从 Docker Hub 下载镜像)等等。我建议你查阅 Docker Compose 的官方文档,以获取更详细的信息。

在 Compose 中使用环境变量 #

环境变量是在编写可移植和可伸缩的应用时的关键组成部分。Docker Compose 支持在 docker-compose.yml 文件中使用环境变量。你可以在配置文件中直接定义环境变量,也可以从环境变量文件中读取。

在我们的例子中,让我们假设我们的 Node.js 应用需要一个环境变量 NODE_ENV 来决定运行的环境,我们还假设 Redis 服务需要一个环境变量 REDIS_PASSWORD 作为密码。

直接在配置文件中定义环境变量 #

docker-compose.yml 文件中,我们可以在 environment 部分定义环境变量,例如:

version: '3'
services:
  web:
    build: .
    ports:
      - "8080:8080"
    environment:
      - NODE_ENV=production
  redis:
    image: "redis:alpine"
    environment:
      - REDIS_PASSWORD=secretpassword

在上面的例子中,我们为 web 服务设置了环境变量 NODE_ENV 的值为 production,同时为 redis 服务设置了环境变量 REDIS_PASSWORD 的值为 secretpassword。

从环境变量文件中读取 #

Docker Compose 也支持从 .env 文件中读取环境变量。默认情况下,Docker Compose 将会在 docker-compose.yml 文件所在的目录中查找 .env 文件。 让我们创建一个 .env 文件,并在其中定义我们的环境变量:

NODE_ENV=production
REDIS_PASSWORD=secretpassword

然后,我们在 docker-compose.yml 文件中引用这些环境变量:

version: '3'
services:
  web:
    build: .
    ports:
      - "8080:8080"
    environment:
      - NODE_ENV
  redis:
    image: "redis:alpine"
    environment:
      - REDIS_PASSWORD

在这个例子中,Docker Compose 将会从 .env 文件中读取 NODE_ENVREDIS_PASSWORD 的值,并将它们设置为对应服务的环境变量。

使用环境变量,可以轻松地配置和调整应用的行为,无论是在开发环境,还是在生产环境。在 Docker Compose 中使用环境变量,让应用更加灵活和可配置。

Docker Compose 的最佳实践 #

使用 Docker Compose 的过程中,有一些最佳实践可以帮助我们更高效、更安全地使用它。

  • 使用 .env 文件: .env 文件是一个很好的方式来管理你的环境变量。你可以将所有敏感的和特定于环境的配置项(例如,数据库密码,API 密钥等)存储在这个文件中,并在 docker-compose.yml 文件中引用它们。这样,你的 docker-compose.yml 文件可以不包含任何敏感信息,更便于共享和版本控制。
  • 使用版本控制系统: 将 docker-compose.yml 文件和相关的 Dockerfile 一起存储在版本控制系统(例如 Git)中。这样可以帮助你追踪历史变更,也可以让其他开发者更容易地参与到项目中来。
  • 利用 Compose 文件的可扩展性: Docker Compose 允许你使用多个 Compose 文件来定义你的应用。这使得你可以有一个基础的 Compose 文件定义你的主要服务,然后使用额外的 Compose 文件来定义开发环境、测试环境和生产环境的特定配置。
  • 定义健康检查: 你可以在 docker-compose.yml 文件中为你的服务定义健康检查。这可以帮助 Docker 确定你的服务是否正常运行。如果服务出现问题,Docker 可以自动重启它,或者停止依赖于它的其他服务。
  • 使用服务的依赖性: Docker Compose 允许你定义服务之间的依赖关系。例如,如果你的 web 服务依赖于数据库服务,你可以在 docker-compose.yml 文件中明确指定这种依赖关系。这样,Docker Compose 会确保在启动 web 服务之前先启动数据库服务。

总结 #

Docker Compose 是一个非常强大的工具,它使得定义和管理多容器 Docker 应用变得简单。虽然文章已经覆盖了许多内容,但 Docker Compose 的功能还远不止于此。在例子中,我只使用了 Compose 的一部分功能。在实际的项目中,你可能还需要处理日志,搭建持续集成/持续部署(CI/CD)环境,以及处理更复杂的网络和卷配置等问题。

希望这篇文章能够帮助你开始使用 Docker Compose,并在你的开发旅程中为你带来帮助。如果你有任何问题或者反馈,随时欢迎向我反馈。