在一個項目的開發過程中,往往會有一些需要反復執行的操作,比如編譯、測試、部署。具體於Flask項目,我一般使用nose執行單元測試、fabric進行部署、pylint執行代碼質量檢測等。這些頻繁需要執行的步驟,是非常枯燥的,那何不交給機器來自動執行呢?最近,我參與的一個校內團隊也遇到了類似的問題,於是打算調研一下相關的工具。
還是習慣性地查閱了下Kenneth Reitz大神的python-guide,果然找到了關於CI的章節。選來選去,最終沒有選擇Python Stack的Buildbot,而是更加成熟的Jenkins。Jenkins基於Java開發,是業界使用得最多的一款持續集成系統,市場份額占到了70%。
關於Jenkins,官方的介紹如下:
monitors executions of repeated jobs, such as building a software project or jobs run by cron。
文檔中也說目前Jenkins專注於以下2個點:
- Building/testing software projects continuously
- Monitoring executions of externally-run jobs
其實Jenkins能夠做的遠不止這兩點,你可以把它理解成可以自動執行任務(類似cron)並收集報告的基礎工具。再加上它有着非常豐富的插件庫,所以使用起來應該是很靈活的。
下面記錄一些在Python Web項目中應用Jenkins的經驗。
安裝
見Installing Jenkins。它也支持通過Docker安裝,真的是緊跟潮流!
需要提的一點是Jenkins比較耗內存,不運行任何構建任務的情況下就吃掉了300多M,再加上構建任務時會占用更多,所以建議服務器的內存至少有1G,512M的話很可能在執行構建任務的時候內存不夠用。
訪問控制
安裝后默認所有人都可以訪問,故需要更改訪問控制策略,具體見Standard Security Setup。
插件清單
下面是我用到的一些插件:
- Git Plugin:使用Git作為源代碼管理
- SSH plugin:遠程ssh登錄server執行命令
- Parameterized Trigger Plugin:觸發其他的job
- Cobertura Plugin:代碼測試覆蓋率報告
- Task Scanner Plugin:檢測代碼中出現的特殊標記(如TODO等)
- Violations:代碼質量檢測,支持pylint、jslint等
- ThinBackup:用於備份Jenkins
- SCM Sync configuration plugin:將Jenkens的配置變更同步到SCM中
這里只是集中地列舉一下,具體的使用會在下面穿插介紹。
測試、Staging部署、Production部署流程
根據本項目的需要,在Jenkins中建立了3個任務:
- tm_test:用於執行測試、代碼質量檢測等
- tm_staging_deploy:用於在staging服務器上deploy代碼
- tm_deploy:用於在production服務器上deploy代碼
其中staging服務器用於進行線上測試,staging服務器和production服務器的環境必須保持完全相同(當然,staging服務器配置可以低一些)。
具體的開發、測試、部署流程是:
- 在開發新功能/修復bug的時候,一般是開新分支;但如果是那種很小的修改,則直接在master上改,這樣比較省事兒
- 新功能開發完成/bug修復后,進行單元測試+人工測試,如果通過,合並到master
- 每次master有變動后,觸發tm_test任務,執行集成的單元測試和代碼質量檢測,如果OK,則自動觸發tm_staging_deploy,部署到staging服務器上
- 若tm_staging_deploy成功,則登陸到運行在staging服務器的測試網站上,人工測試新功能是否OK/bug是否已修復;若tm_staging_deploy失敗,檢查失敗原因,進行修復,直至成功
- 若staging人工測試通過,則手動觸發tm_deploy,部署到生產服務器上
- 登錄到生產服務器上進行人工測試,若出現問題,進行修復;同時密切關注Sentry發送的告警郵件,爭取在第一時間修復錯誤
比較簡單,沒有采用Git-Flow/GitHub-Flow,單元測試寫得很淺,也沒有做代碼審查。不過團隊規模小,從目前來看,上面的流程是夠用的。
下面對這三個任務做較為詳細的介紹。
tm_test
該任務用於執行測試、代碼質量檢測等。
push觸發構建
每當項目倉庫的master分支有變動時,即會觸發tm_test。要做這一點,需要如下步驟:
- 設置tm_test的Build Triggers為Poll SCM,但不填Schedule
- 為項目倉庫添加Web Hook,URL填寫
http://<Jenkins URL>/git/notifyCommit?url=<URL of the Git repository>
使用virtualenv配置測試環境
執行測試之前,需要確定Python版本(一般是2.7),然后根據此版本初始化virtualenv。
在構建中添加Execute shell項:
if [ ! -d "venv" ]; then virtualenv -p /usr/bin/python2.7 venv fi . venv/bin/activate pip install -i http://pypi.douban.com/simple -r requirements.txt
測試與報告
測試中需要2個庫:nose用於執行單元測試,coverage用於統計測試覆蓋率。
需要在Jenkins中安裝Cobertura Plugin插件,用於生成代碼測試覆蓋率報告。
然后在構建中添加Execute shell項,輸入:
nosetests --with-xunit --with-coverage --cover-package=tm && coverage xml
其中--with-xunit
告訴nose輸出JUnit形式的測試報告,--with-coverage
表示同時運行coverage(這個功能相當贊),--cover-package=tm
表示僅對指定的package執行測試覆蓋率檢測,后面的coverage xml
表示輸出xml格式的coverage報告。
然后在構建后操作中,添加如下2項:
- Publish JUnit test result report:填寫nosetests.xml
- Publish Cobertura Coverage Report:填寫coverage.xml
這樣一來,就可以執行測試,並得到測試報告和測試覆蓋率報告啦:
上面的圖表都是可點擊的,點進去后有代碼級的詳細報告,非常贊:
綠色的代碼行表示已經覆蓋到,紅色則沒有。
代碼質量檢測
Jenkins有一個蠻不錯的代碼質量報告插件:Violations,支持非常多的代碼測試工具。目前項目中使用Pylint做Python代碼質量檢測,使用JSHint做JavaScript代碼質量檢測。
在安裝好Pylint后,運行pylint --generate-rcfile > pylintrc
生成配置文件,並將其中的output-format
項的值改為parseable
。
然后在構建中添加2項Execute shell項,
pylint:
pylint tm2 > pylint.xml || exit 0
jshint:
jshint --reporter=jslint $WORKSPACE/tm2/static/js/ > jslint.xml || exit 0
其中的exit 0
是為了告訴Jenkins該命令執行成功。對於jshint來說,report選擇jslint,然后需要使用$WORKSPACE
組成絕對路徑,否則無法看到源碼級的分析報告(是不是一個bug?)。
然后在構建后步驟中添加Violations Report,在對應位置輸入jslint.xml和pylint.xml。
最終的圖形報告如下,可以看到趨勢走向:
源碼級別的分析也有:
檢測代碼中的特殊標注(如TODO)
團隊中約定,在代碼未完成的地方使用TODO
進行標記,因為PyCharm有一個很好的功能就是可以檢測出代碼中的所有TODO信息:
Jenkins中也有一個非常棒的插件Task Scanner Plugin用於檢測代碼中出現的特殊標記,當然,這些特殊標記完全是可以自定義的。
安裝完該插件后,在構建后操作中添加一項Scan workspace for open tasks,根據需要填寫配置:
然后報告就可以出來啦:
郵件告警
如果構建后狀態是unstable或failed,則可以發送郵件告警,及時通知相關負責人進行處理。Jenkins自帶SMTP功能,不過需要你提供SMTP服務器。
我使用的是qq郵箱SMTP服務器,挺好用的,目前沒有發現拒發的情況。有一點需要注意的是,在配置好SMTP的賬戶信息后,還需填寫系統管理員郵件地址,否則會發送失敗,這也是比較容易忽略的地方。
配置好SMTP后,然后在構建后操作中添加E-mail Notification項,填寫負責人的郵箱即可。
觸發下游任務
如果tm_test構建成功,則需要自動觸發tm_staging_deploy任務,這個觸發過程是通過插件Parameterized Trigger Plugin來完成的。
在構建后操作中添加Trigger parameterized build on other projects項,選擇觸發條件為stable,然后填寫待出發的任務名稱即可。
最后的tm_test任務面板如下:
界面是挺out的,不過很實用。
tm_staging_deploy
這一個job用於將最新代碼部署到staging服務器上,我采用的部署方法是通過ssh遠程登陸服務器執行命令的方式,需要一個插件SSH plugin。
然后在構建中添加Shell項:
cd /var/www/tm
export MODE=PRODUCTION git reset --hard HEAD git pull -f source venv/bin/activate pip install -r requirements.txt python manage.py db upgrade supervisorctl restart tm
tm_deploy
此任務和tm_staging_deploy基本差不多,不同的地方有2個:(1)目標服務器不同(2)觸發方式是手動觸發
備份
使用thinBackup進行備份,可設定備份周期。
配置變更同步
除此之外,我還用到了一個很有用的插件SCM Sync configuration plugin,就是把Jenkins的配置(全局配置+各job配置)同步到一個Git倉庫中。這樣的話,每次配置有變動,都會形成一個commit推送到Git倉庫。
這相當於把配置的歷史變遷都記錄下來,如果哪天Jenkins任務掛了,可以看看配置變更進行排錯。
就這些,如果你好的建議,歡迎反饋!