Spring?Boot?Nacos?實現(xiàn)不停服發(fā)布過程詳解
引言
最近,由于業(yè)務(wù)屬性比較重要,對服務(wù)發(fā)布提出了更高的要求,希望能實現(xiàn)不停服發(fā)布。目前,團隊所有項目已經(jīng)完成基于K8s容器化部署,服務(wù)注冊發(fā)現(xiàn)基于Nacos,故本文基于該兩前提下進行討論。
基于該架構(gòu)下,需要解決如下幾個問題:
- K8s Java 應(yīng)用實現(xiàn)滾動發(fā)布,如果新服務(wù)不正常的情況下,不將新服務(wù)發(fā)布上去,且舊服務(wù)不下線
- 服務(wù)從Nacos上主動下線,讓流量不再流入
K8s 滾動發(fā)布
K8s 已天然支持滾動發(fā)布的機制,只需要簡單的配置就可以實現(xiàn)我們的要求,如下是具體配置摘要,我將從上到下進行說明。
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: example-service
name: example-service
spec:
#副本數(shù)量
replicas: {{.pod_replicas}}
selector:
matchLabels:
app: example-service
minReadySeconds: 30 #設(shè)置升級延遲時間15秒,等待15秒后升級
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 #升級過程中最多可以比原先設(shè)置多出的POD數(shù)量
maxUnavailable: 0
template:
metadata:
labels:
app: example-service
monitortype: backend
spec:
#以下內(nèi)容為可選,容器調(diào)度策略,保證同一deployment的多個副本位于不同的機器上,防止單節(jié)點掛掉導(dǎo)致服務(wù)不可用,由于涉及到要與運維溝通資源情況,無法直接給予固定配置。
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- example-service
topologyKey: kubernetes.io/hostname
containers:
- image: example-service:latest
imagePullPolicy: IfNotPresent
name: example-service
lifecycle:
preStop:
exec:
command:
- curl
- '-XPOST'
- '127.0.0.1:8080/actuator/shutdown'
readinessProbe: # 就緒探針
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30 # 延遲加載時間
periodSeconds: 10 # 重試時間間隔
timeoutSeconds: 1 # 超時時間設(shè)置
successThreshold: 1 # 健康閾值
failureThreshold: 3 # 不健康閾值
livenessProbe: # 存活探針
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30 # 延遲加載時間
periodSeconds: 10 # 重試時間間隔
timeoutSeconds: 1 # 超時時間設(shè)置
successThreshold: 1 # 健康閾值
failureThreshold: 3 # 不健康閾值
ports:
- containerPort: 8080
name: backend
protocol: TCP
envFrom:
- configMapRef:
#存放公共環(huán)境變量,比如:數(shù)據(jù)庫,redis,nacos等連接信息,每個項目的各個微服務(wù)基本都是一樣的。
name: pub-cm
- configMapRef:
#存放個性化配置,對于個別的服務(wù),除了公共變量外,會涉及其他引用信息。
name: example-service-cm
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
resources:
#設(shè)定以下是設(shè)定服務(wù)資源根據(jù)你的項目實際情況來
limits: # limits是代表的資源上限,服務(wù)能消耗的資源上限
cpu: {{.pod_cpu_limit}} #{limits_cpu}(必須),目前默認(rèn)單位為m,如果申請一核則為1024m,以此類推,默認(rèn)則為500m
memory: {{.pod_memory_limit}} #{limits_mem}(必須),目前默認(rèn)單位為Mi,如果申請1G內(nèi)存則為1024Mi,以此類推,默認(rèn)則為2048Mi
requests: # requests是服務(wù)所需最小的啟動資源,設(shè)置后如果node達(dá)不到這個資源要求就會部署失敗
cpu: {{.pod_cpu_request}} #{requests_cpu}(必須),目前默認(rèn)單位為m,如果申請一核則為1024m,以此類推,默認(rèn)則為250m
memory: {{.pod_memory_request}} #{requests_mem}(必須),目前默認(rèn)單位為Mi,如果申請1G內(nèi)存則為1024Mi,以此類推,默認(rèn)則為500Mi
# 以下為必須選項,項目做日志采集
volumeMounts:
- name: host-time
readOnly: true
mountPath: /etc/localtime
- name: example-service-log
mountPath: /home/logs # 如果接入日志必須存在容器內(nèi)/home/logs文件夾下存放日志文件
subPathExpr: $(POD_NAME)
volumes:
- name: example-service-log
hostPath:
path: /home/logs
type: DirectoryOrCreate
- name: host-time
hostPath:
path: /etc/localtime
type: ''
imagePullSecrets: # 寫死,前提是要執(zhí)行這個憑證創(chuàng)建命令
- name: harborha-secret001fe
---
apiVersion: policy/v1beta1
kind: PodDisruptionBudget #設(shè)置pod最小可用數(shù)量
metadata:
name: example-service-pdb
spec:
minAvailable: 50%
selector:
matchLabels:
app: example-serviceRollingUpdate 用于配置服務(wù)滾動升級的策略,maxSurge設(shè)置升級過程中最多可以比原先設(shè)置多出的POD數(shù)量。
preStop 在容器下線前執(zhí)行的操作,我這邊是希望他先調(diào)用 Spring Boot Actuator提供的下線接口,讓服務(wù)正常業(yè)務(wù)處理完后,下線掉。
readinessProbe 與 livenessProbe 都是探針,不同的地方是 readinessProbe 在容器啟動時,會檢查服務(wù)是否啟動完全和正常,正常后Pod才會被顯示正常,這種在使用Service或者Ingress的時候非常有用,livenessProbe 則是周期性檢查服務(wù)的健康性,如果服務(wù)不健康將下線掉服務(wù)。
需要注意 readinessProbe 的 initialDelaySeconds 是在服務(wù)啟動時開始計時,基于服務(wù)本身啟動時間設(shè)置一個相對合理的時間,以提高成功率。
PodDisruptionBudget 該控制器主要是通過設(shè)置應(yīng)用 Pod 處于正常狀態(tài)的最低個數(shù)或最低百分比,這樣可以保證在主動銷毀 Pod 的時候,不會銷毀太多的 Pod 導(dǎo)致業(yè)務(wù)異常中斷,從而提高業(yè)務(wù)的可用性。
PodDisruptionBudget與Deployment中 RollingUpdate 配置說明
在滾動更新的時候,會根據(jù)RollngUpdate 配置來,在Eviction(主動驅(qū)逐保護,e.g.存在不健康的節(jié)點,下線服務(wù))會根據(jù)PDB策略來。
Nacos主動下線
我們想象一種場景,A服務(wù)通過Nacos服務(wù)注冊發(fā)現(xiàn)LoadBalance方式直接調(diào)用B服務(wù),即使B服務(wù)容器已經(jīng)被銷毀,但如果A服務(wù)中還存在舊B服務(wù)的地址,那么就會調(diào)用異常,所以我們希望B服務(wù)下線的時候,A服務(wù)是有感知的,故我們選擇在主動通知Nacos服務(wù)下線服務(wù),同時,由Nacos去通知其他服務(wù)下線通知。
新建一個Endpoint,該方式是基于Spring Boot 2.7.X 版本的,其他版本可能有所區(qū)別。
@WebEndpoint(id = "deregister")
@Slf4j
@ConditionalOnClass(value = WebEndpoint.class)
public class NacosServiceDeregisterEndpoint {
private final NacosDiscoveryProperties nacosDiscoveryProperties;
private final NacosRegistration nacosRegistration;
private final NacosServiceRegistry nacosServiceRegistry;
public NacosServiceDeregisterEndpoint(NacosDiscoveryProperties nacosDiscoveryProperties, NacosRegistration nacosRegistration, NacosServiceRegistry nacosServiceRegistry) {
this.nacosDiscoveryProperties = nacosDiscoveryProperties;
this.nacosRegistration = nacosRegistration;
this.nacosServiceRegistry = nacosServiceRegistry;
}
@ReadOperation
public String deregisterEndPoint() {
String serviceName = nacosDiscoveryProperties.getService();
String groupName = nacosDiscoveryProperties.getGroup();
String clusterName = nacosDiscoveryProperties.getClusterName();
String ip = nacosDiscoveryProperties.getIp();
int port = nacosDiscoveryProperties.getPort();
log.info("Deregister from the Nacos, ServiceName:{}, GroupName:{}, ClusterName:{}, IP:{}, Port:{}", serviceName, groupName, clusterName, ip, port);
// 設(shè)置服務(wù)下線
nacosServiceRegistry.setStatus(nacosRegistration, "DOWN");
return "success";
}
}增加和修改bootstrap.yaml 配置
management:
endpoint:
health:
show-details: always
probes:
enabled: true
metrics:
enabled: true
shutdown:
enabled: true
endpoints:
web:
exposure:
include: "health,shutdown,metrics,deregister"
server:
port: 8080
metrics:
tags:
application: ${spring.application.name}后續(xù)
其實上方我們還遺漏一個問題——“服務(wù)更新時,服務(wù)已經(jīng)成功注冊到 Nacos,但容器還沒有被檢測為健康,此時流量打到新節(jié)點上。”這種情況應(yīng)該怎么解決。其實不用苦惱,
以上就是Spring Boot Nacos 實現(xiàn)不停服發(fā)布的詳細(xì)內(nèi)容,更多關(guān)于Spring Boot Nacos不停服發(fā)布的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java線程創(chuàng)建(賣票),線程同步(賣包子)的實現(xiàn)示例
這篇文章主要介紹了Java線程創(chuàng)建(賣票),線程同步(賣包子)的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
Java開發(fā)學(xué)習(xí)之Bean的作用域和生命周期詳解
這篇文章主要介紹了淺談Spring中Bean的作用域,生命周期和注解,從創(chuàng)建到消亡的完整過程,例如人從出生到死亡的整個過程就是一個生命周期。本文將通過示例為大家詳細(xì)講講,感興趣的可以學(xué)習(xí)一下2022-06-06
關(guān)于SpringMVC請求域?qū)ο蟮臄?shù)據(jù)共享問題
這篇文章主要介紹了SpringMVC請求域?qū)ο蟮臄?shù)據(jù)共享問題,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-02-02
使用maven實現(xiàn)redis與idea的連接問題
這篇文章主要介紹了使用maven實現(xiàn)redis與idea的連接問題,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-07-07

