前言

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
// DemoApplication.java
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

// HelloController.java
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello, 七月小站!";
}
}
1
2
3
4
5
6
7
8
9
10
11
# application.yml
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
# ===== 阶段 1:构建(Maven + JDK)=====
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

# ===== 阶段 2:运行(只需 JRE)=====
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

# JVM 调优参数
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
# 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
# docker-compose.yml
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
# application.yml(默认配置)
spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
1
2
3
4
5
6
7
8
9
10
11
12
13
# application-docker.yml(Docker 环境专用)
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
# Dockerfile (适用于所有微服务)
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
# docker-compose.yml — 基础设施
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" # gRPC 端口
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
# gateway-service/src/main/resources/application.yml
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
# user-service/src/main/resources/application.yml
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
# docker-compose.yml — 完整微服务栈
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 前端网关 =====
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;

# API 请求转发到 Gateway
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;
}

# Nacos 控制台
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
// Jenkinsfile
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
# deploy.sh — SpringCloud 微服务一键部署
set -euo pipefail

echo "===== 开始部署 SpringCloud 微服务 ====="

# 1. 拉取最新代码
git pull origin main

# 2. 构建所有服务
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

# 3. 停止旧容器
echo "[2/3] 停止旧容器..."
docker compose down

# 4. 启动所有服务
echo "[3/3] 启动服务..."
docker compose up -d

# 5. 等待服务就绪
echo "等待 Nacos 就绪..."
sleep 15

echo ""
echo "===== 部署完成 ====="
docker compose ps

echo ""
echo "Nacos 控制台: http://localhost:8848/nacos"
echo "API 网关: http://localhost:8080"

七、生产环境注意事项

  1. 不要硬编码密码:使用 Docker Secrets 或环境变量 + .env 文件
  2. 设置资源限制:避免一个服务吃光内存
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
  1. 日志收集:配置日志驱动,将日志发送到 ELK 或 Loki
1
2
3
4
5
6
7
services:
user-service:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
  1. 健康检查:每个关键服务配上 healthcheck
  2. 网络隔离:将数据库放在不对外暴露的内部网络
  3. JVM 调优:在容器中合理设置 -Xmx-Xms

结语

将 SpringBoot/SpringCloud 微服务 Docker 化后,你将获得:

  • 统一环境:开发/测试/生产环境完全一致
  • 一键部署docker compose up -d 就搞定
  • 弹性伸缩:配合 K8s 实现自动扩缩容
  • 快速回滚:切换到上一个镜像版本即可

容器化不是目的,而是让微服务管理更简单的手段。从 SpringBoot 单体开始,逐步演进到完整的容器化微服务体系。🐳