> [!info] 章节导航
> [[docker学习路径|Docker 学习索引]] · [[05-Dockerfile 构建镜像|上一章]] · [[07-Docker Desktop 开发实践与故障排查|下一章]]

## 6. 使用 Docker Compose 管理多容器应用

### 6.1 为什么需要 Docker Compose

启动单容器应用程序很容易。例如，执行特定数据处理任务的 Python 脚本及其所有依赖项都可以在容器中运行。同样，一个服务于静态网站且 API 端点较小的 Node.js 应用程序可以有效地将其所有必要的库和依赖项容器化。然而，随着应用程序规模的扩大，将它们作为单独的容器进行管理变得越来越困难。

Imagine the data processing Python script needs to connect to a database. Suddenly, you're now managing not just the script but also a database server within the same container. If the script requires user logins, you'll need an authentication mechanism, further bloating the container size.
想象一下，数据处理 Python 脚本需要连接到数据库。突然之间，您不仅要管理脚本，还要在同一个容器内管理数据库服务器。如果脚本需要用户登录，您将需要一个身份验证机制，这会进一步增加容器的大小。

One best practice for containers is that each container should do one thing and do it well. While there are exceptions to this rule, avoid the tendency to have one container do multiple things.
容器的最佳实践之一是，每个容器应该只做一件事，并且做好它。虽然这条规则也有例外，但要避免一个容器做多件事。

Now you might ask, "Do I need to run these containers separately? If I run them separately, how shall I connect them all together?"
现在你可能会问：“我需要单独运行这些容器吗？如果我单独运行它们，我该如何将它们连接在一起？”

While `docker run` is a convenient tool for launching containers, it becomes difficult to manage a growing application stack with it. Here's why:
虽然 `docker run` 是一款便捷的容器启动工具，但用它来管理日益增长的应用程序堆栈却变得困难。原因如下：

- Imagine running several `docker run` commands (frontend, backend, and database) with different configurations for development, testing, and production environments. It's error-prone and time-consuming.
  想象一下，在开发、测试和生产环境中运行多个具有不同配置的 `docker run` 命令（前端、后端和数据库）。这很容易出错，而且很耗时。
- Applications often rely on each other. Manually starting containers in a specific order and managing network connections become difficult as the stack expands.
  应用程序通常相互依赖。随着堆栈的扩展，以特定顺序手动启动容器和管理网络连接变得越来越困难。
- Each application needs its `docker run` command, making it difficult to scale individual services. Scaling the entire application means potentially wasting resources on components that don't need a boost.
  每个应用程序都需要自己的 `docker run` 命令，这使得单个服务的扩展变得困难。扩展整个应用程序意味着可能会在不需要提升的组件上浪费资源。
- Persisting data for each application requires separate volume mounts or configurations within each `docker run` command. This creates a scattered data management approach.
  为每个应用程序持久化数据需要在每个 `docker run` 命令中分别挂载或配置卷。这会导致数据管理方法分散。
- Setting environment variables for each application through separate `docker run` commands is tedious and error-prone.
  通过单独的 `docker run` 命令为每个应用程序设置环境变量非常繁琐且容易出错。

That's where Docker Compose comes to the rescue.
这时 Docker Compose 就可以发挥作用了。

Docker Compose defines your entire multi-container application in a single YAML file called `compose.yml`. This file specifies configurations for all your containers, their dependencies, environment variables, and even volumes and networks. With Docker Compose:
Docker Compose 在一个名为 `compose.yml` 的 YAML 文件中定义整个多容器应用程序。此文件指定所有容器的配置、它们的依赖项、环境变量，甚至卷和网络。使用 Docker Compose：

- You don't need to run multiple `docker run` commands. All you need to do is define your entire multi-container application in a single YAML file. This centralizes configuration and simplifies management.
  您无需运行多个 `docker run` 命令。您只需在一个 YAML 文件中定义整个多容器应用程序即可。这可以集中配置并简化管理。
- You can run containers in a specific order and manage network connections easily.
  您可以按照特定顺序运行容器并轻松管理网络连接。
- You can simply scale individual services up or down within the multi-container setup. This allows for efficient allocation based on real-time needs.
  您可以在多容器设置中轻松扩展或缩减单个服务。这可以根据实时需求进行高效分配。
- You can implement persistent volumes with ease.
  您可以轻松实现持久卷。
- It's easy to set environment variables once in your Docker Compose file.
  在 Docker Compose 文件中设置环境变量很容易。

By leveraging Docker Compose for running multi-container setups, you can build complex applications with modularity, scalability, and consistency at their core.
通过利用 Docker Compose 运行多容器设置，您可以构建以模块化、可扩展性和一致性为核心的复杂应用程序。

### 6.2 Compose 基础与常用命令

但是，现在您想要做一些更复杂的事情——运行数据库、消息队列、缓存或各种其他服务。您是将所有东西都安装在一个容器中吗？还是运行多个容器？如果您运行多个容器，如何将它们连接在一起？

使用 Docker Compose，您可以在一个 YAML 文件中定义所有容器及其配置。如果您将此文件添加到代码仓库中，那么任何克隆您仓库的人都可以通过一条命令启动并运行它。

重要的是要理解 Compose 是一个声明式工具（declearative tool） - 您只需定义它即可运行。您不必总是从头开始重新创建所有内容。如果您进行了更改，只需再次运行 `docker compose up` ，Compose 就会协调文件中的更改并智能地应用它们。

Dockerfile 提供构建容器镜像的指令，而 Compose 文件则定义正在运行的容器。通常，Compose 文件会引用 Dockerfile 来构建用于特定服务的镜像。



使用 [`docker compose up`](https://docs.docker.com/reference/cli/docker/compose/up/) 命令启动应用程序：



```console
docker compose up -d --build
```

运行此命令时，您应该看到如下输出：

```console
[+] Running 5/5
✔ app 3 layers [⣿⣿⣿]      0B/0B            Pulled          7.1s
  ✔ e6f4e57cc59e Download complete                          0.9s
  ✔ df998480d81d Download complete                          1.0s
  ✔ 31e174fedd23 Download complete                          2.5s
  ✔ 43c47a581c29 Download complete                          2.0s
[+] Running 4/4
  ⠸ Network todo-list-app_default           Created         0.3s
  ⠸ Volume "todo-list-app_todo-mysql-data"  Created         0.3s
  ✔ Container todo-list-app-app-1           Started         0.3s
  ✔ Container todo-list-app-mysql-1         Started         0.3s
```


这里发生了很多事！以下几点需要提醒一下：

- 
  从 Docker Hub 下载了两个容器镜像 - node 和 MySQL
- 
  已为您的应用程序创建网络
- 
  创建了一个卷，用于在容器重启之间保存数据库文件
- 两个容器已启动，并具备所有必要的配置



由于此应用程序是使用 Docker Compose 启动的，因此完成后很容易将其全部拆除。

1. 在 CLI 中，使用 [`docker compose down`](https://docs.docker.com/reference/cli/docker/compose/down/) 命令删除所有内容：

   

   ```console
   docker compose down
   ```

   

   ```console
   [+] Running 3/3
   ✔ Container todo-list-app-mysql-1  Removed        2.9s
   ✔ Container todo-list-app-app-1    Removed        0.1s
   ✔ Network todo-list-app_default    Removed        0.1s
   ```



   > **Volume persistence 卷持久性**
   >
   > 默认情况下，拆除 Compose 堆栈时*不会*自动删除卷。这样做的目的是，当您重新启动堆栈时，您可能需要恢复数据。
   >
   > 如果您确实想删除卷，请在运行 `docker compose down` 命令时添加 `--volumes` 标志：
   >
   > 
   >
   > ```console
   > docker compose down --volumes
   > [+] Running 1/0
   > ✔ Volume todo-list-app_todo-mysql-data  Removed
   > ```

2. 或者，您可以使用 Docker Desktop GUI 通过选择应用程序堆栈并选择 **“删除”** 按钮来删除容器。

### [6.3 覆盖默认 CMD 和 ENTRYPOINT](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#override-the-default-cmd-and-entrypoint-in-docker-compose)

Sometimes, you might need to override the default commands (`CMD`) or entry points (`ENTRYPOINT`) defined in a Docker image, especially when using Docker Compose.
有时，您可能需要覆盖 Docker 镜像中定义的默认命令（ `CMD` ）或入口点（ `ENTRYPOINT` ），尤其是在使用 Docker Compose 时。

1. Create a `compose.yml` file with the following content:
   创建包含以下内容的 `compose.yml` 文件：

   

   ```yaml
   services:
     postgres:
       image: postgres
       entrypoint: ["docker-entrypoint.sh", "postgres"]
       command: ["-h", "localhost", "-p", "5432"]
       environment:
         POSTGRES_PASSWORD: secret 
   ```

   The Compose file defines a service named `postgres` that uses the official Postgres image, sets an entrypoint script, and starts the container with password authentication.
   Compose 文件定义了一个名为 `postgres` 的服务，该服务使用官方 Postgres 镜像，设置入口点脚本，并使用密码验证启动容器。

2. Bring up the service by running the following command:
   通过运行以下命令启动服务：

   

   ```console
    docker compose up -d
   ```

   This command starts the Postgres service defined in the Docker Compose file.
   此命令启动 Docker Compose 文件中定义的 Postgres 服务。

3. Verify the authentication with Docker Desktop Dashboard.
   使用 Docker Desktop Dashboard 验证身份验证。

   Open the Docker Desktop Dashboard, select the **Postgres** container and select **Exec** to enter into the container shell. You can type the following command to connect to the Postgres database:
   打开 Docker Desktop Dashboard，选择 **Postgres** 容器，然后选择 **Exec** 进入容器 shell。您可以键入以下命令来连接到 Postgres 数据库：

   

   ```console
    psql -U postgres
   ```



您还可以使用以下命令直接使用 `docker run` 命令覆盖默认值：



```console
 docker run -e POSTGRES_PASSWORD=secret postgres docker-entrypoint.sh -h localhost -p 5432
```

This command runs a Postgres container, sets an environment variable for password authentication, overrides the default startup commands and configures hostname and port mapping.
此命令运行 Postgres 容器，设置密码验证的环境变量，覆盖默认启动命令并配置主机名和端口映射
