Spring Boot部署:使用Docker与Kubernetes
在现代应用开发中,Spring Boot凭借其“约定优于配置”的理念和快速开发特性,成为Java开发者的首选框架。但随着微服务架构的普及,Spring Boot应用的部署面临着环境一致性、** scalability**、运维复杂度等多重挑战。Docker的出现解决了“开发环境能跑,生产环境跑不了”的痛点,而Kubernetes则进一步提供了容器编排、自动扩缩容、服务发现等核心能力,让Spring Boot应用的部署和运维进入自动化时代。
本文将从实战角度出发,详细讲解如何用Docker容器化Spring Boot应用,以及如何通过Kubernetes实现规模化部署和管理。全文包含大量代码示例、配置文件和操作命令,覆盖从基础镜像构建到生产级部署的完整流程,适合Spring Boot开发者、运维工程师和DevOps实践者参考。
一、Spring Boot部署的挑战与解决方案
在传统部署模式中,Spring Boot应用的交付和运维面临诸多问题,这些问题在微服务架构下被进一步放大。
1. 传统部署的痛点
- 环境不一致:开发、测试、生产环境的JDK版本、依赖库、配置参数差异,导致“在我电脑上能跑”的经典问题。某团队曾因测试环境使用JDK 11而生产环境为JDK 8,导致Spring Boot 2.6+应用启动失败(依赖JDK 11特性)。
- 部署流程繁琐:手动上传JAR包、配置环境变量、启停服务,步骤多易出错。尤其在多实例部署时,一致性难以保证。
- 资源利用率低:每个应用独占虚拟机资源,空闲时资源浪费严重,扩容需手动申请和配置虚拟机。
- 运维成本高:服务监控、日志收集、故障恢复依赖人工操作,缺乏自动化机制,夜间故障响应延迟。
2. Docker+Kubernetes的解决方案
- 环境一致性:Docker镜像包含应用及所有依赖,确保“一次构建,到处运行”,消除环境差异。
- 轻量级隔离:容器共享主机内核但拥有独立文件系统,比虚拟机更轻量,资源利用率提升30%+。
- 自动化编排:Kubernetes实现容器的自动部署、扩缩容、滚动更新和自愈,减少人工干预。
- 服务治理:内置服务发现、负载均衡、配置管理,简化微服务架构的运维复杂度。
某电商平台的实践显示:采用Docker+Kubernetes部署Spring Boot微服务后,环境问题减少70%,部署时间从小时级缩短至分钟级,服务器资源利用率从40%提升至75%。
二、Docker基础:容器化Spring Boot应用
Docker是一种轻量级容器技术,通过镜像打包应用及依赖,实现跨环境的一致运行。本节将从Docker核心概念出发,详解Spring Boot应用的容器化过程。
1. Docker核心概念
- 镜像(Image):应用及其依赖的只读模板,如Spring Boot应用的JAR包+JRE环境。
- 容器(Container):镜像的运行实例,可被创建、启动、停止、删除,是动态的。
- Dockerfile:用于构建镜像的文本文件,包含一系列构建指令。
- 仓库(Repository):存储和分发镜像的平台,如Docker Hub、私有仓库。
2. 准备Spring Boot应用
首先创建一个简单的Spring Boot应用作为示例,后续将其容器化。
(1)创建Spring Boot项目
使用Spring Initializr创建项目,添加Spring Web
依赖,项目结构如下:
spring-boot-docker-demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── controller/
│ │ │ │ └── DemoController.java
│ │ │ └── SpringBootDockerDemoApplication.java
│ │ └── resources/
│ │ └── application.properties
├── pom.xml
└── Dockerfile
(2)编写测试接口
在DemoController
中添加一个简单接口,用于验证应用运行状态:
package com.example.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@GetMapping("/hello")
public String hello() {
return "Hello, Spring Boot with Docker!";
}
@GetMapping("/health")
public String health() {
return "OK";
}
}
(3)配置端口
在application.properties
中设置服务端口:
server.port=8080
(4)打包应用
执行Maven命令打包生成JAR包:
mvn clean package -Dmaven.test.skip=true
打包成功后,在target
目录下生成spring-boot-docker-demo-0.0.1-SNAPSHOT.jar
。
3. 编写基础Dockerfile
Dockerfile是构建镜像的“食谱”,定义了从基础镜像到应用部署的所有步骤。为Spring Boot应用编写基础Dockerfile:
# 基础镜像:使用官方OpenJDK 17镜像(Alpine版本体积更小)
FROM openjdk:17-jdk-slim
# 维护者信息
LABEL maintainer="yourname@example.com"
# 容器内工作目录
WORKDIR /app
# 将本地JAR包复制到容器内
COPY target/spring-boot-docker-demo-0.0.1-SNAPSHOT.jar app.jar
# 暴露应用端口(与application.properties中一致)
EXPOSE 8080
# 启动命令:运行Spring Boot应用
ENTRYPOINT ["java", "-jar", "app.jar"]
指令说明:
FROM
:指定基础镜像,这里选择轻量级的openjdk:17-jdk-slim
(比openjdk:17
体积小约50%)。WORKDIR
:设置容器内的工作目录,后续命令将基于此目录执行。COPY
:将主机的JAR包复制到容器内的/app
目录。EXPOSE
:声明容器运行时暴露的端口(仅作文档说明,实际需通过-p
映射)。ENTRYPOINT
:容器启动时执行的命令,这里通过java -jar
启动Spring Boot应用。
4. 构建并运行Docker镜像
(1)构建镜像
在Dockerfile所在目录执行以下命令构建镜像(注意末尾的.
表示当前目录):
docker build -t spring-boot-docker-demo:v1 .
-t
:为镜像指定标签(格式为仓库名:标签
),方便后续引用。- 构建成功后,通过
docker images
查看镜像:
docker images | grep spring-boot-docker-demo
# 输出:spring-boot-docker-demo v1 abc12345 2分钟前 420MB
(2)运行容器
使用docker run
命令启动容器,并将容器的8080端口映射到主机的8080端口:
docker run -d -p 8080:8080 --name demo-app spring-boot-docker-demo:v1
-d
:后台运行容器。-p 8080:8080
:将主机的8080端口映射到容器的8080端口(主机端口:容器端口)。--name
:为容器指定名称,方便管理。
(3)验证应用运行
通过docker ps
查看运行中的容器:
docker ps | grep demo-app
# 输出容器ID、状态等信息
访问http://localhost:8080/hello
,若返回Hello, Spring Boot with Docker!
,说明应用部署成功。
(4)查看容器日志
通过docker logs
命令查看应用日志:
docker logs -f demo-app # -f:实时跟踪日志
可看到Spring Boot的启动日志,确认应用正常启动。
(5)停止并删除容器
# 停止容器
docker stop demo-app
# 删除容器
docker rm demo-app
# 删除镜像(可选)
docker rmi spring-boot-docker-demo:v1
5. 优化Docker镜像:多阶段构建
基础镜像构建的Spring Boot镜像体积较大(通常400MB+),主要原因是包含了完整的JDK和构建依赖。通过多阶段构建可显著减小镜像体积。
多阶段构建的原理:
- 第一阶段(构建阶段):使用包含Maven/Gradle的镜像编译源码、打包JAR包。
- 第二阶段(运行阶段):仅复制第一阶段的JAR包到轻量级基础镜像(如JRE),不含构建工具。
优化后的Dockerfile:
# 第一阶段:构建Spring Boot应用
FROM maven:3.8.5-openjdk-17 AS builder
# 设置工作目录
WORKDIR /app
# 复制pom.xml和源码
COPY pom.xml .
COPY src ./src
# 打包应用(跳过测试)
RUN mvn clean package -Dmaven.test.skip=true
# 第二阶段:运行应用(仅保留必要文件)
FROM openjdk:17-jre-slim
WORKDIR /app
# 从构建阶段复制JAR包到当前阶段
COPY --from=builder /app/target/spring-boot-docker-demo-0.0.1-SNAPSHOT.jar app.jar
# 非root用户运行(安全最佳实践)
RUN addgroup --system --gid 1001 appgroup && \
adduser --system --uid 1001 --gid 1001 appuser
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
优化点说明:
- 多阶段构建:
AS builder
标记第一阶段,第二阶段通过--from=builder
复制JAR包,避免将Maven依赖和源码带入最终镜像。 - 使用JRE而非JDK:运行Spring Boot应用只需JRE(Java Runtime Environment),无需JDK(Java Development Kit),镜像体积减少约30%。
- 非root用户运行:创建
appuser
并切换为该用户运行容器,降低容器被入侵后的权限风险。
构建优化后的镜像:
docker build -t spring-boot-docker-demo:v2 .
通过docker images
对比体积:v1约420MB,v2约280MB,体积减少33%。
6. 高级Dockerfile配置:自定义JVM参数
Spring Boot应用的性能与JVM参数密切相关,可在Dockerfile中通过ENTRYPOINT
自定义JVM参数,如堆内存大小、GC策略等。
带JVM参数的Dockerfile片段:
# 自定义JVM参数(根据容器内存调整)
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseG1GC"
# 启动命令中引用环境变量
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
运行时动态调整JVM参数:
启动容器时通过-e
覆盖环境变量,灵活调整JVM参数:
docker run -d -p 8080:8080 -e JAVA_OPTS="-Xms512m -Xmx1024m" --name demo-app-v2 spring-boot-docker-demo:v2
三、Kubernetes基础:容器编排核心概念
Kubernetes(简称K8s)是开源的容器编排平台,提供容器的部署、扩展、负载均衡、自愈等能力。本节介绍K8s核心概念及与Spring Boot部署相关的核心组件。
1. K8s核心概念
- Pod:K8s的最小部署单元,包含一个或多个容器,共享网络和存储。Spring Boot应用通常一个Pod运行一个容器。
- Deployment:管理Pod的创建和更新,定义Pod的副本数、更新策略等,是部署无状态应用的首选。
- Service:定义Pod的访问方式,提供固定访问地址和负载均衡,解决Pod动态IP变化的问题。
- ConfigMap:存储非敏感配置数据(如应用.properties),可被Pod挂载为文件或环境变量。
- Secret:存储敏感信息(如数据库密码、API密钥),与ConfigMap类似,但数据经过Base64编码。
- Namespace:提供资源隔离,可按环境(dev/test/prod)或团队划分命名空间。
- Node:K8s集群的工作节点(物理机或虚拟机),Pod运行在Node上。
- Controller Manager:运行各种控制器(如Deployment控制器、StatefulSet控制器),实现自愈、扩缩容等功能。
- kube-proxy:每个Node上的网络代理,负责Service的负载均衡和流量转发。
2. K8s部署架构
Spring Boot应用在K8s中的典型部署架构如下:
用户 → Service(负载均衡) → Deployment管理的多个Pod(Spring Boot容器) → 存储(ConfigMap/Secret/Volume)
- 用户通过Service的固定IP或域名访问应用。
- Service将请求转发到后端健康的Pod(通过标签选择器关联)。
- Deployment确保Pod的副本数符合预期,若Pod故障则自动重建。
- 应用配置通过ConfigMap/Secret注入,无需硬编码在镜像中。
3. 安装K8s环境(Minikube)
为了本地测试,推荐使用Minikube搭建单节点K8s集群:
(1)安装Minikube
minikube start --driver=docker # 使用Docker驱动
(2)安装kubectl
kubectl是K8s的命令行工具,用于与集群交互:
- 安装指南:https://kubernetes.io/docs/tasks/tools/install-kubectl/
- 验证安装:
kubectl get nodes # 查看节点状态,输出Ready表示集群正常
四、Spring Boot在Kubernetes中的部署实践
本节通过完整示例讲解如何将Spring Boot应用部署到K8s,包括Deployment配置、Service暴露、配置管理等核心步骤。
1. 推送Docker镜像到仓库
K8s集群中的Node需要拉取Docker镜像,因此需将镜像推送到可访问的仓库(如Docker Hub、私有仓库)。
(1)登录Docker Hub
docker login # 输入Docker Hub用户名和密码
(2)为镜像打标签
镜像标签需符合仓库格式(仓库用户名/镜像名:标签
):
docker tag spring-boot-docker-demo:v2 yourusername/spring-boot-docker-demo:v2
(3)推送镜像
docker push yourusername/spring-boot-docker-demo:v2
推送成功后,可在Docker Hub上看到镜像。
2. 创建Deployment配置
Deployment是部署Spring Boot应用的核心资源,定义了Pod模板、副本数、更新策略等。
(1)基础Deployment配置(spring-boot-deployment.yaml
)
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-boot-demo # Deployment名称
namespace: default # 命名空间(默认default)
labels:
app: spring-boot-demo # 标签,用于关联Service和Pod
spec:
replicas: 2 # 副本数(2个Pod实例)
selector:
matchLabels:
app: spring-boot-demo # 匹配Pod的标签
template: # Pod模板
metadata:
labels:
app: spring-boot-demo # Pod标签,需与selector匹配
spec:
containers:
- name: spring-boot-demo # 容器名称
image: yourusername/spring-boot-docker-demo:v2 # 镜像地址(替换为你的仓库)
ports:
- containerPort: 8080 # 容器暴露的端口(与应用端口一致)
resources: # 资源请求和限制
requests:
memory: "256Mi" # 最小内存需求
cpu: "200m" # 最小CPU需求(1核=1000m)
limits:
memory: "512Mi" # 最大内存限制
cpu: "500m" # 最大CPU限制
env: # 环境变量
- name: SPRING_PROFILES_ACTIVE
value: "prod" # 激活prod环境配置
(2)部署Deployment
kubectl apply -f spring-boot-deployment.yaml
(3)查看部署状态
# 查看Deployment
kubectl get deployments | grep spring-boot-demo
# 查看Pod
kubectl get pods -l app=spring-boot-demo # -l:按标签筛选
# 输出2个Pod,状态为Running表示部署成功
(4)查看Pod详情
kubectl describe pod <pod-name> # 替换为实际Pod名称
可查看Pod的IP、启动日志、事件等,排查部署问题。
3. 创建Service暴露应用
Pod的IP是动态变化的,通过Service为应用提供固定访问地址,并实现负载均衡。
(1)Service配置(spring-boot-service.yaml
)
apiVersion: v1
kind: Service
metadata:
name: spring-boot-demo-service # Service名称
namespace: default
spec:
selector:
app: spring-boot-demo # 关联标签为app=spring-boot-demo的Pod
ports:
- port: 80 # Service暴露的端口
targetPort: 8080 # 映射到Pod的端口(与容器端口一致)
type: NodePort # 类型:NodePort(外部可访问)
# 可选:clusterIP(仅集群内部访问)或LoadBalancer(云厂商负载均衡)
(2)创建Service
kubectl apply -f spring-boot-service.yaml
(3)查看Service
kubectl get services | grep spring-boot-demo-service
# 输出:spring-boot-demo-service NodePort 10.96.xx.xx <none> 80:31234/TCP 5m
# 31234是NodePort(随机分配,范围30000-32767)
(4)访问应用
通过Minikube获取访问地址:
minikube service spring-boot-demo-service --url
# 输出:http://192.168.49.2:31234 (Minikube节点IP:NodePort)
访问http://192.168.49.2:31234/hello
,验证应用可访问。Service会自动将请求负载均衡到2个Pod实例。
4. 使用ConfigMap管理配置
Spring Boot应用的配置(如数据库地址、日志级别)应通过ConfigMap外部化管理,避免硬编码在镜像中。
(1)创建ConfigMap(spring-boot-configmap.yaml
)
apiVersion: v1
kind: ConfigMap
metadata:
name: spring-boot-config
namespace: default
data:
# 配置文件内容(键值对形式)
application-prod.properties: |
server.port=8080
logging.level.root=INFO
logging.level.com.example=DEBUG
app.message=Hello from Kubernetes ConfigMap!
这里定义了application-prod.properties
,与Spring Boot的prod
环境配置对应。
(2)创建ConfigMap
kubectl apply -f spring-boot-configmap.yaml
(3)在Deployment中挂载ConfigMap
修改spring-boot-deployment.yaml
,添加volumeMounts
和volumes
:
spec:
template:
spec:
containers:
- name: spring-boot-demo
# ... 其他配置省略 ...
volumeMounts:
- name: config-volume # 与volumes.name对应
mountPath: /app/config # 容器内挂载路径
readOnly: true # 只读权限
volumes:
- name: config-volume
configMap:
name: spring-boot-config # 关联的ConfigMap名称
items:
- key: application-prod.properties # ConfigMap中的键
path: application-prod.properties # 挂载到容器内的文件名
(4)更新Deployment
kubectl apply -f spring-boot-deployment.yaml
(5)验证配置生效
进入Pod查看挂载的配置文件:
# 进入其中一个Pod
kubectl exec -it <pod-name> -- /bin/bash
# 查看配置文件
cat /app/config/application-prod.properties
# 输出ConfigMap中定义的内容
# 查看应用日志,确认日志级别生效
cat /app/logs/application.log # 假设应用日志输出到该路径
(6)通过环境变量注入配置
除了挂载文件,也可将ConfigMap中的配置通过环境变量注入:
# 在Deployment的container中添加
env:
- name: APP_MESSAGE
valueFrom:
configMapKeyRef:
name: spring-boot-config
key: application-prod.properties # 引用整个配置文件(不推荐)
# 或引用具体键(需ConfigMap定义单个键值)
# env:
# - name: APP_MESSAGE
# valueFrom:
# configMapKeyRef:
# name: spring-boot-config
# key: app.message
5. 使用Secret管理敏感信息
数据库密码、API密钥等敏感信息需用Secret存储,而非明文暴露在ConfigMap中。
(1)创建Secret
Secret的数据需经过Base64编码,首先编码敏感信息:
echo -n "dbpassword123" | base64 # 输出:ZGJwYXNzd29yZDEyMw==
创建spring-boot-secret.yaml
:
apiVersion: v1
kind: Secret
metadata:
name: spring-boot-secret
namespace: default
type: Opaque # 通用Secret类型
data:
db-password: ZGJwYXNzd29yZDEyMw== # Base64编码后的密码
api-key: YXBpa2V5MTIzNDU2 # 示例API密钥
(2)创建Secret
kubectl apply -f spring-boot-secret.yaml
(3)在Deployment中使用Secret
有两种方式使用Secret:挂载为文件或注入环境变量。
方式一:挂载为文件
spec:
template:
spec:
containers:
- name: spring-boot-demo
# ... 其他配置省略 ...
volumeMounts:
- name: secret-volume
mountPath: /app/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: spring-boot-secret
应用可通过/app/secrets/db-password
文件读取密码。
方式二:注入环境变量
spec:
template:
spec:
containers:
- name: spring-boot-demo
# ... 其他配置省略 ...
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: spring-boot-secret
key: db-password # 引用Secret中的键
- name: API_KEY
valueFrom:
secretKeyRef:
name: spring-boot-secret
key: api-key
Spring Boot应用可通过@Value("${DB_PASSWORD}")
获取密码。
五、进阶配置:健康检查、扩缩容与更新策略
生产环境中,需配置健康检查、资源限制、自动扩缩容和滚动更新策略,确保应用稳定运行。
1. 健康检查:Liveness和Readiness探针
K8s通过探针监测Pod健康状态,实现自愈能力:
- Liveness探针:检测容器是否运行正常,失败则重启容器。
- Readiness探针:检测容器是否准备好接收请求,失败则从Service中移除。
配置探针(更新Deployment):
spec:
template:
spec:
containers:
- name: spring-boot-demo
# ... 其他配置省略 ...
livenessProbe:
httpGet:
path: /health # 健康检查接口(需Spring Boot Actuator支持)
port: 8080
initialDelaySeconds: 60 # 启动后延迟60秒开始检查
periodSeconds: 10 # 检查间隔10秒
timeoutSeconds: 5 # 超时时间5秒
failureThreshold: 3 # 连续3次失败视为不健康
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
集成Spring Boot Actuator:
为健康检查提供更丰富的指标,需添加Actuator依赖:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
配置Actuator暴露健康端点:
# application-prod.properties
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always # 显示详细健康信息
重建镜像并更新Deployment后,K8s将通过/health
接口监控应用状态。
2. 资源配置:Requests和Limits
合理配置资源请求和限制,避免Pod之间资源竞争:
- requests:Pod所需的最小资源,K8s scheduler根据requests调度Pod。
- limits:Pod可使用的最大资源,超过则被限流或终止(OOM kill)。
推荐配置(根据应用实际需求调整):
resources:
requests:
memory: "512Mi" # 内存请求
cpu: "500m" # CPU请求(500m=0.5核)
limits:
memory: "1Gi" # 内存限制
cpu: "1000m" # CPU限制(1核)
- 内存设置:参考应用启动后的内存占用,requests略低于正常运行内存,limits留一定冗余。
- CPU设置:根据应用CPU使用率调整,避免设置过低导致频繁被限流。
3. 自动扩缩容:Horizontal Pod Autoscaler(HPA)
HPA根据CPU使用率、内存使用率或自定义指标自动调整Pod副本数,应对流量波动。
创建HPA配置(spring-boot-hpa.yaml
):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: spring-boot-hpa
namespace: default
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: spring-boot-demo # 关联的Deployment名称
minReplicas: 2 # 最小副本数
maxReplicas: 10 # 最大副本数
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # CPU使用率超过70%时扩容
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80 # 内存使用率超过80%时扩容
创建HPA:
kubectl apply -f spring-boot-hpa.yaml
查看HPA:
kubectl get hpa
# 输出当前副本数、目标指标、最小/最大副本数
当应用CPU使用率持续超过70%,HPA将自动增加Pod副本数;使用率下降后,会自动缩容至最小副本数。
4. 滚动更新与回滚
K8s支持滚动更新策略,确保应用更新过程中零停机;若更新失败,可快速回滚到上一版本。
配置滚动更新策略(更新Deployment):
spec:
strategy:
type: RollingUpdate # 滚动更新(默认)
rollingUpdate:
maxSurge: 1 # 更新过程中最多可超出期望副本数的数量(或百分比,如25%)
maxUnavailable: 0 # 更新过程中最多不可用的副本数(0表示零停机)
maxSurge=1
:每次更新最多新增1个Pod。maxUnavailable=0
:更新过程中保持所有Pod可用。
执行滚动更新:
- 构建新版本镜像并推送:
docker build -t yourusername/spring-boot-docker-demo:v3 .
docker push yourusername/spring-boot-docker-demo:v3
- 更新Deployment的镜像版本:
kubectl set image deployment/spring-boot-demo spring-boot-demo=yourusername/spring-boot-docker-demo:v3
- 查看更新状态:
kubectl rollout status deployment/spring-boot-demo
# 输出更新进度,直到所有Pod更新完成
回滚到上一版本:
若新版本存在问题,执行回滚:
# 查看历史版本
kubectl rollout history deployment/spring-boot-demo
# 回滚到上一版本
kubectl rollout undo deployment/spring-boot-demo
# 回滚到指定版本(如版本1)
kubectl rollout undo deployment/spring-boot-demo --to-revision=1
六、实战案例:完整Spring Boot微服务部署流程
本节通过一个完整案例,演示从Spring Boot应用开发到K8s部署的全流程,包含配置文件、部署命令和验证步骤。
1. 应用准备
使用前文创建的spring-boot-docker-demo
应用,包含/hello
和/health
接口,集成Actuator健康检查。
2. 镜像构建与推送
# 多阶段构建镜像
docker build -t yourusername/spring-boot-docker-demo:v3 -f Dockerfile .
# 推送镜像
docker push yourusername/spring-boot-docker-demo:v3
3. K8s资源配置文件
创建以下配置文件,统一管理所有资源:
(1)命名空间(namespace.yaml
)
apiVersion: v1
kind: Namespace
metadata:
name: spring-boot-demo-ns # 专用命名空间
(2)ConfigMap(configmap.yaml
)
apiVersion: v1
kind: ConfigMap
metadata:
name: spring-boot-config
namespace: spring-boot-demo-ns
data:
application-prod.properties: |
server.port=8080
management.endpoints.web.exposure.include=health,info
management.endpoint.health.show-details=always
app.version=v3
(3)Secret(secret.yaml
)
apiVersion: v1
kind: Secret
metadata:
name: spring-boot-secret
namespace: spring-boot-demo-ns
type: Opaque
data:
db-password: ZGJwYXNzd29yZDEyMw==
(4)Deployment(deployment.yaml
)
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-boot-demo
namespace: spring-boot-demo-ns
labels:
app: spring-boot-demo
spec:
replicas: 2
selector:
matchLabels:
app: spring-boot-demo
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: spring-boot-demo
spec:
containers:
- name: spring-boot-demo
image: yourusername/spring-boot-docker-demo:v3
ports:
- containerPort: 8080
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: spring-boot-secret
key: db-password
volumeMounts:
- name: config-volume
mountPath: /app/config
readOnly: true
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
volumes:
- name: config-volume
configMap:
name: spring-boot-config
(5)Service(service.yaml
)
apiVersion: v1
kind: Service
metadata:
name: spring-boot-demo-service
namespace: spring-boot-demo-ns
spec:
selector:
app: spring-boot-demo
ports:
- port: 80
targetPort: 8080
type: NodePort
(6)HPA(hpa.yaml
)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: spring-boot-hpa
namespace: spring-boot-demo-ns
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: spring-boot-demo
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
4. 部署与验证
(1)创建命名空间
kubectl apply -f namespace.yaml
(2)部署ConfigMap和Secret
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
(3)部署Deployment和Service
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
(4)部署HPA
kubectl apply -f hpa.yaml
(5)验证部署
# 查看命名空间下的所有资源
kubectl get all -n spring-boot-demo-ns
# 查看Pod日志
kubectl logs -f <pod-name> -n spring-boot-demo-ns
# 获取Service访问地址
minikube service spring-boot-demo-service -n spring-boot-demo-ns --url
# 访问返回的URL+/hello,验证应用正常运行
(6)模拟负载测试(验证HPA)
使用hey
工具模拟请求压力:
# 安装hey(macOS:brew install hey;Linux:下载二进制文件)
hey -n 10000 -c 50 http://<service-url>/hello # 50并发,10000请求
观察HPA状态,CPU使用率上升后,Pod副本数会自动增加:
kubectl get hpa -n spring-boot-demo-ns -w # -w:实时监控
七、最佳实践与性能优化
1. 镜像优化最佳实践
- 使用多阶段构建:减少镜像体积,剔除构建依赖。
- 选择合适基础镜像:优先使用Alpine或Slim版本(如
openjdk:17-jre-slim
)。 - 合并RUN指令:通过
&&
合并多个RUN命令,减少镜像层数(每层镜像都会增加体积)。 - 清理缓存:在构建阶段清理Maven/Gradle缓存(如
RUN mvn clean package && rm -rf ~/.m2
)。 - 非root用户运行:创建专用用户,避免容器以root权限运行,降低安全风险。
2. 安全最佳实践
- 镜像扫描:使用Trivy、Clair等工具扫描镜像漏洞,如
trivy image yourimage:tag
。 - 限制Pod权限:通过SecurityContext限制容器权限,如禁止特权模式、只读文件系统。
securityContext: runAsNonRoot: true readOnlyRootFilesystem: true allowPrivilegeEscalation: false
- 敏感数据加密:Secret仅Base64编码,生产环境需启用K8s Secrets加密(EncryptionConfiguration)。
- 网络策略:通过NetworkPolicy限制Pod间通信,仅允许必要的网络流量。
3. 配置管理最佳实践
- 外部化配置:所有环境相关配置通过ConfigMap/Secret注入,镜像与配置解耦。
- 多环境配置:通过命名空间区分dev/test/prod环境,避免配置混淆。
- 配置热更新:Spring Boot可通过
@RefreshScope
实现配置热更新(需集成Spring Cloud Config或K8s ConfigMap reload)。<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-kubernetes-config</artifactId> </dependency>
4. 监控与日志最佳实践
- 集成Prometheus+Grafana:监控应用 metrics(通过Spring Boot Actuator暴露)。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>io.micrometer</groupId> <artifactId>micrometer-registry-prometheus</artifactId> </dependency>
- 集中式日志:使用ELK Stack(Elasticsearch+Logstash+Kibana)或EFK Stack收集容器日志。
- 结构化日志:Spring Boot应用输出JSON格式日志,方便日志分析。
<dependency> <groupId>net.logstash.logback</groupId> <artifactId>logstash-logback-encoder</artifactId> </dependency>
八、问题排查与常见故障处理
在K8s部署Spring Boot应用时,可能遇到各种问题,以下是常见故障及排查方法。
1. Pod启动失败
症状:
kubectl get pods
显示Pod状态为CrashLoopBackOff
、Error
或Pending
。
排查步骤:
-
查看Pod事件:
kubectl describe pod <pod-name> -n <namespace>
关注
Events
部分,常见原因:ImagePullBackOff
:镜像拉取失败(镜像地址错误、仓库无权限)。- 解决:检查镜像标签是否正确,确保K8s节点能访问仓库(私有仓库需配置imagePullSecrets)。
Pending
:调度失败(资源不足、节点亲和性问题)。- 解决:降低资源requests/limits,或扩容节点资源。
Failed
:容器启动命令错误。- 解决:检查Dockerfile的ENTRYPOINT/CMD,确保命令正确。
-
查看容器日志:
kubectl logs <pod-name> -n <namespace> # 查看当前日志 kubectl logs <pod-name> -n <namespace> --previous # 查看上一次启动日志
常见日志错误:
java.lang.OutOfMemoryError
:JVM内存不足,调整JVM参数或资源limits。Could not find main class
:JAR包损坏或路径错误,检查COPY指令和JAR包名称。
2. 应用访问不通
症状:
Service已创建,但无法通过Service IP或NodePort访问应用。
排查步骤:
-
检查Pod状态:确保Pod正常运行(
Running
状态)。 -
检查Pod内部端口:进入Pod验证应用是否监听正确端口:
kubectl exec -it <pod-name> -- netstat -tuln # 查看监听端口
确认应用监听8080端口(与containerPort一致)。
-
检查Service配置:
kubectl describe service <service-name> -n <namespace>
确保
Selector
标签与Pod标签一致,targetPort
与应用端口一致。 -
检查网络策略:是否有NetworkPolicy阻止了流量,暂时删除策略测试。
3. 配置不生效
症状:
ConfigMap/Secret中的配置未被应用读取。
排查步骤:
-
检查挂载路径:进入Pod确认配置文件是否正确挂载:
kubectl exec -it <pod-name> -- ls /app/config # 查看挂载目录
-
检查配置文件内容:
kubectl exec -it <pod-name> -- cat /app/config/application-prod.properties
确认内容与ConfigMap一致。
-
检查Spring Boot配置加载:查看应用日志,确认激活的profile和配置文件路径:
kubectl logs <pod-name> | grep "Loaded configuration"
确保
SPRING_PROFILES_ACTIVE=prod
,且应用加载了/app/config
目录的配置。
九、总结与展望
通过Docker和Kubernetes部署Spring Boot应用,实现了环境一致性、自动化运维和弹性伸缩,解决了传统部署的诸多痛点。本文从基础镜像构建到生产级部署,详细讲解了Docker镜像优化、K8s资源配置、健康检查、自动扩缩容等核心内容,并通过实战案例演示了完整流程。
随着云原生技术的发展,Spring Boot与Kubernetes的集成将更加紧密:
- Service Mesh:通过Istio等服务网格实现更细粒度的流量管理、熔断和监控。
- Serverless:结合Knative实现Spring Boot应用的Serverless部署,进一步简化运维。
- GitOps:通过ArgoCD等工具实现配置和部署的Git版本控制,自动化应用交付。
掌握Docker和Kubernetes部署技能,是现代Spring Boot开发者的必备能力。希望本文的实战经验能帮助读者快速上手,从“手动部署”走向“云原生自动化部署”,提升应用的可靠性和运维效率。