Docker Compose 完全指南 / 第 3 章 · 基础语法:services、image、ports、volumes
第 3 章 · 基础语法
3.1 Compose 文件的基本结构
一个最小的 Compose 文件只需要一个 services 顶级键:
# compose.yaml(推荐文件名)
# 也可以是 docker-compose.yml 或 docker-compose.yaml
services:
my-service:
image: nginx:alpine
顶级键一览
| 顶级键 | 必填 | 说明 |
|---|---|---|
services | ✅ | 定义应用的服务(容器)列表 |
networks | ❌ | 自定义网络配置 |
volumes | ❌ | 命名数据卷声明 |
configs | ❌ | 配置对象(Swarm/独立) |
secrets | ❌ | 敏感信息声明 |
文件命名优先级
Compose 按以下顺序查找配置文件(找到第一个即停止):
compose.yamlcompose.ymldocker-compose.yamldocker-compose.yml
💡 建议:新项目统一使用
compose.yaml,更简洁且是官方推荐。
多文件指定
# 指定自定义文件名
docker compose -f my-services.yaml up
# 多文件合并(后面的覆盖前面的)
docker compose -f compose.yaml -f compose.prod.yaml up
3.2 services 详解
services 是 Compose 的核心,每个键代表一个服务,对应一个或多个容器。
最小示例
services:
web:
image: nginx:alpine
ports:
- "8080:80"
服务名规则
| 规则 | 示例 |
|---|---|
只能包含 a-z、A-Z、0-9、_、.、- | ✅ my-app、web_server |
| 不能以数字开头 | ❌ 1web |
| 服务名即为容器的主机名 | 容器内通过 web 访问同网络的其他服务 |
3.3 image 指令
指定服务使用的 Docker 镜像。
格式
services:
app:
image: <repository>[:<tag>][@<digest>]
示例
services:
# 标准格式 - 仓库:标签
web:
image: nginx:1.27-alpine
# 使用摘要锁定版本(不可变)
db:
image: postgres:16-alpine@sha256:abc123def456...
# Docker Hub 官方镜像(省略仓库地址)
cache:
image: redis:7
# 第三方镜像(完整路径)
monitoring:
image: grafana/grafana:11.0.0
# 私有仓库镜像
private-app:
image: registry.example.com/myapp:latest
# 本地构建的镜像(与 build 指令配合)
backend:
build: ./backend
# 构建后镜像名自动为 <project>-backend
镜像标签最佳实践
| 做法 | 推荐 | 原因 |
|---|---|---|
image: nginx:latest | ❌ | 每次拉取可能不同,不可复现 |
image: nginx:1.27 | ⚠️ | 次版本更新可能破坏兼容 |
image: nginx:1.27.0-alpine | ✅ | 精确版本 + 轻量基础 |
image: nginx@sha256:... | ✅✅ | 最安全,完全不可变 |
3.4 ports 端口映射
将容器内部端口暴露到宿主机。
格式
services:
web:
ports:
- "HOST_PORT:CONTAINER_PORT" # 短语法
- target: 80 # 长语法
published: "8080"
protocol: tcp
host_ip: 127.0.0.1
短语法详解
services:
web:
ports:
# 宿主机 8080 → 容器 80
- "8080:80"
# 仅暴露端口,宿主机随机分配
- "80"
# 指定协议
- "53:53/udp"
- "53:53/tcp"
# 指定绑定地址(仅本地访问)
- "127.0.0.1:8080:80"
# 端口范围
- "9090-9091:8080-8081"
# 混合使用
- "3000"
- "8080:80"
- "127.0.0.1:5432:5432"
长语法详解
services:
web:
ports:
- target: 80 # 容器端口
published: "8080" # 宿主机端口(引号可选)
protocol: tcp # tcp 或 udp
host_ip: 127.0.0.1 # 绑定地址,默认 0.0.0.0
app_protocol: http # 应用协议(Compose V2.20+)
# 仅容器端口,不发布到宿主机
- target: 9090
published: false
端口映射对照表
| 写法 | 宿主机绑定 | 宿主机端口 | 容器端口 |
|---|---|---|---|
"8080:80" | 0.0.0.0 | 8080 | 80 |
"127.0.0.1:8080:80" | 127.0.0.1 | 8080 | 80 |
"80" | 0.0.0.0 | 随机 | 80 |
"80/tcp" | 0.0.0.0 | 80 | 80 (TCP) |
published: false | — | 不发布 | 80 |
⚠️ 安全提示:端口映射到
0.0.0.0意味着所有网络接口都可访问。生产环境建议绑定到127.0.0.1或使用防火墙。
3.5 volumes 数据卷
数据卷用于持久化数据和共享文件。
三种挂载方式
services:
db:
volumes:
# 1. Named Volume(命名卷)— 推荐用于持久数据
- pgdata:/var/lib/postgresql/data
# 2. Bind Mount(绑定挂载)— 用于开发时同步代码
- ./src:/app/src
# 3. tmpfs Mount(内存挂载)— 用于临时数据
- type: tmpfs
target: /tmp/cache
tmpfs:
size: 100000000 # 100MB
短语法详解
services:
app:
volumes:
# 命名卷
- mydata:/data
# 绑定挂载 — 只读
- ./config/app.conf:/etc/app/app.conf:ro
# 绑定挂载 — 读写(默认)
- ./logs:/var/log/app
# 命名卷 + 指定容器内路径
- uploads:/var/www/uploads
长语法详解
services:
app:
volumes:
- type: volume # 命名卷
source: mydata
target: /data
- type: bind # 绑定挂载
source: ./config
target: /etc/app
read_only: true
- type: tmpfs # 内存挂载
target: /tmp
tmpfs:
size: 104857600 # 100MB
顶级 volumes 声明
使用命名卷时,必须在顶级 volumes 中声明:
services:
db:
image: postgres:16
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata: # 声明命名卷
# 可选配置
name: my_project_pgdata # 自定义卷名(覆盖默认命名)
external: false # 是否为外部卷
卷命名规则
如果不指定 name 属性,命名卷的全名格式为 <项目名>_<卷名>:
# 项目目录名为 myapp
# 卷名 pgdata → 实际卷名 myapp_pgdata
# 查看卷
docker volume ls
# 查看卷详情
docker volume inspect myapp_pgdata
💡 建议:生产环境显式指定卷名(
name属性),避免因项目目录名变化导致卷名不一致。
3.6 完整示例:LAMP 环境
将以上概念组合,搭建一个经典的 LAMP(Linux + Apache + MySQL + PHP)环境:
# compose.yaml
services:
# Apache + PHP
web:
image: php:8.3-apache
ports:
- "8080:80"
volumes:
- ./www:/var/www/html:ro # 挂载网站代码
depends_on:
- db
# MySQL 数据库
db:
image: mysql:8.0
ports:
- "127.0.0.1:3306:3306" # 仅本地访问
volumes:
- mysql_data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: myapp
MYSQL_USER: appuser
MYSQL_PASSWORD: apppass
# phpMyAdmin
adminer:
image: adminer:latest
ports:
- "8081:8080"
depends_on:
- db
volumes:
mysql_data:
使用流程
# 创建项目结构
mkdir lamp-demo && cd lamp-demo
mkdir www
# 创建测试页面
cat > www/index.php << 'EOF'
<?php
echo "<h1>Docker Compose LAMP Demo</h1>";
echo "<p>PHP Version: " . phpversion() . "</p>";
try {
$pdo = new PDO('mysql:host=db;dbname=myapp', 'apppass', 'apppass');
echo "<p>✅ MySQL 连接成功</p>";
} catch (PDOException $e) {
echo "<p>❌ MySQL 连接失败: " . $e->getMessage() . "</p>";
}
?>
EOF
# 启动
docker compose up -d
# 查看状态
docker compose ps
# 查看日志
docker compose logs -f web
# 浏览器访问 http://localhost:8080
3.7 常用管理命令
生命周期命令
# 启动所有服务(前台,Ctrl+C 停止)
docker compose up
# 启动所有服务(后台)
docker compose up -d
# 停止并删除容器、网络
docker compose down
# 停止并删除容器、网络 + 数据卷
docker compose down -v
# 停止并删除容器、网络 + 本地镜像
docker compose down --rmi local
# 重启服务
docker compose restart
docker compose restart web # 重启指定服务
# 停止服务(不删除容器)
docker compose stop
# 启动已停止的服务
docker compose start
# 暂停/恢复服务
docker compose pause
docker compose unpause
查看状态
# 查看运行中的服务
docker compose ps
# 查看所有服务(含已停止的)
docker compose ps -a
# 查看资源使用情况
docker compose top
# 查看日志
docker compose logs
docker compose logs -f # 实时跟踪
docker compose logs -f web # 跟踪指定服务
docker compose logs --tail 50 # 最后 50 行
docker compose logs -f --since 5m # 最近 5 分钟
执行命令
# 在运行中的容器内执行命令
docker compose exec web bash
docker compose exec db mysql -uroot -prootpass
# 执行一次性命令(创建新容器)
docker compose run --rm web python manage.py migrate
docker compose run --rm web npm test
镜像管理
# 拉取所有服务的镜像
docker compose pull
# 构建所有服务的镜像
docker compose build
# 拉取 + 构建 + 启动
docker compose up -d --build
# 查看服务配置(解析后的 YAML)
docker compose config
# 查看镜像
docker compose images
3.8 服务的生命周期状态
docker compose up
│
┌───────────────▼───────────────┐
│ Creating │
│ (创建容器、网络、卷) │
└───────────────┬───────────────┘
│
┌───────────────▼───────────────┐
│ Starting │
│ (启动容器进程) │
└───────────────┬───────────────┘
│
┌───────────────▼───────────────┐
│ Running │
│ (正常运行中) │
└───────┬───────────────┬───────┘
│ │
docker compose 容器退出
stop/down │
│ ┌─────▼──────┐
┌───────▼──────┐ │ Exited │
│ Stopped │ │ (已退出) │
└───────┬──────┘ └─────┬──────┘
│ │
docker compose docker compose
down up
│ │
┌───────▼──────┐ │
│ Removed │ │
│ (已删除) │ (重新创建)
└──────────────┘
3.9 实战:快速搭建 WordPress
# compose.yaml — 一键部署 WordPress
services:
wordpress:
image: wordpress:6-php8.3-apache
ports:
- "8080:80"
volumes:
- wp_data:/var/www/html
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress_pass
WORDPRESS_DB_NAME: wordpress
restart: unless-stopped
db:
image: mysql:8.0
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress_pass
MYSQL_ROOT_PASSWORD: root_secret
restart: unless-stopped
volumes:
wp_data:
db_data:
docker compose up -d
# 访问 http://localhost:8080 完成 WordPress 安装
3.10 小结
| 概念 | 要点 |
|---|---|
services | Compose 核心,每个服务对应一组容器 |
image | 指定镜像,建议使用精确版本标签 |
ports | 宿主机:容器 端口映射,生产环境注意绑定地址 |
volumes | 三种挂载方式,命名卷用于持久化,绑定挂载用于开发 |
| 管理命令 | up -d / down / logs -f / exec / ps |
扩展阅读
上一章:第 2 章 · 安装 ← | 下一章:第 4 章 · 网络 →