如何實(shí)現(xiàn)藍(lán)綠部署?
藍(lán)綠部署中,一共有兩套系統(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ù)情況不同。 開發(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ù),用來(lái)做什么呢?
用來(lái)做發(fā)布前測(cè)試,測(cè)試過(guò)程中發(fā)現(xiàn)任何問(wèn)題,可以直接在藍(lán)色系統(tǒng)上修改,不干擾用戶正在使用的 系統(tǒng)。(注意,兩套系統(tǒng)沒有耦合的時(shí)候才能百分百保證不干擾)藍(lán)色系統(tǒng)經(jīng)過(guò)反復(fù)的測(cè)試、修改、驗(yàn)證,確定達(dá)到上線標(biāo)準(zhǔn)之后,直接將用戶切換到藍(lán)色系統(tǒng):
切換后的一段時(shí)間內(nèi),依舊是藍(lán)綠兩套系統(tǒng)并存,但是用戶訪問(wèn)的已經(jīng)是藍(lán)色系統(tǒng)。這段時(shí)間內(nèi)觀察 藍(lán)色系統(tǒng)(新系統(tǒng))工作狀態(tài),如果出現(xiàn)問(wè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)可以銷毀,將資源釋放出來(lái), 用于部署下一個(gè)藍(lán)色系統(tǒng)。
藍(lán)綠部署的優(yōu)勢(shì)和缺點(diǎn)
優(yōu)點(diǎn):
1、更新過(guò)程無(wú)需停機(jī),風(fēng)險(xiǎn)較少
2、回滾方便,只需要更改路由或者切換?DNS?服務(wù)器,效率較高
缺點(diǎn):?1、成本較高,需要部署兩套環(huán)境。如果新版本中基礎(chǔ)服務(wù)出現(xiàn)問(wèn)題,會(huì)瞬間影響全網(wǎng)用戶;如果新 版本有問(wèn)題也會(huì)影響全網(wǎng)用戶。
2、需要部署兩套機(jī)器,費(fèi)用開銷大?
3、在非隔離的機(jī)器(Docker、VM)上操作時(shí),可能會(huì)導(dǎo)致藍(lán)綠環(huán)境被摧毀風(fēng)險(xiǎn)?
4、負(fù)載均衡器/反向代理/路由/DNS?處理不當(dāng),將導(dǎo)致流量沒有切換過(guò)來(lái)情況出現(xiàn)
通過(guò)?k8s?實(shí)現(xiàn)線上業(yè)務(wù)的藍(lán)綠部署
docker load -i myapp-lan.tar.gz
docker load -i myapp-lv.tar.gz
Kubernetes?不支持內(nèi)置的藍(lán)綠部署。目前最好的方式是創(chuàng)建新的?deployment,然后更新應(yīng)用程 序的?service?以指向新的?deployment?部署的應(yīng)用
1.創(chuàng)建藍(lán)色部署環(huán)境(新上線的環(huán)境,要替代綠色環(huán)境) 下面步驟在?k8s?的控制節(jié)點(diǎn)操作:
kubectl create ns blue-green
cat ?lan.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
? name: myapp-v1
? namespace: blue-green
spec:
? replicas: 3
? selector:
? ? matchLabels:
? ? ? app: myapp
? ? ? version: v1
? template:
? ? metadata:
? ? ? labels:
? ? ? ? app: myapp
? ? ? ? version: v1
? ? spec:
? ? ? containers:
? ? ? - name: myapp
? ? ? ? image:? janakiramm/myapp:v1
? ? ? ? imagePullPolicy: IfNotPresent
? ? ? ? ports:
? ? ? ? - containerPort: 80
cat lv.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
? name: myapp-v1
? namespace: blue-green
spec:
? replicas: 3
? selector:
? ? matchLabels:
? ? ? app: myapp
? ? ? version: v1
? template:
? ? metadata:
? ? ? labels:
? ? ? ? app: myapp
? ? ? ? version: v1
? ? spec:
? ? ? containers:
? ? ? - name: myapp
? ? ? ? image:? janakiramm/myapp:v1
? ? ? ? imagePullPolicy: IfNotPresent
? ? ? ? ports:
? ? ? ? - containerPort: 80
kubectl get pods -n blue-green
2.創(chuàng)建綠色部署環(huán)境(原來(lái)的部署環(huán)境)
n]# cat lv.yaml
apiVersion:? apps/v1
kind: Deployment
metadata:
? name: myapp-v2
? namespace: blue-green
spec:
? replicas: 3
? selector:
? ? matchLabels:
? ? ? app: myapp
? ? ? version: v2
? template:
? ? metadata:
? ? ? labels:
? ? ? ? app: myapp
? ? ? ? version: v2
? ? spec:
? ? ? containers:
? ? ? - name: myapp
? ? ? ? image: janakiramm/myapp:v2
? ? ? ? imagePullPolicy: IfNotPresent
? ? ? ? ports:
? ? ? ? - containerPort: 80
創(chuàng)建前端?service
cat service_lanlv.yaml
apiVersion: v1
kind: Service
metadata:
? name: myapp-lan
? namespace: blue-green
? labels:
? ? app: myapp
? ? version: v1
spec:
? type: NodePort
? ports:
? - port: 80
? ? nodePort: 30062
? ? name: http
? selector:
? ? app: myapp
? ? version: v1
在瀏覽器訪問(wèn)?http://k8s-master?節(jié)點(diǎn)?ip:30062?顯示如下:
修改?service_lanlv.yaml?配置文件,修改標(biāo)簽,讓其匹配到藍(lán)程序(升級(jí)之后的程序)
cat service_lanlv.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-lan namespace: blue-green labels:
app: myapp
version: v1 spec:
type: NodePort ports:
- port: 80
nodePort: 30062
name: http selector:
app: myapp version: v1
在瀏覽器訪問(wèn)?http://k8s-master?節(jié)點(diǎn)?ip:30062?顯示如下:
完成之后,把資源刪除
kubectl delete -f .
通過(guò)?k8s?實(shí)現(xiàn)滾動(dòng)更新-滾動(dòng)更新流程和策略
滾動(dòng)更新簡(jiǎn)介 滾動(dòng)更新是一種自動(dòng)化程度較高的發(fā)布方式,用戶體驗(yàn)比較平滑,是目前成熟型技術(shù)組織所采用的主流發(fā)布方式,一次滾動(dòng)發(fā)布一般由若干個(gè)發(fā)布批次組成,每批的數(shù)量一般是可以配置的(可以通過(guò)發(fā)布模 板定義),例如第一批?1?臺(tái),第二批?10%,第三批?50%,第四批?100%。每個(gè)批次之間留觀察間隔,通 過(guò)手工驗(yàn)證或監(jiān)控反饋確保沒有問(wèn)題再發(fā)下一批次,所以總體上滾動(dòng)式發(fā)布過(guò)程是比較緩慢的
#支持兩種更新,Recreate?和?RollingUpdate
#Recreate 是重建式更新,刪除一個(gè)更新一個(gè)
#RollingUpdate?滾動(dòng)更新,定義滾動(dòng)更新的更新方式的,也就是?pod?能多幾個(gè),少幾個(gè),控制
更新力度的
#我們更新的過(guò)程當(dāng)中最多允許超出的指定的目標(biāo)副本數(shù)有幾個(gè);
它有兩種取值方式,第一種直接給定數(shù)量,第二種根據(jù)百分比,百分比表示原本是?5?個(gè),最多可 以超出?20%,那就允許多一個(gè),最多可以超過(guò)?40%,那就允許多兩個(gè)
#最多允許幾個(gè)不可用
假設(shè)有 5?個(gè)副本,最多一個(gè)不可用,就表示最少有?4?個(gè)可用
deployment?是一個(gè)三級(jí)結(jié)構(gòu),deployment?控制?replicaset,replicaset?控制?pod, 例子:用?deployment?創(chuàng)建一個(gè)?pod
~]# cat deploy-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
? name: myapp-v1
? namespace: blue-green
spec:
? replicas: 2
? selector:
? ? matchLabels:
? ? ? app: myapp
? ? ? version: v1
? template:
? ? metadata:
? ? ? labels:
? ? ? ? app: myapp
? ? ? ? version: v1
? ? spec:
? ? ? containers:
? ? ? - name: myapp
? ? ? ? image:? janakiramm/myapp:v1
? ? ? ? imagePullPolicy: IfNotPresent
? ? ? ? ports:
? ? ? ? - containerPort: 80
kubectl apply -f deploy_demo.yaml
查看?deploy?狀態(tài):
kubectl get deploy -n blue-green
NAME? ? ? READY? UP-TO-DATE? AVAILABLE? AGE
myapp-v1? 2/2? ? 2? ? ? ? ? ? 2? ? ? ? ? 84s
創(chuàng)建的控制器名字是?myapp-v1
kubectl get rs -n blue-green
NAME? ? ? ? ? ? ? ? ? DESIRED? CURRENT? READY? AGE
myapp-v1-67fd9fc9c8? 2? ? ? ? 2? ? ? ? 2? ? ? 118s
創(chuàng)建?deploy?的時(shí)候也會(huì)創(chuàng)建一個(gè)?rs(replicaset),67fd9fc9c8?這個(gè)隨機(jī)數(shù)字是我們引用?pod?的 模板?template?的名字的?hash?值
通過(guò)?deployment?管理應(yīng)用,在更新的時(shí)候,可以直接編輯配置文件實(shí)現(xiàn),比方說(shuō)想要修改副本 數(shù),把?2?個(gè)變成?3?個(gè)
]# cat deploy-demo.yaml?直接修改?replicas?數(shù)量,如下,變成?3
spec:
replicas: 3
kubectl apply -f deploy-demo.yaml
注意:apply?不同于?create,apply?可以執(zhí)行多次;create?執(zhí)行一次,再執(zhí)行就會(huì)報(bào)錯(cuò)有重復(fù)。
get pods -n blue-green
NAME? ? ? ? ? ? ? ? ? ? ? ? READY? STATUS? ? RESTARTS? AGE
myapp-v1-67fd9fc9c8-6jq8g? 1/1? ? Running? 0? ? ? ? ? 3m58s
myapp-v1-67fd9fc9c8-fcmql? 1/1? ? Running? 0? ? ? ? ? 3m58s
myapp-v1-67fd9fc9c8-wtwvs? 1/1? ? Running? 0? ? ? ? ? 27s
#查看?myapp-v1?這個(gè)控制器的詳細(xì)信息
]# kubectl describe deploy myapp-v1 -n blue-green
describe deploy myapp-v1 -n blue-green
Name:? ? ? ? ? ? ? ? ? myapp-v1
Namespace:? ? ? ? ? ? ? blue-green
CreationTimestamp:? ? ? Fri, 18 Jun 2021 14:35:26 +0800
Labels:? ? ? ? ? ? ? ? <none>
Annotations:? ? ? ? ? ? deployment.kubernetes.io/revision: 1
Selector:? ? ? ? ? ? ? app=myapp,version=v1
Replicas:? ? ? ? ? ? ? 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
#默認(rèn)的更新策略?rollingUpdate
StrategyType:? ? ? ? ? RollingUpdate
MinReadySeconds:? ? ? ? 0
RollingUpdateStrategy:? 25% max unavailable, 25% max surge
#最多允許多?25%個(gè)?pod,25%表示不足一個(gè),可以補(bǔ)一個(gè)
Pod Template:
? Labels:? app=myapp
? ? ? ? ? version=v1
? Containers:
? myapp:
? ? Image:? ? ? ? janakiramm/myapp:v1
? ? Port:? ? ? ? 80/TCP
? ? Host Port:? ? 0/TCP
? ? Environment:? <none>
? ? Mounts:? ? ? <none>
? Volumes:? ? ? ? <none>
Conditions:
? Type? ? ? ? ? Status? Reason
? ----? ? ? ? ? ------? ------
? Progressing? ? True? ? NewReplicaSetAvailable
? Available? ? ? True? ? MinimumReplicasAvailable
OldReplicaSets:? <none>
NewReplicaSet:? myapp-v1-67fd9fc9c8 (3/3 replicas created)
Events:
? Type? ? Reason? ? ? ? ? ? Age? ? From? ? ? ? ? ? ? ? ? Message
? ----? ? ------? ? ? ? ? ? ----? ----? ? ? ? ? ? ? ? ? -------
? Normal? ScalingReplicaSet? 4m50s? deployment-controller? Scaled up replica set myapp-v1-67fd9fc9c8 to 2
? Normal? ScalingReplicaSet? 79s? ? deployment-controller? Scaled up replica set myapp-v1-67fd9fc9c8 to 3
例 :測(cè)試滾動(dòng)更新
在終端執(zhí)行如下:
kubectl get pods -l app=myapp -n blue-green -w
pending?表示正在進(jìn)行調(diào)度,ContainerCreating?表示正在創(chuàng)建一個(gè)?pod,running?表示運(yùn) 行一 個(gè)?pod,running?起來(lái)一個(gè)?pod?之后再?Terminating(停掉)一個(gè)?pod,以此類推,直 到所有?pod?完成滾動(dòng)升級(jí)
kubectl get rs -n blue-green
NAME? ? ? ? ? ? ? ? ? DESIRED? CURRENT? READY? AGE
myapp-v1-67fd9fc9c8? 3? ? ? ? 3? ? ? ? 3? ? ? 14m
myapp-v1-75fb478d6c? 0? ? ? ? 0? ? ? ? 0? ? ? 3m25s
上面可以看到?rs?有兩個(gè),下面那個(gè)是升級(jí)之前的,已經(jīng)被停掉,但是可以隨時(shí)回滾
kubectl rollout history deployment myapp-v1 -n blue-green
deployment.apps/myapp-v1
REVISION CHANGE-CAUSE
1 ?<none>
2 ?<none>
回滾操作如下:
~]# kubectl rollout undo deployment/myapp-v1 --to-revision=2 -n blue-green
4. 自定義滾動(dòng)更新策略
maxSurge?和?maxUnavailable?用來(lái)控制滾動(dòng)更新的更新策略
取值范圍
數(shù)值
1. maxUnavailable: [0,?副本數(shù)]
2. maxSurge: [0,?
副本數(shù)]
注意:兩者不能同時(shí)為?0。
比例
1. maxUnavailable: [0%, 100%]?向下取整,比如?10?個(gè)副本,5%的話==0.5?個(gè),但計(jì)算按照?0?個(gè);
2. maxSurge: [0%, 100%]?向上取整,比如?10?個(gè)副本,5%的話==0.5?個(gè),但計(jì)算按照?1?個(gè); 注意:兩者不能同時(shí)為?0。
建議配置
1. maxUnavailable == 0
2. maxSurge == 1?
這是我們生產(chǎn)環(huán)境提供給用戶的默認(rèn)配置。即“一上一下,先上后下”最平滑原則:
1?個(gè)新版本?pod ready(結(jié)合?readiness)后,才銷毀舊版本?pod。此配置適用場(chǎng)景是平滑更新、
保證服務(wù)平穩(wěn),但也有缺點(diǎn),就是“太慢”了。
總結(jié):
maxUnavailable:和期望的副本數(shù)比,不可用副本數(shù)最大比例(或最大值),這個(gè)值越小,越能保 證服務(wù)穩(wěn)定,更新越平滑;
maxSurge:和期望的副本數(shù)比,超過(guò)期望副本數(shù)最大比例(或最大值),這個(gè)值調(diào)的越大,副本更 新速度越快。
自定義策略:
修改更新策略:
maxUnavailable=1,maxSurge=1?
]# kubectl patch deployment myapp-v1 -p'{"spec":{"strategy":{"rollingUpdate": {"maxSurge":1,"maxUnavailable":1}}}}' -n blue-green
查看?myapp-v1?這個(gè)控制器的詳細(xì)信息
]# kubectl describe deployment myapp-v1 -n blue-green
顯示如下:
RollingUpdateStrategy: 1 max unavailable, 1 max surge
上面可以看到?RollingUpdateStrategy: 1 max unavailable, 1 max surge
這個(gè)?rollingUpdate?更新策略變成了剛才設(shè)定的,因?yàn)槲覀冊(cè)O(shè)定的?pod?副本數(shù)是?3,1?和?1?表示最 少不能少于?2?個(gè)?pod,最多不能超過(guò)?4?個(gè)?pod
這個(gè)就是通過(guò)控制?RollingUpdateStrategy?這個(gè)字段來(lái)設(shè)置滾動(dòng)更新策略的
4.3?通過(guò)?k8s?完成線上業(yè)務(wù)的金絲雀發(fā)布
4.3.1?金絲雀發(fā)布簡(jiǎn)介
金絲雀發(fā)布的由來(lái):17?世紀(jì),英國(guó)礦井工人發(fā)現(xiàn),金絲雀對(duì)瓦斯這種氣體十分敏感。空氣中哪怕有
極其微量的瓦斯,金絲雀也會(huì)停止歌唱;當(dāng)瓦斯含量超過(guò)一定限度時(shí),雖然人類毫無(wú)察覺,金絲雀卻早已 毒發(fā)身亡。當(dāng)時(shí)在采礦設(shè)備相對(duì)簡(jiǎn)陋的條件下,工人們每次下井都會(huì)帶上一只金絲雀作為瓦斯檢測(cè)指標(biāo), 以便在危險(xiǎn)狀況下緊急撤離。
金絲雀發(fā)布(又稱灰度發(fā)布、灰度更新):金絲雀發(fā)布一般先發(fā)?1?臺(tái),或者一個(gè)小比例,例如?2%的 服務(wù)器,主要做流量驗(yàn)證用,也稱為金絲雀?(Canary)?測(cè)試 (國(guó)內(nèi)常稱灰度測(cè)試)。
簡(jiǎn)單的金絲雀測(cè)試一般通過(guò)手工測(cè)試驗(yàn)證,復(fù)雜的金絲雀測(cè)試需要比較完善的監(jiān)控基礎(chǔ)設(shè)施配合,通 過(guò)監(jiān)控指標(biāo)反饋,觀察金絲雀的健康狀況,作為后續(xù)發(fā)布或回退的依據(jù)。 如果金絲測(cè)試通過(guò),則把剩余 的?V1?版本全部升級(jí)為?V2?版本。如果金絲雀測(cè)試失敗,則直接回退金絲雀,發(fā)布失敗。
優(yōu)點(diǎn):靈活,策略自定義,可以按照流量或具體的內(nèi)容進(jìn)行灰度(比如不同賬號(hào),不同參數(shù)),出現(xiàn)問(wèn) 題不會(huì)影響全網(wǎng)用戶
? 缺點(diǎn):沒有覆蓋到所有的用戶導(dǎo)致出現(xiàn)問(wèn)題不好排查
4.3.2?在?k8s?中實(shí)現(xiàn)金絲雀發(fā)布
打開一個(gè)標(biāo)簽?
1?監(jiān)測(cè)更新過(guò)程
]# kubectl get pods -l app=myapp -n blue-green -w
打開另一個(gè)標(biāo)簽?2?執(zhí)行如下操作:
]# kubectl set image deployment myapp-v1 myapp=janakiramm/myapp:v2 -n blue-green && kubectl rollout pause deployment myapp-v1 -n blue-green
回到標(biāo)簽?1?觀察,顯示如下:
myapp-v1-75fb478d6c-wddds 0/1 ? ??Pending 0 0s
myapp-v1-75fb478d6c-wddds 0/1 ? ??Pending 0 0s
myapp-v1-75fb478d6c-wddds 0/1 ? ??ContainerCreating? 0 0s
myapp-v1-75fb478d6c-wddds 0/1 ? ? ContainerCreating.?0 1s
myapp-v1-75fb478d6c-wddds 0/1 ? ??Running ? ? ? ??0 ? ? ? ? ?2s
注:上面的解釋說(shuō)明把?myapp?這個(gè)容器的鏡像更新到?janakiramm/myapp:v2?版本 更新鏡像之 后,創(chuàng)建一個(gè)新的?pod?就立即暫停,這就是我們說(shuō)的金絲雀發(fā)布;如果暫停幾個(gè)小時(shí)之后沒有問(wèn)題,那 么取消暫停,就會(huì)依次執(zhí)行后面步驟,把所有?pod?都升級(jí)。
解除暫停:
回到標(biāo)簽?
1?繼續(xù)觀察:
例子:
回滾到之前的部署
Kubectl rollout undo deployment/abc
#檢查daemonset的rollout狀態(tài)
Kubectl rollout狀態(tài)daemonset/foo
可用命令:
history查看rollout歷史
pause將提供的資源標(biāo)記為暫停狀態(tài)
restart重啟資源
resume恢復(fù)暫停的資源
status顯示下線狀態(tài)
撤銷之前的rollout
用法:
kubectl rollout子命令[options]
打開標(biāo)簽?2?執(zhí)行如下:
#查詢幫助 ? ?kubectl rollout --help ??
# kubectl rollout resume --help ?一般后面接控制器 deployment/nginx
kubectl rollout resume deployment myapp-v1 -n blue-green
在標(biāo)簽?1?可以看到如下一些信息,是把余下的?pod?里的容器都更的版本:
kubectl get rs -n blue-green
可以看到?replicaset?控制器有?2?個(gè)了
NAME? ? ? ? ? ? ? ? ? DESIRED? CURRENT? READY? AGE
myapp-v1-67fd9fc9c8? 0? ? ? ? 0? ? ? ? 0? ? ?13m
myapp-v1-75fb478d6c? 3? ? ? ? 3? ? ? ? 3? ? ?7m28s
回滾:
如果發(fā)現(xiàn)剛才升級(jí)的這個(gè)版本有問(wèn)題可以回滾,查看當(dāng)前有哪幾個(gè)版本:
kubectl rollout history deployment myapp-v1 -n blue-green
deployment.apps/myapp-v1
REVISION? CHANGE-CAUSE
1 ? ? ? ?<none>
2 ? ? ? ? <none>
上面說(shuō)明一共有兩個(gè)版本,回滾的話默認(rèn)回滾到上一版本,可以指定參數(shù)回滾:
kubectl rollout undo deployment myapp-v1 -n blue-green --to-revision=1
CURRENT
READY AGE
#回滾到的版本號(hào)是?1
kubectl rollout history deployment myapp-v1 -n blue-green
deployment.apps/myapp-v1
REVISION? CHANGE-CAUSE
2 ? ? ? ? <none>
3 ? ? ? ? <none>
上面可以看到第一版沒了,被還原成了第三版,第三版的前一版是第二版
kubectl get rs -n blue-green -o wide
NAME? ? ? ? ? ? ? ? ? DESIRED? CURRENT? READY? AGE? CONTAINERS? IMAGES? ? ? ? ? ? ? ? SELECTOR
myapp-v1-67fd9fc9c8 ? ?2 ? ? ? ?2 ? ? ? ? 2 ? ? ?1h? myapp? ? ? ? janakiramm/myapp:v1? app=myapp,pod-template-hash=67fd9fc9c8,version=v1
myapp-v1-75fb478d6c? 0? ? ? ? 0? ? ? ? 0 ? ? ?1h? myapp? ? ? ? janakiramm/myapp:v2? app=myapp,pod-template-hash=75fb478d6c,version=v1
以看到上面的?rs?已經(jīng)用第一個(gè)了,這個(gè)就是還原之后的?rs
七層調(diào)度器?Ingress Controller?安裝和配置
4.4.1 Ingress?介紹
Ingress?官網(wǎng)定義:Ingress?可以把進(jìn)入到集群內(nèi)部的請(qǐng)求轉(zhuǎn)發(fā)到集群中的一些服務(wù)上,從而可以把 服務(wù)映射到集群外部。Ingress?能把集群內(nèi)?Service?配置成外網(wǎng)能夠訪問(wèn)的?URL,流量負(fù)載均衡,提供 基于域名訪問(wèn)的虛擬主機(jī)等。
Ingress?簡(jiǎn)單的理解就是你原來(lái)需要改?Nginx?配置,然后配置各種域名對(duì)應(yīng)哪個(gè)?Service,現(xiàn)在把 這個(gè)動(dòng)作抽象出來(lái),變成一個(gè)?Ingress?對(duì)象,你可以用?yaml?創(chuàng)建,每次不要去改?Nginx?了,直接改?yaml?然后創(chuàng)建/更新就行了;那么問(wèn)題來(lái)了:”Nginx?該怎么處理?”
Ingress Controller?這東西就是解決?“Nginx?的處理方式”?的;Ingress Controller?通過(guò)與?Kubernetes API?交互,動(dòng)態(tài)的去感知集群中?Ingress?規(guī)則變化,然后讀取他,按照他自己模板生成一 段?Nginx?配置,再寫到?Ingress Controller Nginx?里,最后?reload?一下,工作流程如下圖:
實(shí)際上?Ingress?也是?Kubernetes API?的標(biāo)準(zhǔn)資源類型之一,它其實(shí)就是一組基于?DNS?名稱 (host)或?URL?路徑把請(qǐng)求轉(zhuǎn)發(fā)到指定的?Service?資源的規(guī)則。用于將集群外部的請(qǐng)求流量轉(zhuǎn)發(fā)到集群 內(nèi)部完成的服務(wù)發(fā)布。我們需要明白的是,Ingress?資源自身不能進(jìn)行“流量穿透”,僅僅是一組規(guī)則的集 合,這些集合規(guī)則還需要其他功能的輔助,比如監(jiān)聽某套接字,然后根據(jù)這些規(guī)則的匹配進(jìn)行路由轉(zhuǎn)發(fā), 這些能夠?yàn)?Ingress?資源監(jiān)聽套接字并將流量轉(zhuǎn)發(fā)的組件就是?Ingress Controller。
注:Ingress?控制器不同于?Deployment?控制器的是,Ingress?控制器不直接運(yùn)行,它不由?kube- controller-manager?進(jìn)行控制,它僅僅是?Kubernetes?集群的一個(gè)附件,類似于?CoreDNS,需要 在集群上單獨(dú)部署。
4.4.2 Ingress Controller?介紹
Ingress Controller?是一個(gè)七層負(fù)載均衡調(diào)度器,客戶端的請(qǐng)求先到達(dá)這個(gè)七層負(fù)載均衡調(diào)度器,
由七層負(fù)載均衡器在反向代理到后端?pod,常見的七層負(fù)載均衡器有?nginx、traefik,以我們熟悉的?nginx?為例,假如請(qǐng)求到達(dá)?nginx,會(huì)通過(guò)?upstream?反向代理到后端?pod?應(yīng)用,但是后端?pod?的?ip?地址是一直在變化的,因此在后端?pod?前需要加一個(gè)?service,這個(gè)?service?只是起到分組的作用,那 么我們?upstream?只需要填寫?service?地址即可
4.4.3 Ingress?和?Ingress Controller?總結(jié)
Ingress Controller
Ingress Controller?
可以理解為控制器,它通過(guò)不斷的跟?Kubernetes API?交互,實(shí)時(shí)獲取后端
Service、Pod?的變化,比如新增、刪除等,結(jié)合?Ingress?定義的規(guī)則生成配置,然后動(dòng)態(tài)更新上邊的?Nginx?或者?trafik?負(fù)載均衡器,并刷新使配置生效,來(lái)達(dá)到服務(wù)自動(dòng)發(fā)現(xiàn)的作用。
Ingress?則是定義規(guī)則,通過(guò)它定義某個(gè)域名的請(qǐng)求過(guò)來(lái)之后轉(zhuǎn)發(fā)到集群中指定的?Service。它可 以通過(guò)?Yaml?文件定義,可以給一個(gè)或多個(gè)?Service?定義一個(gè)或多個(gè)?Ingress?規(guī)則。
4.4.4?使用?Ingress Controller?代理?k8s?內(nèi)部應(yīng)用的流程
(1)部署?Ingress controller,我們?ingress controller?使用的是?nginx
(2)創(chuàng)建?Pod?應(yīng)用,可以通過(guò)控制器創(chuàng)建?pod
(3)創(chuàng)建?Service,用來(lái)分組?pod
(4)創(chuàng)建?Ingress http,測(cè)試通過(guò)?http?訪問(wèn)應(yīng)用
(5)創(chuàng)建?Ingress https,測(cè)試通過(guò)?https?訪問(wèn)應(yīng)用
客戶端通過(guò)七層調(diào)度器訪問(wèn)后端 pod?的方式
使用七層負(fù)載均衡調(diào)度器 ingress controller?時(shí),當(dāng)客戶端訪問(wèn)?kubernetes?集群內(nèi)部的應(yīng)用時(shí), 數(shù)據(jù)包走向如下圖流程所示:
4.4.5?安裝?Nginx Ingress Controller
#把?defaultbackend.tar.gz?和?nginx-ingress-controller.tar.gz?鏡像上傳到 工作節(jié)點(diǎn)
cat nginx-ingress-controller-rbac.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
? name: nginx-ingress-controller
? labels:
? ? k8s-app: nginx-ingress-controller
? namespace: kube-system
spec:
? replicas: 1
? selector:
? ? matchLabels:
? ? ? k8s-app: nginx-ingress-controller
? template:
? ? metadata:
? ? ? labels:
? ? ? ? k8s-app: nginx-ingress-controller
? ? spec:
? ? ? # hostNetwork makes it possible to use ipv6 and to preserve the source IP correctly regardless of docker configuration
? ? ? # however, it is not a hard dependency of the nginx-ingress-controller itself and it may cause issues if port 10254 already is taken on the host
? ? ? # that said, since hostPort is broken on CNI (https://github.com/kubernetes/kubernetes/issues/31307) we have to use hostNetwork where CNI is used
? ? ? # like with kubeadm
? ? ? # hostNetwork: true #注釋表示不使用宿主機(jī)的80口,
? ? ? terminationGracePeriodSeconds: 60
? ? ? hostNetwork: true? #表示容器使用和宿主機(jī)一樣的網(wǎng)絡(luò)
? ? ? serviceAccountName: nginx-ingress-serviceaccount #引用前面創(chuàng)建的serviceacount
? ? ? containers:
? ? ? - image: registry.cn-hangzhou.aliyuncs.com/peter1009/nginx-ingress-controller:0.20.0? ? ? #容器使用的鏡像
? ? ? ? name: nginx-ingress-controller? #容器名
? ? ? ? readinessProbe:? #啟動(dòng)這個(gè)服務(wù)時(shí)要驗(yàn)證/healthz 端口10254會(huì)在運(yùn)行的node上監(jiān)聽。
? ? ? ? ? httpGet:
? ? ? ? ? ? path: /healthz
? ? ? ? ? ? port: 10254
? ? ? ? ? ? scheme: HTTP
? ? ? ? livenessProbe:
? ? ? ? ? httpGet:
? ? ? ? ? ? path: /healthz
? ? ? ? ? ? port: 10254
? ? ? ? ? ? scheme: HTTP
? ? ? ? ? initialDelaySeconds: 10? #每隔10做健康檢查
? ? ? ? ? timeoutSeconds: 1
? ? ? ? ports:
? ? ? ? - containerPort: 80
? ? ? ? ? hostPort: 80? ? #80映射到80
#? ? ? ? - containerPort: 443
#? ? ? ? ? hostPort: 443
? ? ? ? env:
? ? ? ? ? - name: POD_NAME
? ? ? ? ? ? valueFrom:
? ? ? ? ? ? ? fieldRef:
? ? ? ? ? ? ? ? fieldPath: metadata.name
? ? ? ? ? - name: POD_NAMESPACE
? ? ? ? ? ? valueFrom:
? ? ? ? ? ? ? fieldRef:
? ? ? ? ? ? ? ? fieldPath: metadata.namespace
? ? ? ? args:?
? ? ? ? ? - /nginx-ingress-controller
? ? ? ? - --default-backend-service=$(POD_NAMESPACE)/default-http-backend
#? ? ? ? - --default-ssl-certificate=$(POD_NAMESPACE)/ingress-secret? ? #這是啟用Https時(shí)用的
#? ? ? nodeSelector:? #指明運(yùn)行在哪,此IP要和default backend是同一個(gè)IP
#? ? ? ? kubernetes.io/hostname: 10.3.1.17? #上面映射到了hostport80,確保此IP80,443沒有占用.
#
? ? ? nodeName: god64
vim?default-backend.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
? name: default-http-backend
? labels:
? ? k8s-app: default-http-backend
? namespace: kube-system
spec:
? replicas: 1
? selector:
? matchLabels:
? ? k8s-app: default-http-backend
? template:
? ? metadata:
? ? ? labels:
? ? ? ? k8s-app: default-http-backend
? ? spec:
? ? ? terminationGracePeriodSeconds: 60
? ? ? containers:
? ? ? - name: default-http-backend
? ? ? ? # Any image is permissable as long as:
? ? ? ? # 1. It serves a 404 page at /
? ? ? ? # 2. It serves 200 on a /healthz endpoint
? ? ? ? image: registry.cn-hangzhou.aliyuncs.com/hachikou/defaultbackend:1.0
? ? ? ? livenessProbe:
? ? ? ? ? httpGet:
? ? ? ? ? ? path: /healthz? #這個(gè)URI是 nginx-ingress-controller中nginx里配置好的localtion
? ? ? ? ? ? port: 8080
? ? ? ? ? ? scheme: HTTP
? ? ? ? ? initialDelaySeconds: 30? #30s檢測(cè)一次/healthz
? ? ? ? ? timeoutSeconds: 5
? ? ? ? ports:
? ? ? ? - containerPort: 8080
#? ? ? ? resources:
#? ? ? ? ? limits:
#? ? ? ? ? ? cpu: 10m
#? ? ? ? ? ? memory: 20Mi
#? ? ? ? ? requests:
#? ? ? ? ? ? cpu: 10m
#? ? ? ? ? ? memory: 20Mi
? ? ? nodeName: god64
---
apiVersion: v1
kind: Service? ? #為default backend 創(chuàng)建一個(gè)service
metadata:
? name: default-http-backend
? namespace: kube-system
? labels:
? ? k8s-app: default-http-backend
spec:
? ports:
? - port: 80
? ? targetPort: 8080
? selector:
? ? k8s-app: default-http-backend
kubectl apply -f nginx-ingress-controller-rbac.yml
kubectl apply -f default-backend.yaml
kubectl get pods -n kube-system | grep ingress
#顯示如下,說(shuō)明部署成功了:
nginx-ingress-controller-74cf657846-qrvdm 1/1 Running 0 30s?注意:
default-backend.yaml?和?nginx-ingress-controller.yaml?文件指定了?nodeName,表示?default?和?nginx-ingress-controller?部署在 god64 節(jié)點(diǎn),大家的?node?節(jié)點(diǎn) 如果主機(jī)名不是 god64,需要自行修改成自己的主機(jī)名,這樣才會(huì)調(diào)度成功,一定要讓?default- http-backend?和?nginx-ingress-controller?這兩個(gè)?pod?在一個(gè)節(jié)點(diǎn)上。
default-backend.yaml:這是官方要求必須要給的默認(rèn)后端,提供?404?頁(yè)面的。它還提供了一個(gè)?http?檢測(cè)功能,檢測(cè)?nginx-ingress-controll?健康狀態(tài)的,通過(guò)每隔一定時(shí)間訪問(wèn)?nginx-ingress- controll?的/healthz?頁(yè)面,如是沒有響應(yīng)就返回?404?之類的錯(cuò)誤碼。
通過(guò)?Ingress-nginx?實(shí)現(xiàn)灰度發(fā)布
Ingress-Nginx?是一個(gè)?K8S ingress?工具,支持配置?Ingress Annotations?來(lái)實(shí)現(xiàn)不同場(chǎng)景下的 灰度發(fā)布和測(cè)試。?Nginx Annotations?支持以下幾種?Canary?規(guī)則:
假設(shè)我們現(xiàn)在部署了兩個(gè)版本的服務(wù),老版本和?canary?版本?nginx.ingress.kubernetes.io/canary-by-header:基于?Request Header?的流量切分,適用于灰度發(fā)布以及?A/B?測(cè)試。當(dāng)?Request Header?設(shè)置為?always?時(shí),請(qǐng)求將會(huì)被一直發(fā)送到?Canary?版 本;當(dāng)?Request Header?設(shè)置為?never?時(shí),請(qǐng)求不會(huì)被發(fā)送到?Canary?入口。
nginx.ingress.kubernetes.io/canary-by-header-value:要匹配的?Request Header?的值, 用于通知?Ingress?將請(qǐng)求路由到?Canary Ingress?中指定的服務(wù)。當(dāng)?Request Header?設(shè)置為此值 時(shí),它將被路由到?Canary?入口。
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)重為?60?意味著?60%流量轉(zhuǎn)到?canary。權(quán)重為?100?意 味著所有請(qǐng)求都將被發(fā)送到?Canary?入口。
nginx.ingress.kubernetes.io/canary-by-cookie:基于?Cookie?的流量切分,適用于灰度發(fā)布 與?A/B?測(cè)試。用于通知?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?入口。
部署兩個(gè)版本的服務(wù)
這里以簡(jiǎn)單nginx?為例,先部署一個(gè)?v1?版本:
vim 1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
? name: nginx-v1
spec:
? replicas: 1
? selector:
? ? matchLabels:
? ? ? app: nginx
? ? ? version: v1
? template:
? ? metadata:
? ? ? labels:
? ? ? ? app: nginx
? ? ? ? version: v1
? ? spec:
? ? ? containers:
? ? ? - name: nginx
? ? ? ? image: "openresty/openresty:centos"
? ? ? ? ports:
? ? ? ? - name: http
? ? ? ? ? protocol: TCP
? ? ? ? ? containerPort: 80
? ? ? ? volumeMounts:
? ? ? ? - mountPath: /usr/local/openresty/nginx/conf/nginx.conf
? ? ? ? ? name: config
? ? ? ? ? subPath: nginx.conf
? ? ? volumes:
? ? ? - name: config
? ? ? ? configMap:
? ? ? ? ? name: nginx-v1
---
apiVersion: v1
kind: ConfigMap
metadata:
? labels:
? ? app: nginx
? ? version: v1
? name: nginx-v1
data:
? nginx.conf: |-
? ? worker_processes 1;
? ? events {
? ? ? accept_mutex on;
? ? ? multi_accept on;
? ? ? use epoll;
? ? ? worker_connections 1024;
}
http{
? ? ignore_invalid_headers off;
? ? server{
? ? ? listen 80;
? ? ? location/{
? ? ? ? ? access_by_lua '
? ? ? ? ? ? ? local header_str = ngx.say("nginx-v1")
? ? ? ? ? ';
}
? }
}
---
apiVersion: v1
kind: Service
metadata:
? name: nginx-v1
spec:
? type: ClusterIP
? ports:
? - port: 80
? ? protocol: TCP
? ? name: http
? seleor:
? ? app: nginx
? ? version: v1
再部署一個(gè)?v2?版本:
apiVersion: apps/v1
kind: Deployment
metadata:
? name: nginx-v2
spec:
? replicas: 1
? selector:
? ? matchLabels:
? ? ? app: nginx
? ? ? version: v2
? template:
? ? metadata:
? ? ? labels:
? ? ? ? app: nginx
? ? ? ? version: v2
? spec:
? ? containers:
? ? - name: nginx
? ? ? image: "openresty/openresty:centos"
? ? ? ports:
? ? ? - name: http
? ? ? ? protocol: TCP
? ? ? ? containerPort: 80
? ? ? volumeMounts:
? ? ? - mountPath: /usr/local/openresty/nginx/conf/nginx.conf
? ? ? ? name: config
? ? ? ? subPath: nginx.conf
? ? volumes:
? ? - name: config
? ? ? configMap:
? ? ? ? name: nginx-v2
---
apiVersion: v1
kind: ConfigMap
metadata:
? labels:
? ? app: nginx
? ? version: v2
? name: nginx-v2
data:
? nginx.conf: |-
? ? worker_processes 1;
? ? events {
? ? ? accept_mutex on;
? ? ? multi_accept on;
? ? ? use epoll;
? ? ? worker_connections 1024;
}
http{
? ? ignore_invalid_headers off;
? ? server{
? ? ? listen 80;
? ? ? location/{
? ? ? ? ? access_by_lua '
? ? ? ? ? ? ? local header_str = ngx.say("nginx-v1")
? ? ? ? ? ? ';
? ? ? }
? }
}
---
apiVersion: v1
kind: Service
metadata:
? name: nginx-v2
spec:
? type: ClusterIP
? ports:
? - port: 80
? ? protocol: TCP
? ? name: http
? selector:
? ? app: nginx
? ? version: v2
再創(chuàng)建一個(gè)?Ingress,對(duì)外暴露服務(wù),指向?v1?版本的服務(wù):
vim ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
? name: nginx
? annotations:
? kubernetes.io/ingress.class: nginx
spec:
? rules:
? kubernetes.io/ingress.class: nginx
spec:
? rules:
? - host: canary.example.com
? ? http:
? ? ? paths:
? ? ? - backend:
? ? ? ? ? serviceName: nginx-v1
? ? ? ? ? servicePort: 80
? ? ? ? path: /
訪問(wèn)驗(yàn)證一下:
$ curl -H "Host: canary.example.com" http://EXTERNAL-IP # EXTERNAL-IP?替換為?Nginx Ingress?自身對(duì)外暴露的?IP
nginx-v1
基于?Header?的流量切分
創(chuàng)建?Canary Ingress,指定?v2?版本的后端服務(wù),且加上一些?annotation,實(shí)現(xiàn)僅將帶有名為?Region?且值為?cd?或?sz?的請(qǐng)求頭的請(qǐng)求轉(zhuǎn)發(fā)給當(dāng)前?Canary Ingress,模擬灰度新版本給成都和深 圳地域的用戶:
測(cè)試訪問(wèn):
$ curl -H "Host: canary.example.com" -H "Region: cd" http://EXTERNAL-IP # EXTERNAL-IP?替換為?Nginx Ingress?自身對(duì)外暴露的?IP
nginx-v2
$ curl -H "Host: canary.example.com" -H "Region: bj" http://EXTERNAL-IP nginx-v1
$ curl -H "Host: canary.example.com" -H "Region: cd" http://EXTERNAL-IP nginx-v2
$ curl -H "Host: canary.example.com" http://EXTERNAL-IP
nginx-v1
可以看到,只有?header Region?為?cd?或?sz?的請(qǐng)求才由?v2?版本服務(wù)響應(yīng)。
基于?Cookie?的流量切分
與前面?Header?類似,不過(guò)使用?Cookie?就無(wú)法自定義?value?了,這里以模擬灰度成都地域用戶 為例,僅將帶有名為?user_from_cd?的?cookie?的請(qǐng)求轉(zhuǎn)發(fā)給當(dāng)前?Canary Ingress?。先刪除前面基 于?Header?的流量切分的?Canary Ingress,然后創(chuàng)建下面新的?Canary Ingress:
apiVersion: extensions/v1beta1 kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-by-cookie: "user_from_cd"
name: nginx-canary spec:
rules:
- host: canary.example.com
http: paths:
- backend:
serviceName: nginx-v2 servicePort: 80
path: /
測(cè)試訪問(wèn):
$ curl -s -H "Host: canary.example.com" --cookie "user_from_cd=always" http://EXTERNAL-IP # EXTERNAL-IP?替換為?Nginx Ingress?自身對(duì)外暴露的?IP
nginx-v2
$ curl -s -H "Host: canary.example.com" --cookie "user_from_bj=always" http://EXTERNAL-IP
nginx-v1
$ curl -s -H "Host: canary.example.com" http://EXTERNAL-IP?
nginx-v1
可以看到,只有?cookie user_from_cd?為?always?的請(qǐng)求才由?v2?版本的服務(wù)響應(yīng)。
基于服務(wù)權(quán)重的流量切分
基于服務(wù)權(quán)重的?Canary Ingress?就簡(jiǎn)單了,直接定義需要導(dǎo)入的流量比例,這里以導(dǎo)入?10%?流 量到?v2?版本為例?(如果有,先刪除之前的?Canary Ingress):
apiVersion: extensions/v1beta1 kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-weight: "10"
name: nginx-canary spec:
rules:
- host: canary.example.com
http: paths:
- backend:
serviceName: nginx-v2 servicePort: 80
path: /
測(cè)試訪問(wèn):
$ for i in {1..10}; do curl -H "Host: canary.example.com" http://EXTERNAL-IP; done; nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v1
nginx-v2
nginx-v1
nginx-v1
nginx-v1
可以看到,大概只有十分之一的幾率由?v2?版本的服務(wù)響應(yīng),符合?10%?服務(wù)權(quán)重的設(shè)置