Docker Compose 基础指南 – wiki基地


Docker Compose 基础指南:轻松驾驭多容器应用

在现代软件开发和部署中,容器化技术,尤其是 Docker,已经成为不可或缺的一部分。Docker 允许我们将应用程序及其依赖项打包到一个轻量级、可移植的容器中,确保在不同环境中拥有一致的运行表现。然而,当应用程序变得复杂,需要由多个相互依赖的服务(例如 Web 服务器、数据库、缓存服务、消息队列等)组成时,单独管理每个 Docker 容器的启动、连接和生命周期会变得非常繁琐和容易出错。

这时,Docker Compose 应运而生。它是一个用于定义和运行多容器 Docker 应用程序的工具。通过一个简单的 YAML 文件(通常是 docker-compose.yml),你可以配置应用程序所需的所有服务、网络和卷,然后使用一条命令即可启动、停止和管理整个应用集群。

本指南将深入探讨 Docker Compose 的基础知识,涵盖其核心概念、常用命令、docker-compose.yml 文件详解以及一个实战示例,帮助你从入门到熟练掌握这个强大的工具。

1. 为什么需要 Docker Compose?

在了解具体用法之前,我们先明确使用 Docker Compose 的核心优势:

  1. 简化多容器管理:想象一下,你需要手动运行多个 docker run 命令,并配置它们之间的网络连接、端口映射、卷挂载以及启动依赖关系。这不仅命令冗长,而且极易出错。Docker Compose 将所有这些配置集中在一个 docker-compose.yml 文件中,使用 docker-compose up 即可一键启动所有服务。
  2. 环境一致性docker-compose.yml 文件定义了应用程序的完整环境,包括服务版本、配置、网络和数据卷。这确保了开发、测试和生产环境之间的高度一致性,减少了“在我机器上能跑”的问题。
  3. 开发效率提升:开发人员可以在本地快速搭建与生产环境相似的完整应用栈,方便进行开发和调试。修改代码后,通常只需简单的命令即可重建和重启服务。
  4. 声明式配置:YAML 文件提供了一种清晰、易读的方式来描述应用架构。这种声明式的方法让你专注于“需要什么”,而不是“如何一步步实现”。
  5. 易于协作和版本控制docker-compose.yml 文件可以像代码一样纳入版本控制系统(如 Git),方便团队成员共享、协作和追踪环境配置的变更历史。
  6. 集成 Docker 特性:Compose 无缝集成了 Docker 的核心功能,如网络管理(自动创建专用网络)、卷管理(数据持久化)以及构建镜像(通过 build 指令)。

2. 安装 Docker Compose

Docker Compose 的安装通常非常简单:

  • Docker Desktop (Windows/macOS):如果你使用的是 Docker Desktop,它已经内置了 Docker Compose,无需额外安装。你可以通过在终端运行 docker-compose --versiondocker compose version (注意:新版推荐使用 docker compose,集成到 Docker CLI 中) 来验证。
  • Linux
    • 推荐方式 (集成 Docker CLI 插件):按照 Docker 官方文档安装 docker-compose-plugin。这通常涉及下载二进制文件并放置到 Docker CLI 插件目录(如 ~/.docker/cli-plugins/usr/local/lib/docker/cli-plugins)。之后可以使用 docker compose 命令。
    • 独立二进制文件 (旧版):也可以从 Docker Compose 的 GitHub Releases 页面下载独立的 docker-compose 二进制文件,赋予执行权限,并将其移动到系统路径下(如 /usr/local/bin/docker-compose)。这种方式使用 docker-compose 命令。

请参考 Docker 官方文档获取针对你操作系统的最新、最准确的安装指南。

3. Docker Compose 的核心:docker-compose.yml 文件

docker-compose.yml 文件是 Docker Compose 的灵魂。它使用 YAML (YAML Ain’t Markup Language) 格式定义应用程序的服务、网络和卷。YAML 是一种易于人类阅读的数据序列化标准,对缩进非常敏感(通常使用两个空格进行缩进)。

一个典型的 docker-compose.yml 文件结构如下:

“`yaml

可选:指定 Compose 文件格式版本,推荐使用较新版本以利用新特性

version: ‘3.8’ # 或者 ‘3.9’, ‘2.4’ 等

定义应用程序包含的服务(容器)

services:
# 服务名称,可自定义,如 ‘web’, ‘db’, ‘api’ 等
webapp:
# 指定服务使用的镜像
image: nginx:alpine # 可以是 Docker Hub 上的镜像,或私有仓库镜像

# 或者,指定 Dockerfile 的路径来构建镜像
# build: . # 构建当前目录下的 Dockerfile
# build:
#   context: ./app # Dockerfile 所在的上下文路径
#   dockerfile: Dockerfile-dev # 指定 Dockerfile 文件名

# 端口映射 <宿主机端口>:<容器端口>
ports:
  - "8080:80"

# 卷挂载,用于数据持久化或代码同步
volumes:
  # 匿名卷 (由 Docker 管理)
  # - /usr/share/nginx/html
  # 命名卷 (推荐,更易于管理)
  - webdata:/usr/share/nginx/html
  # 绑定挂载 (将宿主机路径映射到容器内)
  # - ./html:/usr/share/nginx/html:ro # ro 表示只读

# 环境变量
environment:
  - NGINX_HOST=myapp.local
  - NGINX_PORT=80
  # 也可以使用字典格式
  # NGINX_SETTINGS: "some_value"

# 网络配置,默认会创建一个默认网络
# networks:
#   - app_network

# 定义服务间的启动依赖关系
depends_on:
  - db # webapp 会在 db 服务启动后才启动

# 覆盖容器默认的启动命令
# command: ["nginx", "-g", "daemon off;"]

# 重启策略
restart: unless-stopped # (no | always | on-failure | unless-stopped)

db:
image: postgres:14-alpine
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
– dbdata:/var/lib/postgresql/data # 使用命名卷持久化数据库数据
# networks:
# – app_network
restart: always

(可选) 定义命名卷

volumes:
webdata: # 与 service.volumes 中对应的命名卷
dbdata:

(可选) 定义自定义网络

networks:

app_network:

driver: bridge # 或其他网络驱动

“`

下面详细解释 docker-compose.yml 中的关键指令:

  • version:指定 Compose 文件语法的版本。不同版本支持的指令和语法可能有所不同。推荐使用 3.x 系列的较新版本。官方文档会列出各版本特性。
  • services:这是文件的核心部分,用于定义构成应用程序的各个服务(容器)。每个服务都在 services 下有一个顶级键,这个键就是服务的名称(如 webapp, db)。

    • image: <image_name>:<tag>:指定服务使用的 Docker 镜像。可以是 Docker Hub 上的公共镜像,也可以是私有仓库的镜像。
    • build: <context_path>build: { context: <path>, dockerfile: <filename>, args: ... }:如果服务需要基于 Dockerfile 构建镜像,则使用 build 指令。
      • context: 指定 Dockerfile 所在的目录(构建上下文)。
      • dockerfile: 指定 Dockerfile 的文件名(如果不是默认的 Dockerfile)。
      • args: 定义构建时变量。
    • ports: ["<HOST_PORT>:<CONTAINER_PORT>"]:将宿主机的端口映射到容器的端口。例如 "8080:80" 表示访问宿主机的 8080 端口会转发到容器的 80 端口。
    • volumes: ["<SOURCE>:<TARGET>:<MODE>"]:定义卷挂载,用于数据持久化或共享文件。
      • 命名卷 (Named Volumes):如 mydata:/app/datamydata 是卷的名称,在文件末尾的 volumes: 部分定义。这是推荐的数据持久化方式,由 Docker 管理,更易于备份和迁移。
      • 绑定挂载 (Bind Mounts):如 ./src:/app/src。将宿主机的路径 (./src) 挂载到容器内的路径 (/app/src)。常用于开发环境,可以直接在宿主机修改代码并立即在容器中生效。
      • MODE (可选): ro (只读), rw (读写,默认)。
    • environment: ["<KEY>=<VALUE>"]environment: { <KEY>: <VALUE> }:设置容器内的环境变量。这对于传递配置信息(如数据库密码、API 密钥)非常有用。也可以使用 .env 文件来管理环境变量(后面会提到)。
    • networks: ["<network_name>"]:将服务连接到指定的网络。默认情况下,Compose 会为所有服务创建一个默认的桥接网络,服务可以通过服务名称相互访问(例如,webapp 可以通过 http://db:5432 访问 db 服务)。如果需要更复杂的网络拓扑,可以定义自定义网络。
    • depends_on: ["<service_name>"]:定义服务间的启动依赖关系。Compose 会按照依赖顺序启动服务。例如,webapp 设置了 depends_on: [db],则 db 服务会先于 webapp 启动。注意:这只保证 db 容器已启动,不保证 db 服务内部的应用程序已完全准备好接受连接。对于需要等待服务就绪的情况,可能需要额外的健康检查或等待脚本。
    • command: <command>command: ["executable", "param1", "param2"]:覆盖容器镜像中定义的默认启动命令 (CMD)。
    • entrypoint: <command>entrypoint: ["executable", "param1", "param2"]:覆盖容器镜像中定义的默认入口点 (ENTRYPOINT)。
    • restart: <policy>:定义容器退出时的重启策略。常用策略包括:
      • no: 不重启(默认)。
      • always: 总是重启,无论退出状态码是什么。
      • on-failure: 仅在退出状态码非 0 时重启。可以指定最大尝试次数 on-failure:5
      • unless-stopped: 总是重启,除非容器被手动停止 (e.g., docker stop, docker-compose stop)。
    • expose: ["<PORT>"]:仅暴露端口给同一网络内的其他服务,而不映射到宿主机。
    • healthcheck: { test: [...], interval: ..., timeout: ..., retries: ..., start_period: ... }:定义如何检查服务是否健康。depends_on 可以结合 condition: service_healthy 来等待依赖服务真正就绪。
  • volumes:在文件顶层定义命名卷。在这里定义的卷可以在 services 部分通过名称引用。
    yaml
    volumes:
    db_data: # 定义一个名为 db_data 的卷
    app_config:
    driver: local # 可以指定卷驱动及选项

  • networks:在文件顶层定义自定义网络。可以在 services 部分将服务连接到这些网络。
    yaml
    networks:
    frontend:
    driver: bridge
    backend:
    driver: bridge
    ipam: # 可以配置 IP 地址管理
    config:
    - subnet: 172.16.238.0/24

4. 常用 Docker Compose 命令

掌握了 docker-compose.yml 的编写,接下来需要学习如何使用 docker-compose (或 docker compose) 命令来管理应用。假设你的 docker-compose.yml 文件位于当前目录下。

  • docker-compose up: 这是最常用的命令。它会根据 docker-compose.yml 文件创建(或重建)并启动所有服务。

    • 默认情况下,它会在前台运行,并将所有服务的日志输出到终端。按 Ctrl+C 会停止并移除容器。
    • docker-compose up -d: 在后台(Detached mode)启动服务。这是生产或长时间运行服务的常用方式。
    • docker-compose up --build: 在启动服务前强制重新构建镜像(即使镜像已存在)。
    • docker-compose up <service_name>: 只启动指定的服务及其依赖项。
  • docker-compose down: 停止并移除由 up 命令创建的容器、网络。

    • docker-compose down -v: 同时移除与服务关联的命名卷(注意:这会删除持久化数据!)。
    • docker-compose down --rmi all: 移除容器的同时,移除所有在 docker-compose.yml 中定义的镜像。
    • docker-compose down --rmi local: 只移除没有自定义标签的镜像。
  • docker-compose ps: 列出当前 Compose 应用中正在运行的容器及其状态。

  • docker-compose logs: 查看服务的日志输出。

    • docker-compose logs -f: 持续跟踪(follow)日志输出。
    • docker-compose logs <service_name>: 只查看指定服务的日志。
    • docker-compose logs --tail="50": 只显示最后 50 行日志。
  • docker-compose build: 构建(或重新构建) docker-compose.yml 文件中定义的服务镜像。

    • docker-compose build <service_name>: 只构建指定服务的镜像。
    • docker-compose build --no-cache: 构建镜像时不使用缓存。
  • docker-compose pull: 拉取 docker-compose.yml 文件中定义的服务所需的镜像。

    • docker-compose pull <service_name>: 只拉取指定服务的镜像。
  • docker-compose stop: 停止正在运行的服务容器,但不移除它们。可以使用 docker-compose start 重新启动。

  • docker-compose start: 启动已存在的、被停止的服务容器。

  • docker-compose restart: 重启服务容器。

  • docker-compose exec <service_name> <command>: 在指定的、正在运行的服务容器内部执行一个命令。非常适合进行调试或执行一次性任务。

    • 例如:docker-compose exec webapp bash 会在 webapp 容器内启动一个交互式 Bash shell。
  • docker-compose run <service_name> <command>: 为指定的服务创建一个新的一次性容器并执行命令。它不会启动服务依赖项(除非指定 --service-ports--use-aliases)。常用于运行数据库迁移、测试脚本等。

    • 例如:docker-compose run --rm webapp python manage.py migrate (假设 webapp 是 Django 应用,--rm 表示命令执行后移除容器)。
  • docker-compose config: 验证并查看 Compose 文件的最终配置(合并了 .env 文件、覆盖文件等)。

    • docker-compose config --services: 只列出服务名称。
    • docker-compose config --volumes: 只列出卷名称。
  • docker-compose -f <file_name.yml> <command>: 使用指定的 Compose 文件(而不是默认的 docker-compose.yml)。可以指定多个 -f 来合并多个文件(例如,基础配置 + 开发环境覆盖配置)。

  • docker-compose --project-name <name> <command>docker-compose -p <name> <command>: 指定一个项目名称。Compose 会使用项目名称作为创建的容器、网络和卷的前缀,以避免不同项目间的命名冲突。默认项目名称是包含 docker-compose.yml 的目录名。

注意: 新版本的 Docker (通常 20.10+) 推荐使用 docker compose (无连字符) 命令,它作为 Docker CLI 的一部分提供。其用法与 docker-compose 基本兼容,并可能提供更好的集成和性能。如果你的系统同时存在两者,建议优先使用 docker compose。例如,docker compose up -d, docker compose down

5. 实战示例:一个简单的 Web 应用 (Python Flask + Redis)

让我们通过一个实例来巩固所学知识。我们将创建一个简单的 Web 应用:一个 Python Flask 应用,它会记录并显示页面的访问次数,并将计数器存储在 Redis 缓存中。

项目结构:

my_compose_app/
├── docker-compose.yml
├── app/
│ ├── app.py
│ └── requirements.txt
└── Dockerfile # Flask 应用的 Dockerfile

1. app/requirements.txt: (Flask 应用的依赖)

txt
flask
redis

2. app/app.py: (简单的 Flask 应用)

“`python
from flask import Flask
import redis
import os
import socket

app = Flask(name)

连接到 Redis 服务,’redis’ 是 docker-compose.yml 中定义的服务名

Docker Compose 会自动处理 DNS 解析,让 ‘redis’ 指向 Redis 容器的 IP

redis_host = os.environ.get(‘REDIS_HOST’, ‘redis’) # 从环境变量读取 Redis 主机名,默认为 ‘redis’
redis_port = int(os.environ.get(‘REDIS_PORT’, 6379)) # 从环境变量读取 Redis 端口,默认为 6379

try:
# 使用 decode_responses=True 让 Redis 返回字符串而不是字节
cache = redis.Redis(host=redis_host, port=redis_port, db=0, decode_responses=True)
cache.ping() # 测试连接
app.logger.info(f”Successfully connected to Redis at {redis_host}:{redis_port}”)
except redis.exceptions.ConnectionError as e:
app.logger.error(f”Could not connect to Redis: {e}”)
# 在无法连接 Redis 时提供一个降级方案或明确的错误处理
cache = None # 标记 Redis 不可用

@app.route(‘/’)
def hello():
hostname = socket.gethostname()
if cache:
try:
# 使用 incr 原子地增加 ‘hits’ 键的值
count = cache.incr(‘hits’)
return f’

Hello from Docker Compose!

I am running on container: {hostname}

This page has been viewed {count} times.


except redis.exceptions.ConnectionError as e:
app.logger.error(f”Redis connection error during request: {e}”)
return f’

Hello from Docker Compose!

I am running on container: {hostname}

Error connecting to Redis counter.

‘, 500
else:
return f’

Hello from Docker Compose!

I am running on container: {hostname}

Redis is not available.

‘, 500

if name == “main“:
# 监听所有网络接口,端口 5000
app.run(host=”0.0.0.0”, port=5000, debug=True)

“`

3. Dockerfile: (用于构建 Flask 应用镜像)

“`dockerfile

使用官方 Python 基础镜像

FROM python:3.9-slim

设置工作目录

WORKDIR /app

复制依赖文件

COPY app/requirements.txt requirements.txt

安装依赖

RUN pip install –no-cache-dir -r requirements.txt

复制应用代码到工作目录

COPY app/ .

暴露 Flask 应用运行的端口

EXPOSE 5000

容器启动时运行的命令

CMD [“python”, “app.py”]
“`

4. docker-compose.yml: (定义服务)

“`yaml
version: ‘3.8’

services:
# Web 应用服务
web:
# 使用当前目录下的 Dockerfile 进行构建
build: .
# 将宿主机的 8000 端口映射到容器的 5000 端口
ports:
– “8000:5000”
# 将当前目录下的 app 文件夹挂载到容器的 /app 目录
# 这使得在宿主机修改代码后,无需重建镜像即可生效(适用于开发)
volumes:
– ./app:/app
# 设置环境变量,传递给 Flask 应用
environment:
– FLASK_ENV=development # 设置 Flask 环境为开发模式
– REDIS_HOST=redis # 指定 Redis 服务的主机名
– REDIS_PORT=6379 # 指定 Redis 服务的端口
# 声明依赖于 redis 服务,确保 redis 先启动
depends_on:
– redis
# 连接到 backend 网络
networks:
– backend

# Redis 缓存服务
redis:
# 使用官方 Redis 镜像
image: “redis:alpine”
# 将 Redis 数据持久化到命名卷 redis_data
volumes:
– redis_data:/data
# 连接到 backend 网络
networks:
– backend
# 总是重启 Redis 服务,除非手动停止
restart: unless-stopped

定义命名卷

volumes:
redis_data: # 用于持久化 Redis 数据

定义自定义网络

networks:
backend: # 所有服务都连接到这个网络,可以通过服务名互相访问
driver: bridge
“`

运行应用:

  1. my_compose_app 目录下打开终端。
  2. 运行命令启动应用:
    bash
    docker compose up -d --build
    # 或者使用旧版命令: docker-compose up -d --build

    • --build 确保会根据 Dockerfile 构建 web 服务的镜像。
    • -d 让应用在后台运行。
  3. 等待命令执行完成。Compose 会:
    • 构建 web 镜像(如果需要)。
    • 拉取 redis 镜像(如果本地没有)。
    • 创建 backend 网络。
    • 创建 redis_data 命名卷。
    • 按照依赖顺序启动 redisweb 容器。
  4. 打开浏览器,访问 http://localhost:8000
  5. 每次刷新页面,你应该能看到访问次数增加,这表明 Flask 应用成功连接到 Redis 并更新了计数器。计数器数据因为使用了命名卷,即使停止并重新启动应用 (docker compose down 后再 docker compose up),计数也会保留。

管理应用:

  • 查看运行状态: docker compose ps
  • 查看日志: docker compose logs -f webdocker compose logs redis
  • 停止并移除容器、网络: docker compose down
  • 停止并移除容器、网络及数据卷(小心!): docker compose down -v

这个例子展示了 Docker Compose 如何协调多个服务(Web 应用和数据库/缓存),管理它们的构建、网络、数据持久化和依赖关系,极大地简化了多容器应用的开发和部署流程。

6. 进阶技巧与最佳实践

  • 使用 .env 文件管理环境变量: 为了避免将敏感信息(如密码、API Key)直接写入 docker-compose.yml 并提交到版本控制,可以在 docker-compose.yml 同级目录下创建一个 .env 文件,Compose 会自动加载它。

    • .env 文件内容 (示例):
      env
      POSTGRES_PASSWORD=supersecret
      API_KEY=xyz123abc
      REDIS_PORT=6380
    • docker-compose.yml 中引用:
      yaml
      services:
      db:
      image: postgres:14
      environment:
      # 会自动从 .env 文件读取 POSTGRES_PASSWORD
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_USER: user
      POSTGRES_DB: myapp
      api:
      image: myapi
      environment:
      API_KEY: ${API_KEY} # 从 .env 读取
      # 也可以提供默认值
      CACHE_PORT: ${REDIS_PORT:-6379} # 如果 .env 中没定义 REDIS_PORT,则使用 6379
    • .env 文件添加到 .gitignore 中,防止敏感信息泄露。
  • 多环境配置 (Override Files): 通常开发、测试、生产环境的配置会有所不同(例如端口映射、卷挂载方式、资源限制等)。可以使用多个 Compose 文件来实现:

    • docker-compose.yml: 存放基础、通用的配置。
    • docker-compose.override.yml: 存放开发环境的特定配置(Compose 默认会加载此文件并合并)。
    • docker-compose.prod.yml: 存放生产环境的特定配置。
    • 运行时通过 -f 参数指定:
      “`bash
      # 开发环境 (自动加载 docker-compose.yml 和 docker-compose.override.yml)
      docker compose up -d

      生产环境

      docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
      “`

  • 优化镜像构建:

    • 利用构建缓存:合理组织 Dockerfile 的指令顺序,将不经常变动的部分(如安装系统依赖)放在前面。
    • 使用 .dockerignore 文件排除不需要复制到镜像中的文件(如 .git, node_modules, 临时文件),减小构建上下文和镜像体积。
    • 考虑多阶段构建 (Multi-stage builds) 来创建更小的生产镜像。
  • 健康检查 (Healthchecks): 在 docker-compose.yml 中为关键服务(如数据库、API)定义 healthcheck,并结合 depends_oncondition: service_healthy,确保依赖的服务不仅已启动,而且真正准备好接受连接。

  • 资源限制: 在 deploy 配置块下(需要较新 Compose 版本)可以设置服务的资源限制(CPU、内存),防止某个服务耗尽系统资源。
    yaml
    services:
    web:
    image: myapp
    deploy:
    resources:
    limits:
    cpus: '0.50'
    memory: 512M
    reservations:
    cpus: '0.25'
    memory: 256M

    注意:deploy 配置通常在 Swarm 模式下完全生效,但在单机 Compose (v2.1+) 中部分资源限制也可能被支持。

  • 理解网络: 默认情况下,Compose 创建的桥接网络提供了服务间的 DNS 解析(通过服务名访问)。了解不同网络驱动(bridge, host, overlay 等)及其适用场景有助于构建更复杂的应用。

7. 总结

Docker Compose 是 Docker 生态系统中一个极其重要且实用的工具。它通过一个简洁的 docker-compose.yml 文件,将复杂的多容器应用配置化、标准化,极大地简化了应用的定义、启动、停止和管理流程。无论是本地开发测试,还是持续集成/持续部署 (CI/CD) 流程,乃至简单的生产环境部署,Docker Compose 都能显著提高效率和一致性。

掌握 Docker Compose 的基础知识——理解 docker-compose.yml 的核心指令、熟练运用常用命令、并通过实践来构建多服务应用——是每一位使用 Docker 的开发人员和运维工程师的必备技能。希望本指南能为你打下坚实的基础,助你更从容地驾驭容器化的世界。随着你经验的增长,可以进一步探索 Compose 的高级特性,如 Profiles、扩展 (extends)、与 Docker Swarm 或 Kubernetes 的集成等,不断提升应用部署和管理的水平。

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部