欢迎光临
我们一直在努力

Docker - Docker Compose初识,一键管理多容器

在这里插入图片描述

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕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: ecommercesystem

# 定义服务
services:

# 👇 PostgreSQL 数据库服务
postgres:
image: postgres:15
container_name: ecommercepostgres
restart: unlessstopped
environment:
POSTGRES_DB: ecommerce
POSTGRES_USER: appuser
POSTGRES_PASSWORD: app123
volumes:
pgdata:/var/lib/postgresql/data
./initdb.sql:/dockerentrypointinitdb.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:7alpine
container_name: ecommerceredis
restart: unlessstopped
command: redisserver appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 20s
timeout: 10s
retries: 5
ports:
"6379:6379"

# 👇 用户微服务(Spring Boot)
user-service:
build:
context: ./userservice
dockerfile: Dockerfile
container_name: ecommerceuserservice
restart: unlessstopped
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:
ecommercenet

# 👇 API 网关(Spring Cloud Gateway)
gateway:
build:
context: ./gateway
dockerfile: Dockerfile
container_name: ecommercegateway
restart: unlessstopped
ports:
"8080:8080"
environment:
SPRING_PROFILES_ACTIVE: docker
depends_on:
user-service:
condition: service_started # 网关仅需 user-service 启动即可路由(不强依赖其健康)
networks:
ecommercenet

# 自定义网络(显式定义,便于理解)
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.timetolive=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: userserviceroute
uri: http://userservice: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/configrepo

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


🙌 感谢你读到这里! 🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。 💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友! 💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿 🔔 关注我,不错过下一篇干货!我们下期再见!✨

赞(0)
未经允许不得转载:171主机测评 » Docker - Docker Compose初识,一键管理多容器
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址