09.Docker compose
Docker Compose 完全指南:从基础到实践
文章目录
一、Docker Compose 基础认知
1.1 核心概念
- Compose 文件:使用 YAML 格式编写的配置文件(默认名称为
docker-compose.yml),定义了应用所需的所有服务、网络、数据卷等资源。 - 服务(Service):构成应用的单个容器实例(如 Web 服务、数据库服务、缓存服务),在配置文件中通过
services节点定义,可指定镜像、端口映射、环境变量等参数。 - 项目(Project):由 Compose 管理的一组关联服务的集合,一个项目对应一个应用,通过
docker-compose命令对项目整体进行启动、停止、重启等操作。 - 网络(Network):Compose 会自动为项目创建默认网络,所有服务容器默认加入该网络,实现容器间的相互通信(可通过服务名直接访问)。
- 数据卷(Volume):用于持久化容器数据,避免容器删除后数据丢失,支持容器间数据共享,在配置文件中通过
volumes节点定义。
1.2 适用场景
- 开发环境:快速搭建包含多服务的本地开发环境(如 Web 服务 + MySQL + Redis),保证团队成员开发环境一致性。
- 测试环境:一键部署完整的应用栈,用于自动化测试或手动测试,降低环境配置成本。
- 小型生产环境:对于服务规模较小、架构简单的应用,可直接使用 Compose 进行部署(大型生产环境更推荐 Kubernetes)。
1.3 安装前提
-
已安装 Docker Engine(Compose 依赖 Docker 运行,需确保 Docker 正常工作)。
-
安装 Docker Compose:
-
Linux 系统:通过命令直接下载(以 v2.24.5 版本为例):
sudo curl -L "https://github.com/docker/compose/releases/download/v2.24.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose
-
-
Windows/macOS 系统:安装 Docker Desktop 后会自动附带 Docker Compose,无需额外安装。
1.4 验证安装
执行以下命令,若输出 Compose 版本信息,则说明安装成功:
docker-compose --version
# 示例输出:docker-compose version v2.24.5, build 1234567
二、Docker Compose 核心命令
Docker Compose 命令通过 docker-compose [选项] [命令] [参数] 格式执行,以下是最常用的命令:
| 命令 | 作用 | 常用选项 | 示例 |
|---|---|---|---|
up | 构建并启动所有服务容器(默认前台运行) | -d:后台运行;--build:启动前重新构建镜像;--force-recreate:强制重新创建容器 | docker-compose up -d(后台启动所有服务) |
down | 停止并删除所有服务容器、网络(默认不删除数据卷) | -v:同时删除数据卷;--rmi all:删除项目相关的所有镜像 | docker-compose down -v(停止并删除容器、网络、数据卷) |
ps | 查看当前项目的所有服务容器状态 | -a:显示所有容器(包括已停止的) | docker-compose ps -a |
start | 启动已停止的服务容器 | 无常用选项 | docker-compose start web(仅启动 web 服务) |
stop | 停止运行中的服务容器(不删除容器) | 无常用选项 | docker-compose stop db(仅停止 db 服务) |
restart | 重启服务容器 | 无常用选项 | docker-compose restart(重启所有服务) |
logs | 查看服务容器的日志输出 | -f:实时跟踪日志;--tail 100:显示最后 100 行日志 | docker-compose logs -f web(实时查看 web 服务日志) |
exec | 进入运行中的服务容器执行命令 | -it:交互式终端(必备) | docker-compose exec -it db bash(进入 db 容器的 bash 终端) |
build | 构建或重新构建服务的自定义镜像 | --no-cache:构建时不使用缓存 | docker-compose build web(仅构建 web 服务的镜像) |
三、docker-compose.yml 配置语法详解
docker-compose.yml 是 Compose 的核心配置文件,遵循 YAML 语法(缩进敏感,建议使用 2 个空格缩进),以下是完整的配置结构和关键节点说明:
3.1 配置结构总览
# 1. 版本声明(必须在第一行,指定 Compose 文件格式版本,需与 Docker Compose 版本兼容)
version: "3.8" # 推荐使用 3.x 系列(3.0+ 支持大部分新特性)
# 2. 服务定义(核心节点,所有容器服务在此配置)
services:
服务1名称: # 自定义服务名(如 web、db、redis)
# 2.1 镜像相关配置
image: nginx:1.23 # 直接使用官方镜像(格式:镜像名:标签)
# build: ./web # 若需自定义镜像,指定 Dockerfile 所在目录(与 image 二选一,优先用 build)
# build:
# context: ./web # Dockerfile 所在上下文目录
# dockerfile: Dockerfile.dev # 自定义 Dockerfile 文件名(默认 Dockerfile)
# args: # 传递给 Dockerfile 的构建参数(仅在 build 时生效)
# - ENV=development
# 2.2 容器名称配置
container_name: my-web # 自定义容器名(默认格式:项目名_服务名_序号)
# 2.3 端口映射(宿主机端口:容器内端口)
ports:
- "8080:80" # 宿主机 8080 端口映射到容器 80 端口(TCP 协议默认)
- "443:443/udp" # 指定 UDP 协议
# 2.4 环境变量配置(两种方式:直接指定或从文件读取)
environment:
- MYSQL_ROOT_PASSWORD=123456 # 直接设置环境变量
- REDIS_PORT=6379
env_file: # 从文件读取环境变量(文件格式:KEY=VALUE,每行一个)
- ./.env # 项目根目录的 .env 文件
- ./db.env # 数据库相关的环境变量文件
# 2.5 数据卷配置(持久化数据或共享文件)
volumes:
- ./html:/usr/share/nginx/html # 宿主机目录 ./html 挂载到容器目录
- nginx-log:/var/log/nginx # 命名数据卷 nginx-log 挂载到容器目录
- ./conf/nginx.conf:/etc/nginx/nginx.conf:ro # ro:只读权限(默认 rw 可读写)
# 2.6 网络配置(指定容器加入的网络)
networks:
- my-network # 加入自定义网络 my-network(需在 networks 节点定义)
# 2.7 依赖关系(控制服务启动顺序,仅保证启动顺序,不保证服务就绪)
depends_on:
- db # 启动当前服务前,先启动 db 服务
- redis # 启动当前服务前,先启动 redis 服务
# 2.8 重启策略(容器退出后的重启规则)
restart: always # 总是重启(无论退出码是什么)
# restart: on-failure # 仅在容器退出码非 0 时重启
# restart: unless-stopped # 除非手动停止,否则总是重启
# 2.9 健康检查(检测服务是否正常运行)
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80/"] # 检测命令(访问 80 端口)
interval: 30s # 每 30 秒检查一次
timeout: 10s # 检查超时时间(超过 10 秒视为失败)
retries: 3 # 连续 3 次失败视为服务不健康
start_period: 60s # 服务启动后,等待 60 秒再开始健康检查
服务2名称: # 如 db(数据库服务)
image: mysql:8.0
container_name: my-db
ports:
- "3306:3306"
environment:
- MYSQL_ROOT_PASSWORD=123456
- MYSQL_DATABASE=mydb # 初始化时创建的数据库名
volumes:
- mysql-data:/var/lib/mysql # 命名数据卷(持久化 MySQL 数据)
- ./init.sql:/docker-entrypoint-initdb.d/init.sql # 初始化 SQL 脚本(MySQL 镜像特有)
networks:
- my-network
restart: always
# 3. 数据卷定义(所有服务可共享的命名数据卷)
volumes:
nginx-log: # 命名数据卷 1(无需指定路径,Docker 自动管理)
mysql-data: # 命名数据卷 2(持久化 MySQL 数据,容器删除后数据不丢失)
# 4. 网络定义(自定义网络,实现服务间隔离或通信)
networks:
my-network: # 自定义桥接网络(默认驱动为 bridge)
driver: bridge # 网络驱动类型(bridge、overlay 等,默认 bridge)
3.2 关键配置说明
(1)version 版本兼容性
version 字段指定 Compose 文件格式版本,需与 Docker Compose 版本匹配,避免出现语法不支持的问题:
- Compose V2.0+ 支持
3.0至3.9版本的配置文件; - 若使用较新特性(如
healthcheck的start_period),建议使用3.4+版本。
(2)volumes 数据卷类型
配置文件中 volumes 有两种使用方式:
- 宿主机目录挂载:格式
宿主机路径:容器内路径[:权限],如./html:/usr/share/nginx/html,适合开发环境(实时同步代码); - 命名数据卷:格式
数据卷名:容器内路径,如mysql-data:/var/lib/mysql,适合生产环境(Docker 统一管理数据,避免宿主机路径依赖)。
(3)depends_on 启动顺序
depends_on 仅保证服务的启动顺序(如先启动 db 再启动 web),但不保证 db 服务 “完全就绪”(如 MySQL 启动后需 10 秒才能接受连接)。若需等待服务就绪,需在应用代码中添加重试逻辑(如 Web 服务连接 MySQL 失败时重试)。
(4)env_file 环境变量文件
环境变量文件需满足 KEY=VALUE 格式,每行一个变量,示例(.env 文件):
# .env 文件
REDIS_HOST=redis
REDIS_PORT=6379
APP_ENV=production
四、实战示例:多服务应用部署
以下通过 2 个典型示例,演示 Docker Compose 的实际应用。
示例 1:Nginx + PHP 网站(LNMP 简化版)
需求:部署一个 PHP 网站,Nginx 作为反向代理,PHP-FPM 处理 PHP 脚本,数据卷持久化网站代码。
1. 项目结构
lnmp-demo/
├── docker-compose.yml # Compose 配置文件
├── nginx/
│ ├── nginx.conf # Nginx 配置文件
│ └── conf.d/
│ └── default.conf # 网站虚拟主机配置
├── php/
│ └── Dockerfile # PHP-FPM 自定义镜像(安装必要扩展)
└── www/
└── index.php # PHP 网站代码
2. 配置文件编写
(1)docker-compose.yml
version: "3.8"
services:
# Nginx 服务
nginx:
image: nginx:1.23
container_name: lnmp-nginx
ports:
- "80:80" # 宿主机 80 端口映射到 Nginx 80 端口
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro # Nginx 主配置(只读)
- ./nginx/conf.d:/etc/nginx/conf.d:ro # 虚拟主机配置(只读)
- ./www:/usr/share/nginx/html # 网站代码目录(持久化)
- nginx-log:/var/log/nginx # Nginx 日志数据卷
depends_on:
- php # 先启动 PHP-FPM 服务
networks:
- lnmp-network
restart: always
# PHP-FPM 服务
php:
build: ./php # 基于 ./php/Dockerfile 构建镜像
container_name: lnmp-php
volumes:
- ./www:/usr/share/nginx/html # 与 Nginx 共享网站代码目录
networks:
- lnmp-network
restart: always
# 数据卷定义
volumes:
nginx-log: # 持久化 Nginx 日志
# 网络定义
networks:
lnmp-network:
driver: bridge
(2)PHP Dockerfile(./php/Dockerfile)
# 基础镜像(PHP-FPM 8.2 版本,基于 Debian)
FROM php:8.2-fpm
# 安装常用扩展(如 mysqli、pdo_mysql)
RUN docker-php-ext-install mysqli pdo_mysql
# 暴露 PHP-FPM 默认端口(9000)
EXPOSE 9000
# 启动 PHP-FPM 服务(前台运行,确保容器不退出)
CMD ["php-fpm"]
(3)Nginx 虚拟主机配置(./nginx/conf.d/default.conf)
server {
listen 80;
server_name localhost; # 网站域名(本地测试用 localhost)
root /usr/share/nginx/html; # 网站根目录(与 PHP 共享的目录)
index index.php index.html; # 默认首页
# 处理 PHP 请求:转发到 PHP-FPM 服务(服务名即 "php")
location ~ \.php$ {
fastcgi_pass php:9000; # PHP 服务名:端口(Compose 内部可通过服务名访问)
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# 静态文件缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
}
}
(4)PHP 测试代码(./www/index.php)
<?php
phpinfo(); // 输出 PHP 环境信息
?>
3. 启动与验证
-
进入项目根目录(
lnmp-demo/); -
构建并启动服务:
docker-compose up -d --build # --build 确保 PHP 镜像重新构建
3. 验证:打开浏览器访问 `http://localhost`,若显示 PHP 信息页面,则部署成功;
4. 查看日志(可选):
```bash
docker-compose logs -f nginx # 实时查看 Nginx 日志
示例 2:Spring Boot + MySQL 后端服务
需求:部署一个 Spring Boot 后端应用,连接 MySQL 数据库,数据卷持久化数据库数据和应用配置。
1. 项目结构
springboot-mysql-demo/
├── docker-compose.yml # Docker Compose 配置文件
├── backend/ # Spring Boot 后端服务
│ ├── Dockerfile # 后端服务 Dockerfile
│ ├── pom.xml # Maven 依赖配置
│ └── src/ # 源代码目录
└── mysql/ # MySQL 相关配置
└── init.sql # 数据库初始化脚本
2. 配置文件编写
1. spring boot 后端服务
(1)backend/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>springboot-mysql-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-mysql-demo</name>
<description>Demo project for Spring Boot with MySQL</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok (简化代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring Boot Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
(2)/SpringbootMysqlDemoApplication.java
backend/src/main/java/com/example/demo/SpringbootMysqlDemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringbootMysqlDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootMysqlDemoApplication.class, args);
}
}
(3)/User.java
backend/src/main/java/com/example/demo/model/User.java
package com.example.demo.model;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false, unique = true)
private String email;
}
(4)/UserRepository.java
backend/src/main/java/com/example/demo/repository/UserRepository.java
package com.example.demo.repository;
import com.example.demo.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
(5)/UserController.java
backend/src/main/java/com/example/demo/controller/UserController.java
package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserRepository userRepository;
// 获取所有用户
@GetMapping
public List<User> getAllUsers() {
return userRepository.findAll();
}
// 创建新用户
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User savedUser = userRepository.save(user);
return new ResponseEntity<>(savedUser, HttpStatus.CREATED);
}
// 获取单个用户
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return userRepository.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
// 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User userDetails) {
return userRepository.findById(id)
.map(user -> {
user.setName(userDetails.getName());
user.setEmail(userDetails.getEmail());
User updatedUser = userRepository.save(user);
return ResponseEntity.ok(updatedUser);
})
.orElse(ResponseEntity.notFound().build());
}
// 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
return userRepository.findById(id)
.map(user -> {
userRepository.delete(user);
return ResponseEntity.noContent().build();
})
.orElse(ResponseEntity.notFound().build());
}
}
(6)/application.properties
backend/src/main/resources/application.properties
# 服务器端口
server.port=8080
# 数据库配置(值将通过环境变量注入)
spring.datasource.url=jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:demo_db}?useSSL=false&allowPublicKeyRetrieval=true
spring.datasource.username=${DB_USERNAME:root}
spring.datasource.password=${DB_PASSWORD:password}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# JPA 配置
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.format_sql=true
(7)backend/Dockerfile
# 构建阶段
FROM maven:3.8.5-openjdk-17 AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
# 运行阶段
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
2. MySQL 数据库配置
mysql/init.sql
-- 创建数据库(如果不存在)
CREATE DATABASE IF NOT EXISTS demo_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 使用创建的数据库
USE demo_db;
-- 这里可以添加初始数据
-- INSERT INTO users (name, email) VALUES ('John Doe', 'john@example.com');
3. Docker Compose 配置
(1)docker-compose.yml
version: '3.8'
services:
# MySQL 数据库服务
mysql:
image: mysql:8.0
container_name: mysql-db
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-password}
MYSQL_DATABASE: ${DB_NAME:-demo_db}
MYSQL_USER: ${DB_USERNAME:-appuser}
MYSQL_PASSWORD: ${DB_PASSWORD:-password}
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql # 数据持久化
- ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql # 初始化脚本
networks:
- app-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$$MYSQL_ROOT_PASSWORD"]
interval: 10s
timeout: 5s
retries: 5
# Spring Boot 后端服务
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: springboot-app
restart: always
depends_on:
mysql:
condition: service_healthy # 等待数据库健康检查通过后再启动
environment:
DB_HOST: mysql # 对应 MySQL 服务名
DB_PORT: 3306
DB_NAME: ${DB_NAME:-demo_db}
DB_USERNAME: ${DB_USERNAME:-appuser}
DB_PASSWORD: ${DB_PASSWORD:-password}
ports:
- "8080:8080"
networks:
- app-network
# 自定义网络(服务间通信)
networks:
app-network:
driver: bridge
# 数据卷(持久化数据)
volumes:
mysql-data:
4. 部署与运行步骤
(1). 构建和启动服务
# 在项目根目录执行
docker compose up -d
(2). 查看服务状态
docker compose ps
(3). 查看日志
# 查看后端服务日志
docker compose logs -f backend
# 查看数据库服务日志
docker compose logs -f mysql
(4). 测试 API
服务启动后,可以通过以下端点测试 API:
-
获取所有用户:
GET http://localhost:8080/api/users -
创建用户:
POST http://localhost:8080/api/users,请求体:{ "name": "John Doe", "email": "john@example.com" } -
获取指定用户:
GET http://localhost:8080/api/users/{id} -
更新用户:
PUT http://localhost:8080/api/users/{id} -
删除用户:
DELETE http://localhost:8080/api/users/{id}
(5). 停止服务
# 停止服务但不删除数据
docker compose down
# 停止服务并删除数据卷(谨慎使用,会清除所有数据)
docker compose down -v
5. 关键特性说明
- 服务依赖管理:通过
depends_on和健康检查确保数据库准备就绪后才启动后端服务 - 数据持久化:使用 Docker 数据卷
mysql-data保存 MySQL 数据,容器重启数据不丢失 - 环境变量配置:敏感信息通过环境变量注入,避免硬编码
- 网络隔离:自定义网络
app-network确保服务间安全通信 - 自动重启:配置
restart: always确保服务故障后自动恢复
更多推荐



所有评论(0)