DevUI是一支兼具設計視角和工程視角的團隊,服務於華為雲DevCloud平臺和華為內部數箇中後臺系統,服務於設計師和前端工程師。
官方網站:devui.design
Ng元件庫:ng-devui(歡迎Star)
引言
灰度釋出,又稱金絲雀釋出。
金絲雀釋出這一術語源於煤礦工人把籠養的金絲雀帶入礦井的傳統。礦工通過金絲雀來了解礦井中一氧化碳的濃度,如果一氧化碳的濃度過高,金絲雀就會中毒,從而使礦工知道應該立刻撤離。 ——《DevOps實踐指南》
對應到軟體開中,則是指在釋出新的產品特性時通過少量的使用者試點確認新特性沒有問題,確保無誤後推廣到更大的使用者使用群體。
整合灰度釋出的流水線在DevOps中是一個非常重要的工具和高效的實踐,然而筆者在入職以前對流水線和灰度釋出知之甚少。在瞭解一個新東西時,先從邏輯上打通所有的關鍵環節,然後再完成一個最簡單的Demo,對於我們來說是比較有意思的學習路徑,因此便有了這篇文章。
本文理論內容較少,主要是從零到一的搭建流程實踐,適合對工程化感興趣的初級前端開發者。
01 伺服器準備
獲取伺服器
上面提到,灰度釋出是通過少量的使用者試點來驗證新功能有沒有問題。所以要保證有兩批使用者能在同一時間體驗到不同的功能。這就要求我們準備兩臺伺服器,分別部署不同的程式碼版本。
如果你已經有了一臺伺服器,也可以通過在不同埠部署服務的方式來模擬兩臺伺服器。如果你還一臺伺服器都沒有,那麼可以參考這個過程購買兩臺雲伺服器,如果是按需購買,完成本文的Demo,大概要花費20塊錢。
獲取雲伺服器教程:github.com/TerminatorS…
工具安裝
Git
首先,確保你的伺服器上已經安裝了git,如果沒有的話使用以下命令進行安裝,安裝好了以後生成ssh 公鑰,放到你的github 裡,後面拉取程式碼的時候會用到。
yum install git複製程式碼
Nginx
如果你的伺服器沒有Nginx,先按照以下操作進行安裝,Linux 下安裝Nginx非常簡單:
sudo yum install nginx複製程式碼
安裝完了,在終端輸入nginx -t檢查一下是否安裝成功。如果安裝成功,它會顯示Nginx 配置檔案的狀態,以及位置。
此時nginx還沒有啟動,在終端中輸入nginx
或nginx -s reload
命令即可啟動,此時看到的nginx相關程式如下,表明已經啟動成功。
在瀏覽器裡訪問你的伺服器公網IP,如果能看到下面的頁面說明Nginx 可以正常工作。
Jenkins (耗時比較久)
第一次接觸Jenkins 可能會有很多疑問,Jenkins 是什麼?能完成什麼事情?我為什麼要使用Jenkins 等諸如此類。很難講清楚Jenkins 是什麼東西,所以這裡簡單介紹一下Jenkins 可以做什麼。簡單來講,你在任何一臺伺服器上進行的任何操作命令,Jenkins 都可以幫你完成,只要你提前在Jenkins上建立好任務,指定任務內容和觸發時機,比如定時觸發或者在特定的情況下觸發。
(1)安裝
Jenkins穩定版本list:pkg.jenkins-ci.org/redhat-stab…
// 科學上網會快一些,記得留意網站上java和jenkins版本匹配資訊,別下錯了
wget http://pkg.jenkins-ci.org/redhat-stable/jenkins-2.204.5-1.1.noarch.rpm
rpm -ivh jenkins-2.204.5-1.1.noarch.rpm複製程式碼
修改Jenkins埠,不衝突可不修改
// line 56 JENKINS_PORT
vi /etc/sysconfig/jenkins複製程式碼
(2)啟動
啟動jenkins
service jenkins start/stop/restart
// 密碼位置
/var/lib/jenkins/secrets/initialAdminPassword複製程式碼
(3)訪問
訪問伺服器的8080埠,輸入從上述位置獲取的密碼,點選繼續
建立一個賬戶然後登入
看到Jenkins 已就緒的頁面表示安裝已經完成,伺服器準備工作到此結束。
02 程式碼準備
準備兩份程式碼
因為要做灰度部署,所以需要準備兩份不一樣的程式碼,以驗證我們實施的灰度操作是否生效。這裡選擇使用Angular 的Angular-CLI 來建立程式碼。建立的專案並不簡潔,但是勝在操作簡單。我們一次性把兩份程式碼準備好,簡化開發側工作。
// 安裝angular-cli,前提是已經安裝了node,如果沒有node真的要去自行百度了...
npm install -g @angular/cli
// 快速建立一個新專案,一路回車
ng new canaryDemocd canaryDemo
// 執行完這個命令後訪問http://localhost:4200 檢視頁面資訊
ng serve複製程式碼
訪問localhost 的4200 埠檢視頁面,然後把專案根目錄下src 中的index.html 的title 改成A-CanaryDemo,可以看到頁面會進行實時地重新整理。在這個例子中,我們用title 來標識灰度釋出過程中兩邊不同的服務需要部署的程式碼。
接下來,我們進行兩次打包,兩次打包的title 分別為A-CanaryDemo 和 B-CanaryDemo, 把這兩個資料夾放好備用,作為一會灰度釋出的新老程式碼。
ng build --prod複製程式碼
配置Nginx
在上述完成Nginx 的安裝操作時,我們訪問伺服器的IP 看到的是Nginx 的頁面,現在我們想訪問到自己的頁面,首先把上面打包得到的A-CanaryDemo 傳送到兩臺伺服器上任意位置,這裡我們把它放到/var/canaryDemo。
// 將A-CanaryDemo 資料夾複製到你的公網伺服器上,xx部分是你的伺服器公網ip
scp -r ./dist/A-CanaryDemo root@xx.xx.xx.xx:/var/canaryDemo複製程式碼
去伺服器上/var 的位置上看一下,是否已經有了這個檔案,如果有了的話,接著到下一步。即修改Nginx 配置把訪問該伺服器IP 的請求轉發到我們剛剛上傳上來的頁面上。上面提到過可以通過nginx -t 這個命令來檢視Nginx 配置檔案的位置,在這一步,我們要去編輯那個檔案。
vi /etc/nginx/nginx.conf複製程式碼
修改47-50行新增下圖相關的內容,即將訪問到該伺服器IP 的流量轉發到/var/canaryDemo 下的index.html.
修改完畢,儲存退出,重啟一下nginx
nginx -s reload複製程式碼
這時候去訪問我們伺服器的IP 地址可以看到頁面已經變成了剛剛我們在本地改的頁面,而且title 確實是A-CanaryDemo。兩臺伺服器都操作完成後,兩邊都可以訪問到title 為A-CanaryDemo 的頁面。此時的狀態相當於生產環境已經在提供穩定服務的兩臺機器。
03 定義灰度策略
接下來,我們要開始進行灰度釋出的部分,在進行相關操作之前,我們需要定義一個灰度策略,即滿足什麼情況下的流量會走到灰度邊,而其他流量走向正常邊。這裡為了簡單起見,我們使用名字為canary 的cookie 來區分,如果檢測到這個cookie 的值為devui,就訪問灰度邊機器,否則就訪問正常邊機器。按照此規則配置Nginx 結果如下,此處分別使用11.11.11.11和22.22.22.22代表兩臺伺服器的IP地址:
# Canary Deployment
map $COOKIE_canary $group {
# canary account
~*devui$ server_canary;
default server_default;
}
upstream server_canary {
# 兩臺機器的IP,第一臺設定埠號8000是為了防止nginx轉發出現死迴圈導致頁面報錯
server 11.11.11.11:8000 weight=1 max_fails=1 fail_timeout=30s;
server 22.22.22.22 weight=1 max_fails=1 fail_timeout=30s;
}
upstream server_default {
server 11.11.11.11:8000 weight=2 max_fails=1 fail_timeout=30s;
server 22.22.22.22 weight=2 max_fails=1 fail_timeout=30s;
}
# 相應地,要配置8000埠的轉發規則,8000埠預設不開啟訪問,需要去雲伺服器控制檯安全組新增8000
server {
listen 8000;
server_name _;
root /var/canaryDemo;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
root /var/canaryDemo;
index index.html;
}
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
# root /usr/share/nginx/html;
root /var/canaryDemo;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
proxy_pass http://$group;
# root /var/canaryDemo;
# index index.html;
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.h
}複製程式碼
此時,灰度流量和正常流量都會隨機分配到AB兩邊的機器。下面,我們通過建立Jenkins 任務執行Nginx 檔案修改的方式實現灰度釋出。
04 實現灰度釋出
流程梳理
在建立用於實現灰度釋出的Jenkins任務之前我們先梳理一下要達到灰度釋出的目標需要哪幾個任務,以及每個任務負責完成什麼事情。灰度釋出一般遵循這樣的流程(假設我們有AB兩臺伺服器用於提供生產環境的服務,我們稱之為AB邊):
(1)新程式碼部署到A邊
(2)符合灰度策略的小部分流量切到A邊,剩餘大部分流量仍去往B邊
(3)手動驗證A邊功能是否正常可用
(4)驗證無誤後,大部分流量轉到A邊,灰度流量去往B邊
(5)手動驗證B邊功能是否正常可用
(6)驗證無誤後,流量像往常一樣均分到AB邊
任務拆解
通過上述的拆解,我們得出灰度釋出的6個步驟,其中(3)和(5)是需要手動驗證的環節,所以我們以這兩個任務為分割點,建立三個Jenkins 任務(Jenkins 任務建立在A 邊機器上)如下:
(1)Canary_A(灰度測試A),這個任務又包含兩個部分,更新A邊的程式碼,然後修改流量分發策略使得灰度流量到達A,其他流量到達B
(2)Canary_AB(上線A灰度測試B),更新B邊程式碼,灰度流量達到B,其他流量到達A
(3)Canary_B(上線B),所有流量均分到AB
建立任務
先按照任務拆解部分的設定建立三個FreeStyle 型別的Jenkins 任務,記得使用英文名字,中文名字後面建資料夾比較麻煩。任務詳情資訊可以不填,直接儲存就好,下一步我們再來配置每個任務的具體資訊。
配置任務
現在已經建立好了三個任務,先點選進入每一個任務進行一次空的構建(否則後面可能導致修改後的構建任務無法啟動),然後我們來對每個任務進行詳細的配置。
現代前端專案都要進行構建打包這一步。但是廉價的雲伺服器在完成構建方面有些力不從心,CPU 經常爆表。所以我們在這裡把打包出得出的生產包納入git 管理,每次的程式碼更新會同步最新的生產包到github,因此Jenkins 任務把生產包拉下來,放在指定位置即可完成一次新程式碼的部署。
這一步操作,其實我們在之前就已經完成了,我們在上面打了兩份tilte 不一樣的生產包,此時可以派上用場了。
首先來配置灰度測試A,這個任務內容上面也基本講清楚了,首先要關聯該任務到遠端的github 倉庫(需要手動建立一個,存放上面打包的B-CanaryDemo,並命名為dist)讓它知道可以去哪裡拉取最新程式碼。
執行一次構建任務(在git fetch 那一步耗時不穩定,有時比較久),然後點選本次構建進去檢視Console Output,可以確定執行Jenkins 任務的位置是位於伺服器上的/var/lib/jenkins/workspace/Canary_A
繼續編輯灰度測試A 任務,新增build shell,也就是每次任務執行時要執行的命令:
(1)先拉取最新的程式碼
(2)把程式碼根目錄下的dist目錄複製到部署程式碼的位置,這裡我們指定的位置是/var/canaryDemo
(3)修改Nginx 配置使灰度流量到達A邊
就步驟(3)而言,修改灰度流量的方式其實就是選擇性註釋Nginx 配置檔案中的內容,註釋方式如下即可實現灰度測試A。
upstream server_canary {
# 灰度流量訪問A 邊
server 11.11.11.11:8080 weight=1 max_fails=1 fail_timeout=30s;
# server 22.22.22.22 weight=1 max_fails=1 fail_timeout=30s;
}複製程式碼
upstream server_default {
# 正常流量訪問B 邊,為了在修改檔案的時候把這段的配置和上面的server_canary 區分開,我們把這裡的weight 設為2
# server 11.11.11.11:8080 weight=2 max_fails=1 fail_timeout=30s;
server 22.22.22.22 weight=2 max_fails=1 fail_timeout=30s;
}複製程式碼
這一步填寫的shell 命令在使用jenkins 使用者執行時可能會遇到許可權問題,可以先用root 使用者登入,把/var 目錄的歸屬改為jenkins 使用者,/etc/nginx/ngix.conf也需要新增可寫許可權。由此,最終得到的shell 命令如下:
git pull
rm -rf /var/canaryDemo
scp -r dist /var/canaryDemo
sed -i 's/server 22.22.22.22 weight=1/# server 22.22.22.22 weight=1/' /etc/nginx/nginx.conf
sed -i 's/server 11.11.11.11:8000 weight=2/# server 11.11.11.11:8000 weight=2/' /etc/nginx/nginx.conf
nginx -s reload複製程式碼
灰度測試A 任務內容配置完成,接下來依次配置上線A 灰度測試B 和上線B。
灰度測試B 的要執行的任務是把最新的程式碼拉到A 邊(因為我們的Jenkins 任務都是建立在A 邊的),複製dist 下的程式碼到B 邊Nginx 指定訪問位置,然後修改A 邊Nginx 配置,使灰度流量到達B 邊。
git pull
rm -rf canaryDemo
mv dist canaryDemo
scp -r canaryDemo root@xx.xx.xx.xx:/var
sed -i 's/# server 22.22.22.22 weight=1/server 22.22.22.22 weight=1/' /etc/nginx/nginx.conf
sed -i 's/# server 11.11.11.11:8000 weight=2/server 11.11.11.11:8000 weight=2/' /etc/nginx/nginx.conf
sed -i 's/server 22.22.22.22 weight=2/# server 22.22.22.22 weight=2/' /etc/nginx/nginx.conf
sed -i 's/server 11.11.11.11:8000 weight=1/# server 11.11.11.11:8000 weight=1/' /etc/nginx/nginx.conf
nginx -s reload複製程式碼
這一步的任務內容涉及到從A 邊伺服器向B 邊伺服器傳送程式碼,這個過程一般來說需要輸入B 邊伺服器的密碼。我們想要做到免密傳送,因此要通過把A 邊機器~/.ssh/id_rsa.pub 中的內容新增到B 邊伺服器~/.ssh/authorized_keys 中使得A 獲得免密像B 傳送檔案的許可權。
上線B 則是通過取消對A 邊Nginx 配置的註釋使所有流量均分到AB 邊.
sed -i 's/# server 22.22.22.22 weight=2/server 22.22.22.22 weight=2/' /etc/nginx/nginx.conf
sed -i 's/# server 11.11.11.11:8000 weight=1/server 11.11.11.11:8000 weight=1/' /etc/nginx/nginx.conf
nginx -s reload複製程式碼
至此,我們就從零到一搭建了一個灰度釋出環境。在程式碼更新後,通過手動執行Jenkins 任務的方式實現灰度部署和手工測試,保證新功能平滑上線。
總結
本文從伺服器準備、程式碼準備、灰度策略制定和實現灰度釋出四個方面介紹了從零搭建一個灰度釋出環境的必備流程。灰度釋出的核心其實就是通過對Nginx 檔案的修改實現流量的定向分發。內容頗為簡單,但是從零到一的整個流程操作下來還是比較繁瑣,希望各位看官能夠有所收穫。
另外,這只是一個最簡易的Demo,在真正的DevOps 開發過程中,還需要整合編譯構建、程式碼檢查、安全掃描和自動化測試用例等其他操作,期待後續團隊的其他成員進行更多的專項擴充套件!