单独使用 Docker 时,我们通常需要执行:
docker run
来启动一个容器。
但一个实际项目往往并不只有一个容器。
例如一个 PHP/Hyperf 项目可能需要:
Nginx
PHP / Hyperf
MySQL
Redis
RabbitMQ
如果全部手动使用 docker run 管理,不仅命令很长,而且容器之间的网络、Volume、环境变量、启动顺序等都需要自己维护。
Docker Compose 就是为解决这个问题而设计的。
Docker 官方将 Compose 定义为用于定义和运行多容器应用的工具,可以在一个 Compose 文件中定义服务、网络和 Volume,并通过命令统一管理整个应用栈。当前推荐使用 Compose Specification。
1. Docker Compose 是什么
Docker Compose 的核心思想是:
一个应用
│
├── PHP
├── Nginx
├── MySQL
└── Redis
│
▼
compose.yaml
│
▼
docker compose up -d
也就是说:
Docker 负责容器,Compose 负责组织容器。
例如:
services:
php:
image: php:8.3-cli
mysql:
image: mysql:8.4
redis:
image: redis:7
然后:
docker compose up -d
Compose 就可以根据配置创建并启动这些服务。
2. Compose 文件基本结构
当前推荐使用:
compose.yaml
也可以使用:
docker-compose.yaml
docker-compose.yml
一个最简单的 Compose 文件:
services:
nginx:
image: nginx:1.27
redis:
image: redis:7
核心结构:
services
│
├── nginx
│
└── redis
其中:
nginx:
和:
redis:
是两个 Service。
Compose Specification 的核心顶层元素包括 services、networks、volumes、configs、secrets 等。
3. Service:Compose 的核心
Compose 最重要的概念就是:
services:
一个 Service 描述一个应用组件。
例如:
services:
php:
image: php:8.3-cli
mysql:
image: mysql:8.4
redis:
image: redis:7
可以理解成:
php service
↓
PHP container
mysql service
↓
MySQL container
redis service
↓
Redis container
Service 通常至少需要定义:
image:
或者:
build:
使用 image
services:
redis:
image: redis:7
表示直接使用已有镜像。
使用 build
services:
php:
build:
context: .
dockerfile: Dockerfile
表示根据项目中的 Dockerfile 构建镜像。
因此 Dockerfile 与 Compose 的关系是:
Dockerfile
↓
构建 Image
Compose
↓
组织和运行 Container
Compose 的 build 配置就是用来定义如何为 Service 构建镜像的。
4. Service 常用配置
一个实际 Service 通常会涉及:
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
APP_ENV: production
volumes:
- ./runtime:/app/runtime
depends_on:
- mysql
- redis
networks:
- app
这些配置分别解决不同问题。
image
image: nginx:1.27
指定镜像。
build
build:
context: .
指定镜像构建方式。
ports
ports:
- "8080:80"
将:
宿主机 8080
↓
容器 80
进行映射。
environment
environment:
APP_ENV: production
设置容器运行环境。
volumes
volumes:
- mysql-data:/var/lib/mysql
挂载持久化数据。
depends_on
depends_on:
- mysql
定义服务依赖关系。
networks
networks:
- app
把服务加入指定网络。
5. Network:Compose 中的服务通信
Compose 会为应用创建网络,使同一个 Compose 项目中的服务可以互相通信。
例如:
services:
php:
image: php:8.3-cli
mysql:
image: mysql:8.4
PHP 连接 MySQL 时,可以使用:
mysql
作为主机名。
例如:
DB_HOST=mysql
DB_PORT=3306
而不是:
DB_HOST=localhost
因为:
localhost
代表当前容器。
而:
mysql
代表 Compose 中名为 mysql 的 Service。
可以理解为:
Docker Network
┌───────────────────────┐
│ │
│ PHP │
│ │ │
│ │ mysql:3306 │
│ ▼ │
│ MySQL │
│ │
│ Redis │
│ │
└───────────────────────┘
如果需要显式定义网络:
networks:
app:
然后:
services:
php:
networks:
- app
mysql:
networks:
- app
这样就可以明确控制服务属于哪个网络。
6. Volume:管理持久化数据
容器本身适合运行应用,但数据库数据不能依赖容器生命周期。
例如:
services:
mysql:
image: mysql:8.4
volumes:
- mysql-data:/var/lib/mysql
volumes:
mysql-data:
这里:
mysql-data
是一个命名 Volume。
关系:
mysql-data
│
▼
/var/lib/mysql
│
▼
MySQL 数据
Compose 的顶层 volumes 用于定义可持久化、可复用的数据存储。
Bind Mount
开发环境也经常使用:
volumes:
- ./:/app
表示:
宿主机项目目录
↓
容器 /app
这样修改代码后,容器可以直接看到代码变化。
因此:
Named Volume
更适合:
数据库
Redis 数据
持久化业务数据
而:
Bind Mount
非常适合:
本地开发
代码同步
配置文件
7. 环境变量与配置管理
Compose 支持直接定义:
environment:
APP_ENV: production
DB_HOST: mysql
DB_PORT: 3306
也可以:
environment:
APP_ENV: ${APP_ENV}
DB_PASSWORD: ${DB_PASSWORD}
然后通过环境变量提供:
APP_ENV=production
DB_PASSWORD=******
实际项目中,更推荐把不同环境的配置与 Compose 文件分离。
例如:
compose.yaml
.env
.env.example
.env.example:
APP_ENV=local
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=app
DB_USERNAME=root
DB_PASSWORD=
而真正的:
.env
不应该提交敏感生产配置到 Git。
Compose 还支持 configs 和 secrets 等机制,用于将配置或敏感数据显式授予指定 Service。
8. Service 依赖与启动顺序
例如 PHP 依赖 MySQL:
services:
php:
build:
context: .
depends_on:
- mysql
mysql:
image: mysql:8.4
表示 PHP Service 依赖 MySQL。
但需要注意:
depends_on 不等于“数据库已经可以正常接受连接”。
它主要描述 Compose 中的服务依赖关系。
例如:
MySQL Container 启动
↓
MySQL 开始初始化
↓
MySQL 还不能连接
↓
PHP 启动
↓
PHP 立即连接 MySQL
↓
连接失败
因此实际生产项目还需要考虑:
Healthcheck
应用重试
数据库初始化状态
服务启动策略
例如:
services:
mysql:
image: mysql:8.4
healthcheck:
test:
- CMD
- mysqladmin
- ping
- "-h"
- localhost
interval: 5s
timeout: 3s
retries: 10
php:
build: .
depends_on:
mysql:
condition: service_healthy
这样可以让应用依赖数据库的健康状态,而不仅仅是容器是否已经启动。
9. Compose 常用命令
Compose 最重要的命令是:
docker compose up
后台启动:
docker compose up -d
如果需要重新构建:
docker compose up -d --build
查看服务:
docker compose ps
查看日志:
docker compose logs
查看指定服务:
docker compose logs php
持续查看:
docker compose logs -f php
进入容器:
docker compose exec php sh
重启:
docker compose restart php
停止:
docker compose stop
删除 Compose 创建的容器和网络:
docker compose down
同时删除 Volume:
docker compose down -v
注意:
docker compose down -v
可能删除数据库数据,因此生产环境必须谨慎使用。
10. Compose 配置检查与多文件管理
实际项目中,Compose 文件很容易越来越复杂。
因此在启动之前,可以使用:
docker compose config
检查 Compose 最终解析出来的配置。
例如:
docker compose config -q
只进行配置校验。
docker compose config 会解析、合并 Compose 文件并展开变量,可以用于检查最终模型是否符合预期。
多 Compose 文件
Compose 支持:
docker compose \
-f compose.yaml \
-f compose.dev.yaml \
up -d
例如:
compose.yaml
compose.dev.yaml
compose.prod.yaml
基础配置:
services:
php:
image: my-app:latest
开发环境覆盖:
services:
php:
build: .
volumes:
- ./:/app
生产环境则可以使用:
services:
php:
image: registry.example.com/my-app:1.0.0
Compose 会按照指定文件的顺序合并配置,后面的文件可以覆盖前面的配置。
这样就可以形成:
基础配置
│
├── 开发环境
│
├── 测试环境
│
└── 生产环境
而不是维护三套完全重复的 Compose 文件。
11. PHP + Nginx + MySQL + Redis 实战
下面使用一个比较典型的 PHP 项目作为完整示例。
目录:
project/
├── compose.yaml
├── Dockerfile
├── .dockerignore
├── composer.json
├── composer.lock
├── app/
├── config/
└── public/
Dockerfile
FROM php:8.3-cli
WORKDIR /app
COPY composer.json composer.lock ./
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
RUN composer install \
--no-dev \
--prefer-dist \
--no-interaction \
--optimize-autoloader
COPY . .
EXPOSE 9501
CMD ["php", "bin/hyperf.php", "start"]
compose.yaml
services:
php:
build:
context: .
dockerfile: Dockerfile
container_name: app-php
environment:
APP_ENV: local
DB_HOST: mysql
DB_PORT: 3306
DB_DATABASE: app
DB_USERNAME: root
DB_PASSWORD: example
REDIS_HOST: redis
REDIS_PORT: 6379
ports:
- "9501:9501"
depends_on:
mysql:
condition: service_healthy
networks:
- app
mysql:
image: mysql:8.4
container_name: app-mysql
environment:
MYSQL_ROOT_PASSWORD: example
MYSQL_DATABASE: app
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test:
- CMD
- mysqladmin
- ping
- "-h"
- localhost
interval: 5s
timeout: 3s
retries: 10
networks:
- app
redis:
image: redis:7
container_name: app-redis
networks:
- app
networks:
app:
volumes:
mysql-data:
启动:
docker compose up -d --build
查看:
docker compose ps
查看 PHP:
docker compose logs -f php
进入 PHP:
docker compose exec php sh
此时 PHP 容器访问 MySQL:
mysql:3306
访问 Redis:
redis:6379
宿主机访问 Hyperf:
localhost:9501
整个架构变成:
Host
│
:9501
│
▼
┌─────────────┐
│ PHP │
│ Hyperf │
└──────┬──────┘
│
┌──────┴──────┐
│ │
▼ ▼
┌─────────┐ ┌─────────┐
│ MySQL │ │ Redis │
└─────────┘ └─────────┘
Docker Network
这就是 Compose 最典型的使用场景:
把一个由多个容器组成的应用,定义成一个整体。
12. Docker Compose 使用规范
实际项目中,建议遵循以下原则。
1. 一个 Service 尽量承担一个明确职责
例如:
php
nginx
mysql
redis
而不是把所有软件都安装到一个容器里。
2. 应用与数据库分离
应用容器:
PHP / Hyperf
数据库:
MySQL
通过 Docker Network 通信。
3. 数据库必须使用 Volume
不要依赖容器文件系统保存数据库数据。
4. 服务之间使用 Service Name 通信
例如:
mysql:3306
redis:6379
不要:
localhost:3306
localhost:6379
5. 配置与镜像分离
镜像负责:
应用程序
运行环境
依赖
Compose/环境变量负责:
环境配置
数据库地址
Redis 地址
端口
运行参数
6. 不要把生产密码直接提交到 Git
尤其避免:
MYSQL_ROOT_PASSWORD: 123456
作为真实生产配置提交。
7. 使用 docker compose config 检查最终配置
特别是存在:
.env
多个 Compose 文件
环境变量
时,非常有必要先检查最终配置。
8. 谨慎执行 down -v
开发环境可以:
docker compose down -v
但生产环境必须确认 Volume 是否包含重要数据。
13. 总结
Docker Compose 最核心的作用,就是把多个容器从“零散的 Docker 命令”变成“一份应用配置”。
它解决的是:
多个 Container
↓
Network
↓
Volume
↓
Environment
↓
Service Dependency
↓
统一生命周期管理
因此 Dockerfile 与 Compose 的关系可以这样理解:
Dockerfile
│
│ build
▼
Image
│
│
┌──────────┼──────────┐
▼ ▼ ▼
PHP Nginx Redis
│ │ │
└──────────┼──────────┘
│
Docker Compose
│
Network / Volume
│
▼
Application
Dockerfile 负责“怎么构建一个应用镜像”,Compose 负责“怎么运行和组织多个应用容器”。
掌握这两个文件之后,Docker 的实际开发模式基本就清晰了:
Dockerfile
↓
构建应用镜像
compose.yaml
↓
定义应用服务
docker compose up -d
↓
启动完整开发环境
这也是 PHP/Hyperf 项目进入 Docker 化开发后最常见的一套基础结构。
Docker Compose 官方文档:Docker Compose
Compose 文件官方参考:Compose file reference