背景:
藍(lán)綠發(fā)布、金絲雀發(fā)布、滾動(dòng)發(fā)布、A/B測試 ,是大家日常常見的發(fā)布工作。所以發(fā)布的原理和實(shí)操是一個(gè)非常、非常核心的面試知識(shí)點(diǎn)。
在40歲老架構(gòu)師 尼恩的讀者交流群(50+)中,其相關(guān)面試題是一個(gè)非常、非常高頻的交流話題。
只要一面試,基本就會(huì)問:
對(duì)灰度發(fā)布了解嗎?
對(duì)藍(lán)綠發(fā)布了解嗎?
對(duì)金絲雀發(fā)布了解嗎?
對(duì)滾動(dòng)發(fā)布發(fā)布了解嗎?
等等等等
很多小伙伴,回答起來,就是干巴巴的幾點(diǎn)。 導(dǎo)致給面試官的用戶體驗(yàn),非常差。
這里尼恩給大家 調(diào)優(yōu),做一下系統(tǒng)化、體系化的梳理。
在面試之前,也可以復(fù)習(xí)一下,使得大家可以充分展示一下大家雄厚的 “技術(shù)肌肉”,讓面試官愛到 “不能自已、口水直流”。
也一并把這些寶貴內(nèi)容作為“K8S云原生學(xué)習(xí)”重要的內(nèi)容,收入咱們的《K8S學(xué)習(xí)圣經(jīng)》,供后面的小伙伴參考,提升大家的 3高 架構(gòu)、設(shè)計(jì)、開發(fā)水平。
注:本文以 PDF 持續(xù)更新,最新尼恩 架構(gòu)筆記、面試題 的PDF文件,請(qǐng)從這里獲?。捍a云
先介紹一下藍(lán)綠發(fā)布、金絲雀發(fā)布、滾動(dòng)發(fā)布、A/B測試 的概念
再介紹實(shí)操
藍(lán)綠發(fā)布、金絲雀發(fā)布、滾動(dòng)發(fā)布、A/B測試 核心原理
藍(lán)綠發(fā)布(Blue-green Deployments) 核心原理
藍(lán)綠發(fā)布的目的是減少發(fā)布時(shí)的中斷時(shí)間
、能夠快速撤回發(fā)布
。
It’s basically a technique for releasing your application in a predictable manner with an goal of reducing any downtime associated with a release. It’s a quick way to prime your app before releasing, and also quickly roll back if you find issues.
藍(lán)綠發(fā)布中,一共有兩套系統(tǒng):一套是正在提供服務(wù)系統(tǒng),標(biāo)記為“綠色”;另一套是準(zhǔn)備發(fā)布的系統(tǒng),標(biāo)記為“藍(lán)色”。
兩套系統(tǒng)都是功能完善的,并且正在運(yùn)行的系統(tǒng),只是系統(tǒng)版本和對(duì)外服務(wù)情況不同。
最初,沒有任何系統(tǒng),沒有藍(lán)綠之分。
然后,第一套系統(tǒng)開發(fā)完成,直接上線,這個(gè)過程只有一個(gè)系統(tǒng),也沒有藍(lán)綠之分。
后來,開發(fā)了新版本,要用新版本替換線上的舊版本,在線上的系統(tǒng)之外搭建了一個(gè)使用新版本代碼的全新系統(tǒng)。 這時(shí)候,一共有兩套系統(tǒng)在運(yùn)行,正在對(duì)外提供服務(wù)的老系統(tǒng)是綠色系統(tǒng),新部署的系統(tǒng)是藍(lán)色系統(tǒng)。
藍(lán)色系統(tǒng)不對(duì)外提供服務(wù),用來做啥?
用來做發(fā)布前測試,測試過程中發(fā)現(xiàn)任何問題,可以直接在藍(lán)色系統(tǒng)上修改,不干擾用戶正在使用的系統(tǒng)。(注意,兩套系統(tǒng)沒有耦合的時(shí)候才能百分百保證不干擾)
藍(lán)色系統(tǒng)經(jīng)過反復(fù)的測試、修改、驗(yàn)證,確定達(dá)到上線標(biāo)準(zhǔn)之后,直接將用戶切換到藍(lán)色系統(tǒng):
切換后的一段時(shí)間內(nèi),依舊是藍(lán)綠兩套系統(tǒng)并存,但是用戶訪問的已經(jīng)是藍(lán)色系統(tǒng)。這段時(shí)間內(nèi)觀察藍(lán)色系統(tǒng)(新系統(tǒng))工作狀態(tài),如果出現(xiàn)問題,直接切換回綠色系統(tǒng)。
當(dāng)確信對(duì)外提供服務(wù)的藍(lán)色系統(tǒng)工作正常,不對(duì)外提供服務(wù)的綠色系統(tǒng)已經(jīng)不再需要的時(shí)候,藍(lán)色系統(tǒng)正式成為對(duì)外提供服務(wù)系統(tǒng),成為新的綠色系統(tǒng)。 原先的綠色系統(tǒng)可以銷毀,將資源釋放出來,用于部署下一個(gè)藍(lán)色系統(tǒng)。
藍(lán)綠發(fā)布只是上線策略中的一種,它不是可以應(yīng)對(duì)所有情況的萬能方案。 藍(lán)綠發(fā)布能夠簡單快捷實(shí)施的前提假設(shè)是目標(biāo)系統(tǒng)是非常內(nèi)聚的,如果目標(biāo)系統(tǒng)相當(dāng)復(fù)雜,那么如何切換、兩套系統(tǒng)的數(shù)據(jù)是否需要以及如何同步等,都需要仔細(xì)考慮。
BlueGreenDeployment中給出的一張圖特別形象:
藍(lán)綠發(fā)布缺點(diǎn):
切換是全量的,如果新版本有問題,則對(duì)用戶體驗(yàn)有直接影響, 需要雙倍機(jī)器資源。
藍(lán)綠發(fā)布需要路由或者ingress的配合。
金絲雀發(fā)布(anCanary Releases) 核心原理
金絲雀發(fā)布(Canary)也是一種發(fā)布策略,和國內(nèi)常說的灰度發(fā)布
是同一類策略。
藍(lán)綠發(fā)布是準(zhǔn)備兩套系統(tǒng),在兩套系統(tǒng)之間進(jìn)行切換,金絲雀策略是只有一套系統(tǒng),逐漸替換這套系統(tǒng)。
譬如說,目標(biāo)系統(tǒng)是一組無狀態(tài)的Web服務(wù)器,但是數(shù)量非常多,假設(shè)有一萬臺(tái)。這時(shí)候,藍(lán)綠發(fā)布就不能用了,因?yàn)槟悴豢赡苌暾?qǐng)一萬臺(tái)服務(wù)器專門用來部署藍(lán)色系統(tǒng)(在藍(lán)綠發(fā)布的定義中,藍(lán)色的系統(tǒng)要能夠承接所有訪問)。
可以想到的一個(gè)方法是: 只準(zhǔn)備幾臺(tái)服務(wù)器,在上面部署新版本的系統(tǒng)并測試驗(yàn)證。測試通過之后,擔(dān)心出現(xiàn)意外,還不敢立即更新所有的服務(wù)器。 先將線上的一萬臺(tái)服務(wù)器中的10臺(tái)更新為最新的系統(tǒng),然后觀察驗(yàn)證。確認(rèn)沒有異常之后,再將剩余的所有服務(wù)器更新。這個(gè)方法就是金絲雀發(fā)布。
金絲雀發(fā)布(canary release)的命名原因:
人們發(fā)現(xiàn)金絲雀這種生物對(duì)于有毒氣體很敏感。因此礦工在下井采礦之前會(huì)把金絲雀鳥兒投入或攜帶到礦井中,如果鳥兒能夠從礦井中飛出就表示井下有氧氣,礦工就可以安心下井采礦了。
通過這個(gè)故事,我們就可以看出金絲雀部署就是先把新版本試水的一部分就叫金絲雀發(fā)布。金絲雀發(fā)布可以快速而有效地發(fā)現(xiàn)軟件新版本存在的問題。
它的原理就是部署的時(shí)候讓一小部分用戶先試用功能 ,通過日志監(jiān)控或者服務(wù)器監(jiān)控,看下新用戶的反饋。如果沒有嚴(yán)重問題,盡快部署這個(gè)新版本,否則快速會(huì)退。小代價(jià)去試錯(cuò)
金絲雀發(fā)布(canary release)實(shí)際操作中還可以做更多控制,譬如說給最初更新的10臺(tái)服務(wù)器設(shè)置較低的權(quán)重、控制發(fā)送給這10臺(tái)服務(wù)器的請(qǐng)求數(shù),然后逐漸提高權(quán)重、增加請(qǐng)求數(shù)。
這個(gè)控制叫做“流量切分”,既可以用于金絲雀發(fā)布,也可以用于后面的A/B測試。
金絲雀部署也就是灰度發(fā)布的一種方式。
藍(lán)綠發(fā)布和金絲雀發(fā)布是兩種發(fā)布策略,都不是萬能的。
有時(shí)候兩者都可以使用,有時(shí)候只能用其中一種。
上面的例子中可以用金絲雀,不能用藍(lán)綠,那么什么時(shí)候可以用藍(lán)綠呢?整個(gè)系統(tǒng)只有一臺(tái)服務(wù)器的時(shí)候?;蛘哒f有足夠的資源,同時(shí)支撐運(yùn)行兩套系統(tǒng)的時(shí)候。
金絲雀發(fā)布缺點(diǎn): 自動(dòng)化流程不夠,發(fā)布期間需要人為去操作,可能會(huì)引起服務(wù)中斷等。
滾動(dòng)發(fā)布的 核心原理
滾動(dòng)發(fā)布是在金絲雀發(fā)布基礎(chǔ)上的進(jìn)一步優(yōu)化改進(jìn),是一種自動(dòng)化程度較高的發(fā)布方式,用戶體驗(yàn)比較平滑,是目前成熟型技術(shù)組織所采用的主流發(fā)布方式。
一次滾動(dòng)式發(fā)布一般由若干個(gè)發(fā)布批次組成,每批的數(shù)量一般是可以配置的(可以通過發(fā)布模板定義)。
例如,第一批1臺(tái)(金絲雀),第二批10%,第三批 50%,第四批100%。
每個(gè)批次之間留觀察間隔,通過手工驗(yàn)證或監(jiān)控反饋確保沒有問題再發(fā)下一批次,所以總體上滾動(dòng)式發(fā)布過程是比較緩慢的 (其中金絲雀的時(shí)間一般會(huì)比后續(xù)批次更長,比如金絲雀10 分鐘,后續(xù)間隔 2分鐘)。
A/B測試(A/B Testing) 核心原理
首先需要明確的是,A/B測試和藍(lán)綠發(fā)布以及金絲雀,完全是兩回事。
藍(lán)綠發(fā)布和金絲雀是發(fā)布策略,目標(biāo)是確保新上線的系統(tǒng)穩(wěn)定,關(guān)注的是新系統(tǒng)的BUG、隱患。A/B測試是效果測試,同一時(shí)間有多個(gè)版本的服務(wù)對(duì)外服務(wù),這些服務(wù)都是經(jīng)過足夠測試,達(dá)到了上線標(biāo)準(zhǔn)的服務(wù),有差異但是沒有新舊之分(它們上線時(shí)可能采用了藍(lán)綠發(fā)布的方式)。
A/B測試關(guān)注的是不同版本的服務(wù)的實(shí)際效果,譬如說轉(zhuǎn)化率、訂單情況等。
A/B版本
一般A/B版本用在創(chuàng)業(yè)公司第一次發(fā)布新版本時(shí),不清楚顧客更喜歡哪一個(gè)新版本的時(shí)候用的。
同時(shí)部署A和B兩個(gè)版本,通過后臺(tái)統(tǒng)計(jì)數(shù)據(jù),分析顧客更喜歡哪一個(gè)版本,然后選擇這個(gè)版本上線。在新產(chǎn)品搶占市場份額時(shí)作用巨大。
A/B測試時(shí),線上同時(shí)運(yùn)行多個(gè)版本的服務(wù),這些服務(wù)通常會(huì)有一些體驗(yàn)上的差異,譬如說頁面樣式、顏色、操作流程不同。相關(guān)人員通過分析各個(gè)版本服務(wù)的實(shí)際效果,選出效果最好的版本。
在A/B測試中,需要能夠控制流量的分配,譬如說,為A版本分配10%的流量,為B版本分配10%的流量,為C版本分配80%的流量。
藍(lán)綠發(fā)布、金絲雀發(fā)布、滾動(dòng)發(fā)布實(shí)操
spring cloud 灰度實(shí)操
spring cloud 灰度的實(shí)操方案比較多:
方案一:spring cloud gateway也可以實(shí)現(xiàn)灰度發(fā)布,
方案二:還有一款 spring cloud 的Discovery增強(qiáng)組件,可以實(shí)現(xiàn)灰度、藍(lán)綠等功能(https://github.com/Nepxion/Discovery)
簡單來說,灰度發(fā)布實(shí)質(zhì)是讓指定用戶訪問指定版本的服務(wù)。
spring cloud gateway也可以實(shí)現(xiàn)灰度發(fā)布大概的思路:
首先,需要指定用戶匹配到指定的路由規(guī)則。
其次,服務(wù)的版本號(hào)信息可以通過HTTP請(qǐng)求頭字段來指定。
最后,負(fù)載均衡算法需要能夠根據(jù)版本號(hào)信息來做服務(wù)實(shí)例的選擇。
在實(shí)操層面,spring cloud gateway 灰度發(fā)布的實(shí)現(xiàn)思路應(yīng)該比較簡單:
1、首先編寫自己的Predicate,實(shí)現(xiàn)指定用戶匹配到指定的路由規(guī)則中;
2、動(dòng)態(tài)修改請(qǐng)求,添加版本號(hào)信息,版本號(hào)信息可以放在HTTP Header中(此處可以通過原生AddRequestHeaderGatewayFilterFactory來實(shí)現(xiàn),無需自己寫代碼);
3、自定義路由規(guī)則,重寫負(fù)載均衡算法,根據(jù)版本號(hào)信息從注冊(cè)中心的服務(wù)實(shí)例上選擇相應(yīng)的服務(wù)版本進(jìn)行請(qǐng)求的轉(zhuǎn)發(fā)。
具體的方案,后面在尼恩的《SpringCloud 學(xué)習(xí)圣經(jīng)》PDF中,進(jìn)行補(bǔ)充。
反向代理網(wǎng)關(guān)灰度實(shí)操
利用云原生網(wǎng)關(guān)比如 APISIX :
- https://apisix.apache.org/zh/docs/apisix/plugins/traffic-split/#藍(lán)綠發(fā)布
- https://apisix.apache.org/zh/docs/apisix/plugins/traffic-split/#灰度發(fā)布
Kubernetes 中的灰度策略
Kubernetes 中常見的發(fā)布策略主要有如下六種:
重建(recreate) :即停止一個(gè)原有的容器,然后進(jìn)行容器的新建。
滾動(dòng)更新(rollingUpdate) :停掉一個(gè)容器,然后更新一個(gè)容器。
藍(lán)綠布署(blue/green ):準(zhǔn)備一套藍(lán)色的容器和一套綠色的容器,進(jìn)行流量切換。
金絲雀發(fā)布(canary) :更新部分容器,沒有問題后進(jìn)行逐步替換,直到切完。
A/B測試發(fā)布:即將發(fā)布的結(jié)果面向部分用戶,這塊沒有現(xiàn)成的組件,需要進(jìn)行自行處理,比如使用 Istio、Linkerd、Traefik 等。這種方式采用在 Http 的 Header 上進(jìn)行處理。
無損發(fā)布:現(xiàn)在很多發(fā)布都是將容器停掉,當(dāng)沒有請(qǐng)求的時(shí)候發(fā)布,實(shí)現(xiàn)無損發(fā)布。
Deployment金絲雀部署:按照流量比例
金絲雀部署就是將部分新版本發(fā)在舊的容器池里邊,然后進(jìn)行流量觀察,比如 30% 的流量切到新服務(wù)上,60% 的流量還在舊服務(wù)上。
這里,采用 svc 的方式進(jìn)行部署的選擇,這里使用 label 進(jìn)行 pod的選擇。
兩個(gè) Deployment 文件如下:
app-v1-canary.yaml里邊有 2 個(gè) pod 支撐這個(gè)服務(wù)。
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v1
labels:
app: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
version: v1.0.0
template:
metadata:
labels:
app: my-app
version: v1.0.0
spec:
containers:
- name: my-app
image: nien/nginx-gateway:v0.0.1
ports:
- name: http
containerPort: 8008
- name: probe
containerPort: 8008
env:
- name: VERSION
value: v1.0.0
- name: env_flag
value: VERSION-v1.0.0
livenessProbe:
httpGet:
path: /env
port: probe
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /env
port: probe
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: my-app
labels:
app: my-app
spec:
type: NodePort
ports:
- name: http
port: 8008 #對(duì)應(yīng)deployment的容器端口
targetPort: http
nodePort: 30808 #外部端口
selector:
app: my-app
接下來,我們創(chuàng)建 v2 版本 app-v2-canary.yaml 文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v2
labels:
app: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
version: v2.0.0
template:
metadata:
labels:
app: my-app
version: v2.0.0
spec:
containers:
- name: my-app
image: nien/nginx-gateway:v0.0.1
ports:
- name: http
containerPort: 8008
- name: probe
containerPort: 8008
env:
- name: VERSION
value: v2.0.0
- name: env_flag
value: VERSION-v2.0.0
livenessProbe:
httpGet:
path: /env
port: probe
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /env
port: probe
periodSeconds: 5
說一下要執(zhí)行的步驟:
- 啟動(dòng) V1 的服務(wù)版本:2 個(gè)復(fù)本。
- 啟動(dòng) V2 的服務(wù)版本:1 個(gè)復(fù)本。
- 觀察 V2 流量正常的情況的話,那么啟動(dòng) V2 的 2 個(gè)復(fù)本。
- 刪除 V1 的 2 個(gè)復(fù)本,流量全部到 V2 上。
step1 :啟動(dòng) V1 服務(wù),查看服務(wù)是否正確,然后觀察一下服務(wù)。
cd /vagrant/chapter28/k8s/deployPolicy
kubectl apply -f app-v1-canary.yaml
kubectl get svc -l app=my-app
curl http://192.168.49.2:30808
watch kubectl get pod
,能看到兩個(gè) pod
step2:啟動(dòng) V2 的服務(wù)版本:1 個(gè)復(fù)本
新打開容器,執(zhí)行命令,啟動(dòng) V2 服務(wù)。
上邊的 watch 將觀察到新增了 1 個(gè) pod, 此時(shí)共有 3 個(gè) pod, 2.0.0 的版本已經(jīng)上來了。
cd /vagrant/chapter28/k8s/deployPolicy
kubectl apply -f app-v2-canary.yaml
while sleep 1; do curl http://192.168.49.2:30808 | egrep VERSION; done
此時(shí)我們觀察版本 2 的服務(wù)是否正確
step3: 觀察 V2 流量正常的情況的話,那么啟動(dòng) V2 的 2 個(gè)復(fù)本。
v2能收到分發(fā)的流量,是正確的
如果正確,那么我們將版本 2 擴(kuò)展到 2個(gè)副本。
kubectl scale --replicas=2 deploy my-app-v2
執(zhí)行如下
watch 將觀察到新增了 1 個(gè) pod, 此時(shí)共有4個(gè) pod, 2.0.0 的版本已經(jīng)上來了。
這個(gè)時(shí)候版本 1 和版本 2 一樣了。
step4:刪除 V1 的 2 個(gè)復(fù)本,流量全部到 V2 上。
我們?cè)賹⒗习姹緞h除, 將 版本 1 的2個(gè) pod 給清除掉
kubectl delete deploy my-app-v1
清除之后,watch的效果,只剩新版本的2個(gè)pod,到此為止, 金絲雀發(fā)布完成
注意:因?yàn)橛胁糠职姹驹诰€上運(yùn)行,我們能夠?qū)ζ淙罩具M(jìn)行觀察和追蹤、定位問題;如果有問題也能快速將新版本清理掉;
kubectl delete deploy my-app-v1
實(shí)操總結(jié)
從實(shí)操的過程中,發(fā)現(xiàn)金絲雀發(fā)布較慢;
但是風(fēng)險(xiǎn)比較小,如果對(duì)代碼信心不足的情況, 可以采用此方法,影響范圍較小。
具體的實(shí)操過程,可以參見 尼恩的視頻。
實(shí)驗(yàn)完成,可以清理所有服務(wù)
kubectl delete all -l app=my-app
上面是按照流量的pod的比例,進(jìn)行 流量的控制。
Service會(huì)對(duì)提供同一個(gè)服務(wù)的多個(gè)Pod進(jìn)行聚合,并且提供一個(gè)統(tǒng)一的入口地址,通過訪問Service的入口地址就能訪問到后面的Pod服務(wù)。
Service底層的 流量分發(fā)策略,默認(rèn)為隨機(jī)或者 輪詢,具體與proxy代理的策略有關(guān)系。但是無論 隨機(jī)或者 輪詢, 整體上都是按照pod的比例,進(jìn)行流量的分發(fā)。
除了按照pod的比例分流,如果你想用更精細(xì)粒度的話,可以使用 滾動(dòng)發(fā)布。如下圖所示:
Deployment實(shí)現(xiàn)滾動(dòng)發(fā)布
利用Deployment的滾動(dòng)更新策略maxSurge和maxUnavailable設(shè)置最大可超期望的節(jié)點(diǎn)數(shù)和最大不可用節(jié)點(diǎn)數(shù)可實(shí)現(xiàn)簡單的金絲雀發(fā)布。
rollingUpdate.maxSurge最大可超期望的節(jié)點(diǎn)數(shù),百分比 10% 或者絕對(duì)數(shù)值 5
rollingUpdate.maxUnavailable最大不可用節(jié)點(diǎn)數(shù),百分比或者絕對(duì)數(shù)值
滾動(dòng)更新步驟:
- 準(zhǔn)備一個(gè)新版本的 POD,比如新版本為 V2,舊版本為 V1。
- 當(dāng) V2 版本的 POD 準(zhǔn)備好后,在負(fù)載均衡中的實(shí)例池中將 V2 的版本加入。
- 在實(shí)例池中剔除一個(gè) V1 版本的 POD。
- 逐個(gè)實(shí)例替換,直到每個(gè)版本都是 V2 版本。
如下圖所示:
滾動(dòng)更新使用的 YAML 方式如下:
spec:
replicas: 5
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 一次可以添加多少個(gè)Pod
maxUnavailable: 1 # 滾動(dòng)更新期間最大多少個(gè)Pod不可用
我們準(zhǔn)備兩個(gè)版本, app-v1.yaml 文件。
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v1
labels:
app: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
version: v1.0.0
template:
metadata:
labels:
app: my-app
version: v1.0.0
spec:
containers:
- name: my-app
image: nien/nginx-gateway:v0.0.1
ports:
- name: http
containerPort: 8008
- name: probe
containerPort: 8008
env:
- name: VERSION
value: v1.0.0
- name: env_flag
value: VERSION-v1.0.0
livenessProbe:
httpGet:
path: /env
port: probe
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /env
port: probe
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: my-app
labels:
app: my-app
spec:
type: NodePort
ports:
- name: http
port: 8008 #對(duì)應(yīng)deployment的容器端口
targetPort: http
nodePort: 30808 #外部端口
selector:
app: my-app
然后我們?cè)贉?zhǔn)備一下滾動(dòng)更新的 v2 版本,app-v2-rolling.yaml 文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v2
labels:
app: my-app
spec:
replicas: 3
# maxUnavailable設(shè)置為0可以完全確保在滾動(dòng)更新期間服務(wù)不受影響,還可以使用百分比的值來進(jìn)行設(shè)置。
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: my-app
version: v2.0.0
template:
metadata:
labels:
app: my-app
version: v2.0.0
spec:
containers:
- name: my-app
image: nien/nginx-gateway:v0.0.1
ports:
- name: http
containerPort: 8008
- name: probe
containerPort: 8008
env:
- name: VERSION
value: v2.0.0
- name: env_flag
value: VERSION-v2.0.0
livenessProbe:
httpGet:
path: /env
port: probe
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /env
port: probe
periodSeconds: 5
接下來,我們按如下步驟進(jìn)行操作:
- 啟動(dòng) app-v1 應(yīng)用
- 啟動(dòng) app-v2-rolling 應(yīng)用
- 觀察所有容器版本變?yōu)?V2 版本
step1 :啟動(dòng) V1 服務(wù),查看服務(wù)是否正確,然后觀察一下服務(wù)。
cd /vagrant/chapter28/k8s/deployPolicy
kubectl apply -f app-v1.yaml
watch kubectl get pod
kubectl get svc -l app=my-app
curl http://192.168.49.2:30808
while sleep 1; do curl http://192.168.49.2:30808 | egrep VERSION; done
啟動(dòng) app-v1 應(yīng)用并觀察,都已經(jīng)啟動(dòng),我們進(jìn)行接口調(diào)用。
step2: 啟動(dòng) app-v2-rolling 進(jìn)行滾動(dòng)發(fā)布
打開一個(gè)新的窗口,然后執(zhí)行滾動(dòng)更新命令
kubectl apply -f app-v2-rolling.yaml
在 watch 的終端觀察,開始的時(shí)候并沒刪除舊的 pod,而是創(chuàng)建好新的 pod 后,
step3:觀察所有容器版本變?yōu)?V2 版本
然后進(jìn)行挨個(gè)替,我們可以使用如下命令觀察接口請(qǐng)求, 漸漸的有了第二個(gè)版本的請(qǐng)求。
while sleep 1; do curl http://192.168.49.2:30808 | egrep VERSION; done
在這個(gè)過程中,我們發(fā)現(xiàn)第二個(gè)版本有問題,我們需要進(jìn)行回滾,此時(shí)我們需要執(zhí)行一下如下命令
kubectl rollout undo deploy my-app
如果想兩個(gè)版本都觀察一下,這個(gè)時(shí)候需要執(zhí)行命令。
kubectl rollout pause deploy my-app
如果發(fā)現(xiàn)第二個(gè)版本沒有問題,那么我們要恢復(fù)執(zhí)行
kubectl rollout resume deploy my-app
最后我們清理一下所有的部署
kubectl delete -l app=my-app
實(shí)操總結(jié)
從實(shí)操的過程中,發(fā)現(xiàn):滾動(dòng)部署沒有控制流量的情況;各個(gè)版本部署的時(shí)候需要一定的時(shí)間。
具體的實(shí)操過程,可以參見 尼恩的視頻。
實(shí)驗(yàn)完成,可以清理所有服務(wù)
kubectl delete all -l app=my-app
Deployment實(shí)現(xiàn)藍(lán)綠部署
我們需要準(zhǔn)備兩個(gè)版本,一個(gè)藍(lán)版本,一個(gè)綠藍(lán)本,這兩個(gè)版本同時(shí)存在,如下圖所示:
且兩個(gè)版本是獨(dú)立的,這個(gè)時(shí)候可以瞬間對(duì)兩個(gè)版本的切換。
接下來,我們采用 svc 的方式,通過 label selector 來進(jìn)行版本之間的選擇。
selector:
app: my-app
version: v1.0.0
我們創(chuàng)建一下 app-v1-svc.yaml 文件,我們首先創(chuàng)建一個(gè) svc 服務(wù),然后通過 label selector 來指定一下版本。
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v1
labels:
app: my-app
spec:
replicas: 2
selector:
matchLabels:
app: my-app
version: v1.0.0
template:
metadata:
labels:
app: my-app
version: v1.0.0
spec:
containers:
- name: my-app
image: nien/nginx-gateway:v0.0.1
ports:
- name: http
containerPort: 8008
- name: probe
containerPort: 8008
env:
- name: VERSION
value: v1.0.0
- name: env_flag
value: VERSION-v1.0.0
livenessProbe:
httpGet:
path: /env
port: probe
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /env
port: probe
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: my-app
labels:
app: my-app
spec:
type: NodePort
ports:
- name: http
port: 8008 #對(duì)應(yīng)deployment的容器端口
targetPort: http
nodePort: 30808 #外部端口
selector:
app: my-app
# 注意這里我們匹配 app 和 version 標(biāo)簽,當(dāng)要切換流量的時(shí)候,我們更新 version 標(biāo)簽的值,比如:v2.0.0
version: v1.0.0
接下來,我們?cè)賱?chuàng)建另一個(gè)版本 app-v2.yaml 文件
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app-v2
labels:
app: my-app
spec:
replicas: 1
selector:
matchLabels:
app: my-app
version: v2.0.0
template:
metadata:
labels:
app: my-app
version: v2.0.0
spec:
containers:
- name: my-app
image: nien/nginx-gateway:v0.0.1
ports:
- name: http
containerPort: 8008
- name: probe
containerPort: 8008
env:
- name: VERSION
value: v2.0.0
- name: env_flag
value: VERSION-v2.0.0
livenessProbe:
httpGet:
path: /env
port: probe
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /env
port: probe
periodSeconds: 5
接下來,我們按如下步驟執(zhí)行:
- 啟動(dòng)版本 1 服務(wù)
- 啟動(dòng)版本 2 服務(wù)
- 將版本 1 服務(wù)切換到版本 2,觀察服務(wù)情況
step1 :啟動(dòng) V1 服務(wù),查看服務(wù)是否正確,然后觀察一下服務(wù)。
啟動(dòng)版本的服務(wù)并觀察,沒有問題。
cd /vagrant/chapter28/k8s/blueGreenPolicy
kubectl apply -f app-v1-svc.yaml
watch kubectl get pod
kubectl get svc -l app=my-app
curl http://192.168.49.2:30808
while sleep 1; do curl http://192.168.49.2:30808 | egrep VERSION; done
step2:啟動(dòng) V2 的服務(wù)版本
啟動(dòng)版本 2 的服務(wù), 因?yàn)榘姹?2 沒有掛到 SVC,所以沒有辦法觀察,但是我們可以先啟動(dòng)。
kubectl apply -f app-v2.yaml
step3:將版本 1 服務(wù)切換到版本 2,觀察服務(wù)情況
這個(gè)時(shí)候我們進(jìn)行版本的切換,通過切換標(biāo)簽的方式
kubectl patch service my-app -p'{"spec":{"selector":{"version":"v2.0.0"}}}'
同時(shí),當(dāng)發(fā)現(xiàn)問題的時(shí)候,我們?cè)侔寻姹厩谢氐?v1.0.0 即可。
kubectl patch service my-app -p'{"spec":{"selector":{"version":"v1.0.0"}}}'
看看請(qǐng)求的變化
patch
如果一個(gè)pod已經(jīng)在運(yùn)行,這時(shí)需要對(duì)pod屬性進(jìn)行修改,又不想刪除pod,或不方便通過replace的方式進(jìn)行更新,這時(shí)就可以使用patch命令。
命令格式
kubectl patch \
(-f FILENAME | TYPE NAME) \
[-p PATCH|--patch-file FILE] \
[options]
實(shí)操總結(jié)
從實(shí)操的過程中,發(fā)現(xiàn):藍(lán)綠部署需要準(zhǔn)備兩套資源,相對(duì)有的時(shí)候需要的資源會(huì)多; 這種情況可以快速還原版本,不會(huì)出現(xiàn)滾動(dòng)更新那樣,回滾需要的時(shí)間會(huì)很久。
具體的實(shí)操過程,可以參見 尼恩的視頻。
實(shí)驗(yàn)完成,可以清理所有服務(wù)
kubectl delete all -l app=my-app
Ingress Annotations實(shí)現(xiàn)金絲雀發(fā)布
Ingress-Nginx 是一個(gè)K8S ingress工具,支持配置 Ingress Annotations 來實(shí)現(xiàn)不同場景下的灰度發(fā)布和測試。 Nginx Annotations 支持以下 4 種 Canary 規(guī)則:
- nginx.ingress.kubernetes.io/canary-by-header:基于 Request Header 的流量切分,適用于灰度發(fā)布以及 A/B 測試。當(dāng) Request Header 設(shè)置為 always時(shí),請(qǐng)求將會(huì)被一直發(fā)送到 Canary 版本;當(dāng) Request Header 設(shè)置為 never時(shí),請(qǐng)求不會(huì)被發(fā)送到 Canary 入口;對(duì)于任何其他 Header 值,將忽略 Header,并通過優(yōu)先級(jí)將請(qǐng)求與其他金絲雀規(guī)則進(jìn)行優(yōu)先級(jí)的比較。
- nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的 Request Header 的值,用于通知 Ingress 將請(qǐng)求路由到 Canary Ingress 中指定的服務(wù)。當(dāng) Request Header 設(shè)置為此值時(shí),它將被路由到 Canary 入口。該規(guī)則允許用戶自定義 Request Header 的值,必須與上一個(gè) annotation (即:canary-by-header)一起使用。
- nginx.ingress.kubernetes.io/canary-weight:基于服務(wù)權(quán)重的流量切分,適用于藍(lán)綠部署,權(quán)重范圍 0 - 100 按百分比將請(qǐng)求路由到 Canary Ingress 中指定的服務(wù)。權(quán)重為 0 意味著該金絲雀規(guī)則不會(huì)向 Canary 入口的服務(wù)發(fā)送任何請(qǐng)求。權(quán)重為 100 意味著所有請(qǐng)求都將被發(fā)送到 Canary 入口。
- nginx.ingress.kubernetes.io/canary-by-cookie:基于 Cookie 的流量切分,適用于灰度發(fā)布與 A/B 測試。用于通知 Ingress 將請(qǐng)求路由到 Canary Ingress 中指定的服務(wù)的cookie。當(dāng) cookie 值設(shè)置為 always時(shí),它將被路由到 Canary 入口;當(dāng) cookie 值設(shè)置為 never時(shí),請(qǐng)求不會(huì)被發(fā)送到 Canary 入口;對(duì)于任何其他值,將忽略 cookie 并將請(qǐng)求與其他金絲雀規(guī)則進(jìn)行優(yōu)先級(jí)的比較。
注意:金絲雀規(guī)則按優(yōu)先順序進(jìn)行如下排序:
canary-by-header - > canary-by-cookie - > canary-weight
我們可以把以上的四個(gè) annotation 規(guī)則可以總體劃分為以下兩類:
- 基于權(quán)重的 Canary 規(guī)則
- 基于用戶請(qǐng)求的 Canary 規(guī)則
注意: Ingress-Nginx 實(shí)在0.21.0 版本 中,引入的Canary 功能,因此要確保ingress版本OK
Ingress Annotations實(shí)現(xiàn)金絲雀發(fā)布 實(shí)操
在 《K8S學(xué)習(xí)圣經(jīng)》 最新PDF中給出,具體請(qǐng)參見 公眾號(hào): 技術(shù)自由圈
華為云的金絲雀發(fā)布
采用金絲雀部署,你可以在生產(chǎn)環(huán)境的基礎(chǔ)設(shè)施中小范圍的部署新的應(yīng)用代碼。
一旦應(yīng)用簽署發(fā)布,只有少數(shù)用戶被路由到它。最大限度的降低影響。
如果沒有錯(cuò)誤發(fā)生,新版本可以逐漸推廣到整個(gè)基礎(chǔ)設(shè)施。
下圖示范了金絲雀部署:
下圖為華為云的金絲雀發(fā)布界面:
步驟一:將流量從待部署節(jié)點(diǎn)移出,更新該節(jié)點(diǎn)服務(wù)到待發(fā)布狀態(tài),將該節(jié)點(diǎn)稱為金絲雀節(jié)點(diǎn);
步驟二:根據(jù)不同策略,將流量引入金絲雀節(jié)點(diǎn)。
策略可以根據(jù)情況指定,比如隨機(jī)樣本策略(隨機(jī)引入)、狗糧策略(就是內(nèi)部用戶或員工先嘗鮮)、分區(qū)策略(不同區(qū)域用戶使用不同版本)、用戶特征策略(這種比較復(fù)雜,需要根據(jù)用戶個(gè)人資料和特征進(jìn)行分流,類似于千人千面);
步驟三:金絲雀節(jié)點(diǎn)驗(yàn)證通過后,選取更多的節(jié)點(diǎn)稱為金絲雀節(jié)點(diǎn),
重復(fù)步驟一和步驟二,直到所有節(jié)點(diǎn)全部更新
金絲雀部署和藍(lán)綠有點(diǎn)像,但是它更加規(guī)避風(fēng)險(xiǎn)。
你可以階段性的進(jìn)行,而不用一次性從藍(lán)色版本切換到綠色版本。
參考
1、《k8s學(xué)習(xí)圣經(jīng)》PDF
2、https://blog.csdn.net/qq_42494960/article/details/119385952
技術(shù)自由的實(shí)現(xiàn)路徑:
實(shí)現(xiàn)你的 架構(gòu)自由:
《吃透8圖1模板,人人可以做架構(gòu)》
《10Wqps評(píng)論中臺(tái),如何架構(gòu)?B站是這么做的!??!》
《阿里二面:千萬級(jí)、億級(jí)數(shù)據(jù),如何性能優(yōu)化? 教科書級(jí) 答案來了》
《峰值21WQps、億級(jí)DAU,小游戲《羊了個(gè)羊》是怎么架構(gòu)的?》
《100億級(jí)訂單怎么調(diào)度,來一個(gè)大廠的極品方案》
《2個(gè)大廠 100億級(jí) 超大流量 紅包 架構(gòu)方案》
… 更多架構(gòu)文章,正在添加中
實(shí)現(xiàn)你的 響應(yīng)式 自由:
《響應(yīng)式圣經(jīng):10W字,實(shí)現(xiàn)Spring響應(yīng)式編程自由》
這是老版本 《Flux、Mono、Reactor 實(shí)戰(zhàn)(史上最全)》
實(shí)現(xiàn)你的 spring cloud 自由:
《Spring cloud Alibaba 學(xué)習(xí)圣經(jīng)》 PDF
《分庫分表 Sharding-JDBC 底層原理、核心實(shí)戰(zhàn)(史上最全)》
《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之間混亂關(guān)系(史上最全)》
實(shí)現(xiàn)你的 linux 自由:
《Linux命令大全:2W多字,一次實(shí)現(xiàn)Linux自由》
實(shí)現(xiàn)你的 網(wǎng)絡(luò) 自由:
《TCP協(xié)議詳解 (史上最全)》
《網(wǎng)絡(luò)三張表:ARP表, MAC表, 路由表,實(shí)現(xiàn)你的網(wǎng)絡(luò)自由?。 ?/p>
實(shí)現(xiàn)你的 分布式鎖 自由:
《Redis分布式鎖(圖解 - 秒懂 - 史上最全)》
《Zookeeper 分布式鎖 - 圖解 - 秒懂》
實(shí)現(xiàn)你的 王者組件 自由:
《隊(duì)列之王: Disruptor 原理、架構(gòu)、源碼 一文穿透》
《緩存之王:Caffeine 源碼、架構(gòu)、原理(史上最全,10W字 超級(jí)長文)》
《緩存之王:Caffeine 的使用(史上最全)》
《Java Agent 探針、字節(jié)碼增強(qiáng) ByteBuddy(史上最全)》文章來源:http://www.zghlxwxcb.cn/news/detail-756980.html
實(shí)現(xiàn)你的 面試題 自由:
4000頁《尼恩Java面試寶典 》 40個(gè)專題文章來源地址http://www.zghlxwxcb.cn/news/detail-756980.html
到了這里,關(guān)于1W字長文:藍(lán)綠發(fā)布、金絲雀發(fā)布、滾動(dòng)發(fā)布、A/B測試 原理和實(shí)操的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!