Java 部署滾動更新的方法(K8s RollingUpdate 策略)

在現(xiàn)代云原生應用開發(fā)中,持續(xù)交付和零停機部署已成為企業(yè)級 Java 應用的標配。而 Kubernetes(簡稱 K8s)作為事實上的容器編排平臺,其 RollingUpdate(滾動更新) 策略為實現(xiàn)平滑、安全、無感知的應用升級提供了強大支持。本文將深入探討如何在 Kubernetes 中對 Java 應用實施滾動更新,涵蓋原理、配置、最佳實踐、故障處理以及完整的代碼與部署示例。
無論你是剛接觸 K8s 的 Java 開發(fā)者,還是已有一定經(jīng)驗但希望優(yōu)化部署流程的 DevOps 工程師,本文都將為你提供實用、可落地的指導。讓我們從基礎(chǔ)開始,逐步構(gòu)建一個健壯、可維護的滾動更新體系 ??。
什么是滾動更新(Rolling Update)?
滾動更新是一種漸進式部署新版本應用的策略。它通過逐個替換舊 Pod的方式,確保在整個更新過程中服務(wù)始終可用,從而實現(xiàn)零停機(Zero Downtime) 的目標。
在 Kubernetes 中,當你更新一個 Deployment 的鏡像版本或配置時,如果策略設(shè)置為 RollingUpdate(默認值),K8s 會自動執(zhí)行以下操作:
- 啟動一個或多個新版本的 Pod;
- 等待新 Pod 就緒(通過就緒探針 Ready Probe 判斷);
- 將流量從舊 Pod 逐步切換到新 Pod;
- 刪除一個或多個舊 Pod;
- 重復上述過程,直到所有舊 Pod 被替換。
這種“邊下邊上”的方式,避免了傳統(tǒng)“藍綠部署”或“全量替換”可能帶來的服務(wù)中斷風險。
?? 小知識:Kubernetes 的滾動更新是基于 ReplicaSet 實現(xiàn)的。每次更新 Deployment 時,K8s 會創(chuàng)建一個新的 ReplicaSet,并逐步縮容舊 ReplicaSet、擴容新 ReplicaSet。
為什么 Java 應用特別需要滾動更新?
Java 應用通常具有以下特點,使其對滾動更新有更高需求:
- 啟動時間較長:JVM 預熱、Spring Boot 初始化等過程可能需要數(shù)秒甚至數(shù)十秒;
- 內(nèi)存占用高:頻繁重啟可能導致資源競爭;
- 有狀態(tài)中間件依賴:如數(shù)據(jù)庫連接池、緩存客戶端等,需優(yōu)雅關(guān)閉;
- 高可用要求:企業(yè)級系統(tǒng)通常要求 99.9% 以上的可用性。
若采用一次性刪除所有舊實例再啟動新實例的方式,用戶將經(jīng)歷明顯的請求失敗或超時。而滾動更新通過控制并發(fā)替換數(shù)量,確保始終有足夠健康的實例處理請求,極大提升了用戶體驗和系統(tǒng)穩(wěn)定性 ?。
Kubernetes 滾動更新的核心機制
Kubernetes 的滾動更新由兩個關(guān)鍵參數(shù)控制:
maxUnavailable:更新過程中允許不可用的 Pod 最大數(shù)量(相對于期望副本數(shù));maxSurge:更新過程中允許超出期望副本數(shù)的最大 Pod 數(shù)量。
這兩個參數(shù)共同決定了更新的速度與安全性。
默認值
對于一個 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)。
因此,更新過程可能如下:
- 創(chuàng)建 1 個新 Pod(總數(shù)變?yōu)?4);
- 等待新 Pod 就緒;
- 刪除 1 個舊 Pod(總數(shù)回到 3);
- 重復直到全部替換。
參數(shù)詳解
| 參數(shù) | 類型 | 說明 |
|---|---|---|
maxUnavailable | int 或百分比 | 更新期間可處于“未就緒”狀態(tài)的 Pod 數(shù)量上限。設(shè)為 0 可實現(xiàn)完全無損更新(但需配合 maxSurge > 0)。 |
maxSurge | int 或百分比 | 允許超過 replicas 設(shè)定值的額外 Pod 數(shù)量。用于提前啟動新實例,加快更新速度。 |
?? 注意:
maxUnavailable和maxSurge不能同時為 0,否則更新將無法進行。
構(gòu)建一個支持滾動更新的 Java 應用
為了演示滾動更新,我們先構(gòu)建一個簡單的 Spring Boot 應用,包含健康檢查端點。
1. 創(chuàng)建 Spring Boot 項目
使用 Spring Initializr 創(chuàng)建一個 Web 項目,添加 Spring Web 和 Spring Boot Actuator 依賴。
2. 編寫主類
// src/main/java/com/example/rollingupdate/DemoApplication.java
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. 添加控制器
// src/main/java/com/example/rollingupdate/HelloController.java
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 中啟用健康檢查端點:
# src/main/resources/application.yml
management:
endpoints:
web:
exposure:
include: health,info
endpoint:
health:
show-details: alwaysActuator 默認提供 /actuator/health 端點,返回 {"status":"UP"} 表示應用健康。
5. 構(gòu)建 Docker 鏡像
編寫 Dockerfile:
# Dockerfile FROM eclipse-temurin:17-jre-alpine WORKDIR /app COPY target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]
構(gòu)建并打標簽(假設(shè)你的 Docker Hub 用戶名為 yourname):
./mvnw clean package -DskipTests docker build -t yourname/java-rolling-demo:v1 . docker push yourname/java-rolling-demo:v1
?? 你可以參考 Docker 官方文檔 了解如何構(gòu)建鏡像。
編寫 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關(guān)鍵配置說明
readinessProbe:決定 Pod 是否準備好接收流量。只有當探針成功,Service 才會將請求轉(zhuǎn)發(fā)給該 Pod。這對滾動更新至關(guān)重要——新 Pod 必須完全就緒后才能加入服務(wù)。livenessProbe:用于判斷應用是否存活。若失敗,K8s 會重啟容器。resources:限制資源使用,避免單個 Pod 占用過多資源影響其他實例。maxUnavailable: 1:最多允許 1 個 Pod 不可用(3 個副本時,至少 2 個在線)。maxSurge: 1:允許臨時多啟動 1 個 Pod,加快更新速度。
?? 重要:
readinessProbe是滾動更新平滑性的核心!務(wù)必確保其路徑能真實反映應用就緒狀態(tài)。
部署并驗證初始狀態(tài)
應用上述配置:
kubectl apply -f deployment.yaml
檢查 Pod 狀態(tài):
kubectl get pods -l app=java-rolling-demo
輸出應類似:
NAME READY STATUS RESTARTS AGE java-rolling-demo-7d5b8c9f45-abc12 1/1 Running 0 2m java-rolling-demo-7d5b8c9f45-def34 1/1 Running 0 2m java-rolling-demo-7d5b8c9f45-ghi56 1/1 Running 0 2m
測試服務(wù):
# 獲取 Service IP(或使用 NodePort/Ingress) kubectl get svc java-rolling-demo-svc # 使用 curl 測試(假設(shè)已暴露到外部) curl http://<SERVICE-IP>/version # 返回: v1.0.0
執(zhí)行滾動更新:從 v1 到 v2
現(xiàn)在,我們準備發(fā)布新版本 v2。
1. 修改 Java 應用代碼
更新 HelloController.java:
@GetMapping("/version")
public String version() {
return "v2.0.0"; // 改為 v2
}并修改歡迎語:
@GetMapping("/hello")
public String hello() {
return "Hello from Java App v2! ??";
}2. 構(gòu)建并推送 v2 鏡像
./mvnw clean package -DskipTests docker build -t yourname/java-rolling-demo:v2 . docker push yourname/java-rolling-demo:v2
3. 觸發(fā)滾動更新
有兩種方式:
方式一:直接修改 Deployment YAML
將 image 字段改為 yourname/java-rolling-demo:v2,然后執(zhí)行:
kubectl apply -f deployment.yaml
方式二:使用kubectl set image(推薦)
kubectl set image deployment/java-rolling-demo app=yourname/java-rolling-demo:v2
? 這種方式無需修改文件,適合 CI/CD 自動化。
4. 監(jiān)控更新過程
查看滾動更新狀態(tài):
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
同時,觀察 Pod 變化:
kubectl get pods -w -l app=java-rolling-demo
你將看到舊 Pod 逐步被終止,新 Pod 逐步啟動:
NAME READY STATUS RESTARTS AGE java-rolling-demo-7d5b8c9f45-abc12 1/1 Running 0 5m java-rolling-demo-7d5b8c9f45-def34 1/1 Running 0 5m java-rolling-demo-7d5b8c9f45-ghi56 1/1 Running 0 5m java-rolling-demo-6b8d9f7c54-jkl78 0/1 ContainerCreating 0 1s java-rolling-demo-6b8d9f7c54-jkl78 1/1 Running 0 10s java-rolling-demo-7d5b8c9f45-abc12 1/1 Terminating 0 5m30s ...
5. 驗證新版本
多次調(diào)用 /version 接口:
for i in {1..10}; do curl -s http://<SERVICE-IP>/version; echo; done初期可能返回 v1.0.0 和 v2.0.0 混合結(jié)果,隨著更新完成,最終全部返回 v2.0.0。
這正是滾動更新的典型特征:流量逐步切換,用戶無感知。
可視化滾動更新過程
下面是一個 Mermaid 圖表,展示滾動更新中 Pod 狀態(tài)的變化:
?? 圖中顯示:新 Pod 在舊 Pod 終止前已啟動并就緒,確保服務(wù)連續(xù)性。
滾動更新中的關(guān)鍵保障:探針(Probes)
前面提到,readinessProbe 是滾動更新平滑的關(guān)鍵。讓我們深入理解其作用。
Readiness Probe(就緒探針)
- 作用:告訴 K8s “我準備好接收流量了嗎?”
- 觸發(fā)時機:Pod 啟動后周期性調(diào)用。
- 行為:若失敗,Pod 不會被加入 Service 的 Endpoints,即不會收到任何請求。
- Java 應用建議:
- 初始延遲(
initialDelaySeconds)應大于應用啟動時間(如 Spring Boot 通常 10-30 秒); - 路徑應返回真實業(yè)務(wù)就緒狀態(tài)(如數(shù)據(jù)庫連接成功、緩存初始化完成等)。
- 初始延遲(
Liveness Probe(存活探針)
- 作用:告訴 K8s “我還活著嗎?”
- 行為:若連續(xù)失敗,K8s 會重啟容器。
- 注意:不要將復雜邏輯放入 liveness probe,否則可能導致誤殺。
示例:增強健康檢查
你可以自定義健康指示器,確保只有在所有依賴就緒后才返回 UP:
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// 檢查數(shù)據(jù)庫連接
if (isDatabaseConnected()) {
return Health.up().withDetail("database", "available").build();
}
return Health.down().withDetail("database", "unavailable").build();
}
}這樣,即使 Spring Boot 啟動完成,若數(shù)據(jù)庫未連通,/actuator/health 仍返回 DOWN,Pod 不會接收流量,避免錯誤請求。
?? 更多關(guān)于 Spring Boot 健康檢查的信息,可參考 Spring Boot Actuator 文檔。
處理滾動更新失?。夯貪L(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,并執(zhí)行反向滾動更新。
回滾到指定版本
kubectl rollout undo deployment/java-rolling-demo --to-revision=1
自動回滾(高級)
Kubernetes 本身不支持“自動回滾”,但可通過以下方式實現(xiàn):
- 結(jié)合監(jiān)控告警:如 Prometheus + Alertmanager 檢測到錯誤率飆升,觸發(fā) webhook 執(zhí)行
kubectl rollout undo; - 使用 Argo Rollouts:這是一個 K8s 擴展,支持金絲雀發(fā)布、自動回滾等高級功能。
?? Argo Rollouts 官網(wǎng) 提供了更強大的漸進式交付能力。
優(yōu)化滾動更新體驗的最佳實踐
1. 設(shè)置合理的探針參數(shù)
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 20 # 根據(jù)應用啟動時間調(diào)整
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 32. 控制更新速度
- 若系統(tǒng)敏感,可設(shè)置
maxUnavailable: 0和maxSurge: 1,確保始終有全部副本在線; - 若追求速度,可增大
maxSurge(如 50%),但需注意資源壓力。
3. 使用 preStop Hook 實現(xiàn)優(yōu)雅關(guān)閉
在 Java 應用終止前,執(zhí)行清理操作(如關(guān)閉連接池、拒絕新請求):
containers:
- name: app
image: yourname/java-rolling-demo:v2
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15"]同時,在 Spring Boot 中啟用優(yōu)雅關(guān)閉:
# application.yml
server:
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 30s這樣,當 Pod 被刪除時:
- K8s 先從 Service Endpoints 中移除該 Pod(不再接收新請求);
- 執(zhí)行
preStophook(等待 15 秒,讓現(xiàn)有請求完成); - 發(fā)送 SIGTERM 信號給 Java 進程;
- Spring Boot 優(yōu)雅關(guān)閉;
- 若超時未退出,發(fā)送 SIGKILL。
?? 更多關(guān)于優(yōu)雅關(guān)閉的內(nèi)容,可參考 Kubernetes 官方文檔 - Termination of Pods。
4. 監(jiān)控滾動更新指標
通過 Prometheus 監(jiān)控以下指標:
kube_deployment_status_replicas_updated:已更新的副本數(shù);kube_pod_status_ready:Pod 就緒狀態(tài);- 應用層指標:錯誤率、延遲、吞吐量。
一旦異常,立即告警或自動回滾。
滾動更新 vs 其他部署策略
Kubernetes 還支持其他部署策略,適用于不同場景:
| 策略 | 特點 | 適用場景 |
|---|---|---|
| RollingUpdate | 逐步替換,零停機 | 大多數(shù)生產(chǎn)環(huán)境 |
| Recreate | 先刪所有舊 Pod,再建新 Pod | 可接受短暫停機,或有狀態(tài)應用需完全重置 |
| Blue/Green | K8s 原生不支持,需 Service 切換 | 需要快速回滾、全量驗證 |
| Canary | K8s 原生不支持,需 Ingress 或 Service Mesh | 漸進式發(fā)布,小流量驗證 |
?? 對于 Java 應用,RollingUpdate 是最常用且最安全的選擇。
常見問題與排查
Q1: 更新卡住了,怎么辦?
- 檢查新 Pod 是否就緒:
kubectl describe pod <new-pod> - 查看日志:
kubectl logs <new-pod> - 檢查 readinessProbe 是否失??;
- 確認鏡像是否拉取成功(網(wǎng)絡(luò)、權(quán)限問題)。
Q2: 用戶偶爾收到 502 錯誤?
- 可能是舊 Pod 被終止時仍有請求在處理;
- 解決方案:配置
preStophook + 優(yōu)雅關(guān)閉; - 確保 Service 的
sessionAffinity未開啟(除非必要)。
Q3: 如何暫停/恢復滾動更新?
# 暫停 kubectl rollout pause deployment/java-rolling-demo # 恢復 kubectl rollout resume deployment/java-rolling-demo
適用于調(diào)試或分階段更新。
總結(jié)
滾動更新是 Kubernetes 為 Java 應用提供的強大部署能力,它通過漸進式替換 Pod,結(jié)合就緒探針和優(yōu)雅關(guān)閉機制,實現(xiàn)了零停機、高可用的持續(xù)交付。
要成功實施滾動更新,你需要:
- 構(gòu)建健康的 Java 應用:提供準確的
/actuator/health端點; - 合理配置 Deployment:設(shè)置
maxUnavailable、maxSurge、探針參數(shù); - 實現(xiàn)優(yōu)雅關(guān)閉:使用
preStophook 和 Spring Boot 的 graceful shutdown; - 監(jiān)控與回滾:建立可觀測性,準備快速回滾方案。
隨著云原生生態(tài)的成熟,滾動更新已成為 Java 應用上線的標準姿勢。掌握它,你就能在微服務(wù)時代游刃有余地交付高質(zhì)量軟件 ??。
?? 最后提醒:永遠在非生產(chǎn)環(huán)境充分測試你的滾動更新流程!自動化、標準化、可重復,是 DevOps 的核心精神。
到此這篇關(guān)于Java 部署滾動更新的方法(K8s RollingUpdate 策略)的文章就介紹到這了,更多相關(guān)Java 滾動更新內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java獲取網(wǎng)絡(luò)圖片上傳到OSS的方法
這篇文章主要為大家詳細介紹了java獲取網(wǎng)絡(luò)圖片上傳到OSS,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-10-10
skywalking源碼解析javaAgent工具ByteBuddy應用
這篇文章主要為大家介紹了skywalking源碼解析javaAgent工具ByteBuddy應用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-03-03
mybatis,foreach,找不到參數(shù)報錯問題及解決
這篇文章主要介紹了mybatis,foreach,找不到參數(shù)報錯問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
Java之判斷2000~2023年有哪些年份是閏年并打印輸出
這篇文章主要介紹了Java之判斷2000~2023年有哪些年份是閏年并打印輸出,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
Java的Hibernate框架中集合類數(shù)據(jù)結(jié)構(gòu)的映射編寫教程
Hibernate可以將Java中幾個內(nèi)置的集合結(jié)構(gòu)映射為數(shù)據(jù)庫使用的關(guān)系模型,下面我們就來看一下Java的Hibernate框架中集合類數(shù)據(jù)結(jié)構(gòu)的映射編寫教程:2016-07-07
java的MybatisPlus調(diào)用儲存過程的返回數(shù)據(jù)問題
這篇文章主要介紹了java的MybatisPlus調(diào)用儲存過程的返回數(shù)據(jù)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
@Autowired與@Resource在實現(xiàn)對象注入時的區(qū)別
這篇文章主要介紹了@Autowired與@Resource在實現(xiàn)對象注入時的區(qū)別,有需要的朋友可以借鑒參考下,希望能夠有所幫助2023-04-04

