第 19 章 · Docker/K8s 微服务交付与模块结业
本章目标:为 svc-spring-demo 各服务编写多阶段 Dockerfile 与统一镜像规范;使用 docker-compose 编排应用与基础设施;在 deploy/k8s/ 编写 Deployment / Service / Ingress YAML;配置 ConfigMap / Secret 与健康探针;理解微服务交付与单体(spring-boot-web ch19)的差异;完成 Python/Java 微服务栈对比(§19.11)、spring-cloud-web 全模块回顾与 100 分进阶验收表;说明与 xiaozi-cloud、architecture 模块的衔接路径。
学时建议:8~10 小时(含 4 小时容器构建与 K8s 部署演练)
前置:spring-cloud-web ch01~ch18;spring-boot-web ch19 容器基础;ch12 毕业项目;选修 xiaozi-cloud ch01~ch03。
19.1 微服务交付 vs 单体交付
spring-boot-web ch19 spring-cloud-web ch19(本章)
shop-spring-demo svc-spring-demo
1 个 Dockerfile N 个服务 Dockerfile + 公共父 POM 构建
1 个 Deployment gateway / bff / order / product / ...
1 个 MySQL 每服务独立库 + 共享中间件
| 维度 | 单体 | 微服务 |
|---|---|---|
| 镜像数量 | 1 | 6~10+ |
| 配置 | 单 application.yml | Nacos + ConfigMap |
| 启动顺序 | app 依赖 DB | infra → 注册中心 → 业务服务 → gateway |
| 滚动更新 | 单 Deployment | 多 Deployment 协调(ch15 灰度) |
| 资源 | 单 Pod 2Gi | 按服务分配 requests/limits |
目标拓扑(虚构 api.example.com):
Internet
│
▼ Ingress (api.example.com)
┌─────────────┐
│ gateway-svc │ ×2
└──────┬──────┘
│ ClusterIP
┌──────┴──────────────────────────────┐
│ bff │ order │ product │ user │ auth │
└─────────────────────────────────────┘
│
┌──────┴──────────────────────────────┐
│ Nacos │ MySQL×N │ Redis │ RabbitMQ │
│ Zipkin │ Seata TC │ Prometheus │
└─────────────────────────────────────┘
19.2 多模块 Maven 构建策略
方案 A:每服务独立 Dockerfile(推荐教学)
svc-spring-demo/
├── order-svc/Dockerfile
├── product-svc/Dockerfile
├── gateway-svc/Dockerfile
└── pom.xml # 父 POM
方案 B:根目录统一构建脚本
# scripts/build-images.sh(虚构)
services=(gateway-svc bff-svc order-svc product-svc user-svc auth-svc)
for svc in "${services[@]}"; do
docker build -f "$svc/Dockerfile" -t "registry.example.com/svc-spring/$svc:${GIT_SHA}" .
done
19.3 服务 Dockerfile 模板
以 order-svc/Dockerfile 为例(衔接 spring-boot-web ch19):
# ── 构建阶段:在仓库根目录构建(-f order-svc/Dockerfile .)
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /build
COPY pom.xml ./
COPY svc-common/pom.xml svc-common/
COPY order-svc/pom.xml order-svc/
# 仅下载依赖层(利用缓存)
RUN mvn -q -pl order-svc -am dependency:go-offline -DskipTests
COPY svc-common svc-common
COPY order-svc order-svc
RUN mvn -q -pl order-svc -am package -DskipTests
# ── 运行阶段
FROM eclipse-temurin:21-jre-alpine AS runtime
RUN addgroup -g 1000 app && adduser -u 1000 -G app -D app
WORKDIR /app
COPY --from=builder /build/order-svc/target/order-svc-*.jar app.jar
USER app
EXPOSE 8080 9090
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
# alpine 镜像的 wget 来自 busybox;若改用非 alpine 基础镜像(如 jammy)需先 apt 安装 wget/curl
HEALTHCHECK --interval=30s --timeout=3s --start-period=90s \
CMD wget -qO- http://127.0.0.1:9090/actuator/health/liveness || exit 1
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
健康检查前提:9090管理端口与liveness/readiness探针端点来自 ch18 的observability.yml(management.server.port: 9090+probes.enabled: true);二者缺一,HEALTHCHECK 会持续失败。
.dockerignore(仓库根):
**/target/
.git/
.idea/
*.iml
.env
logs/
deploy/
| 实践 | 微服务特别注意 |
|---|---|
| 非 root | 每个镜像统一 UID 1000 |
| 多阶段 | 镜像不含源码与 JDK |
| 健康检查 | management 端口 9090(ch18) |
| 标签 | registry.example.com/svc-spring/order-svc:1.0.0 |
19.4 docker-compose 分层编排
沿用 ch12 结构,拆为三层:
# deploy/docker-compose.infra.yml — Nacos, MySQL, Redis, RabbitMQ, Zipkin, Seata, Prometheus
# deploy/docker-compose.apps.yml — 各业务镜像
# deploy/docker-compose.observability.yml — ch18 Grafana(可选)
docker-compose.apps.yml 片段:
services:
gateway-svc:
image: registry.example.com/svc-spring/gateway-svc:${VERSION:-latest}
ports: ["8443:8443"]
environment:
SPRING_PROFILES_ACTIVE: docker
NACOS_SERVER: nacos:8848
depends_on:
nacos: { condition: service_healthy }
healthcheck:
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:9090/actuator/health/readiness"]
interval: 15s
timeout: 5s
retries: 10
order-svc:
image: registry.example.com/svc-spring/order-svc:${VERSION:-latest}
environment:
SPRING_PROFILES_ACTIVE: docker
NACOS_SERVER: nacos:8848
depends_on: [nacos, mysql-order, rabbitmq]
deploy:
replicas: 2 # compose v3 swarm 模式;教学可用 scale
启动顺序:
docker compose -f deploy/docker-compose.infra.yml up -d
# 等待 Nacos 健康
docker compose -f deploy/docker-compose.apps.yml up -d
docker compose -f deploy/docker-compose.apps.yml run --rm scripts/smoke-svc.sh
19.5 Kubernetes 核心清单
Namespace svc-spring(ch13 Istio 选修):
# deploy/k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: svc-spring
labels:
istio-injection: enabled # 选修 ch13
order-svc Deployment:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-svc
namespace: svc-spring
spec:
replicas: 2
selector:
matchLabels: { app: order-svc }
template:
metadata:
labels: { app: order-svc, version: v1 }
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
prometheus.io/path: "/actuator/prometheus"
spec:
containers:
- name: order-svc
image: registry.example.com/svc-spring/order-svc:1.0.0
ports:
- { name: http, containerPort: 8080 }
- { name: mgmt, containerPort: 9090 }
envFrom:
- configMapRef: { name: svc-spring-config }
- secretRef: { name: svc-spring-secrets }
resources:
requests: { memory: "512Mi", cpu: "250m" }
limits: { memory: "1Gi", cpu: "1000m" }
livenessProbe:
httpGet: { path: /actuator/health/liveness, port: mgmt }
initialDelaySeconds: 90
readinessProbe:
httpGet: { path: /actuator/health/readiness, port: mgmt }
initialDelaySeconds: 30
apiVersion: v1
kind: Service
metadata:
name: order-svc
namespace: svc-spring
spec:
selector: { app: order-svc }
ports: [{ port: 8080, targetPort: http }]
Gateway Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata: