
👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕Docker这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!
文章目录
- Docker – Docker Compose初识,一键管理多容器 🐳✨
-
- 一、为什么是 Docker Compose?——告别“终端风暴” 🌪️
- 二、Docker Compose 核心概念速览 🧩
- 三、动手实践:搭建 Spring Boot 微服务演示系统 🛠️
-
- 3.1 初始化项目结构
- 3.2 编写 `docker-compose.yml`(核心配置文件)📄
- 3.3 编写 `user-service` 的 Java 代码(Spring Boot)💻
-
- `user-service/pom.xml`(精简版)
- `user-service/src/main/resources/application-docker.yml`
- `user-service/src/main/java/com/ecommerce/user/entity/User.java`
- `user-service/src/main/java/com/ecommerce/user/repository/UserRepository.java`
- `user-service/src/main/java/com/ecommerce/user/service/UserService.java`
- `user-service/src/main/java/com/ecommerce/user/controller/UserController.java`
- `user-service/src/main/java/com/ecommerce/user/UserServiceApplication.java`
- `user-service/Dockerfile`
- 3.4 编写 `gateway` 的 Java 代码(Spring Cloud Gateway)🌐
-
- `gateway/pom.xml`(关键依赖)
- `gateway/src/main/resources/application-docker.yml`
- `gateway/src/main/java/com/ecommerce/gateway/GatewayApplication.java`
- `gateway/Dockerfile`
- 四、启动与验证:见证一键编排的力量 ⚡
-
- 4.1 验证数据库连接(从 user-service 容器内部)
- 4.2 验证 Redis 连接
- 4.3 通过网关调用用户服务(宿主机 curl)
- 4.4 查看实时日志流
- 4.5 健康检查端点(Production Ready)
- 五、进阶能力:让 Compose 更强大 🚀
-
- 5.1 环境隔离:`.env` 文件与多环境配置
- 5.2 配置外置:`config` 驱动与远程配置中心
- 5.3 依赖服务等待:`wait-for-it.sh` 作为兜底方案
- 5.4 卷(Volume)深度实践:数据持久化与迁移
- 六、常见陷阱与避坑指南 🚫
- 七、Docker Compose 与 Kubernetes:如何选择?🤔
- 八、总结:拥抱声明式基础设施 🌟
Docker – Docker Compose初识,一键管理多容器 🐳✨
在现代云原生应用开发中,单体架构正加速向微服务化演进。一个典型的企业级 Java 应用,往往不再由一个 JAR 包承载全部功能,而是被拆解为:用户服务(User Service)、订单服务(Order Service)、商品服务(Product Service)、认证中心(Auth Service)、API 网关(Gateway)、Redis 缓存、PostgreSQL 数据库、Elasticsearch 搜索引擎、RabbitMQ 消息队列……甚至前端 Vue/React 应用也独立部署。当这些组件各自运行在不同进程、不同端口、不同配置下时,本地开发与测试的复杂度陡然飙升——你是否曾为启动 7 个终端窗口、逐条执行 java -jar、docker run、redis-server、psql 而手忙脚乱?又是否因环境变量漏配、网络未互通、依赖启动顺序错乱导致“在我机器上是好的”(It works on my machine 🧪)成为团队高频梗?
此时,Docker Compose 就像一位沉稳可靠的运维指挥官,只需一份声明式 YAML 文件,即可完成「一键拉起、统一编排、网络互通、依赖感知、日志聚合」的全生命周期管理。它不是 Docker 的替代品,而是 Docker CLI 的强力增强;它不解决容器镜像构建问题,却完美填补了「多容器协同」这一关键空白。
本文将带你从零深入 Docker Compose 的核心理念与实战脉络:理解 docker-compose.yml 的语法骨架与语义逻辑,亲手搭建一个基于 Spring Boot 的微服务演示系统(含 API 网关 + 用户服务 + PostgreSQL + Redis),掌握服务发现、健康检查、环境隔离、配置外置等生产级能力,并通过 Java 代码直连多容器服务验证端到端连通性。全程无黑盒,每一步皆可复现,每一个命令皆有回响 💫。
一、为什么是 Docker Compose?——告别“终端风暴” 🌪️
设想这样一个场景:你正在开发一个电商后端,本地需同时运行:
- gateway: Spring Cloud Gateway,监听 8080
- user-service: Spring Boot 微服务,暴露 8081,依赖数据库与缓存
- postgres: 关系型数据库,端口 5432
- redis: 缓存中间件,端口 6379
若全部手动启动,你需要依次执行:
# 终端 1:启动 PostgreSQL
docker run -d –name my-postgres \\
-e POSTGRES_PASSWORD=pass123 \\
-p 5432:5432 \\
-v pgdata:/var/lib/postgresql/data \\
postgres:15
# 终端 2:启动 Redis
docker run -d –name my-redis \\
-p 6379:6379 \\
redis:7-alpine
# 终端 3:构建并启动 user-service(假设已打包为 jar)
java -jar user-service.jar \\
–spring.datasource.url=jdbc:postgresql://localhost:5432/ecommerce \\
–spring.redis.host=localhost \\
–server.port=8081
# 终端 4:启动 gateway(需确保 user-service 已就绪)
java -jar gateway.jar \\
–spring.cloud.gateway.routes[0].id=user-route \\
–spring.cloud.gateway.routes[0].uri=http://localhost:8081 \\
–server.port=8080
⚠️ 问题立现:
- localhost 在容器内 ≠ 宿主机 localhost → user-service 连不上 postgres
- gateway 启动时 user-service 可能尚未 ready → 503 错误频发
- 每次重启需手动 docker stop / kill -9 → 容易残留僵尸进程
- 配置散落各处(命令行、properties、环境变量)→ 难以版本化与协作
- 无法定义服务启动顺序与健康依赖 → 系统脆弱如纸
而 Docker Compose 的哲学是:用代码定义基础设施(Infrastructure as Code)。你只需编写一个 docker-compose.yml,所有服务即成为一个逻辑单元(project),共享默认网络,自动 DNS 解析,支持健康检查驱动的启动顺序,且一条命令即可启停整套环境:
docker compose up -d # 后台启动全部服务 ✅
docker compose logs -f gateway # 实时查看网关日志 ✅
docker compose down # 彻底清理(含网络、卷)✅
💡 关键认知:Docker Compose 是 Docker Engine 的客户端工具,它本身不运行容器,而是调用 Docker API 创建容器、网络、卷等资源。它的价值在于抽象与编排,而非虚拟化。
二、Docker Compose 核心概念速览 🧩
在深入 YAML 之前,先建立对 Compose 体系的关键术语共识:
| Project | 由 docker-compose.yml 定义的一组关联服务的集合,以目录名或 -p 参数命名 | 一个“软件系统”(如 ecommerce-system) |
| Service | services: 下定义的一个可缩放、可独立部署的容器化组件(如 gateway, user-service) | 一个“微服务进程”或“中间件实例” |
| Container | Service 运行时的具体实例(1 个 Service 可有 N 个 Container,通过 scale 控制) | 一个“Java 进程”或“Redis 进程”的具体运行体 |
| Network | Compose 自动创建的默认桥接网络(<project>_default),所有 Service 容器自动加入,支持 service-name 作为 DNS 名解析 | 一个“局域网”,所有设备可通过主机名通信 |
| Volume | 持久化数据的存储机制,独立于容器生命周期 | “U 盘”——拔掉容器,数据仍在 |
✅ 默认网络行为: 在 ecommerce 项目中,若定义了 postgres 和 user-service 两个 service,则:
- user-service 容器内可直接 ping postgres 或 jdbc:postgresql://postgres:5432/…
- postgres 容器内可 telnet user-service 8081(若开放端口)
- 无需 –link,无需 host.docker.internal,无需硬编码 IP —— 这是 Compose 最优雅的魔法之一 🪄
三、动手实践:搭建 Spring Boot 微服务演示系统 🛠️
我们构建一个极简但具备完整链路的电商子系统:
- gateway: Spring Cloud Gateway(路由至用户服务)
- user-service: Spring Boot WebFlux 应用(提供 /api/users REST 接口)
- postgres: 存储用户数据(users 表)
- redis: 缓存用户详情(user:{id})
所有服务均通过 Docker Compose 统一编排,Java 代码使用标准 Spring Data JPA + Spring Cache,零侵入 Compose。
3.1 初始化项目结构
mkdir ecommerce-demo && cd ecommerce-demo
mkdir gateway user-service
项目结构概览:
ecommerce-demo/
├── docker-compose.yml # 👈 Compose 主配置(核心!)
├── gateway/
│ ├── pom.xml
│ └── src/main/java/… # Gateway 启动类、路由配置
├── user-service/
│ ├── pom.xml
│ ├── src/main/resources/application.yml
│ └── src/main/java/… # User 实体、Repository、Controller、Cache 配置
└── init-db.sql # PostgreSQL 初始化脚本(可选)
3.2 编写 docker-compose.yml(核心配置文件)📄
# docker-compose.yml
version: '3.8'
# 全局设置:项目名称(覆盖目录名)
name: ecommerce–system
# 定义服务
services:
# 👇 PostgreSQL 数据库服务
postgres:
image: postgres:15
container_name: ecommerce–postgres
restart: unless–stopped
environment:
POSTGRES_DB: ecommerce
POSTGRES_USER: appuser
POSTGRES_PASSWORD: app123
volumes:
– pgdata:/var/lib/postgresql/data
– ./init–db.sql:/docker–entrypoint–initdb.d/init.sql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U appuser -d ecommerce"]
interval: 30s
timeout: 10s
retries: 5
start_period: 40s
ports:
– "5432:5432"
# 👇 Redis 缓存服务
redis:
image: redis:7–alpine
container_name: ecommerce–redis
restart: unless–stopped
command: redis–server ––appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 20s
timeout: 10s
retries: 5
ports:
– "6379:6379"
# 👇 用户微服务(Spring Boot)
user-service:
build:
context: ./user–service
dockerfile: Dockerfile
container_name: ecommerce–user–service
restart: unless–stopped
environment:
SPRING_PROFILES_ACTIVE: docker
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/ecommerce
SPRING_DATASOURCE_USERNAME: appuser
SPRING_DATASOURCE_PASSWORD: app123
SPRING_REDIS_HOST: redis
SPRING_REDIS_PORT: 6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
ports:
– "8081:8081"
# 显式声明网络(虽默认存在,但显式更清晰)
networks:
– ecommerce–net
# 👇 API 网关(Spring Cloud Gateway)
gateway:
build:
context: ./gateway
dockerfile: Dockerfile
container_name: ecommerce–gateway
restart: unless–stopped
ports:
– "8080:8080"
environment:
SPRING_PROFILES_ACTIVE: docker
depends_on:
user-service:
condition: service_started # 网关仅需 user-service 启动即可路由(不强依赖其健康)
networks:
– ecommerce–net
# 自定义网络(显式定义,便于理解)
networks:
ecommerce-net:
driver: bridge
# 持久化卷(独立于容器生命周期)
volumes:
pgdata:
🔍 关键点解析:
- version: '3.8': 使用最新稳定版 Compose 文件规范(兼容 Docker Engine 20.10+)
- healthcheck: 为 postgres 和 redis 定义健康探针,是 depends_on 实现智能等待的基础 🔍
- depends_on + condition:
- service_healthy → 等待目标服务通过健康检查(推荐用于数据库/缓存)
- service_started → 仅等待容器创建成功(适用于无健康检查的普通服务,如 gateway)
- environment: 所有环境变量均以 SPRING_* 开头,将被 Spring Boot 自动绑定,无需修改 Java 代码。
- build.context: 指向 ./user-service 目录,Compose 将在此路径下执行 docker build。
- networks & volumes: 显式声明,提升可读性与可维护性。
🌐 网络原理图(Mermaid 渲染):
渲染错误: Mermaid 渲染失败: Parse error on line 5: …graph Docker Network
ecommerce-ne ———————–^ Expecting 'SEMI', 'NEWLINE', 'SPACE', 'EOF', 'GRAPH', 'DIR', 'subgraph', 'SQS', 'end', 'AMP', 'COLON', 'START_LINK', 'STYLE', 'LINKSTYLE', 'CLASSDEF', 'CLASS', 'CLICK', 'DOWN', 'UP', 'NUM', 'NODE_STRING', 'BRKT', 'MINUS', 'MULT', 'UNICODE_TEXT', got 'TAGSTART'
该图清晰展示了四个容器如何在同一个自定义桥接网络 ecommerce-net 中通过服务名(postgres, redis, user-service)实现无缝通信 —— 这是 Docker Compose 解决“网络发现”问题的核心机制。
3.3 编写 user-service 的 Java 代码(Spring Boot)💻
user-service/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
http://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.5</version>
<relativePath/>
</parent>
<groupId>com.ecommerce</groupId>
<artifactId>user-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>user-service</name>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
user-service/src/main/resources/application-docker.yml
# application-docker.yml —— Compose 中指定的 profile
spring:
datasource:
url: ${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/ecommerce}
username: ${SPRING_DATASOURCE_USERNAME:appuser}
password: ${SPRING_DATASOURCE_PASSWORD:app123}
driver-class-name: org.postgresql.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
redis:
host: ${SPRING_REDIS_HOST:localhost}
port: ${SPRING_REDIS_PORT:6379}
cache:
type: redis
# 自定义缓存配置
spring.cache.redis.time–to–live=3600000 # 1h
# Actuator 端点(用于健康检查)
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
⚠️ 注意:application-docker.yml 中的 ${SPRING_DATASOURCE_URL} 等占位符,会自动被 Compose 的 environment 值覆盖。这是 Spring Boot 与 Docker Compose 天然契合的体现 ✅。
user-service/src/main/java/com/ecommerce/user/entity/User.java
package com.ecommerce.user.entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String username;
@Column(nullable = false)
private String email;
}
user-service/src/main/java/com/ecommerce/user/repository/UserRepository.java
package com.ecommerce.user.repository;
import com.ecommerce.user.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
user-service/src/main/java/com/ecommerce/user/service/UserService.java
package com.ecommerce.user.service;
import com.ecommerce.user.entity.User;
import com.ecommerce.user.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// ✅ 启用 Redis 缓存:方法返回值将被序列化存入 Redis,Key 为 "user::#{id}"
@Cacheable(value = "users", key = "#id")
public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not found: " + id));
}
public User save(User user) {
return userRepository.save(user);
}
}
user-service/src/main/java/com/ecommerce/user/controller/UserController.java
package com.ecommerce.user.controller;
import com.ecommerce.user.entity.User;
import com.ecommerce.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findById(id); // ✅ 触发 @Cacheable
return ResponseEntity.ok(user);
}
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User saved = userService.save(user);
return ResponseEntity.ok(saved);
}
}
user-service/src/main/java/com/ecommerce/user/UserServiceApplication.java
package com.ecommerce.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching // ✅ 启用 Spring Cache 抽象
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
user-service/Dockerfile
# user-service/Dockerfile
FROM eclipse/jetty:11-jre17-slim
# 复制 JAR 到容器
COPY target/user-service-0.0.1-SNAPSHOT.jar /app.jar
# 暴露端口
EXPOSE 8081
# 启动命令
ENTRYPOINT ["java","-Dspring.profiles.active=docker","-jar","/app.jar"]
✅ 构建说明: 在 user-service/ 目录下执行 mvn clean package -DskipTests,生成 target/user-service-0.0.1-SNAPSHOT.jar。Compose 的 build.context 会自动找到此 JAR 并 COPY 进镜像。
3.4 编写 gateway 的 Java 代码(Spring Cloud Gateway)🌐
gateway/pom.xml(关键依赖)
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>4.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
gateway/src/main/resources/application-docker.yml
spring:
cloud:
gateway:
routes:
– id: user–service–route
uri: http://user–service:8081 # ✅ 直接使用 service name!
predicates:
– Path=/api/users/**
filters:
– RewritePath=/api/users/(?<segment>.*), /$\\{segment}
# 允许跨域(开发友好)
spring.web.cors.allowed-origins: http://localhost:3000
spring.web.cors.allowed-methods: GET,POST,PUT,DELETE
gateway/src/main/java/com/ecommerce/gateway/GatewayApplication.java
package com.ecommerce.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
gateway/Dockerfile
FROM openjdk:17-jre-slim
COPY target/gateway-0.0.1-SNAPSHOT.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java","-Dspring.profiles.active=docker","-jar","/app.jar"]
四、启动与验证:见证一键编排的力量 ⚡
一切就绪,进入项目根目录 ecommerce-demo/,执行:
# 第一次启动(会构建镜像、拉取基础镜像、初始化卷)
docker compose up -d
# 查看所有服务状态
docker compose ps
预期输出(部分):
NAME COMMAND SERVICE STATUS PORTS
ecommerce-gateway "java -Dspring.profi…" gateway running (healthy) 0.0.0.0:8080->8080/tcp
ecommerce-postgres "docker-entrypoint.s…" postgres running (healthy) 0.0.0.0:5432->5432/tcp
ecommerce-redis "docker-entrypoint.s…" redis running (healthy) 0.0.0.0:6379->6379/tcp
ecommerce-user-service "java -Dspring.profi…" user-service running (healthy) 0.0.0.0:8081->8081/tcp
✅ 所有 STATUS 列显示 running (healthy),表明健康检查通过,服务已就绪。
4.1 验证数据库连接(从 user-service 容器内部)
# 进入 user-service 容器
docker exec -it ecommerce-user-service sh
# 在容器内尝试连接 postgres(使用 service name)
/ # apk add postgresql-client # 安装 psql 客户端(alpine)
/ # psql -h postgres -U appuser -d ecommerce
Password for user appuser: app123
ecommerce=# \\dt — 查看表,应看到 users 表
List of relations
Schema | Name | Type | Owner
——–+——-+——-+——–
public | users | table | appuser
(1 row)
4.2 验证 Redis 连接
/ # apk add redis
/ # redis-cli -h redis ping
PONG
4.3 通过网关调用用户服务(宿主机 curl)
# 创建用户
curl -X POST http://localhost:8080/api/users \\
-H "Content-Type: application/json" \\
-d '{"username":"alice","email":"alice@example.com"}'
# 响应:{"id":1,"username":"alice","email":"alice@example.com"}
# 查询用户(首次:查 DB + 写缓存)
curl http://localhost:8080/api/users/1
# 再次查询(命中 Redis 缓存,无 DB 查询日志)
curl http://localhost:8080/api/users/1
🔍 验证缓存生效:观察 user-service 日志(docker compose logs -f user-service),第二次请求不应出现 Hibernate: select … from users SQL 日志,证明 @Cacheable 已生效。
4.4 查看实时日志流
# 查看整个系统的实时日志(按服务分色)
docker compose logs -f
# 或只看网关日志(绿色高亮)
docker compose logs -f gateway
# 或查看 PostgreSQL 的初始化日志
docker compose logs postgres | head -20
4.5 健康检查端点(Production Ready)
所有 Spring Boot 服务均暴露 /actuator/health:
- http://localhost:8080/actuator/health → Gateway 健康
- http://localhost:8081/actuator/health → User Service 健康(含 db, redis, diskSpace)
响应示例:
{
"status": "UP",
"components": {
"db": { "status": "UP" },
"redis": { "status": "UP" },
"diskSpace": { "status": "UP" }
}
}
这正是 Compose healthcheck 与 Spring Boot Actuator 协同工作的结果 🤝。
五、进阶能力:让 Compose 更强大 🚀
5.1 环境隔离:.env 文件与多环境配置
在项目根目录创建 .env:
# .env
COMPOSE_PROJECT_NAME=ecommerce-dev
DB_PASSWORD=dev123
REDIS_PORT=6379
修改 docker-compose.yml 中的环境变量引用:
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
SPRING_REDIS_PORT: ${REDIS_PORT}
再创建 docker-compose.prod.yml(生产环境覆盖):
# docker-compose.prod.yml
services:
user-service:
deploy:
replicas: 3
resources:
limits:
memory: 1G
cpus: '0.5'
postgres:
environment:
POSTGRES_PASSWORD: ${PROD_DB_PASSWORD}
volumes:
– /mnt/pgdata:/var/lib/postgresql/data
启动生产环境:
PROD_DB_PASSWORD=prodPass123 docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
🌐 更多环境管理技巧可参考官方文档:Docker Compose Environment Variables
5.2 配置外置:config 驱动与远程配置中心
对于大型系统,可将 application.yml 抽离为独立配置服务。Spring Cloud Config Server 是业界标准方案,其官方文档详尽清晰:Spring Cloud Config Reference Guide
Compose 可轻松集成:
config-server:
image: springio/configserver
environment:
SPRING_CLOUD_CONFIG_SERVER_GIT_URI: https://github.com/myorg/config–repo
5.3 依赖服务等待:wait-for-it.sh 作为兜底方案
尽管 healthcheck + depends_on 已很强大,但某些旧版应用(如未暴露健康端点的 Legacy Java App)仍需手动等待。此时可引入轻量脚本 wait-for-it.sh:
# 在服务 Dockerfile 中
COPY wait-for-it.sh /wait-for-it.sh
RUN chmod +x /wait-for-it.sh
ENTRYPOINT ["/wait-for-it.sh", "postgres:5432", "–", "java", "-jar", "/app.jar"]
5.4 卷(Volume)深度实践:数据持久化与迁移
当前 pgdata 卷是匿名卷,数据随 docker compose down 保留,但不易备份。升级为命名卷 + 备份脚本:
# 创建命名卷(显式控制)
docker volume create ecommerce-pgdata
# 修改 docker-compose.yml
volumes:
pgdata:
name: ecommerce-pgdata
备份脚本 backup-pg.sh:
#!/bin/bash
docker exec ecommerce-postgres pg_dump -U appuser ecommerce > backup_$(date +%F).sql
💾 数据安全是生产基石,Docker 卷管理是必修课。深入学习请访问:Docker Volumes Documentation
六、常见陷阱与避坑指南 🚫
| localhost 在容器内失效 | user-service 连 postgres 报 Connection refused | ✅ 使用服务名 postgres,而非 localhost;localhost 在容器内指代自身 |
| depends_on 不等待健康 | user-service 启动时报 Connection refused to postgres | ✅ 必须配合 healthcheck + condition: service_healthy;depends_on 默认只等容器启动 |
| 端口冲突 | docker compose up 报 Bind for 0.0.0.0:5432 failed | ✅ 检查宿主机是否已有 PostgreSQL 进程:lsof -i :5432 或 netstat -tulpn | grep :5432 |
| Spring Profile 未生效 | application-docker.yml 未加载 | ✅ 确认 Dockerfile 中 ENTRYPOINT 包含 -Dspring.profiles.active=docker;或在 environment 中设 SPRING_PROFILES_ACTIVE=docker |
| Redis 连接超时 | user-service 启动慢、报 Cannot connect to redis | ✅ 检查 redis 服务的 healthcheck 是否配置正确;增加 start_period 给 Redis 充足启动时间 |
| 卷权限问题(Linux) | PostgreSQL 启动失败,报 Permission denied | ✅ 在 postgres service 中添加 user: "1001:1001"(匹配宿主机 UID/GID),或使用 :z 标签(SELinux) |
🧭 遇到疑难问题?Docker 官方故障排除指南是权威参考:Docker Compose Troubleshooting
七、Docker Compose 与 Kubernetes:如何选择?🤔
开发者常困惑:该用 Compose 还是 Kubernetes?
简单说:
-
Docker Compose = 本地开发 & CI/CD 测试环境 & 小型生产部署 ✅ 语法简洁,学习曲线平缓,YAML 直观,适合单机或多节点(通过 Swarm)编排。 ✅ 是理解容器编排的绝佳起点。
-
Kubernetes = 大规模生产环境(高可用、自动扩缩、滚动更新、服务网格) ✅ 提供 Pod、Deployment、Service、Ingress、HPA 等丰富原语。 ✅ 生态庞大(Istio, Prometheus, Argo CD)。
💡 关键洞察:Compose 的 docker-compose.yml 可通过 Kompose 工具一键转换为 Kubernetes YAML(deployment.yaml, service.yaml),实现平滑演进。这意味着:今天用 Compose 写的编排,明天就能上 K8s。
🌐 Kompose 官网提供详细转换示例与最佳实践:Kompose Documentation
八、总结:拥抱声明式基础设施 🌟
Docker Compose 不仅仅是一个“多容器启动脚本”,它是 现代软件交付流水线的基石。通过本文的完整实践,你应该已经深刻体会到:
- ✅ 一份 YAML,定义整个系统拓扑:从数据库、缓存到业务服务,逻辑清晰,一目了然;
- ✅ 服务名即 DNS:postgres, redis, user-service 成为可靠通信标识,彻底告别 IP 绑定;
- ✅ 健康驱动启动:depends_on + healthcheck 让依赖关系真正“活”起来;
- ✅ 环境一致性:开发、测试、预发环境使用同一份 Compose 文件,消除“环境差异”魔咒;
- ✅ Java 生态无缝集成:Spring Boot 的外部化配置(application-{profile}.yml)与 Compose environment 天然契合;
- ✅ 可扩展性强:从单机开发到 Swarm 集群,再到 Kubernetes 迁移,路径清晰。
🐳 最后,以 Docker 官方对 Compose 的定义作结: “Docker Compose is a tool for defining and running multi-container Docker applications.” —— 简洁,有力,永恒。
当你下次面对一个包含 5 个服务的 Spring Boot 项目时,请不要打开 5 个终端。请打开你的 docker-compose.yml,敲下 docker compose up -d,然后泡一杯咖啡,静待整个系统如交响乐般和谐奏响 🎻。
世界因此而简单,开发因此而快乐。这就是 Docker Compose 的魅力所在。
✨ 延伸思考: 如果你想进一步探索容器化监控,Prometheus + Grafana 是黄金组合,其官方入门指南极为友好:Prometheus Getting Started
若想深入理解容器网络底层,Docker 官方网络教程是必读经典:Docker Networking Overview
而对于 Java 开发者,Spring Boot 官方文档中关于容器部署的最佳实践,值得反复研读:Spring Boot Docker Reference
🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。 💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿 🔔 关注我,不错过下一篇干货!我们下期再见!✨





