Spring Boot应用在Kubernetes上运行,并不只是把jar包塞进容器再部署到集群。真正意义上的整合,是让应用的生命周期、健康状态和配置行为都符合Kubernetes的调度契约。容器化只是入口,后续的副本管理、滚动更新、流量接入和配置外置才是编排的核心。

一、容器化是整合的第一步
要把Spring Boot交给Kubernetes管理,首先要有一个稳定、体积适中的容器镜像。很多项目直接使用基础JDK镜像,结果镜像体积膨胀到几百MB,拉取速度变慢,节点缓存效率也低。实际上使用eclipse-temurin:17-jdk-alpine或openjdk:17-jdk-slim这类精简镜像,配合分层构建,可以把体积控制在合理范围。
Dockerfile中需要注意两点:一是把依赖层和应用层分开,充分利用构建缓存;二是设置ENTRYPOINT时采用exec形式,确保Java进程能收到SIGTERM信号,而不是被shell包一层导致信号丢失。Kubernetes停止Pod时会先发送SIGTERM,如果应用收不到信号,只能等待terminationGracePeriodSeconds超时被强制kill,造成请求中断。
FROM eclipse-temurin:17-jdk-alpine AS builder WORKDIR /app COPY target/*.jar app.jar RUN java -Djarmode=layertools -jar app.jar extract FROM eclipse-temurin:17-jre-alpine WORKDIR /app COPY --from=builder /app/dependencies/ ./ COPY --from=builder /app/spring-boot-loader/ ./ COPY --from=builder /app/snapshot-dependencies/ ./ COPY --from=builder /app/application/ ./ RUN addgroup -S spring && adduser -S spring -G spring USER spring:spring ENTRYPOINT ["java","org.springframework.boot.loader.launch.JarLauncher"]
这个例子利用Spring Boot的分层jar特性,把第三方依赖、应用class和加载器拆开。这样每次业务代码变更重建镜像时,依赖层不会变化,构建和推送速度都会更快。同时容器以非root用户运行,降低安全风险。
如果不想使用分层工具,直接java -jar app.jar也可以,但务必在Deployment中配置合理的terminationGracePeriodSeconds,并让Spring Boot开启优雅停机,否则滚动更新时旧Pod中的正在处理的请求会被直接切断。
二、用Deployment和Service建立编排模型
容器镜像准备好以后,需要一个Deployment来声明期望副本数、更新策略和Pod模板。Kubernetes的控制循环会不断比较当前状态和期望状态,当副本数不足时自动创建Pod,Pod挂掉后也会被替换。这就是最基础的编排能力。
下面的Deployment设置了三个副本,并采用滚动更新策略,最多允许一个实例不可用,同时最多创建一个新实例。镜像地址使用registry.ipipp.com/boot-app:1.0.0,实际项目中替换成自己的镜像仓库地址。
apiVersion: apps/v1
kind: Deployment
metadata:
name: boot-app
labels:
app: boot-app
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
selector:
matchLabels:
app: boot-app
template:
metadata:
labels:
app: boot-app
spec:
terminationGracePeriodSeconds: 30
containers:
- name: boot-app
image: registry.ipipp.com/boot-app:1.0.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: http
env:
- name: JAVA_OPTS
value: "-Xms512m -Xmx512m -Dfile.encoding=UTF-8"
resources:
requests:
memory: "512Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
副本数由replicas控制,资源请求和限制保证Pod不会被调度到资源不足的节点,也避免单个Pod挤占节点。设置terminationGracePeriodSeconds为30秒,给Spring Boot留出处理存量请求和关闭连接池的时间。
Deployment只负责Pod,流量接入需要Service。Service通过标签选择器找到app: boot-app的Pod,并提供一个稳定访问入口。Kubernetes Service本身有简单的负载均衡,配合集群内的kube-proxy或CNI实现数据转发。
apiVersion: v1
kind: Service
metadata:
name: boot-app-svc
spec:
type: ClusterIP
selector:
app: boot-app
ports:
- port: 80
targetPort: 8080
protocol: TCP
如果服务只在集群内部调用,使用ClusterIP类型即可。外部访问可以通过Ingress暴露HTTP接口,或使用NodePort和LoadBalancer。对于Spring Boot微服务,Service既可以作为内部负载均衡器,也可以被Ingress Controller发现,无需在应用代码中引入Eureka等注册中心组件。
三、让Spring Boot暴露正确的健康信号
Kubernetes默认通过进程存活状态判断Pod是否健康,但进程活着不代表应用能正常响应。Spring Boot Actuator提供了细粒度的健康端点,Kubernetes可以通过readinessProbe和livenessProbe探针调用这些端点,获得更准确的实例状态。
首先在pom.xml或build.gradle中引入Actuator依赖,然后配置探针端点。Spring Boot 2.3及以上版本支持专门的就绪和存活探针,分别在/actuator/health/readiness和/actuator/health/liveness路径下提供状态。
management:
endpoints:
web:
exposure:
include: health,info,metrics,env
endpoint:
health:
probes:
enabled: true
health:
livenessState:
enabled: true
readinessState:
enabled: true
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 20s
这个配置开启了优雅停机,并设置每个关闭阶段最长等待20秒。当Deployment发起滚动更新时,Kubernetes会先标记Pod为Terminating,同时调用PreStop钩子或发送SIGTERM。Spring Boot收到信号后停止接收新请求,等待存量请求处理完成,再关闭应用上下文。
在Deployment模板中加入探针配置,Kubernetes会周期性请求这些端点。就绪探针失败时,Service会暂时把该Pod从后端列表摘除,等恢复后再重新加入;存活探针失败则触发容器重启。两者要区分开,存活探针不宜检查数据库等外部依赖,否则一个DB抖动会引发所有实例重启。
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
启动延迟时间需要根据应用实际情况调整,太短会导致Pod还没初始化完就被判定为失败,太长则拖慢滚动更新速度。
四、外部化配置与服务发现
Kubernetes环境中的应用实例通常跨越多个命名空间和环境,配置硬编码在jar中会带来很大运维负担。ConfigMap和Secret可以将配置与镜像解耦。ConfigMap适合存储非敏感配置,例如数据库地址、线程池大小、日志级别;Secret用于密码、令牌等敏感数据,虽然Secret默认只是base64编码,需要配合RBAC和加密插件才能真正安全。
在Deployment中,可以通过envFrom把ConfigMap的键值注入为环境变量,也可以挂载为文件到指定目录。Spring Boot的配置优先级中环境变量高于application.properties,因此可以直接覆盖本地配置。
apiVersion: v1
kind: ConfigMap
metadata:
name: boot-app-config
data:
application.yaml: |
spring:
datasource:
url: jdbc:mysql://mysql-svc:3306/bootdb?useSSL=false
username: appuser
jpa:
hibernate:
ddl-auto: validate
logging:
level:
org.springframework: INFO
---
apiVersion: v1
kind: Secret
metadata:
name: boot-app-secret
type: Opaque
data:
database-password: c2VjdXJlcGFzcw==
挂载ConfigMap时,可以把整个application.yaml放到镜像的config目录,Spring Boot启动时自动加载。这样修改配置只需要更新ConfigMap并重启Pod,不必重新构建镜像。Secret中的密码则通过环境变量注入,避免出现在配置文件中。
如果希望Spring Boot能自动发现Kubernetes集群内的其他服务,可以使用Spring Cloud Kubernetes。它提供了基于Kubernetes API的服务发现和配置加载能力。客户端通过Service名称直接发现后端Pod,可以替代Eureka在K8s场景下的部分功能。引入spring-cloud-starter-kubernetes-client后,使用RestTemplate或WebClient时,可以通过http://服务名发起调用,服务名会解析为对应的ClusterIP或Pod IP。
不过需要明确,Service发现不等于应用编排。Kubernetes本身的Service已经提供了负载均衡,Spring Cloud Kubernetes更多是让应用读取ConfigMap或Secret时更符合Spring Boot的配置模型。对于大多数项目,直接使用Deployment、Service、探针和ConfigMap已经能完成主流编排需求。
五、滚动发布与回滚策略
滚动更新是Kubernetes最实用的编排能力之一。修改Deployment的镜像版本后,控制平面按照maxSurge和maxUnavailable的约束逐步替换Pod。旧实例不会全部停止,新实例也不会一次性全部创建,保证整个过程中始终有可用副本处理流量。
实际项目里还需要关注更新触发条件。如果只修改了ConfigMap的内容,Deployment本身不会自动感知,需要借助Reloader这类控制器或者重启Pod。镜像标签最好使用不可变的版本号或Git提交哈希,不要使用latest,否则回滚和审计都很困难。
当新版本出现启动失败或健康检查不通过时,Deployment的滚动更新会暂停,保留旧实例继续服务。可以通过kubectl rollout undo deployment/boot-app快速回滚到上一个版本。结合kubectl rollout status可以查看更新进度,排查卡住的原因。
kubectl set image deployment/boot-app boot-app=registry.ipipp.com/boot-app:1.0.1 kubectl rollout status deployment/boot-app kubectl rollout undo deployment/boot-app
这些命令展示了从更新到回滚的完整链路。配合探针和优雅停机,可以做到用户无感发布。整体来看,Spring Boot整合Kubernetes并不是要在代码里做什么特殊改造,而是把应用自身的健康状态、配置读取和生命周期管理暴露给平台,让Kubernetes用声明式的方式完成编排。
Spring BootKubernetes容器编排修改时间:2026-09-25 15:51:03