09.Docker compose
Docker Compose 完全指南:从基础到实践
文章目录
- Docker Compose 完全指南:从基础到实践
- 一、Docker Compose 基础认知
- 1.1 核心概念
- 1.2 适用场景
- 1.3 安装前提
- 1.4 验证安装
- 二、Docker Compose 核心命令
- 三、docker-compose.yml 配置语法详解
- 3.1 配置结构总览
- 3.2 关键配置说明
- (1)version 版本兼容性
- (2)volumes 数据卷类型
- (3)depends_on 启动顺序
- (4)env_file 环境变量文件
- 四、实战示例:多服务应用部署
- 示例 1:Nginx + PHP 网站(LNMP 简化版)
- 1. 项目结构
- 2. 配置文件编写
- (1)docker-compose.yml
- (2)PHP Dockerfile(./php/Dockerfile)
- (3)Nginx 虚拟主机配置(./nginx/conf.d/default.conf)
- (4)PHP 测试代码(./www/index.php)
- 3. 启动与验证
- 示例 2:Spring Boot + MySQL 后端服务
- 1. 项目结构
- 2. 配置文件编写
- 1. spring boot 后端服务
- (1)`backend/pom.xml`
- (2)`backend/src/main/java/com/example/demo/SpringbootMysqlDemoApplication.java`
- (3)`backend/src/main/java/com/example/demo/model/User.java`
- (4)`backend/src/main/java/com/example/demo/repository/UserRepository.java`
- (5)`backend/src/main/java/com/example/demo/controller/UserController.java`
- (6)`backend/src/main/resources/application.properties`
- (7)`backend/Dockerfile`
- 2. MySQL 数据库配置
- `mysql/init.sql`
- 3. Docker Compose 配置
- (1)docker-compose.yml
- 4. 部署与运行步骤
- (1). 构建和启动服务
- (2). 查看服务状态
- (3). 查看日志
- (4). 测试 API
- (5). 停止服务
- 5. 关键特性说明
一、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=6379env_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.0container_name: my-dbports:- "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-networkrestart: 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.23container_name: lnmp-nginxports:- "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-networkrestart: always# PHP-FPM 服务php:build: ./php # 基于 ./php/Dockerfile 构建镜像container_name: lnmp-phpvolumes:- ./www:/usr/share/nginx/html # 与 Nginx 共享网站代码目录networks:- lnmp-networkrestart: 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)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)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)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)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 {@Autowiredprivate UserRepository userRepository;// 获取所有用户@GetMappingpublic List<User> getAllUsers() {return userRepository.findAll();}// 创建新用户@PostMappingpublic 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)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.0container_name: mysql-dbrestart: alwaysenvironment: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-networkhealthcheck:test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p$$MYSQL_ROOT_PASSWORD"]interval: 10stimeout: 5sretries: 5# Spring Boot 后端服务backend:build:context: ./backenddockerfile: Dockerfilecontainer_name: springboot-apprestart: alwaysdepends_on:mysql:condition: service_healthy # 等待数据库健康检查通过后再启动environment:DB_HOST: mysql # 对应 MySQL 服务名DB_PORT: 3306DB_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
确保服务故障后自动恢复