基於容器服務的持續整合與雲端交付(四)- 多種發佈方式

來源:互聯網
上載者:User

摘要: 前言哲學有各種各樣的流派,百家爭鳴,但是只有一個哲學問題是嚴肅的,那就是生與死。而雲端交付程序中也只有三個問題是嚴肅的。如何重建你的系統How to recreate your system?如何安全地部署你的系統How to safely change your system? 部署後的問題監控與解決 When something has gone wrong? 在前面的本文中,我們講述了什麼是雲端交付,如何搭建從零搭建一個持續傳遞系統,而今天我們要談的是如何安全的部署你的系統,部署這個名詞包含了很多的含義,最簡單的解釋就是如何讓你的程式執行在最終的環境上。

前言

哲學有各種各樣的流派,百家爭鳴,但是只有一個哲學問題是嚴肅的,那就是生與死。而雲端交付程序中也只有三個問題是嚴肅的。

  1. 如何重建你的系統 How to recreate your system?
  2. 如何安全地部署你的系統 How to safely change your system?
  3. 部署後的問題監控與解決 When something has gone wrong?

在前面的本文中,我們講述了什麼是雲端交付,如何搭建從零搭建一個持續傳遞系統,而今天我們要談的是如何安全的部署你的系統,部署這個名詞包含了很多的含義,最簡單的解釋就是如何讓你的程式執行在最終的環境上。但是部署的方式上面有非常多的最佳實踐。接下來我們來討論下常見的幾種發佈方式,以及如何利用容器發佈實現最常用的零宕機發佈方式藍綠發佈。

發佈原則

常見的發佈原則有藍綠發佈、金絲雀發佈(灰階發佈)、ABTest,在國內的開發人員中,對這幾個概念有硬地的理解。藍綠發佈通常被大家成為熱部署;金絲雀發佈在國內的名頭完全被他的變種發佈方式蓋過了,主要是灰階發佈與ABTtest,下面我們來詳細的為大家解釋一下他們之間的異同。

藍綠發佈

在發佈的程序中用戶無感知服務的重啟,通常情況下是通過新舊組建並存的方式實現,也就是說在發佈的流程中,新的組建和舊版組建是相互熱備的,通過切換路由權數的方式(非0即100)實現不同的套用的上線或者下線。

22c0f969a692ff28a501a8c8585f25108f6a92e0

金絲雀發佈

通過線上上啟動並執行服務中,新上線少量的新組建的服務,然後從這少量的新組建中快速獲得回饋,根據回饋決定最後的交付形態。

灰階發佈

灰階發佈是通過切換線上並存組建之間的路由權數,逐步從一個組建切換為另一個組建的程序。雖然馬丁·扶老耳朵大人認為灰階發佈與金絲雀發佈是等同的,但是在具體的動作和目的上面個還是有些許差別的。金絲雀發佈更傾向於追蹤快速的回饋,而灰階發佈更傾向於從一個組建到另一個組建平穩的切換。

90c8d36866b247d2edd9502b33ae9980038a55df

e0c63b94b936587a6b7fc60ace6b06fedf81b201

5a9ca9875ef60c6435ff938519c54691ff407fff

ABTest

ABTest和灰階發佈非常像,但是從發佈的目的上,可以簡單的區分灰階發佈與ABTest,ABTest側重的是從A組建或者B組建之間的差異,並根據這個結果進行決策。最終選擇一個組建進行部署。因此和灰階發佈相比,ABTest更傾向於去決策,和金絲雀發佈相比,ABTest在權數和流量的切換上更靈活。

ac8b1d89edd8904d1225ccff1706037b068516dc

阿裡雲容器服務實現藍綠發佈

下面我們通過一個簡單的例子來示範藍綠發佈的流程。假設我們要進行藍綠發佈的套用是一個nginx的靜態頁面,初始的套用範本如下

nginx-v1:
image: 'registry.aliyuncs.com/ringtail/nginx:1.0'
labels:
aliyun.routing.port_80: nginx
restart: always

部署後會有“Welcome to nginx”的成功網頁通知。如果用戶想變更設定,可以在動作面板上選擇“變更設定”,進入資訊修改階段,選擇變更的發佈原則與新組建服務的設定。

b2c3264b5f55e9d7248f1476d1f16e4e19f076df

nginx-v2:
image: 'registry.aliyuncs.com/ringtail/nginx:2.0'
labels:
aliyun.routing.coexist: true
aliyun.routing.port_80: nginx
restart: always

在藍綠發佈中,新組建與舊組建不能共用同一個名字;如果共用同一個路由位址,那麼需要新增aliyun.routing.coexist的label,這個label的含義是,本期的服務與其他服務共用路由位址,在藍綠發佈的場景中,為了許諾套用的零宕機切換,新組建的服務的路由權數預設為0,需要通過路由管理網頁進行調整,方可進行流量切換。

在進行發佈的程序中,會經歷兩個狀態,一個是藍綠發佈中,一個是藍綠發佈待確認。“藍綠發佈中”表示,新組建的服務尚未啟動完成;而“藍綠發佈待確認”表示新組建的服務已經啟動完成,此時需要進行發佈確認或者發佈復原方可進行下一次發佈。其中,新組建的套用和舊組建的套用並存,前者用綠色標明,後者用藍色標明。如果一個服務在前後兩個組建中都存在且沒有變化,那麼會使用黃色標記,表示這個套用在藍綠發佈中不會出現任何變化。

dbd3a39ebe4b94f2e7029de5e5980f512f1c66b0

選擇“路由清單”之後再點選“設定服務權數”,調整與之對應的路由權數。如圖所示,舊組建服務的權數為100,新組建服務的權數為0;下面我們將舊組建服務的權數調整為0,新組建服務的權數調整為100。

66e195c726713a4e79211889f56c0a2a5d58e993

fed3fae35900b872264b6d28771b4d14d6c504da

由於預設路由服務是進行對話保留的,您可以打開一個新的瀏覽器視窗,存取新的組建,結果如下。

092be26066d208c877d808a360911fe00f60c1c2

當整個發佈流程驗證完畢後,需要進行發佈確認,方可進行下一次發佈。

點選發佈確認後,查看套用的詳情,可以看到套用的服務清單已經更新了,舊版服務已經完全下線移除了。

6cddaf9185d1a2411ad3812c17a7716361f79fc7

複雜拓撲下的藍綠發佈

場景剖析

大於大多數場景而言,對客戶提供服務的軟體的形態有三種。一種是前端類服務,用戶可以直接或者間接通過網頁、介面叫用使用該服務提供的能力;一種是後端類服務,用戶無法直接使用該服務提供的功能,該服務主要的消費者是其他服務,並通過其他服務最終將處理後的結果回饋給用戶;第三種是調度任務類服務,即不被用戶使用也不被其他服務叫用,它的生命週期只存在在一個任務的執行生命週期中,通常任務的執行循環完畢,服務的生命週期就停止,通常為無狀態資源密集性服務。

對於上述三種場景,以路由權數切換為主要實現方式的發佈原則例如藍綠發佈、A/BTest等通常情況下比較適用於前端類服務與後端類服務。下面我們用一個簡單的例子來拆解下如何使用阿裡雲容器服務來實現這兩類服務的藍綠發佈。

在上一篇發佈原則的本文中,有的開發人員會問我的應用程式的拓撲關聯不是單體的,不能通過單一容器等級的路由權數切換解決。在此要明確的一件事情,藍綠發佈是一種發佈原則,部署的最小維度是容器,而發佈的最小維度是套用。藍綠發佈的原理是老的套用的組建不變,新的套用組建進行部署,如果新組建與老組建之間套用的名字以及相關的設定沒有改變,那麼會認為這個套用是新老組建中共用的,無需變更;需要進行變更的套用通過名字的不同進行區分。簡單的來講,藍綠發佈是一個套用等級的更新動作,你可以對一個服務進行兩個組建之間的切換,服務是一個邏輯的概念,而不是容器這樣一個實體的概念,藍綠發佈可以做複雜拓撲的應用程式更新動作。

DEMO

下面我們通過一個拓撲結構複雜一點的例子來講述藍綠發佈。套用的拓撲結構如下:

e26fe16bb8d8b2253196424e97c50d2fd5b2c521

serviceB會叫用serviceA,這兩個都是python構建的,代碼如下:

serviceA通過介面返回資料“world!”

from flask import Flask
app = Flask(__name__)

@app.route('/')
def world():
return 'world!'

if __name__ == '__main__':
app.run(port=5000,debug=True,host='0.0.0.0')

serviceB叫用serviceA的介面,並將返回的資料欄位前面新增“Hello”

from flask import Flask
import requests
import os

dep_endpoint = os.getenv('dep_endpoint');

app = Flask(__name__)

@app.route('/')
def hello():
msg = requests.get(dep_endpoint);
return 'Hello ' + msg.text;

if __name__ == '__main__':
app.run(port=5001,debug=True,host='0.0.0.0')

傳統套用之間的呼叫者式可以是通過設定IP位址或者網域名稱來叫用,也可以通過服務註冊中心中的位址的方式叫用,但是對於一個無狀態的多實例的服務,常見的做法是使用用戶端的負載平衡器或者伺服器的服務等化器端點來實現。在容器服務中使用的方式是使用伺服器端的負載平衡端點的方式,提供內部叫用的路由端點,來實現後端服務的負載平衡。大致的呼叫者式如下。

serviceA-v1:
image: 'registry-internal.cn-hangzhou.aliyuncs.com/ringtail/demo-service-a:1.0'
labels:
aliyun.scale: 3
aliyun.routing.port_5000: servicea.local
serviceB-v1:
image: 'registry-internal.cn-hangzhou.aliyuncs.com/ringtail/demo-service-b:1.0'
environment:
- 'dep_endpoint=http://servicea.local'
labels:
aliyun.scale: 2
aliyun.routing.port_5001: serviceb
external_links:
- "servicea.local"

serviceB通過external_links的方式將serviceA的內部路由端點引入,在環境變數中將

dep_endpoint=http://servicea.local,使用servicea.local作為叫用的位址。

存取serviceB的對外存取位址,可以得到:

18b2375693f7771cd5a6f7113a39aa384727a69d

我們最開始基本結構的套用就已經部署完畢了,下面開始進行不同服務的藍綠發佈。

前端服務的藍綠發佈

首先我們先看如果做前端服務的藍綠發佈,也就是說要對serviceB進行藍綠發佈的流程。大致的結構圖如下。

3b7bbcf6708ca3f40f40586e744cee137e53f023

在這個套用中前端服務的藍綠發佈,也就是對serviceB進行藍綠發佈,下面我們修改serviceB的代碼

from flask import Flask
import requests
import os

dep_endpoint = os.getenv('dep_endpoint');

app = Flask(__name__)

@app.route('/')
def hello():
msg = requests.get(dep_endpoint);
return 'Welcome to the ' + msg.text; //ץද"Hello " => "Welcome to the "

if __name__ == '__main__':
app.run(port=5001,debug=True,host='0.0.0.0')

修改編排範本,進行藍綠發佈

serviceA-v1:
image: 'registry-internal.cn-hangzhou.aliyuncs.com/ringtail/demo-service-a:1.0'
labels:
aliyun.scale: 3
aliyun.routing.port_5000: servicea.local
serviceB-v2:
image: 'registry-internal.cn-hangzhou.aliyuncs.com/ringtail/demo-service-b:2.0'
environment:
- 'dep_endpoint=http://servicea.local'
labels:
aliyun.scale: 2
aliyun.routing.port_5001: serviceb
external_links:
- "servicea.local"

進行藍綠發佈更新後,可以看到更新後的服務清單,其中黃色的serviceA-v1表示本期的套用在藍綠發佈的程序中不會產生變化,serviceB-v1為老組建,serviceB-v2為新組建

7939953a0d6d6a8b19e9955c1be156da94acf80d

通過路由清單選擇相應的權數管理,進行權數的調整,將serviceA-v1的權數調整為0,將serviceA-v2的權數調整為100,此時存取serviceB的網址可以發現。

驗證完畢,點選確認發佈完成,完成藍綠發佈。

後端服務的藍綠發佈

我們再看下如何做後端服務的藍綠發佈,也就是說對serviceA進行藍綠發佈。大致的結構圖如下。

2703f2dbf237b3a36db30d3ab596b29ca815eb00

修改編排範本

serviceA-v2:
image: 'registry-internal.cn-hangzhou.aliyuncs.com/ringtail/demo-service-a:2.0'
labels:
aliyun.scale: 3
aliyun.routing.coexist: true
aliyun.routing.port_5000: servicea.local
serviceB-v2:
image: 'registry-internal.cn-hangzhou.aliyuncs.com/ringtail/demo-service-b:2.0'
environment:
- 'dep_endpoint=http://servicea.local'
labels:
aliyun.scale: 2
aliyun.routing.coexist: true
aliyun.routing.port_5001: serviceb
external_links:
- "servicea.local"

部署後的服務清單:

此時可以發現serviceB-v2在本次發佈中不會進行變更,調整服務的權數

此時再存取serviceB的位址,可以得到如下的結果

280e2076dcd04aa275a92db71f6e6263e371dacf

總結

藍綠發佈是一種用於升級與更新的發佈原則,對於增量升級有比較好的支援,但是對於涉及資料工作表結構變更等等不可逆轉的升級,並不完全合適用藍綠發佈來實現,需要結合一些商務的邏輯以及資料移轉與復原的原則才可以完全滿足需求,希望給位開發人員可以在自己的商務場景中,更靈活的使用和實現藍綠發佈。

附錄

代碼倉庫位址:https://github.com/ringtail/BlueGreenDevelopment

簡歷

莫源,阿裡雲進階研發工程師。在上線阿裡巴巴之前,先後在北京天方地圓科技有限公司、微軟亞洲研究院任職。現主要負責阿裡雲容器服務供應項目的底層服務發現系統、集群管理系統的研發,從事容器的持續傳遞、持續整合的方案的設計與實現。在雲端運算、分散式系統、影像辨識與虛擬實境方向有多年的開發經驗。

相關產品:

  1. 容器服務(Docker)
  2. 雲效
  3. 巨量資料計算服務(MaxCompute)
  4. 雲端服務器ECS
相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.