引言:从单个容器到应用“全家桶”

在上一篇教程中,我们领略了用 docker run命令快速启动单个服务(如PostgreSQL)的便捷。但在真实的Web应用开发中,一个应用通常由多个相互关联的服务组成。

一个典型的例子就是:

  • 一个 Web服务器 (如 Nginx 或 Apache) 用于托管前端静态文件。
  • 一个 后端应用 (如 Node.js, Python, Java) 提供API。
  • 一个 数据库 (如 PostgreSQL 或 MySQL) 用于存储数据。

如果还用 docker run来一个一个地启动和管理它们,你需要处理:

  • 多个长长的 docker run命令。
  • 手动创建Docker网络,让容器之间可以相互通信。
  • 复杂的启动和关闭顺序。

这正是 Docker Compose 大显身手的舞台。它是一个用于定义和运行多容器Docker应用的工具。你只需使用一个 docker-compose.yml的YAML文件来配置你的应用服务栈,然后用一条命令,就能根据你的配置创建并启动所有服务。

案例:Nginx + PostgreSQL

为了简化,我们将构建一个由两个服务组成的经典组合:

  1. db: 一个PostgreSQL数据库服务。
  2. web: 一个Nginx Web服务器,它将显示一个简单的欢迎页面。

步骤1:创建项目文件夹结构

首先,创建一个项目文件夹,并在其中组织好我们的文件。

my-web-app/
├── docker-compose.yml   <-- Docker Compose的配置文件
└── nginx/
    └── index.html       <-- Nginx要显示的网页内容
  • nginx/index.html 的内容可以很简单:
    <!DOCTYPE html>
    <html>
    <head>
        <title>Welcome to My App!</title>
    </head>
    <body>
        <h1>Hello from Nginx, running in Docker Compose!</h1>
        <p>My database is running in another container.</p>
    </body>
    </html>
    

步骤2:编写 docker-compose.yml

这是我们的核心。在 my-web-app根目录下创建 docker-compose.yml文件,并写入以下内容:

version: '3.8' # 指定compose文件版本

services:  # 定义一组服务
  db: # 服务名称1:数据库
    image: postgres:13 # 使用的镜像
    container_name: my_postgres_compose # 容器名
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: myappdb
    volumes:
      - postgres_data:/var/lib/postgresql/data # 数据卷挂载
    networks:
      - app_network # 加入网络

  web: # 服务名称2:Web服务器
    image: nginx:latest # 使用的镜像
    container_name: my_nginx_compose # 容器名
    ports:
      - "8080:80" # 端口映射:宿主机8080 -> 容器80
    volumes:
      - ./nginx:/usr/share/nginx/html # 挂载本地nginx文件夹到容器中
    depends_on:
      - db # 声明依赖关系,先启动db
    networks:
      - app_network # 加入网络

volumes: # 定义数据卷
  postgres_data:

networks: # 定义网络
  app_network:
    driver: bridge

关键配置解读

  • services: 定义了我们应用的两个核心部分 dbweb
  • environment: 和 docker run -e一样,用于设置环境变量。这里我们为PostgreSQL设置了用户名、密码和默认数据库名。
  • volumes: 这是实现数据持久化的关键。
    • postgres_data:/var/lib/postgresql/data: 将一个名为 postgres_data的**具名卷(named volume)**挂载到容器内存储PostgreSQL数据的目录。这样做的好处是,即使我们删除了 db容器,这个数据卷依然存在,下次启动时数据不会丢失。
    • ./nginx:/usr/share/nginx/html: 将我们本地的 ./nginx文件夹,直接映射到Nginx容器存放网页的目录。这样,我们修改本地的 index.html,网页会立刻更新,无需重建容器。
  • networks: 我们创建了一个名为 app_network的自定义桥接网络。所有加入这个网络的服务,都可以通过服务名作为主机名直接相互访问。例如,web服务里的程序可以直接通过 db这个主机名连接到PostgreSQL,而无需关心它的IP地址。
  • depends_on: 声明了 web服务依赖于 db服务。这让Compose知道应该先启动数据库,再启动Web服务器。

步骤3:一键启动!

现在,在 my-web-app文件夹的根目录(即 docker-compose.yml所在的目录)下,打开终端,运行:

docker-compose up -d
  • up: 启动服务。
  • -d: 同样是在后台分离模式下运行。

Compose会读取 docker-compose.yml文件,自动创建网络、数据卷,并按顺序拉取镜像、启动 dbweb两个容器。

步骤4:验证

  • 访问网页:打开浏览器,访问 http://localhost:8080。你应该能看到我们编写的 index.html的内容。
  • 查看容器:运行 docker ps,你会看到 my_postgres_composemy_nginx_compose两个容器正在愉快地运行。

管理你的应用栈

  • 一键关闭并删除容器: 在 docker-compose.yml所在目录运行:

    docker-compose down
    

    这条命令会优雅地停止并删除由 up命令创建的所有容器和网络。但我们定义的 postgres_data数据卷默认不会被删除,以防数据丢失。

总结

Docker Compose是 docker run命令的自然演进,是管理现代微服务应用的必备工具。它通过一个声明式的YAML文件,将复杂的应用环境配置“代码化”,让应用的启动、销毁和共享变得前所未有的简单。

现在,你已经掌握了用Compose编排多容器应用的核心思想。在你的下一个项目中,尝试用 docker-compose.yml来定义你的开发环境吧!