釋出系統有那麼難麼?
如果一個上點規模的公司,技術團隊有什麼值得一做的系統,那麼釋出系統算一個。
jenkins
用的好好的,為什麼要自己搞呢?總結下來,有下面幾點原因:
1)每個公司的流程和技術棧都是不一樣的,生搬硬套,就像便祕一樣不順暢。
2)釋出系統技術開發成本不高,很容易搞。你要是覺得難搞,那一定是卡在複雜的公司人員環境和流程上。
3) jenkins
這樣的工具要想做個增強功能,不比自己開發簡單。比如加個審批環節,做個表單嵌入什麼的。
一句話,自己搞個套餐不比改造現成的難。
概要
開發一個釋出系統是很簡單的,雖然需要全棧的知識(前端、後臺、指令碼)。有多簡單呢? 我們提供了兩個人力,只花了10個工作日就全部完成了。 看完本文,你要是覺得不簡單,那就是我們太牛掰了(囧),畢竟也是見識過七八個高開去做同樣事情的豪華陣容。
流程上是幫不了你什麼忙了,但在技術上,我將奉上個人覺得很不錯的一套實現。如果你的架構是基於 SpringCloud
的,你會發現很貼心。釋出系統主要有以下功能:構建模組、部署模組、許可權管理模組、審計模組
構建模組
使用者登入系統後,會看到所有擁有許可權的專案列表。點點選前往,則進入構建頁面。
構建模組其實是非常簡單的。不論你是用 maven
,還是 gradle
,再或者 cnpm
, g++
等,都是通過傳入一堆引數到指令碼中執行。在這裡推薦使用 python
指令碼進行更多控制。
構建者可以選擇任何一次提交進行構建。構建成功後,會自動給提交打tag(也可以自定義tag)。同一個專案不允許 同時打包 。構建失敗,可以重新進行構建,構建者會看到實時滾動的日誌。這裡有兩個技術點:
1)如何獲取git的提交記錄並進行切換?
2)如何顯示滾動日誌?
獲取gitlab的提交記錄
拿gitlab來說(因為用的最多)。加入gitlab的maven即可使用。
<dependency> <groupId>org.gitlab</groupId> <artifactId>java-gitlab-api</artifactId> <version>4.1.0</version></dependency>
獲取最近提交記錄。
api.getAllCommits(projectId,page, branch)
打tag
api.addTag(projectId, tagName, hash, tagTitle, tagContent);
滾動日誌實現
見本公眾號文章《滾動日誌的實現》
部署模組
很多釋出系統讓人很不爽的一點,就是不支援單臺或者多臺釋出,不方便而且風險大。
部署頁面只顯示已經打包成功的記錄,按照提交時間倒序顯示。支援重新部署,不管是上次部署成功還是失敗。部署介面如下:
點選部署按鈕,即可顯示部署機器列表,可以選擇一個或者多個進行部署。
部署記錄都會按照打包記錄進行分組,顯示在日誌列中。同一次部署,如果有一臺部署失敗,則預設部署整體部署失敗,可以從部署日誌判斷當前的部署狀態。
可以看到系統其實是沒有回滾的概念的,只有部署哪個版本的概念。一個服務可能有上百臺機器,如何更優雅的顯示多版本共存的關係,有條件的團隊是不會放過這個改進的。
SpringCloud的部署過程
許可權管理
github上有很多開源的實現,隨便搬弄一套整合即可。我們主要談一下許可權理念。
許可權設計有兩個要點。第一不能阻礙研發的開發效率,第二要嚴控線上的安全。所以線上和非線上環境是分開設計的。
1)一個系統的使用者,要麼是超級管理員(就是神馬都能幹的那總);要麼是帶有線上許可權標識的使用者;再就是普通使用者了。
2)一個使用者,要麼是某個專案的成員,要麼不是
許可權圖如下:
操作都會被記錄進操作歷史,並且傳送郵件(或者其他hook):
1)修改專案,傳送給專案成員所有人
2)構建專案,傳送給構建者構建結果
3)部署專案,傳送給專案成員所有人
所有的操作記錄,在專案中都可查。
指令碼
釋出系統的web端,不過是套層皮囊。真正去執行的,還是我們的指令碼。
使用 python
指令碼進行構建和釋出,是非常方便的。有些 java
開發人員對指令碼不是很熟,我這裡挑比較重要的點說明一下。
執行過程顯示
建議使用 logging
模組控制。效果見下圖。
import loggingdef setup_logging(): root = logging.getLogger() root.setLevel(logging.DEBUG) ch = logging.StreamHandler(sys.stdout) ch.setLevel(logging.DEBUG) formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) root.addHandler(ch)
指令碼異常記錄
預設python執行異常是不輸出到stdout的,我們需要強行轉換一下。
import sysimport tracebacktry: setup_logging()#...except Exception as e: exc_type, exc_value, exc_traceback_obj = sys.exc_info() traceback.print_exception(exc_type, exc_value, exc_traceback_obj, limit=2, file=sys.stdout)
遠端執行
釋出系統的機器應該都做了免密登入。通過
ssh -t "su - sth"
可以切換到任何一個使用者在遠端執行程式。
但實踐證明,通過python的 subprocess
模組執行存在諸多問題。所以我們才用了 paramiko
庫進行了遠端呼叫。
s = paramiko.SSHClient() s.load_system_host_keys() s.connect(srv, 22 ) (stdin, stdout, stderr) = s.exec_command(cmd) last_line = ""for line in stdout.readlines(): last_line = line print( line) s.close()
安全的殺死程序
大家都應該知道 kill -15
和 kill -9
的區別。以下指令碼讓程式等待10秒,然後使用 kill -9
殺掉她。
注意:以下指令碼有巨坑~,一定要傳參
n=0while [[ $n-lt10 ]]do let "n++" ex=`ps aux|grep \/${flag}|grep -v grep | grep -v sc. | awk '{print $2}' `echo $ex if [[ $ex == "" ]]then echo "program not exist" break else echo "send kill -15 to below:" echo $ex ps aux|grep \/${flag}|grep -v grep | grep -v sc. | awk '{print $2}' |xargs kill -15 sleep 1 fi if [[ $n-eq 10 ]]then # after 10s , try to send kill -9 ps aux|grep \/${flag}|grep -v grep | grep -v sc. | awk '{print $2}' |xargs kill -9 fidone##start jarecho "start jar"nohup-jar $jar>/dev/null 2>&1 &echo "restart ${flag} !!!"sleep 2ex=`ps aux|grep \/${flag}|grep -v grep | grep -v sc. | awk '{print $2}' `if [[ $ex == "" ]]then echo "Fail"else echo "new PID is " echo $ex echo "OK"fiexit
前端
vue
幹這個大材小用了。我們選擇了後臺都熟悉的 bootstrap
,配上一個比較古老好看的 AdminLTE
框架。
但這麼多頁面寫起來也是非常浪費時間的,所以我們也集成了 ejs
模版引擎。
對技術團隊的建議
1)要信得過自己團隊,信得過自己。 如果公司環境複雜。少開會去討論,少扯皮。先閉關鎖國再改革開放 。先閉門造車,做出個東東來,再讓各位大爺們品頭論足,進行修改。畢竟也花不了多長時間,原型驅動再好不過了,你又不是一次性做個百分專案。
2)設計時一定要考慮專案型別的多樣性和分散式。別等著需求來了,量來了,把你連根拔起。
3)自動化雖然好,也要準備好應急的手動化方式。如果CTO親自給你來電了,這並不見得是一件好事。