跳到主要内容
Java 部署:滚动更新(K8s RollingUpdate 策略) | 极客日志
Java java
Java 部署:滚动更新(K8s RollingUpdate 策略) Kubernetes 滚动更新策略通过逐步替换 Pod 实现零停机部署。介绍 Java Spring Boot 应用配置健康检查端点,设置 Deployment 的 maxUnavailable 和 maxSurge 参数,利用 readinessProbe 确保流量平滑切换,并结合 preStop Hook 实现优雅关闭。同时涵盖回滚机制、监控指标及常见问题排查,提供完整的 v1 到 v2 版本升级实操指南。
FlinkHero 发布于 2026/3/22 更新于 2026/4/30 7 浏览在现代云原生应用开发中,持续交付和零停机部署已成为企业级 Java 应用的标配。而 Kubernetes(简称 K8s)作为事实上的容器编排平台,其 RollingUpdate(滚动更新) 策略为实现平滑、安全、无感知的应用升级提供了强大支持。本文将深入探讨如何在 Kubernetes 中对 Java 应用实施滚动更新,涵盖原理、配置、最佳实践、故障处理以及完整的代码与部署示例。
无论你是刚接触 K8s 的 Java 开发者,还是已有一定经验但希望优化部署流程的 DevOps 工程师,本文都将为你提供实用、可落地的指导。让我们从基础开始,逐步构建一个健壮、可维护的滚动更新体系。
什么是滚动更新(Rolling Update)?
滚动更新是一种渐进式部署新版本应用 的策略。它通过逐个替换旧 Pod 的方式,确保在整个更新过程中服务始终可用,从而实现零停机(Zero Downtime) 的目标。
在 Kubernetes 中,当你更新一个 Deployment 的镜像版本或配置时,如果策略设置为 RollingUpdate(默认值),K8s 会自动执行以下操作:
启动一个或多个新版本的 Pod;
等待新 Pod 就绪(通过就绪探针 Ready Probe 判断);
将流量从旧 Pod 逐步切换到新 Pod;
删除一个或多个旧 Pod;
重复上述过程,直到所有旧 Pod 被替换。
这种'边下边上'的方式,避免了传统'蓝绿部署'或'全量替换'可能带来的服务中断风险。
💡 小知识 :Kubernetes 的滚动更新是基于 ReplicaSet 实现的。每次更新 Deployment 时,K8s 会创建一个新的 ReplicaSet,并逐步缩容旧 ReplicaSet、扩容新 ReplicaSet。
为什么 Java 应用特别需要滚动更新?
Java 应用通常具有以下特点,使其对滚动更新有更高需求:
启动时间较长 :JVM 预热、Spring Boot 初始化等过程可能需要数秒甚至数十秒;
内存占用高 :频繁重启可能导致资源竞争;
有状态中间件依赖 :如数据库连接池、缓存客户端等,需优雅关闭;
高可用要求 :企业级系统通常要求 99.9% 以上的可用性。
若采用一次性删除所有旧实例再启动新实例的方式,用户将经历明显的请求失败或超时。而滚动更新通过控制并发替换数量,确保始终有足够健康的实例处理请求,极大提升了用户体验和系统稳定性。
Kubernetes 滚动更新的核心机制
Kubernetes 的滚动更新由两个关键参数控制:
maxUnavailable:更新过程中允许不可用的 Pod 最大数量 (相对于期望副本数);
maxSurge:更新过程中允许超出期望副本数的最大 Pod 数量 。
这两个参数共同决定了更新的速度与安全性。
默认值
对于一个 replicas: 3 的 Deployment,其默认滚动更新策略如下:
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25 %
maxSurge: 25 %
这意味着:
最多允许 1 个 Pod 不可用(3 × 25% ≈ 0.75 → 向上取整为 1);
最多允许临时增加 1 个 Pod(3 × 25% ≈ 0.75 → 向上取整为 1)。
创建 1 个新 Pod(总数变为 4);
等待新 Pod 就绪;
删除 1 个旧 Pod(总数回到 3);
重复直到全部替换。
参数详解 参数 类型 说明 maxUnavailableint 或百分比 更新期间可处于'未就绪'状态的 Pod 数量上限。设为 0 可实现完全无损更新(但需配合 maxSurge > 0)。 maxSurgeint 或百分比 允许超过 replicas 设定值的额外 Pod 数量。用于提前启动新实例,加快更新速度。
⚠️ 注意:maxUnavailable 和 maxSurge 不能同时为 0,否则更新将无法进行。
构建一个支持滚动更新的 Java 应用 为了演示滚动更新,我们先构建一个简单的 Spring Boot 应用,包含健康检查端点。
1. 创建 Spring Boot 项目
2. 编写主类
package com.example.rollingupdate;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main (String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
3. 添加控制器
package com.example.rollingupdate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello () {
return "Hello from Java App v1! 🌟" ;
}
@GetMapping("/version")
public String version () {
return "v1.0.0" ;
}
}
4. 配置 Actuator 健康端点 在 application.yml 中启用健康检查端点:
management:
endpoints:
web:
exposure:
include: health,info
endpoint:
health:
show-details: always
Actuator 默认提供 /actuator/health 端点,返回 {"status":"UP"} 表示应用健康。
5. 构建 Docker 镜像 # Dockerfile
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
构建并打标签(假设你的 Docker Hub 用户名为 yourname):
./mvnw clean package -DskipTests
docker build -t yourname/java-rolling-demo:v1 .
docker push yourname/java-rolling-demo:v1
编写 Kubernetes Deployment 配置 接下来,我们将这个 Java 应用部署到 Kubernetes,并配置滚动更新策略。
deployment.yaml apiVersion: apps/v1
kind: Deployment
metadata:
name: java-rolling-demo
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
maxSurge: 1
selector:
matchLabels:
app: java-rolling-demo
template:
metadata:
labels:
app: java-rolling-demo
spec:
containers:
- name: app
image: yourname/java-rolling-demo:v1
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "200m"
---
apiVersion: v1
kind: Service
metadata:
name: java-rolling-demo-svc
spec:
selector:
app: java-rolling-demo
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
关键配置说明
readinessProbe :决定 Pod 是否准备好接收流量。只有当探针成功,Service 才会将请求转发给该 Pod。这对滚动更新至关重要——新 Pod 必须完全就绪后才能加入服务。
livenessProbe :用于判断应用是否存活。若失败,K8s 会重启容器。
resources :限制资源使用,避免单个 Pod 占用过多资源影响其他实例。
maxUnavailable: 1 :最多允许 1 个 Pod 不可用(3 个副本时,至少 2 个在线)。
maxSurge: 1 :允许临时多启动 1 个 Pod,加快更新速度。
📌 重要 :readinessProbe 是滚动更新平滑性的核心!务必确保其路径能真实反映应用就绪状态。
部署并验证初始状态 kubectl apply -f deployment.yaml
kubectl get pods -l app=java-rolling-demo
NAME READY STATUS RESTARTS AGE
java- rolling- demo-7 d5b8c9f45- abc12 1 / 1 Running 0 2 m
java- rolling- demo-7 d5b8c9f45- def34 1 / 1 Running 0 2 m
java- rolling- demo-7 d5b8c9f45- ghi56 1 / 1 Running 0 2 m
kubectl get svc java-rolling-demo-svc
curl http://<SERVICE-IP>/version
执行滚动更新:从 v1 到 v2
1. 修改 Java 应用代码 @GetMapping("/version")
public String version () {
return "v2.0.0" ;
}
@GetMapping("/hello")
public String hello () {
return "Hello from Java App v2! 🚀" ;
}
2. 构建并推送 v2 镜像 ./mvnw clean package -DskipTests
docker build -t yourname/java-rolling-demo:v2 .
docker push yourname/java-rolling-demo:v2
3. 触发滚动更新
方式一:直接修改 Deployment YAML 将 image 字段改为 yourname/java-rolling-demo:v2,然后执行:
kubectl apply -f deployment.yaml
方式二:使用 kubectl set image(推荐) kubectl set image deployment/java-rolling-demo app=yourname/java-rolling-demo:v2
✅ 这种方式无需修改文件,适合 CI/CD 自动化。
4. 监控更新过程 kubectl rollout status deployment/java-rolling-demo
Waiting for deployment "java-rolling-demo" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "java-rolling-demo" rollout to finish: 2 out of 3 new replicas have been updated...
deployment "java-rolling-demo" successfully rolled out
kubectl get pods -w -l app=java-rolling-demo
你将看到旧 Pod 逐步被终止,新 Pod 逐步启动:
NAME READY STATUS RESTARTS AGE
java- rolling- demo-7 d5b8c9f45- abc12 1 / 1 Running 0 5 m
java- rolling- demo-7 d5b8c9f45- def34 1 / 1 Running 0 5 m
java- rolling- demo-7 d5b8c9f45- ghi56 1 / 1 Running 0 5 m
java- rolling- demo-6 b8d9f7c54- jkl78 0 / 1 ContainerCreating 0 1 s
java- rolling- demo-6 b8d9f7c54- jkl78 1 / 1 Running 0 10 s
java- rolling- demo-7 d5b8c9f45- abc12 1 / 1 Terminating 0 5 m30s
...
5. 验证新版本 for i in {1..10}; do curl -s http://<SERVICE-IP>/version; echo ; done
初期可能返回 v1.0.0 和 v2.0.0 混合结果,随着更新完成,最终全部返回 v2.0.0。
这正是滚动更新的典型特征:流量逐步切换,用户无感知 。
可视化滚动更新过程 下面是一个 Mermaid 图表,展示滚动更新中 Pod 状态的变化:
00:00 -> Pod-A (v1), Pod-B (v1), Pod-C (v1)
00:01 -> Pod-D (v2) 启动
00:02 -> Pod-E (v2) 启动,Pod-A (v1) 终止
00:03 -> Pod-F (v2) 启动,Pod-B (v1) 终止
...
💡 图中显示:新 Pod 在旧 Pod 终止前已启动并就绪,确保服务连续性。
滚动更新中的关键保障:探针(Probes) 前面提到,readinessProbe 是滚动更新平滑的关键。让我们深入理解其作用。
Readiness Probe(就绪探针)
作用 :告诉 K8s '我准备好接收流量了吗?'
触发时机 :Pod 启动后周期性调用。
行为 :若失败,Pod 不会被加入 Service 的 Endpoints,即不会收到任何请求 。
Java 应用建议 :
初始延迟(initialDelaySeconds)应大于应用启动时间(如 Spring Boot 通常 10-30 秒);
路径应返回真实业务就绪状态(如数据库连接成功、缓存初始化完成等)。
Liveness Probe(存活探针)
作用 :告诉 K8s '我还活着吗?'
行为 :若连续失败,K8s 会重启容器。
注意 :不要将复杂逻辑放入 liveness probe,否则可能导致误杀。
示例:增强健康检查 你可以自定义健康指示器,确保只有在所有依赖就绪后才返回 UP:
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
@Override
public Health health () {
if (isDatabaseConnected()) {
return Health.up().withDetail("database" , "available" ).build();
}
return Health.down().withDetail("database" , "unavailable" ).build();
}
}
这样,即使 Spring Boot 启动完成,若数据库未连通,/actuator/health 仍返回 DOWN,Pod 不会接收流量,避免错误请求。
处理滚动更新失败:回滚(Rollback) 尽管我们做了充分准备,但更新仍可能失败(如新版本有 bug、配置错误等)。Kubernetes 提供了便捷的回滚机制。
查看更新历史 kubectl rollout history deployment/java-rolling-demo
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployment/java-rolling-demo app=yourname/java-rolling-demo:v2
回滚到上一版本 kubectl rollout undo deployment/java-rolling-demo
K8s 会自动将镜像切回 v1,并执行反向滚动更新。
回滚到指定版本 kubectl rollout undo deployment/java-rolling-demo --to-revision=1
自动回滚(高级) Kubernetes 本身不支持'自动回滚',但可通过以下方式实现:
结合监控告警 :如 Prometheus + Alertmanager 检测到错误率飙升,触发 webhook 执行 kubectl rollout undo;
使用 Argo Rollouts :这是一个 K8s 扩展,支持金丝雀发布、自动回滚等高级功能。
优化滚动更新体验的最佳实践
1. 设置合理的探针参数 readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 20
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
2. 控制更新速度
若系统敏感,可设置 maxUnavailable: 0 和 maxSurge: 1,确保始终有全部副本在线;
若追求速度,可增大 maxSurge(如 50%),但需注意资源压力。
3. 使用 preStop Hook 实现优雅关闭 在 Java 应用终止前,执行清理操作(如关闭连接池、拒绝新请求):
containers:
- name: app
image: yourname/java-rolling-demo:v2
lifecycle:
preStop:
exec:
command: ["/bin/sh" , "-c" , "sleep 15" ]
同时,在 Spring Boot 中启用优雅关闭:
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s
K8s 先从 Service Endpoints 中移除该 Pod(不再接收新请求);
执行 preStop hook(等待 15 秒,让现有请求完成);
发送 SIGTERM 信号给 Java 进程;
Spring Boot 优雅关闭;
若超时未退出,发送 SIGKILL。
4. 监控滚动更新指标
kube_deployment_status_replicas_updated:已更新的副本数;
kube_pod_status_ready:Pod 就绪状态;
应用层指标:错误率、延迟、吞吐量。
滚动更新 vs 其他部署策略 Kubernetes 还支持其他部署策略,适用于不同场景:
策略 特点 适用场景 RollingUpdate 逐步替换,零停机 大多数生产环境 Recreate 先删所有旧 Pod,再建新 Pod 可接受短暂停机,或有状态应用需完全重置 Blue/Green K8s 原生不支持,需 Service 切换 需要快速回滚、全量验证 Canary K8s 原生不支持,需 Ingress 或 Service Mesh 渐进式发布,小流量验证
💡 对于 Java 应用,RollingUpdate 是最常用且最安全的选择 。
常见问题与排查
Q1: 更新卡住了,怎么办?
检查新 Pod 是否就绪:kubectl describe pod <new-pod>
查看日志:kubectl logs <new-pod>
检查 readinessProbe 是否失败;
确认镜像是否拉取成功(网络、权限问题)。
Q2: 用户偶尔收到 502 错误?
可能是旧 Pod 被终止时仍有请求在处理;
解决方案:配置 preStop hook + 优雅关闭;
确保 Service 的 sessionAffinity 未开启(除非必要)。
Q3: 如何暂停/恢复滚动更新?
kubectl rollout pause deployment/java-rolling-demo
kubectl rollout resume deployment/java-rolling-demo
总结 滚动更新是 Kubernetes 为 Java 应用提供的强大部署能力,它通过渐进式替换 Pod ,结合就绪探针 和优雅关闭机制 ,实现了零停机、高可用 的持续交付。
构建健康的 Java 应用 :提供准确的 /actuator/health 端点;
合理配置 Deployment :设置 maxUnavailable、maxSurge、探针参数;
实现优雅关闭 :使用 preStop hook 和 Spring Boot 的 graceful shutdown;
监控与回滚 :建立可观测性,准备快速回滚方案。
随着云原生生态的成熟,滚动更新已成为 Java 应用上线的标准姿势。掌握它,你就能在微服务时代游刃有余地交付高质量软件。
🌟 最后提醒 :永远在非生产环境充分测试你的滚动更新流程!自动化、标准化、可重复,是 DevOps 的核心精神。
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online