Docker Compose 完全指南:轻松驾驭多容器应用


哈喽,大家好!今天我们来聊一个能极大提升开发效率的工具——Docker Compose。

如果你已经在使用 Docker,肯定感受过它带来的便利。但是,当你的应用变得复杂,需要同时运行后端、数据库、缓存、Web 服务器等多个容器时,你会发现,手动管理这些容器简直是一场噩梦。你需要敲一长串的 docker run 命令,还要手动处理容器间的网络连接、数据存储,想想都头大。

别担心,Docker Compose 就是来拯救我们的!它是一个用来定义和运行多容器 Docker 应用的工具。你只需要一个 YAML 配置文件,就能把整个应用的所有服务、网络、数据卷都安排得明明白白,然后用一条命令,就能让它们全部启动或停止。

听起来是不是很酷?这篇文章会带你从头到尾,通过一个实战案例,彻底搞懂 Docker Compose。

为什么你需要 Docker Compose?

在开始之前,我们先明确一下,费心学这个东西到底值不值。答案是:绝对值!

  • 一键启动,一键关闭:告别繁琐的 docker run 命令。用 docker compose up 就能启动所有服务,docker compose down 则能干净利落地关闭并移除所有相关资源。
  • 配置集中化,清晰明了:所有的服务配置都写在一个 docker-compose.yml 文件里,整个应用的架构一目了然,维护起来超级方便。
  • 环境一致性:无论是你的电脑,还是同事的电脑,或是测试服务器,只要有 Docker 和 Docker Compose,就能保证应用运行环境的高度一致,再也不会出现“在我电脑上明明是好的”这种尴尬情况了。
  • 服务间通信简单:Compose 会自动创建一个默认的网络,让你的所有服务都在这个网络里,它们之间可以用服务名直接通信,非常方便。

核心概念:docker-compose.yml 文件

docker-compose.yml 是 Compose 的灵魂。它是一个 YAML 格式的文件,你在这里描述你的应用由哪些部分组成。 它主要包含几个顶级配置项:

  • version:指定 Compose 文件的版本。虽然新版本已经不强制要求写这个了,但写上总是个好习惯。
  • services:定义应用中的各个服务(也就是容器)。这是最核心的部分。
  • volumes:定义数据卷,用于数据的持久化。
  • networks:定义网络,用于容器间的通信。

实战案例:搭建一个带 Redis 计数器的 Web 应用

理论说再多,不如上手练一练。我们来搭建一个简单的项目,它包含三个服务:

  1. **webapp**:一个用 Python Flask 编写的后端服务,它会连接 Redis 并显示一个页面访问计数器。
  2. **redis**:一个 Redis 缓存服务,用来存储计数值。
  3. **nginx**:一个 Nginx 反向代理,作为 Web 流量的入口。

第一步:项目结构

首先,我们先创建好项目的文件结构:

docker-compose-demo/
├── docker-compose.yml
├── webapp/
│   ├── Dockerfile
│   ├── app.py
│   └── requirements.txt
└── nginx/
    └── nginx.conf

第二步:编写应用代码

1. Python 应用 (webapp/)

requirements.txt

flask
redis

app.py
这是一个非常简单的 Flask 应用,它会连接到名为 redis 的服务,每次访问页面时,计数器加一。

import time
import redis
from flask import Flask

app = Flask(__name__)
# 注意这里的主机名是 'redis',也就是我们在 docker-compose.yml 中定义的服务名
cache = redis.Redis(host='redis', port=6379)

def get_hit_count():
    retries = 5
    while True:
        try:
            # cache.incr('hits') 会返回增加后的值
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)

@app.route('/')
def hello():
    count = get_hit_count()
    return f'Hello from Docker! I have been seen {count} times.\n'

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)

Dockerfile
这个 Dockerfile 负责将我们的 Flask 应用打包成一个镜像。

# 使用官方的 Python 镜像
FROM python:3.9-slim

# 设置工作目录
WORKDIR /app

# 复制依赖文件并安装
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 暴露端口并启动应用
CMD ["python", "app.py"]

2. Nginx 配置 (nginx/)

nginx.conf
这个配置文件让 Nginx 监听 80 端口,并将所有请求转发给我们刚才写的 webapp 服务的 5000 端口。

server {
    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://webapp:5000; # 同样,这里使用服务名 'webapp'
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

第三步:编写 docker-compose.yml

好了,所有准备工作都已就绪,现在开始编写最关键的 docker-compose.yml 文件。

version: '3.8'

services:
  # Python 后端服务
  webapp:
    build: ./webapp # 指定 Dockerfile 的位置
    container_name: webapp_service
    restart: always # 容器退出时总是重启
    environment:
      - FLASK_ENV=development
    expose:
      - "5000" # 只在内部网络暴露端口,不映射到主机
    networks:
      - app-network

  # Redis 服务
  redis:
    image: "redis:alpine" # 直接使用官方镜像
    container_name: redis_cache
    restart: always
    volumes:
      - redis-data:/data # 挂载一个命名卷,用于数据持久化
    networks:
      - app-network

  # Nginx 服务
  nginx:
    image: "nginx:stable-alpine"
    container_name: nginx_proxy
    restart: always
    ports:
      - "8080:80" # 将主机的 8080 端口映射到容器的 80 端口
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf # 挂载 Nginx 配置文件
    depends_on:
      - webapp # 确保 webapp 服务先于 Nginx 启动
    networks:
      - app-network

# 统一管理数据卷
volumes:
  redis-data:

# 统一管理网络
networks:
  app-network:

代码解析

让我们来逐段解析一下这个文件:

  • services:
    • webapp:
      • build: ./webapp: 指示 Compose 去 webapp 目录下寻找 Dockerfile 来构建镜像。
      • restart: always: 这是一个很好的生产实践,确保服务在意外崩溃后能自动重启。
      • expose: expose 用于在内部网络中暴露端口,这样 Nginx 就可以通过 http://webapp:5000 访问它,但我们不能从主机直接访问。
    • redis:
      • image: "redis:alpine": 直接从 Docker Hub 拉取官方的 Redis 镜像。
      • volumes: - redis-data:/data: 这是数据持久化的关键。我们将一个名为 redis-data命名卷挂载到 Redis 容器存放数据的 /data 目录。这样即使容器被删除,我们的访问计数也不会丢失。
    • nginx:
      • ports: - "8080:80": 将我们电脑的 8080 端口映射到 Nginx 容器的 80 端口。这样我们就可以通过访问 http://localhost:8080 来看到网页了。
      • volumes: - ./nginx/nginx.conf:/...: 将本地的 Nginx 配置文件挂载到容器内,覆盖默认配置。
      • depends_on: - webapp: 定义服务间的启动顺序。这确保了 webapp 服务会比 nginx 服务先启动。
  • volumes:
    • redis-data:: 在这里声明我们上面用到的命名卷。这是一种最佳实践。
  • networks:
    • app-network:: 定义一个自定义的桥接网络。让所有服务都连接到同一个网络可以提供更好的隔离和组织。

运行与管理你的应用

现在,激动人心的时刻到了!在项目根目录(也就是 docker-compose.yml 所在的目录)下,打开你的终端。

启动应用

docker compose up -d
  • up: 这个命令会构建镜像、创建并启动容器、网络和数据卷。
  • -d: detach 模式,让容器在后台运行。

命令执行后,你可以看到 Compose 会依次拉取镜像、构建 webapp 镜像,然后启动所有容器。

现在,打开浏览器访问 http://localhost:8080,你应该能看到 “Hello from Docker! I have been seen 1 times.”。刷新页面,数字会不断增加!

查看服务状态

想看看你的服务们现在怎么样了?

docker compose ps

这个命令会列出当前 Compose 项目中的所有容器及其状态。

查看日志

某个服务出问题了?查看日志是排查问题的第一步。

# 查看所有服务的日志
docker compose logs

# 只看特定服务的日志,并持续跟踪
docker compose logs -f webapp

停止并移除应用

当你不想玩了,可以用下面的命令来停止并移除所有相关的容器、网络。

docker compose down

如果你也想把 Redis 的数据卷一并删除(注意:数据会丢失!),可以加上 -v 参数:

docker compose down -v

其他常用命令

  • docker compose stop: 仅停止容器,但不移除。
  • docker compose start: 启动已停止的容器。
  • docker compose restart: 重启所有服务。
  • docker compose exec webapp /bin/sh: 进入 webapp 容器的命令行,方便进行调试。

总结

通过这个简单的例子,你应该已经感受到了 Docker Compose 的强大之处。它将复杂的多容器部署流程简化为一份配置文件和几个简单的命令,让你能更专注于代码本身,而不是繁琐的环境配置。

这只是一个入门教程,Docker Compose 还有很多高级功能,比如使用 .env 文件管理环境变量、多阶段构建、服务伸缩等,等待你去探索。

希望这篇文章能帮你敲开 Docker Compose 的大门,快去用它来改造你的项目吧!


  目录