低成本快速實(shí)現(xiàn)容器化鏡像部署,小紅書(shū)在容器環(huán)境的CD實(shí)踐
自容器推出以來(lái),它給軟件開(kāi)發(fā)帶來(lái)了極具傳染性的振奮和創(chuàng)新,并獲得了來(lái)自各個(gè)行業(yè)、各個(gè)領(lǐng)域的巨大的支持——從大企業(yè)到初創(chuàng)公司,從研發(fā)到各類 IT 人員等等。
知名跨境電商平臺(tái)小紅書(shū)隨著業(yè)務(wù)的鋪開(kāi),線上部署單元的數(shù)量急劇增加,以 Jenkins 調(diào)用腳本進(jìn)行文件推送的部署模式已經(jīng)不能適應(yīng)需求。
本文介紹小紅書(shū)如何以最小的投入,最低的開(kāi)發(fā)量快速的實(shí)現(xiàn)容器化鏡像部署,以及由此帶來(lái)的收益。
圖 1
小紅書(shū)是一個(gè)從社區(qū)做起來(lái)的跨境電商,目前已經(jīng)有 5 千萬(wàn)的用戶,電商平臺(tái)的 SKU 已經(jīng)上到了十萬(wàn)級(jí)。用戶喜歡在我們的平臺(tái)上發(fā)布關(guān)于生活、健身、購(gòu)物體驗(yàn)、旅游等相關(guān)帖子,每日有 1 億次筆記曝光。
我們從社區(qū)里的用戶創(chuàng)建的筆記生成相關(guān)的標(biāo)簽,關(guān)聯(lián)相關(guān)商品,同時(shí)在商品頁(yè)面也展示社區(qū)內(nèi)的和這商品有關(guān)的用戶筆記。
小紅目前還處在創(chuàng)業(yè)階段,我們的技術(shù)團(tuán)隊(duì)規(guī)模還不大,當(dāng)然運(yùn)維本身也是一個(gè)小團(tuán)隊(duì),團(tuán)隊(duì)雖小,運(yùn)維有關(guān)的技術(shù)方面卻都得涉及。
小公司資源有限,一個(gè)是人力資源有限,二是我們很多業(yè)務(wù)往前趕。在如何做好 CI/CD,怎么務(wù)實(shí)的落地方面,我們的策略就是開(kāi)源優(yōu)先,優(yōu)先選擇開(kāi)源的產(chǎn)品,在開(kāi)源的基礎(chǔ)上,發(fā)現(xiàn)不滿足需求的地方再自行開(kāi)發(fā)。
小紅書(shū)應(yīng)用上線流程
圖 2
如圖 2 是之前應(yīng)用上線的過(guò)程,開(kāi)發(fā)向運(yùn)維提需求,需要多少臺(tái)服務(wù)器,運(yùn)維依據(jù)需求去做初始化并交付給開(kāi)發(fā)。
我們現(xiàn)在有一個(gè)運(yùn)維平臺(tái),所有服務(wù)器的部署都是由這個(gè)平臺(tái)來(lái)完成,平臺(tái)調(diào)用騰訊云 API 生成服務(wù)器,做環(huán)境初始化,配置監(jiān)控和報(bào)警,最后交付給開(kāi)發(fā)的是一個(gè)標(biāo)準(zhǔn)化好的服務(wù)器。
圖 3
開(kāi)發(fā)者拿到服務(wù)器準(zhǔn)備線上發(fā)布時(shí),采用 Jenkins 觸發(fā)腳本的方式:用 Jenkins 的腳本做測(cè)試,執(zhí)行代碼推送。
當(dāng)需要新加一臺(tái)服務(wù)器或者下線一臺(tái)服務(wù)器,要去修改這個(gè)發(fā)布腳本。發(fā)布流程大概是:Jenkins 腳本先往 Beta 環(huán)境發(fā)布,開(kāi)發(fā)者在 Beta 環(huán)境里做自測(cè),自測(cè)環(huán)境沒(méi)有問(wèn)題就全量發(fā)布。
我們遇到不少的情況都是在開(kāi)發(fā)者自測(cè)的時(shí)候沒(méi)有問(wèn)題,然后在線上發(fā),線上都是全量發(fā),結(jié)果就掛了。
回退的時(shí)候,怎么做呢?只能整個(gè)流程跑一遍,開(kāi)發(fā)者回退老代碼,再跑一次 Jenkins 腳本,整個(gè)過(guò)程最長(zhǎng)需要 10 來(lái)分鐘,這段過(guò)程線上故障一直存在,所以這個(gè)效率很低。
以上的做法其實(shí)是大多數(shù)公司的現(xiàn)狀,但是對(duì)于我們已經(jīng)不太能適應(yīng)了,目前我們整個(gè)技術(shù)在做更迭,環(huán)境的復(fù)雜度越來(lái)越高。
如果還是維持現(xiàn)有的代碼上線模式,顯然會(huì)有失控的風(fēng)險(xiǎn),而且基于這樣的基礎(chǔ)架構(gòu)做例如自動(dòng)容量管理等,都是很難做到的。
技術(shù)團(tuán)隊(duì)的問(wèn)題與需求
首先,我們整個(gè)技術(shù)團(tuán)隊(duì)人數(shù)在增加,再加上技術(shù)棧在變。以前都是純 Python 的技術(shù)環(huán)境,現(xiàn)在不同的團(tuán)隊(duì)在嘗試 Java、Go、Node。
還有就是我們?cè)谧鑫⒎?wù)的改造,以前的單體應(yīng)用正在加速拆分成各個(gè)微服務(wù),所以應(yīng)用的數(shù)量也增加很多。
拆分微服務(wù)后,團(tuán)隊(duì)也變得更細(xì)分了,同時(shí)我們還在做前后端的拆分,原來(lái)很多 APP 的頁(yè)面是后端渲染的,現(xiàn)在在做前后端的拆分,后端程序是 API,前端是展示頁(yè)面,各種應(yīng)用的依賴關(guān)系也變得越來(lái)越多。
再加上電商每年大促銷,擴(kuò)容在現(xiàn)有模式也很耗時(shí)耗力。所以現(xiàn)在的模式已經(jīng)很難持續(xù)下去。
我們團(tuán)隊(duì)在兩三個(gè)月以前就思考怎么解決這些問(wèn)題,怎么把線上環(huán)境和代碼發(fā)布做得更好一點(diǎn)。我們需要做如下幾點(diǎn):
- 重構(gòu)“從代碼到上線”的流程。
- 支持 Canary 發(fā)布的策略,實(shí)現(xiàn)流量的細(xì)顆粒度管理。
- 能快速回退。
- 實(shí)踐自動(dòng)化測(cè)試,要有一個(gè)環(huán)境讓自動(dòng)化測(cè)試可以跑。
- 要求服務(wù)器等資源管理透明化,不要讓開(kāi)發(fā)者關(guān)心應(yīng)用跑在哪個(gè)服務(wù)器上,這對(duì)開(kāi)發(fā)者沒(méi)有意義,他只要關(guān)心開(kāi)發(fā)就可以了。
- 要能夠方便的擴(kuò)容、縮容。
快速實(shí)現(xiàn)容器化鏡像部署的方法
我們一開(kāi)始就考慮到容器化,就是用 Kubernetes 的框架做容器化的管理。
為什么是容器化?因?yàn)槿萜骱臀⒎?wù)是一對(duì)“好朋友”,從開(kāi)發(fā)環(huán)境到線上環(huán)境可以做到基本一致。
為什么用 Kubernetes?這和運(yùn)行環(huán)境和部署環(huán)境有關(guān)系,我們是騰訊云的重度用戶,騰訊云又對(duì) Kubernetes 提供了非常到位的原生支持。
所謂原生支持是指它有如下幾個(gè)方面的實(shí)現(xiàn):
- 網(wǎng)絡(luò)層面,Kubernetes 在裸金屬的環(huán)境下,要實(shí)現(xiàn) Overlay 網(wǎng)絡(luò),或者有 SDN 網(wǎng)絡(luò)的環(huán)境,而在騰訊云的環(huán)境里。它本身就是軟件定義網(wǎng)絡(luò),在網(wǎng)絡(luò)上的實(shí)現(xiàn)可以做到在容器環(huán)境里和原生的網(wǎng)絡(luò)一樣的快,沒(méi)有任何的性能犧牲。
- 在騰訊云的環(huán)境里,負(fù)載均衡器和 Kubernetes 里的 service 可以捆綁,可以通過(guò)創(chuàng)建 Kubernetes 的 service 去維護(hù)云服務(wù)的 L4 負(fù)載均衡器。
- 騰訊云的網(wǎng)盤可以被 Kubernetes 管理,實(shí)現(xiàn) PVC 等,當(dāng)然 Kubernetes 本身提供的特性是足夠滿足我們的需求的。
圖 4
我們作為創(chuàng)業(yè)公司都是以開(kāi)源為主,在新的環(huán)境里應(yīng)用了這樣的一些開(kāi)源技術(shù)(圖 4),Jenkins、GitLab、Prometheus 和 Spinnaker。Jenkins 和 GitLab 大家應(yīng)該都聽(tīng)說(shuō)過(guò),并且都在用,Prometheus、Docker 也都是目前很主流的開(kāi)源產(chǎn)品。
這里重點(diǎn)介紹兩個(gè)比較新,現(xiàn)在相當(dāng)火的開(kāi)源技術(shù):
- Spinnaker,這是一個(gè)我個(gè)人認(rèn)為非常優(yōu)秀的開(kāi)源的發(fā)布系統(tǒng),它是由 Netflix 在去年開(kāi)源的,是基于 Netflix 內(nèi)部一直在使用的發(fā)布系統(tǒng)做的開(kāi)源,可以說(shuō)是 Netflix 在 CD 方面的最佳實(shí)踐,整個(gè)社區(qū)非?;钴S,它對(duì) Kubernetes 的環(huán)境支持非常好。
- Traefik,在我們的環(huán)境里用來(lái)取代 Nginx 反向代理,Traefik 是用 Go 寫(xiě)的一個(gè)反向代理服務(wù)軟件。
01.Spinnaker
Spinnaker有如下幾個(gè)特點(diǎn):
Netflix 的開(kāi)源項(xiàng)目。Netflix 的開(kāi)源項(xiàng)目在社區(qū)一直有著不錯(cuò)的口碑。
有開(kāi)放性和集成能力。它原生就可以支持 Jenkins、GitLab 的整合,它還支持 Webhook,就是說(shuō)在某一個(gè)環(huán)境里,如果后面的某個(gè)資源的控制組件,本身是個(gè) API,那它就很容易整合到 Spinnaker 里。
擁有較強(qiáng)的 Pipeline 表達(dá)能力。它的 Pipeline 可以復(fù)雜到非常復(fù)雜,Pipeline 之間還可以關(guān)聯(lián)。
有強(qiáng)大的表達(dá)式功能。可以在任何的環(huán)節(jié)里用表達(dá)式來(lái)替代靜態(tài)參數(shù)和值,在 Pipeline 開(kāi)始的時(shí)候,生成的過(guò)程變量都可以被 Pipeline 的每個(gè) stage 調(diào)用。
比如說(shuō)這個(gè) Pipeline 是什么時(shí)候開(kāi)始的,觸發(fā)時(shí)的參數(shù)是什么,某一個(gè)步驟是成功還是失敗了,此次要部署的鏡像是什么,線上目前是什么版本,這些都可以通過(guò)變量訪問(wèn)到。
界面友好,支持多種云平臺(tái)。目前支持 Kubernetes、OpenStack、亞馬遜的容器云平臺(tái)。
圖 5
圖 5 是 Spinnaker 的架構(gòu),它是一個(gè)微服務(wù)的架構(gòu),里面包含用戶界面 Deck,API 網(wǎng)關(guān) Gate 等。
API 網(wǎng)關(guān)是可以對(duì)外開(kāi)放的,我們可以利用它和其他工具做一些深度整合。Rosco 是它做鏡像構(gòu)建的組件,我們也可以不用 Rosco 來(lái)做鏡像構(gòu)建。Orca 是它的核心,就是流程引擎。
Echo 是通知系統(tǒng),Igor 是用來(lái)集成 Jenkins 等 CI 系統(tǒng)的一個(gè)組件。Front52 是存儲(chǔ)管理,Cloud driver 是它用來(lái)適配不同的云平臺(tái)的,比如 Kubernetes 就有專門的 Cloud driver,也有亞馬遜容器云的 Cloud driver。Fiat 是它一個(gè)鑒權(quán)的組件。
圖 6
圖 6 是 Spinnaker 的界面,界面一眼看上去挺亂,實(shí)際上它還是有很好的邏輯性。
這里每一個(gè)塊都有三種顏色來(lái)表示 Kubernetes 環(huán)境里的某個(gè)實(shí)例的當(dāng)前狀態(tài)。綠色代表是活著的實(shí)例,右邊是實(shí)例的信息,包括實(shí)例的 YML 配置,實(shí)例所在的集群,實(shí)例的狀態(tài)和相關(guān) event。
圖 7
圖 7 是 Pipeline 的界面。首先,我覺(jué)得這個(gè)界面很好看很清晰。二是 Pipeline 可以做得非常靈活,可以在執(zhí)行了前幾個(gè)步驟之后,等所有的步驟執(zhí)行完了再執(zhí)行某個(gè)步驟。
這個(gè)步驟是某個(gè)用戶做某個(gè)審批,再分別執(zhí)行三個(gè)步驟其中的一個(gè)步驟,然后再執(zhí)行某個(gè)環(huán)節(jié)。也可以發(fā)布還是回退,發(fā)布是走發(fā)布的流程,回退就是回退的流程。
總之在這里,你所期待的 Pipeline 的功能都可以提供,如果實(shí)在不行,還有 Webhook 的模式讓你方便和外部系統(tǒng)做整合。
圖 8
圖 8 是 Pipeline 步驟的類型。左上 Check Precondltions 前置條件滿足的時(shí)候才執(zhí)行某個(gè)步驟。
例如當(dāng)前面的第一次發(fā)布里所有的實(shí)例都存活的時(shí)候,才執(zhí)行某個(gè)步驟?;蛘弋?dāng)前面的步驟達(dá)到了某個(gè)狀態(tài),再執(zhí)行下一個(gè)步驟。
Deploy 是在 Kubernetes 環(huán)境里生成的 Replication Set,可以在 Deploy 里更新一個(gè)服務(wù)器組、禁用一個(gè)集群、把集群的容量往下降、往上升等等。
也可以跑某一個(gè)腳本,這個(gè)腳本是在某一個(gè)容器里,有時(shí)候可能有這樣的需求,比如說(shuō) Java,這個(gè) Java 跑起來(lái)之后并不是馬上能夠接入流量,可能要到 Java 里跑一個(gè) job,從數(shù)據(jù)庫(kù)加載數(shù)據(jù)并做些初始化工作后,才開(kāi)始承接流量。
圖 9
Pipeline 表達(dá)式很厲害,它的表達(dá)式是用 Grovvy 來(lái)做的。Grovvy 是一個(gè)動(dòng)態(tài)語(yǔ)言,凡是 Grovvy 能用的語(yǔ)法,在有字符串的地方都可以用。
所以,這些步驟中,可以說(shuō)這個(gè)步驟參數(shù)是來(lái)自表達(dá)式;也可以說(shuō)有條件的執(zhí)行,生成環(huán)境的時(shí)候才做這樣的東西;也可以有前置條件,當(dāng)滿足這個(gè)條件的時(shí)候,這個(gè)流程和 stage 可以繼續(xù)走下去。
圖 10
如圖 10 是各種類型的表達(dá)式,從現(xiàn)在來(lái)看,基本上各種需求我們都能滿足了。
圖 11
Pipeline 可以自動(dòng)觸發(fā)(圖 11),可以每天、每周、每月、每年,某一天的時(shí)候被自動(dòng)觸發(fā),做一個(gè)自動(dòng)發(fā)布等等;也可以在鏡像有新 tag 推送到鏡像倉(cāng)庫(kù)時(shí),Pipeline 去做發(fā)布。
02.Spinnaker 和 Kubernetes 的關(guān)系
圖 12
Spinnaker 和 Kubernetes 有什么關(guān)系?它有很多概念是一對(duì)一的,Spinnaker 有一個(gè)叫 Account 的,Account 對(duì)應(yīng)到 Kubernetes 是 Kubernetes Cluster。
我們的環(huán)境里現(xiàn)在有三個(gè) Kubernetes 的 Cluster,分別對(duì)應(yīng)到開(kāi)發(fā)、測(cè)試和生產(chǎn),它也是對(duì)應(yīng)到 Spinnaker 的 三個(gè) Account;Instance 對(duì)應(yīng)到 Kubernetes 里是 Pod,一個(gè) Pod 就是一個(gè)運(yùn)行的單元。
還有 Server Group,這個(gè) Server Group 對(duì)應(yīng)的是 Replica Set 或者是 Deepionment。然后是 Load Balance,在 Spinnaker 里稱之為 Load Balance 的東西在 Kubernetes 里就是 Service。
03.Traefik
圖 13
Traefik 的幾個(gè)亮點(diǎn):
- 配置熱加載,無(wú)需重啟。為什么我們用 Traefik 而不用 Nginx 做反向代理呢?首先 Traefik 是一個(gè)配置熱加載,用 Nginx 時(shí)更新路由規(guī)則,做后端服務(wù)器的上線、下線都需要重載,但 Traefik 不需要。
- 自帶熔斷功能。Traefik 自帶熔斷功能,可以定義后端某個(gè)實(shí)例錯(cuò)誤率超過(guò)比如 50% 的時(shí)候,主動(dòng)熔斷它,請(qǐng)求再也不發(fā)給它了。
traefik.backend.circuitbreaker:NetworkErrorRatio() > 0.5
- 動(dòng)態(tài)權(quán)重的輪詢策略。它會(huì)記錄 5 秒鐘之內(nèi)所有后端實(shí)例對(duì)請(qǐng)求的響應(yīng)時(shí)間或連接數(shù),如果某個(gè)后端實(shí)例響應(yīng)特別慢,那接下來(lái)的 5 秒鐘就會(huì)將這個(gè)后端的權(quán)重降低,直到它恢復(fù)到正常性能,這個(gè)過(guò)程是在不斷的調(diào)整中,這是我們需要的功能。
traefik.backend.loadbalancer.method:drr
因?yàn)樯狭巳萜髦?,我們很難保證一個(gè)應(yīng)用的所有實(shí)例都部署在相同處理能力的節(jié)點(diǎn)上,云服務(wù)商采購(gòu)服務(wù)器也是按批量來(lái)的,每一批不可能完全一致,很難去保證所有的節(jié)點(diǎn)性能都是一致的。
圖 14
圖 14 是 Traefik 自帶的界面。我們定義的規(guī)則,后端實(shí)例的情況都可以實(shí)時(shí)的展現(xiàn)。
04.為什么在 Kubernetes 環(huán)境里選擇了 Traefik?
Traefik 和 Kubernetes 有什么關(guān)系呢?為什么在 Kubernetes 環(huán)境里選擇了 Traefik?我們總結(jié)了如下幾點(diǎn):
- Kubernetes 集群中的 Ingress Controller。Traefik 在 Kubernetes 是以 Ingress Controller 存在,大家知道 Kubernetes 到 1.4 之后就引進(jìn)了 Ingress 的概念。
Kubernetes 原來(lái)只有一個(gè) Service 來(lái)實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)和負(fù)載均衡,service 是四層的負(fù)載均衡,它做不到基于規(guī)則的轉(zhuǎn)發(fā)。
- 動(dòng)態(tài)加載 Ingress 更新路由規(guī)則。在 Kubernetes 里 Ingress 是屬于七層 HTTP 的實(shí)現(xiàn),當(dāng)然 Kubernetes 本身不去做七層的負(fù)載均衡,它是通過(guò) Ingress Controller 實(shí)現(xiàn)的,Traefik 在 Kubernetes 里就是一種 Ingress Controller。
- 根據(jù) Service 的定義動(dòng)態(tài)更新后端 Pod。它可以動(dòng)態(tài)加載 Kubernetes 里的 Ingress 所定義的路由規(guī)則,Ingress 里也定義了一個(gè)路由規(guī)則所對(duì)應(yīng)的 Service,而 Service 又和具體的 Pod 相關(guān)。
- 根據(jù) Pod 的 Liveness 檢查結(jié)果動(dòng)態(tài)調(diào)整可用 Pod。Pod列表是根據(jù) Pod 的 Liveness 和 Readiness 狀態(tài)做動(dòng)態(tài)的調(diào)整。
- 請(qǐng)求直接發(fā)送到 Pod。Traefik 據(jù)此可以將請(qǐng)求直接發(fā)送給目標(biāo) Pod,無(wú)需通過(guò) Service 所維護(hù)的 iptables 來(lái)做轉(zhuǎn)發(fā)。
圖 15
圖 15 是新發(fā)布的一個(gè)流程或者是開(kāi)發(fā)的流程。我們有如下三個(gè)環(huán)節(jié):
- 開(kāi)發(fā)階段。
- 集成測(cè)試。
- 上線。
開(kāi)發(fā)階段,開(kāi)發(fā)者在迭代開(kāi)始時(shí)生成一個(gè) Feature 分支,以后的每次更新都將這個(gè) Feature 分支推送到 GitLab 。
GitLab 里配置的 Webhook 觸發(fā)一個(gè) Jenkins job,這個(gè) job 做單元測(cè)試和鏡像構(gòu)建,構(gòu)建成一個(gè) Feature 分支的鏡像,給這個(gè)鏡像一個(gè)特定的 tag。生成新的鏡像之后,觸發(fā) Spinnaker 的部署,這個(gè)部署只在開(kāi)發(fā)環(huán)境里。
開(kāi)發(fā)者怎么訪問(wèn)剛剛部署的開(kāi)發(fā)環(huán)境呢?如果這是個(gè) HTTP 應(yīng)用,假設(shè)應(yīng)用叫做 APP1,而分支名稱叫 A,那開(kāi)發(fā)者就通過(guò) APP1-A.dev.xiaohongshu.com 就可以訪問(wèn)到 Feature A 的代碼。
在整個(gè)周期里可以不斷的迭代,最后開(kāi)發(fā)者覺(jué)得完成了這個(gè) Feature 了,就可以推送到 release。一旦把代碼推往 release 就觸發(fā)另一個(gè)構(gòu)建,基本上和前面的過(guò)程差不多。
最后會(huì)有一個(gè)自動(dòng)化的測(cè)試,基本上是由測(cè)試團(tuán)隊(duì)提供的自動(dòng)化測(cè)試的工具,用 Spinnaker 調(diào)用它,看結(jié)果是什么樣。
如果今天很有信心了,決定往生產(chǎn)發(fā)了,可以在 Git 上生成一個(gè) tag,比如這個(gè) tag 是 0.1.1,今天要發(fā) 0.1.1 版了,同樣也會(huì)觸發(fā)一個(gè)鏡像的構(gòu)建。
這三個(gè)不同的階段構(gòu)建的鏡像 tag 不一樣,每生成一個(gè)新 tag, Spinnaker 會(huì)根據(jù) tag 的命名規(guī)則觸發(fā)不同的 Pipeline,做不同環(huán)境的部署。
05.Canary
最重要的是我們有一個(gè) Canary 的發(fā)布過(guò)程,我們?cè)?Spinnaker 的基礎(chǔ)上,開(kāi)發(fā)了一套 Canary 的機(jī)制。
Canary 和 Beta 差不多,但 Canary 是真實(shí)引入流量,它把線上用戶分為兩組:
- 穩(wěn)定版的流量用戶。
- Canary 版的用戶。
他們會(huì)率先使用新版本,我們的具體策略是先給公司、先給我們自己辦公室的人來(lái)用,這個(gè)灰度如果沒(méi)問(wèn)題了,用戶反饋 OK,看看監(jiān)控?cái)?shù)據(jù)也覺(jué)得沒(méi)有問(wèn)題,再按照 1%-10%-20%-50%-100% 的階段隨機(jī)挑選線上用戶繼續(xù)灰度。
在這整個(gè)過(guò)程都有監(jiān)控?cái)?shù)據(jù)可以看,任何時(shí)候如果有異常都可以通過(guò) Spinnaker 進(jìn)行回退。
圖 16
這個(gè)是 Canary 的示意圖,線上用戶被分成兩組,大部分用戶訪問(wèn)老版本,特定用戶通過(guò)負(fù)載均衡轉(zhuǎn)發(fā)到特定的版本里,后臺(tái)有監(jiān)控?cái)?shù)據(jù)方便去比較兩個(gè)版本之間的差異。
圖 17
這是我們?cè)谌萜鳝h(huán)境里實(shí)現(xiàn)的 Canary 的架構(gòu)(圖 17),用戶請(qǐng)求從前面進(jìn)來(lái),首先打到 Traefik,如果沒(méi)有做 Canary 的過(guò)程,Traefik 是直接把請(qǐng)求打到組實(shí)例。
如果要發(fā)布一個(gè)新的版本,有一個(gè) HTTP 的 API 控制 project service,決定把什么樣的流量可以打到這個(gè)里面版本。
我們的策略可能是把辦公室用戶,可以通過(guò) IP 看到 IP,或者把線上的安卓用戶,或者線上 1% 的安卓用戶打給它,這些都是可以定義的。
圖 18
如圖 18 所示是線上真實(shí)的部署流程。首先是要設(shè)置一個(gè) Canary 策略,這個(gè)策略可以指定完全隨機(jī)還是根據(jù)用戶的特定來(lái)源。
比如說(shuō)是一個(gè)辦公室用戶,或者所有上海的用戶等等,然后去調(diào)整參數(shù),是 1% 的上海用戶,還是所有的上海用戶。
然后開(kāi)始部署服務(wù),接下來(lái)把這個(gè) Canary 實(shí)例做擴(kuò)展,在流量進(jìn)來(lái)之前,實(shí)例的容量一定要先準(zhǔn)備好。
進(jìn)來(lái)之后把流量做重新定向,把流量從原來(lái)直接打給后端的 Pod 改成打到 Canary 代理服務(wù)上,由 Canary 代理服務(wù)根據(jù)策略和用戶來(lái)源做進(jìn)一步的流量分發(fā)。
整個(gè)過(guò)程不斷的迭代,有 1% 的線上用戶開(kāi)始慢慢到達(dá) 100%。在達(dá)到 100% 后,就采用紅黑的策略替換掉所有舊版本:先把所有的新版本實(shí)例生成出來(lái),等所有的新版本通過(guò)健康檢查,都在線了,舊的版本再批量下線,這樣完成一個(gè)灰度。
如果中途發(fā)現(xiàn)問(wèn)題不能繼續(xù),馬上就可以回退,所謂的回退就是把流量打回到線上版本去。
圖 19
如上圖(圖 19)是我們的 Canary 策略。這是我們自己實(shí)現(xiàn)的一套東西,圖中的例子是把來(lái)自指定網(wǎng)段一半的 iPhone 用戶進(jìn)行 Canary。
用戶分組的維度還可以有其他規(guī)則,現(xiàn)在我們支持的是完全隨機(jī)/特定 IP/特定設(shè)備類型,這些規(guī)則可以組合起來(lái)。
我們的用戶分組是有一致性保證的,一旦為某個(gè)用戶分組了,那在當(dāng)前灰度期間,這個(gè)用戶的分組不會(huì)變,否則會(huì)影響用戶體驗(yàn)。
未來(lái)發(fā)展
我們下一步打算做兩件事情:
- 做自動(dòng)灰度分析,叫 ACA,現(xiàn)在 AIOps 概念很熱門,自動(dòng)灰度分析可以說(shuō)是一個(gè)具體的 AIOps 落地。
在灰度的過(guò)程中,人肉判斷新版本是否正常,如果日志采集夠完整的話,這個(gè)判斷可以由機(jī)器來(lái)做,機(jī)器根據(jù)所有數(shù)據(jù)來(lái)為新版本做評(píng)分,然后發(fā)布系統(tǒng)根據(jù)評(píng)分結(jié)果自動(dòng)繼續(xù)發(fā)布或者終止發(fā)布并回退。
- 再往下可以做自動(dòng)的容量管理,當(dāng)然是基于 Kubernetes 的基礎(chǔ)上,做自動(dòng)容量管理,以便更好的善用計(jì)算資源。
最后總結(jié)一下:一個(gè)好的 CD 系統(tǒng)應(yīng)該能夠控制發(fā)布帶來(lái)的風(fēng)險(xiǎn);我們?cè)谌肆Y源有限的情況下傾向于采用開(kāi)源的方法解決問(wèn)題,如果開(kāi)源不滿足的話,我們?cè)匍_(kāi)發(fā)一些適配的功能。
孫國(guó)清
小紅書(shū)運(yùn)維團(tuán)隊(duì)負(fù)責(zé)人
浙大計(jì)算機(jī)系畢業(yè),曾在傳統(tǒng)企業(yè) IT 部門工作多年,最近幾年開(kāi)始在互聯(lián)網(wǎng)行業(yè)從事技術(shù)及技術(shù)管理工作,曾就職于攜程基礎(chǔ)架構(gòu),負(fù)責(zé) Linux 系統(tǒng)標(biāo)準(zhǔn)化及分布式存儲(chǔ)的研究和落地,目前在小紅書(shū)帶領(lǐng)運(yùn)維團(tuán)隊(duì),負(fù)責(zé)業(yè)務(wù)應(yīng)用、基礎(chǔ)架構(gòu)以及IT支持。個(gè)人接觸的技術(shù)比較雜,從開(kāi)發(fā)到運(yùn)維的一些領(lǐng)域都有興趣,是 Scala 語(yǔ)言的愛(ài)好者,曾翻譯了“The Neophyte's Guide to Scala”,有上千 Scala 開(kāi)發(fā)者從中受益,到小紅書(shū)后開(kāi)始負(fù)責(zé)系統(tǒng)化落地 DevOps 和提高運(yùn)維效率。