Skip to content

Spring Boot Docker Compose 集成指南 🐳

前言:为什么需要 Docker Compose 集成?

在现代微服务开发中,我们经常需要依赖各种外部服务,如数据库、消息队列、缓存等。传统的开发方式需要开发者手动安装和配置这些服务,这不仅繁琐,还容易导致环境不一致的问题。

NOTE

Spring Boot 从 3.1 版本开始,提供了与 Docker Compose 的原生集成支持,让开发者能够更轻松地管理开发环境中的依赖服务。

核心概念理解

Docker Compose 集成的设计哲学

Spring Boot 的 Docker Compose 集成遵循"约定优于配置"的原则:

  • 自动发现:Spring Boot 会自动查找项目根目录下的 compose.yamldocker-compose.yaml 文件
  • 智能连接:自动为容器化的服务创建相应的连接配置
  • 生命周期管理:与应用启动/停止同步管理容器的生命周期

实战应用场景

场景一:自定义 JDBC URL 参数

在实际开发中,我们经常需要为数据库连接添加特定的参数,比如 SSL 配置、连接池设置等。

yaml
services:
  postgres:
    image: 'postgres:15.3'
    environment:
      - 'POSTGRES_USER=myuser'
      - 'POSTGRES_PASSWORD=secret'
      - 'POSTGRES_DB=mydb'
    ports:
      - '5432:5432'
yaml
services:
  postgres:
    image: 'postgres:15.3'
    environment:
      - 'POSTGRES_USER=myuser'
      - 'POSTGRES_PASSWORD=secret'
      - 'POSTGRES_DB=mydb'
    ports:
      - '5432:5432'
    labels:
      # 关键配置:自定义 JDBC 参数
      org.springframework.boot.jdbc.parameters: 'ssl=true&sslmode=require&connectTimeout=30000'

TIP

使用 org.springframework.boot.jdbc.parameters 标签,Spring Boot 会自动将这些参数附加到 JDBC URL 中,最终生成的连接字符串为: jdbc:postgresql://127.0.0.1:5432/mydb?ssl=true&sslmode=require&connectTimeout=30000

Kotlin 代码示例

kotlin
@RestController
@RequestMapping("/api/users")
class UserController(
    private val userRepository: UserRepository
) {
    
    @GetMapping
    fun getAllUsers(): List<User> {
        // Spring Boot 会自动使用带有 SSL 参数的数据库连接
        return userRepository.findAll()
    }
    
    @PostMapping
    fun createUser(@RequestBody user: User): User {
        return userRepository.save(user)
    }
}

@Entity
@Table(name = "users")
data class User(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0,
    
    @Column(nullable = false)
    val username: String,
    
    @Column(nullable = false)
    val email: String
)

@Repository
interface UserRepository : JpaRepository<User, Long>

场景二:多应用间共享服务

在微服务架构中,多个应用可能需要共享同一套基础设施服务(如数据库、Redis 等)。

项目结构示例

microservices-project/
├── shared-services/
│   └── compose.yaml          # 共享的服务定义
├── user-service/
│   ├── src/
│   └── application.yml       # 用户服务配置
├── order-service/
│   ├── src/
│   └── application.yml       # 订单服务配置
└── notification-service/
    ├── src/
    └── application.yml       # 通知服务配置
共享服务配置详情
yaml
# shared-services/compose.yaml
services:
  postgres:
    image: 'postgres:15.3'
    environment:
      - 'POSTGRES_USER=microservices'
      - 'POSTGRES_PASSWORD=secret'
      - 'POSTGRES_DB=shared_db'
    ports:
      - '5432:5432'
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: 'redis:7.0'
    ports:
      - '6379:6379'
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data

  rabbitmq:
    image: 'rabbitmq:3.12-management'
    ports:
      - '5672:5672'
      - '15672:15672'
    environment:
      - 'RABBITMQ_DEFAULT_USER=admin'
      - 'RABBITMQ_DEFAULT_PASS=secret'
    volumes:
      - rabbitmq_data:/var/lib/rabbitmq

volumes:
  postgres_data:
  redis_data:
  rabbitmq_data:

各服务的配置

yaml
spring:
  docker:
    compose:
      file: ../shared-services/compose.yaml  # 指向共享服务文件
      lifecycle-management: start-only      # 只启动,不停止
  
  application:
    name: user-service
  
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
yaml
spring:
  docker:
    compose:
      file: ../shared-services/compose.yaml  # 指向共享服务文件
      lifecycle-management: start-only      # 只启动,不停止
  
  application:
    name: order-service
  
  jpa:
    hibernate:
      ddl-auto: update
yaml
spring:
  docker:
    compose:
      file: ../shared-services/compose.yaml  # 指向共享服务文件
      lifecycle-management: start-only      # 只启动,不停止
  
  application:
    name: notification-service
  
  rabbitmq:
    host: localhost
    port: 5672
    username: admin
    password: secret

IMPORTANT

lifecycle-management: start-only 是多应用共享服务的关键配置。如果使用默认的 start-and-stop,当任何一个应用停止时,共享的服务也会被停止,影响其他正在运行的应用。

实际业务代码示例

kotlin
// 用户服务
@Service
class UserService(
    private val userRepository: UserRepository,
    private val redisTemplate: RedisTemplate<String, Any>
) {
    
    fun getUserById(id: Long): User? {
        // 先从 Redis 缓存中查找
        val cacheKey = "user:$id"
        val cachedUser = redisTemplate.opsForValue().get(cacheKey) as? User
        
        return cachedUser ?: run {
            // 缓存未命中,从数据库查询
            val user = userRepository.findById(id).orElse(null)
            user?.let {
                // 将查询结果缓存到 Redis
                redisTemplate.opsForValue().set(cacheKey, it, Duration.ofMinutes(10))
            }
            user
        }
    }
}

// 通知服务
@Component
class NotificationListener {
    
    @RabbitListener(queues = ["user.created"])
    fun handleUserCreated(userCreatedEvent: UserCreatedEvent) {
        // 处理用户创建事件,发送欢迎邮件
        println("发送欢迎邮件给用户: ${userCreatedEvent.username}")
        // 实际的邮件发送逻辑...
    }
}

// 订单服务
@Service
class OrderService(
    private val orderRepository: OrderRepository,
    private val rabbitTemplate: RabbitTemplate
) {
    
    fun createOrder(order: Order): Order {
        val savedOrder = orderRepository.save(order)
        
        // 发布订单创建事件
        val orderCreatedEvent = OrderCreatedEvent(
            orderId = savedOrder.id,
            userId = savedOrder.userId,
            amount = savedOrder.amount
        )
        
        rabbitTemplate.convertAndSend("order.created", orderCreatedEvent)
        
        return savedOrder
    }
}

生命周期管理策略

配置选项对比

配置值行为描述适用场景
start-and-stop (默认)应用启动时启动容器,停止时停止容器单应用开发
start-only应用启动时启动容器,停止时不停止容器多应用共享服务
none不管理容器生命周期手动管理容器

手动管理共享服务

当所有应用都停止后,共享服务仍在运行。可以通过以下命令手动管理:

bash
# 进入共享服务目录
cd shared-services

# 停止所有服务
docker compose stop

# 停止并删除容器(保留数据卷)
docker compose down

# 停止并删除容器和数据卷(谨慎使用)
docker compose down -v

WARNING

使用 docker compose down -v 会删除所有数据卷,这将导致数据库数据丢失。在生产环境或重要开发数据时请谨慎使用。

最佳实践建议

1. 项目结构组织

project-root/
├── compose.yaml              # 开发环境服务定义
├── compose.prod.yaml         # 生产环境服务定义(如果需要)
├── src/
├── application.yml
└── application-dev.yml       # 开发环境特定配置

2. 环境隔离

yaml
services:
  postgres:
    image: 'postgres:15.3'
    environment:
      - 'POSTGRES_USER=dev_user'
      - 'POSTGRES_PASSWORD=dev_password'
      - 'POSTGRES_DB=dev_db'
    ports:
      - '5432:5432'
    # 开发环境可以使用临时卷
    volumes:
      - postgres_dev_data:/var/lib/postgresql/data

volumes:
  postgres_dev_data:
yaml
services:
  postgres:
    image: 'postgres:15.3'
    environment:
      - 'POSTGRES_USER=${DB_USER}'
      - 'POSTGRES_PASSWORD=${DB_PASSWORD}'
      - 'POSTGRES_DB=${DB_NAME}'
    ports:
      - '5432:5432'
    # 生产环境使用持久化存储
    volumes:
      - /var/lib/postgresql/data:/var/lib/postgresql/data

3. 配置管理

kotlin
@ConfigurationProperties(prefix = "app.database")
data class DatabaseConfig(
    val maxConnections: Int = 10,
    val connectionTimeout: Duration = Duration.ofSeconds(30),
    val enableSsl: Boolean = false
)

@Configuration
@EnableConfigurationProperties(DatabaseConfig::class)
class DataSourceConfiguration(
    private val databaseConfig: DatabaseConfig
) {
    
    @Bean
    @Primary
    fun dataSource(): DataSource {
        return HikariDataSource().apply {
            maximumPoolSize = databaseConfig.maxConnections 
            connectionTimeout = databaseConfig.connectionTimeout.toMillis() 
            
            if (databaseConfig.enableSsl) { 
                addDataSourceProperty("ssl", "true") 
                addDataSourceProperty("sslmode", "require") 
            }
        }
    }
}

总结 🎉

Spring Boot 的 Docker Compose 集成为开发者提供了一种优雅的方式来管理开发环境中的依赖服务。通过合理的配置和最佳实践:

简化环境搭建:一条命令启动所有依赖服务
提高开发效率:自动化的连接配置和生命周期管理
增强团队协作:统一的开发环境配置
支持复杂场景:多应用共享服务、自定义连接参数等

TIP

记住,Docker Compose 集成主要用于开发和测试环境。在生产环境中,建议使用专门的容器编排工具如 Kubernetes 或 Docker Swarm。

通过掌握这些技能,你将能够更高效地进行 Spring Boot 应用开发,专注于业务逻辑而不是环境配置! 🚀