Docker 进阶(一) Compose

Docker Compose

Docker Compose 是 Docker 官方提供的多容器应用编排工具

如果说 docker run 是手动驾驶一辆车(一次启动一个容器),那么 docker-compose 就是指挥整个车队(一次性启动、配置和管理多个相互关联的容器)。

它通过一个 YAML 格式的配置文件 (通常命名为 docker-compose.yml),以声明式的方式定义了整个应用架构中的所有服务、网络和数据卷

操作方式 命令示例 适用场景
Docker CLI docker run ... (执行多次) 单个容器,临时测试
Docker Compose docker compose up (执行一次) 微服务、完整应用栈、开发/生产环境

核心功能

Docker Compose 主要解决"多容器协作"的复杂性:

  1. 一键启停整个应用栈
    不再需要依次手动启动数据库、缓存、后端、前端。一条命令 docker compose up -d 全部搞定。
  2. 服务依赖管理
    可以定义"先启动数据库,等数据库就绪了,再启动后端服务"。
  3. 内部网络自动互通
    自动创建一个专属网络,容器之间可以通过服务名直接互相访问(DNS 解析),无需暴露端口给宿主机。
  4. 数据持久化统一配置
    统一管理数据卷(Volumes),确保数据库重启后数据不丢失。
  5. 环境隔离与复用
    开发、测试、生产环境可以使用不同的配置文件(如 docker-compose.dev.yml, docker-compose.prod.yml)进行叠加,保证环境一致性。

正面影响

  • 🚀 开发效率提升 80% :新员工入职,只需 git clone 然后运行 docker compose up,5 分钟内即可拥有完整的本地开发环境,彻底告别"在我机器上是好的"这种环境问题。
  • 🔗 微服务编排标准化 :对于由 Spring Boot + MySQL + Redis + Nginx 组成的微服务系统,Compose 文件成为了基础设施即代码 (IaC) 的一部分,清晰记录了服务间的依赖关系。
  • 🔄 CI/CD 集成简化:在 Jenkins/GitLab CI 中,可以直接使用 Compose 文件来搭建临时的测试环境,运行自动化测试后再一键销毁,干净利落。
  • 🛡️ 配置集中管理 :所有环境变量、端口映射、挂载路径都集中在一个 YAML 文件中,修改配置无需记忆长长的 docker run 参数。

局限性

单机限制 :Docker Compose 主要用于单节点 (单台服务器)编排。如果需要跨多台服务器(集群)部署,通常需要结合 Docker SwarmKubernetes (K8s)

工作原理

  1. 解析 YAML :Docker Compose 读取 docker-compose.yml 文件,解析其中定义的服务(Services)、网络(Networks)和数据卷(Volumes)。
  2. 调用 Docker API:它本质上是一个 Python/Go 编写的客户端,通过 Docker Daemon 的 API 发送指令。
  3. 资源创建顺序
    • 首先创建定义的 Networks(默认创建一个桥接网络)。
    • 其次创建定义的 Volumes(用于数据持久化)。
    • 最后按照依赖关系(depends_on)依次创建并启动 Containers
  4. 服务发现 :在创建的网络中,Docker 内置的 DNS 服务器会将服务名 解析为对应的容器 IP。例如,服务名为 db,其他容器 ping db 就能通。

实现方案架构

一个典型的 Compose 架构包含三个层级:

  • 定义层 (YAML):用户编写声明式配置。
  • 编排层 (Compose Engine):处理依赖逻辑、生命周期管理、日志聚合。
  • 执行层 (Docker Daemon):真正创建 Linux 命名空间、Cgroup、网络桥接和文件系统挂载。

常用命令速查表

命令 说明
docker compose up -d 后台启动所有服务
docker compose up -d --build 重新构建镜像并启动 (当 Dockerfile 变更时)
docker compose ps 查看当前运行的服务状态
docker compose logs -f [service_name] 实时查看指定服务的日志
docker compose exec [service_name] bash 进入指定容器内部 (如 exec wordpress bash)
docker compose stop 暂停服务 (不删除容器)
docker compose start 启动已暂停的服务
docker compose down 停止并删除容器、网络 (保留数据卷)
docker compose down -v 停止并删除容器、网络、数据卷 (数据清空)
docker compose top 查看容器内的进程资源占用

实例:构建一个 "博客系统"

假设我们要部署一个简单的博客系统,包含三个部分:

  1. WordPress (前端 + 后端)
  2. MySQL (数据库)
  3. phpMyAdmin (数据库可视化管理工具,可选)
第一步:创建目录结构
复制代码
mkdir my-blog
cd my-blog
第二步:编写 docker-compose.yml

新建文件 docker-compose.yml,内容如下

bash 复制代码
services:
  # 1. 数据库服务
  db:
    image: mysql:8.0               # 指定镜像及标签,如本地有dockerfile 可使用build
    #build: .                      # 使用当前目录下的 Dockerfile 
    container_name: blog-db        # 容器名称
    restart: always                # 重启策略
    environment:                   # 环境变量配置
      MYSQL_ROOT_PASSWORD: rootpassword123
      MYSQL_DATABASE: wordpress_db
      MYSQL_USER: wp_user
      MYSQL_PASSWORD: wp_password123
    volumes:                       # 数据卷挂载
      - db_data:/var/lib/mysql     # 数据持久化:即使删除容器,数据也在
    networks:                      # 网络
      - blog-net
    # 健康检查:确保数据库完全启动后再让 WordPress 连接
    healthcheck:                   # 健康检查
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5

  # 2. WordPress 服务
  wordpress:
    image: wordpress:latest
    container_name: blog-web
    restart: always
    ports:
      - "8080:80"  # 映射宿主机 8080 到容器 80
    environment:
      WORDPRESS_DB_HOST: db:3306       # 关键:直接使用服务名 'db' 作为主机名
      WORDPRESS_DB_USER: wp_user
      WORDPRESS_DB_PASSWORD: wp_password123
      WORDPRESS_DB_NAME: wordpress_db
    depends_on:                    # 依赖控制
      db:
        condition: service_healthy     # 等待 db 健康检查通过后才启动
    networks:
      - blog-net
    volumes:
      - wp_content:/var/www/html       # 挂载主题和插件目录

  # 3. phpMyAdmin (可选,方便管理数据库)
  phpmyadmin:
    image: phpmyadmin:latest
    container_name: blog-pma
    restart: always
    ports:
      - "8081:80"
    environment:
      PMA_HOST: db
      PMA_PORT: 3306
    depends_on:
      - db
    networks:
      - blog-net

# 定义网络
networks:
  blog-net:
    driver: bridge

# 定义数据卷 (持久化存储)
volumes:
  db_data:
  wp_content:
第三步:启动项目

docker compose up -d # 启动所有服务 (后台运行 -d)

docker compose ps # 查看运行状态

docker compose logs -f # 查看实时日志 (排查报错必备

第四步:验证与使用
  1. 访问博客 :打开浏览器访问 http://localhost:8080,开始 WordPress 安装流程。
  2. 管理数据库 :访问 http://localhost:8081,输入 root 密码即可管理数据。
  3. 停止项目: docker compose down

深度参数解析

1. YAML 文件的最外层定义了三大资源池:
  • services : 最核心部分 。定义应用包含的所有容器(服务)。每个子项(如 db, wordpress)就是一个服务的名称。
  • networks : 定义自定义网络。如果不写,Docker 会默认创建一个名为 default 的网络。
  • volumes: 定义命名数据卷。用于声明持久化存储的空间,防止数据随容器删除而丢失。
2. 服务级参数 services
image :镜像来源
container_name: 容器名称
restart: 重启策略

常用值:

no : 不自动重启(默认)

always : 无论任何原因退出,都自动重启如果容器被手动停止 (docker stop),重启守护进程也会尝试拉起它。生产环境推荐

on-failure : 只有当容器非正常退出(退出码不为 0)时才重启

unless-stopped : 类似 always,但如果容器被手动停止过,则不再自动重启。

environment: 环境变量

向容器内部传递环境变量,通常用于配置软件

格式:

复制代码
environment:
  KEY: VALUE       # 方式一:键值对
  - KEY=VALUE      # 方式二:列表格式 (等价)

可以使用 .env 文件引用,如 PASSWORD: ${DB_PASS},避免将敏感信息硬编码在 YAML 中。

volumes : 数据卷挂载
  • 格式 : - 源路径:目标路径
  • 示例 : - db_data:/var/lib/mysql
    • db_data: 这是在顶层 volumes 中定义的命名卷 (Named Volume)。Docker 会管理它的实际存储位置(通常在 /var/lib/docker/volumes/...)。
    • /var/lib/mysql: 容器内 MySQL 存放数据的目录。
  • 作用 : 即使你执行 docker compose down 删除了容器,db_data 里的数据依然存在。下次启动新容器挂载该卷,数据还在。
networks: 网络归属
复制代码
networks:
  - blog-net
  • 加入 blog-net 后,该服务可以与同在该网络下的其他服务通过服务名互相访问。
  • 如果不指定,所有服务默认加入一个名为 default 的网络。
  • 如果一个服务加入了多个网络,它可以充当不同网络间的网关。

跨 Compose 项目通信 :如果你有两个独立的 Compose 项目(例如 project-Aproject-B),想让 A 里的容器访问 B 里的数据库。你需要手动创建一个外部网络,然后在两个 YAML 中都引用这个外部网络

bash 复制代码
networks:
  shared-net:
    external: true  # 告诉 Compose:这个网络已经存在了,别创建,直接连
ports: 端口映射
  • 含义: 将容器的端口暴露给宿主机,让外部(浏览器、其他机器)能访问。
  • 格式 : - "宿主机端口:容器端口"
  • 示例 : - "8080:80"
    • 访问 http://localhost:8080 -> 转发到 -> 容器内部的 80 端口。
  • 注意 :
    • 如果只写容器端口(如 - "80"),宿主机端口会随机分配。
    • 如果不需要外部访问(如纯内部数据库),不要写 ports,这样更安全。
healthcheck : 健康检查
  • 含义: 定期运行一个命令来检测容器内的服务是否真正"可用"(不仅仅是进程在跑)。
  • 参数详解 :
    • test: 执行的命令。["CMD", ...] 是直接执行,["CMD-SHELL", ...] 是通过 shell 执行。
      • 例:mysqladmin ping 如果成功返回 0,失败返回非 0。
    • interval: 检查间隔 (如 10s)。
    • timeout: 命令超时时间 (如 5s)。
    • retries: 连续失败多少次算作 "unhealthy"。
    • start_period: (可选) 容器启动后给予多少秒的缓冲期,期间失败不计入重试次数。
  • 状态 : 容器状态会显示 healthy, starting, 或 unhealthy
depends_on: 依赖控制
  • 含义: 定义服务启动的先后顺序。

    depends_on:
    - db

只是控制启动顺序 :先启动 db 容器,再启动 wordpress 容器。但不保证 db 已经准备好接受连接(MySQL 启动可能需要几秒到几十秒)

高级用法 (配合 healthcheck):

复制代码
depends_on:
  db:
    condition: service_healthy

强烈推荐 。这意味着:启动 db -> 等待 db 通过健康检查 (变为 healthy) -> 才启动 wordpress。这能有效解决 "App 启动时报错连不上数据库" 的问题。

3. 网络级参数 networks
bash 复制代码
networks:
  blog-net:
    driver: bridge
  • blog-net : 网络名称,需与 services 中引用的名称一致。
  • driver : 网络驱动类型。
    • bridge: (默认) 桥接模式,单机容器通信的标准方式。
    • host: 移除网络隔离,容器直接使用宿主机网络 (Linux 特有,性能高但端口易冲突)。
    • none: 无网络。
    • overlay: 用于 Swarm 集群跨主机通信 (Compose 单机模式下较少用)。
4. 数据卷级参数 volumes
bash 复制代码
volumes:
  db_data:
  wp_content:
  • 含义: 声明命名的数据卷。
  • 作用 :
    • 这里只需要写出名字(如 db_data),不需要写具体路径。Docker 会自动在默认位置创建它。
    • 如果在 services 中使用了该名字,Docker 就会关联起来。
    • 优势 : 相比直接绑定宿主机路径(如 /home/user/data),命名卷由 Docker 管理,权限问题更少,迁移更方便。

Dockerfile 与 Compose 的关联

假如项目环境是这样的,通过Dockerfile 与Compose 的关联 运行容器时

使用 build 参数替代 image 参数。

dockerfile

bash 复制代码
# 基础镜像
FROM python:3.11-slim

# 设置工作目录
WORKDIR /app

# 复制依赖文件并安装 (利用缓存层加速构建)
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 复制源代码
COPY . .

# 暴露端口
EXPOSE 8000

# 启动命令
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
docker-compose.yml 中的关联写法
bash 复制代码
services:
  api-server:
    # ❌ 不使用 image: python:3.11
    # ✅ 使用 build 指向 Dockerfile 所在目录
    build: 
      context: ./backend       # 构建上下文路径 (Dockerfile 在这里)
      dockerfile: Dockerfile   # 指定文件名 (默认就是 Dockerfile,可省略)
      args:                    # (可选) 传递构建参数 (ARG)
        BUILD_VERSION: "v1.2.0"
    
    container_name: eco-api
    restart: always
    # 其他配置...
  • 当你运行 docker compose up --build 时,Compose 会先读取 ./backend/Dockerfile,执行构建步骤生成一个新的镜像(例如 my-ecommerce-api-server),然后再用这个新镜像启动容器。
完整实战代码

docker-compose.yml

bash 复制代码
version: '3.8' # v2 中可省略,但写上也没错

services:
  # 1. 数据库
  db:
    image: postgres:15-alpine
    container_name: eco-db
    restart: always
    environment:
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASS}
      POSTGRES_DB: ${DB_NAME}
    volumes:
      - pg_data:/var/lib/postgresql/data
      - ./data/db-init:/docker-entrypoint-initdb.d # 自动执行初始化脚本
    networks:
      - eco-net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
      interval: 5s
      timeout: 5s
      retries: 5

  # 2. 后端 (自定义构建 + 多卷挂载)
  api:
    build:
      context: ./backend
      dockerfile: Dockerfile
      args:
        ENVIRONMENT: production
    container_name: eco-api
    restart: always
    depends_on:
      db:
        condition: service_healthy
    environment:
      DATABASE_URL: postgresql://${DB_USER}:${DB_PASS}@db:5432/${DB_NAME}
      REDIS_URL: redis://cache:6379
    volumes:
      # 开发时:挂载代码实现热重载 (生产环境建议不打此包,直接构建进镜像)
      - ./backend:/app 
      # 配置文件
      - ./config/settings.yaml:/app/config.yaml:ro
      # 日志
      - ./data/logs:/app/logs
      # 用户文件持久化
      - app_uploads:/app/uploads
    networks:
      - eco-net
    ports:
      - "8000:8000"

  # 3. 缓存
  cache:
    image: redis:7-alpine
    container_name: eco-redis
    restart: always
    networks:
      - eco-net
    volumes:
      - redis_data:/data

  # 4. 反向代理 (Nginx)
  proxy:
    image: nginx:alpine
    container_name: eco-proxy
    ports:
      - "80:80"
    volumes:
      - ./frontend/nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./frontend/dist:/usr/share/nginx/html:ro # 挂载构建好的前端静态文件
    depends_on:
      - api
    networks:
      - eco-net

networks:
  eco-net:
    driver: bridge

volumes:
  pg_data:
  redis_data:
  app_uploads:

.env文件

bash 复制代码
DB_USER=admin
DB_PASS=secure_password_123
DB_NAME=ecommerce_db
总结
  1. 关于 Build

    • 开发环境:可以使用 volumes 挂载代码,配合 --build 频繁更新。
    • 生产环境:建议不要挂载代码目录,而是通过 CI/CD 流水线构建好包含最新代码的镜像,推送到仓库,Compose 只负责拉取镜像运行。这样更干净、安全。
  2. 关于 Network

    • 放心地在 YAML 里定义,不用提前建。
    • 如果需要跨项目,记得加 external: true
  3. 关于 Volumes

    • 数据 (DB, Redis, 用户上传) -> 必须用 Named Volumes (pg_data)。
    • 配置 -> 推荐 Bind Mount + :ro (./config:/conf:ro)。
    • 代码 -> 仅开发环境用 Bind Mount,生产环境打入镜像。
    • 日志 -> 推荐 Bind Mount 到宿主机的统一日志目录,方便 filebeat 等工具采集。
  4. 关于 Secrets

    • 千万不要把密码硬编码在 docker-compose.yml 里!
    • 一定要用 .env 文件,并将 .env 加入 .gitignore
    • 在 K8s 或 Swarm 高级场景中,使用 Docker Secrets 机制。
相关推荐
我爱小疯喵喵1 小时前
1 Docker 完全操作指南
docker·容器·eureka
pupudawang2 小时前
docker 安装 mysql
mysql·adb·docker
m_136872 小时前
OpenClaw v2026.3.12 离线源码构建与 Docker 部署完整教程
运维·docker·容器·openclaw
赴前尘2 小时前
docker buildx进行多架构镜像仓库迁移
docker·容器·架构
劲墨难解苍生苦3 小时前
docker 和k8s 环境下达梦数据库开启ssl连接配置流程
数据库·docker·kubernetes
Andy Dennis3 小时前
1panel阿里云部署
阿里云·docker·云计算·1panel
从入门到放弃-咖啡豆3 小时前
服务器部署docker 运行.NET 8 项目
服务器·docker·容器
SMF19193 小时前
【Docker】Linux系统上卸载旧Docker、卸载Podman并重新安装Docker及配置国内镜像源
linux·docker·podman
qq_297574673 小时前
K8s系列第二篇:CentOS7/Ubuntu 一键搭建 K8s 集群(kubeadm 完整版)
ubuntu·容器·kubernetes