前言
Java 生态中,SpringBoot + SpringCloud 是微服务架构的主流选择。将这套体系 Docker 化,可以实现一键部署、环境统一、弹性伸缩。
本文通过完整实战,带你将 SpringBoot 单体和 SpringCloud 微服务分别容器化部署。
一、SpringBoot 项目 Docker 化
1.1 示例 SpringBoot 项目
一个典型的 SpringBoot 项目结构:
1 2 3 4 5 6 7 8 9 10
| demo-api/ ├── pom.xml ├── src/ │ └── main/ │ ├── java/com/example/demo/ │ │ ├── DemoApplication.java │ │ └── controller/ │ │ └── HelloController.java │ └── resources/ │ └── application.yml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
@RestController public class HelloController { @GetMapping("/hello") public String hello() { return "Hello, 七月小站!"; } }
|
1 2 3 4 5 6 7 8 9 10 11
| server: port: 8080
spring: application: name: demo-api datasource: url: jdbc:mysql://${DB_HOST:localhost}:3306/demo_db username: ${DB_USER:root} password: ${DB_PASSWORD:root}
|
1.2 Dockerfile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline -B
COPY src ./src RUN mvn clean package -DskipTests -B
FROM eclipse-temurin:17-jre-alpine
RUN addgroup -g 1000 app && \ adduser -u 1000 -G app -s /bin/sh -D app
WORKDIR /app COPY --from=builder /build/target/*.jar app.jar
USER app EXPOSE 8080
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
|
1.3 构建与运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| docker build -t demo-api:1.0 .
docker run -d \ --name demo-api \ -p 8080:8080 \ -e DB_HOST=192.168.1.100 \ -e DB_USER=root \ -e DB_PASSWORD=secret123 \ demo-api:1.0
curl http://localhost:8080/hello
|
二、Docker Compose 单机部署
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| version: "3.8"
services: mysql: image: mysql:8.0 container_name: demo-mysql environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: demo_db volumes: - mysql-data:/var/lib/mysql - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql ports: - "3306:3306" healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5 networks: - demo-net
app: build: . container_name: demo-api ports: - "8080:8080" environment: - DB_HOST=mysql - DB_USER=root - DB_PASSWORD=root123 - SPRING_PROFILES_ACTIVE=docker depends_on: mysql: condition: service_healthy restart: unless-stopped networks: - demo-net
volumes: mysql-data:
networks: demo-net: driver: bridge
|
启动:
1 2 3 4 5 6 7 8
| docker compose up -d --build
docker compose logs -f app
curl http://localhost:8080/hello
|
三、多环境配置
3.1 SpringBoot 多环境配置
1 2 3 4
| spring: profiles: active: ${SPRING_PROFILES_ACTIVE:dev}
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| server: port: 8080
spring: datasource: url: jdbc:mysql://${DB_HOST}:3306/demo_db?useSSL=false&allowPublicKeyRetrieval=true username: ${DB_USER} password: ${DB_PASSWORD}
logging: level: root: INFO
|
3.2 通过环境变量注入
1 2 3 4 5 6 7
| docker run -d \ -e SPRING_PROFILES_ACTIVE=docker \ -e DB_HOST=production-db.example.com \ -e DB_USER=prod_user \ -e DB_PASSWORD=SuperSecret123 \ -e JAVA_OPTS="-Xms512m -Xmx1024m" \ demo-api:1.0
|
四、SpringCloud 微服务架构 Docker 化
4.1 典型微服务架构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| ┌─────────────┐ │ Nginx │ │ (网关入口) │ └──────┬──────┘ │ ┌──────▼──────┐ │ Gateway │ ← Spring Cloud Gateway │ (路由网关) │ └──────┬──────┘ │ ┌────────────┼────────────┐ │ │ │ ┌─────▼─────┐ ┌───▼────┐ ┌────▼─────┐ │ User Svc │ │Order Svc│ │Product Svc│ │ 用户服务 │ │订单服务 │ │ 商品服务 │ └─────┬─────┘ └───┬────┘ └────┬─────┘ │ │ │ ┌─────▼────────────▼───────────▼─────┐ │ Nacos (注册中心+配置中心) │ └────────────────────────────────────┘
|
4.2 各服务 Dockerfile(统一模板)
所有 SpringCloud 微服务使用相同的 Dockerfile:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| FROM maven:3.9-eclipse-temurin-17 AS builder WORKDIR /build COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn clean package -DskipTests -B
FROM eclipse-temurin:17-jre-alpine RUN addgroup -g 1000 app && adduser -u 1000 -G app -D app WORKDIR /app COPY --from=builder /build/target/*.jar app.jar USER app EXPOSE 8080 ENV JAVA_OPTS="-Xms128m -Xmx256m" ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
|
4.3 Nacos 注册中心
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| services: nacos: image: nacos/nacos-server:v2.3.0 container_name: nacos environment: - MODE=standalone - NACOS_AUTH_ENABLE=true - NACOS_AUTH_IDENTITY_KEY=serverIdentity - NACOS_AUTH_IDENTITY_VALUE=security - NACOS_AUTH_TOKEN=SecretKey012345678901234567890123456789012345678901234567890123456789 ports: - "8848:8848" - "9848:9848" volumes: - nacos-data:/home/nacos/data restart: unless-stopped
mysql-nacos: image: mysql:8.0 container_name: mysql-nacos environment: MYSQL_ROOT_PASSWORD: nacos123 MYSQL_DATABASE: nacos volumes: - mysql-nacos-data:/var/lib/mysql
|
4.4 微服务配置示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| server: port: 8080
spring: application: name: gateway-service cloud: nacos: discovery: server-addr: ${NACOS_HOST:nacos}:8848 namespace: ${NACOS_NAMESPACE:} config: server-addr: ${NACOS_HOST:nacos}:8848 gateway: routes: - id: user-service uri: lb://user-service predicates: - Path=/api/users/** - id: order-service uri: lb://order-service predicates: - Path=/api/orders/** - id: product-service uri: lb://product-service predicates: - Path=/api/products/**
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| server: port: 8080
spring: application: name: user-service cloud: nacos: discovery: server-addr: ${NACOS_HOST:nacos}:8848 config: server-addr: ${NACOS_HOST:nacos}:8848 datasource: url: jdbc:mysql://${DB_HOST:mysql-user}:3306/user_db username: ${DB_USER:root} password: ${DB_PASSWORD:root}
|
4.5 完整 Compose 编排
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
| version: "3.8"
services: nacos: image: nacos/nacos-server:v2.3.0 container_name: nacos environment: - MODE=standalone ports: - "8848:8848" - "9848:9848" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8848/nacos/v1/console/health"] interval: 10s timeout: 5s retries: 10 networks: - micro-net
mysql-user: image: mysql:8.0 container_name: mysql-user environment: MYSQL_ROOT_PASSWORD: user123 MYSQL_DATABASE: user_db volumes: - mysql-user-data:/var/lib/mysql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5 networks: - micro-net
mysql-order: image: mysql:8.0 container_name: mysql-order environment: MYSQL_ROOT_PASSWORD: order123 MYSQL_DATABASE: order_db volumes: - mysql-order-data:/var/lib/mysql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5 networks: - micro-net
redis: image: redis:7-alpine container_name: micro-redis command: redis-server --appendonly yes volumes: - redis-data:/data networks: - micro-net
user-service: build: ./user-service container_name: user-service environment: - NACOS_HOST=nacos - DB_HOST=mysql-user - DB_USER=root - DB_PASSWORD=user123 - JAVA_OPTS=-Xms128m -Xmx256m depends_on: nacos: condition: service_healthy mysql-user: condition: service_healthy restart: unless-stopped networks: - micro-net
order-service: build: ./order-service container_name: order-service environment: - NACOS_HOST=nacos - DB_HOST=mysql-order - DB_USER=root - DB_PASSWORD=order123 - JAVA_OPTS=-Xms128m -Xmx256m depends_on: nacos: condition: service_healthy mysql-order: condition: service_healthy restart: unless-stopped networks: - micro-net
product-service: build: ./product-service container_name: product-service environment: - NACOS_HOST=nacos - JAVA_OPTS=-Xms128m -Xmx256m depends_on: nacos: condition: service_healthy restart: unless-stopped networks: - micro-net
gateway-service: build: ./gateway-service container_name: gateway-service ports: - "8080:8080" environment: - NACOS_HOST=nacos - JAVA_OPTS=-Xms128m -Xmx256m depends_on: nacos: condition: service_healthy restart: unless-stopped networks: - micro-net
nginx: image: nginx:alpine container_name: micro-nginx ports: - "80:80" volumes: - ./nginx.conf:/etc/nginx/nginx.conf:ro depends_on: - gateway-service restart: unless-stopped networks: - micro-net
volumes: mysql-user-data: mysql-order-data: redis-data: nacos-data:
networks: micro-net: driver: bridge
|
4.6 Nginx 配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| events { worker_connections 1024; }
http { upstream gateway { server gateway-service:8080; }
server { listen 80; location /api { proxy_pass http://gateway; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /nacos { proxy_pass http://nacos:8848/nacos; } } }
|
五、Jenkins + Docker 自动化构建(CI/CD)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| pipeline { agent any environment { DOCKER_REGISTRY = "registry.example.com" IMAGE_NAME = "user-service" IMAGE_TAG = "${env.BUILD_NUMBER}" } stages { stage('Checkout') { steps { git url: 'https://gitee.com/team/user-service.git' } } stage('Build') { steps { sh 'docker build -t ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} .' sh 'docker tag ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} ${DOCKER_REGISTRY}/${IMAGE_NAME}:latest' } } stage('Push') { steps { sh 'docker push ${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}' sh 'docker push ${DOCKER_REGISTRY}/${IMAGE_NAME}:latest' } } stage('Deploy') { steps { sh ''' ssh deploy@server " docker pull ${DOCKER_REGISTRY}/${IMAGE_NAME}:latest && cd /opt/microservices && docker compose up -d --no-deps user-service " ''' } } } }
|
六、一键部署脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #!/bin/bash
set -euo pipefail
echo "===== 开始部署 SpringCloud 微服务 ====="
git pull origin main
echo "[1/3] 构建 Docker 镜像..." for service in user-service order-service product-service gateway-service; do echo " 构建 $service ..." cd "./$service" docker build -t "$service:latest" . cd .. done
echo "[2/3] 停止旧容器..." docker compose down
echo "[3/3] 启动服务..." docker compose up -d
echo "等待 Nacos 就绪..." sleep 15
echo "" echo "===== 部署完成 =====" docker compose ps
echo "" echo "Nacos 控制台: http://localhost:8848/nacos" echo "API 网关: http://localhost:8080"
|
七、生产环境注意事项
- 不要硬编码密码:使用 Docker Secrets 或环境变量 +
.env 文件
- 设置资源限制:避免一个服务吃光内存
1 2 3 4 5 6 7 8 9 10
| services: user-service: deploy: resources: limits: cpus: '1' memory: 512M reservations: cpus: '0.5' memory: 256M
|
- 日志收集:配置日志驱动,将日志发送到 ELK 或 Loki
1 2 3 4 5 6 7
| services: user-service: logging: driver: "json-file" options: max-size: "10m" max-file: "3"
|
- 健康检查:每个关键服务配上
healthcheck
- 网络隔离:将数据库放在不对外暴露的内部网络
- JVM 调优:在容器中合理设置
-Xmx 和 -Xms
结语
将 SpringBoot/SpringCloud 微服务 Docker 化后,你将获得:
- ✅ 统一环境:开发/测试/生产环境完全一致
- ✅ 一键部署:
docker compose up -d 就搞定
- ✅ 弹性伸缩:配合 K8s 实现自动扩缩容
- ✅ 快速回滚:切换到上一个镜像版本即可
容器化不是目的,而是让微服务管理更简单的手段。从 SpringBoot 单体开始,逐步演进到完整的容器化微服务体系。🐳