本文是2017年3月13日晚9點(diǎn)在“AHA面對面”線上分享的“單件流的力量-伍斌_Ben面對面”的操練步驟,這里是報(bào)名鏈接。
操練目的
練習(xí)在CloudBees Jenkins Enterprise上手工配置部署流水線,使得每次代碼提交,都能自動觸發(fā)該部署流水線,并將這個(gè)過程可視化,以便一眼就能看到誰的代碼提交在哪個(gè)環(huán)節(jié)引起了什么質(zhì)量問題,以便快速進(jìn)行修復(fù)。
因?yàn)楸敬尾倬毜闹饕康氖鞘止ご罱ú渴鹆魉€,為節(jié)省時(shí)間,被部署的代碼并不是一個(gè)完整的Web應(yīng)用程序,而是使用了一個(gè)Java應(yīng)用程序和一個(gè)Robot Framework (Python) Web UI自動化應(yīng)用程序:前者僅僅是一個(gè)用Maven創(chuàng)建的有單元測試的簡單Java應(yīng)用,后者僅僅是Robot Framework官網(wǎng)上用于演示用的webdemo應(yīng)用程序。用兩者的結(jié)合來模擬一個(gè)完整的Web應(yīng)用程序:前者模擬自動化單元測試,后者模擬自動化驗(yàn)收測試。
在ThoughtWorks中國區(qū),大家親熱地把用手工搭建部署流水線的人稱為“CI搭建獸”,意指這種手工配置過程既繁瑣費(fèi)神又毫無樂趣,比較原始,比那些已經(jīng)進(jìn)化的人所從事的工作要低級。;-))
而更加高級的工作應(yīng)該是“流水線即代碼”的實(shí)踐,來讓配置腳本能與代碼一起進(jìn)行版本控制。這樣的好處是:Ops可以不用通過訪問生產(chǎn)環(huán)境,就能知道生產(chǎn)環(huán)境上的配置情況;非運(yùn)維人員如Dev,就有機(jī)會去學(xué)習(xí)這些運(yùn)維配置代碼并且加以修改,提升整個(gè)團(tuán)隊(duì)的DevOps能力;另外工具能方便地讀取這些代碼,來自動化地維護(hù)基層設(shè)施,大幅度提升Ops的工作效率。想了解更多相關(guān)的DevOps的良好實(shí)踐,不妨閱讀我的文章“實(shí)例化DevOps原則”。
為了知道“流水線即代碼”到底有多甜,需要先吃一點(diǎn)“CI搭建獸”的苦。本文會先描述“CI搭建獸”的辛苦手工工作,最后會把這些手工工作用10行“流水線即代碼”寫出來并加以運(yùn)行。
準(zhǔn)備工作
環(huán)境準(zhǔn)備
本文以macOS Sierra 10.12.3為例來準(zhǔn)備環(huán)境。
- 安裝JDK 1.8
- 安裝Maven 3.3.9
- 安裝Git 2.10.1
- 安裝Python 2.7(macOS應(yīng)該自帶)
- 安裝Python包管理工具Pip 9.0.1
- 安裝Robot Framework 3.0.2
pip install robotframework - 安裝robotframework-selenium2library
pip install robotframework-selenium2library - 安裝Java IDE IntelliJ 社區(qū)版來編輯Java單元測試代碼
- 安裝PyCharm 社區(qū)版來編輯Python Web UI測試代碼
- 準(zhǔn)備測試代碼:可以用下述命令git clone 本次操練的代碼,其中mobilebanking文件夾存放了一個(gè)Java應(yīng)用程序,robotframework-webdemo存放了一個(gè)Web UI測試程序;
git clone https://github.com/wubin28/jenkins-mobile-banking.git- 也可以用下面的方法自己動手來創(chuàng)建和下載這兩個(gè)程序:
- 用下述Maven命令來創(chuàng)建一個(gè)帶有單元測試的簡單的Java應(yīng)用程序mobilebanking
mvn archetype:generate -DarchetypeCatalog=internal - 下載WebDemo-20150901.zip作為Web UI測試代碼,并將該壓縮包解壓到文件夾robotframework-webdemo中
- 用下述Maven命令來創(chuàng)建一個(gè)帶有單元測試的簡單的Java應(yīng)用程序mobilebanking
- 也可以用下面的方法自己動手來創(chuàng)建和下載這兩個(gè)程序:
- 下載CloudBees Jenkins Enterprise,可以選擇On-Premise來下載WAR包(v 2.46.2.1)
單獨(dú)運(yùn)行自動化單元測試
在配置流水線前,先看看Java應(yīng)用程序的單元測試能否運(yùn)行通過。
- 打開命令行窗口,進(jìn)入上述mobilebanking所在的文件夾,執(zhí)行下面命令,
mvn clean test
單獨(dú)運(yùn)行自動化Web UI測試
再看看Python的自動化Web UI測試程序能否正常運(yùn)行。
- 在命令行窗口中進(jìn)入上述robotframework-webdemo文件夾,運(yùn)行下面的命令來啟動一個(gè)待Web UI測試的Web應(yīng)用程序
python demoapp/server.py - 用瀏覽器訪問下面的鏈接,來查看待測試的Web應(yīng)用程序;如果想成功登錄這個(gè)Web頁面,那么用戶名請?zhí)顚?code>demo,密碼請?zhí)顚?code>mode
http://localhost:7272/ - 在命令行窗口的robotframework-webdemo文件夾中,運(yùn)行下面的命令來單獨(dú)運(yùn)行Web UI自動化測試。如果運(yùn)行時(shí)發(fā)現(xiàn)瀏覽器驅(qū)動的錯(cuò)誤,則需要另行下載安裝相應(yīng)的瀏覽器驅(qū)動程序,并配置到PATH環(huán)境變量里(例如,從chromedriver網(wǎng)站上下載Chrome瀏覽器驅(qū)動.ZIP文件,解壓后,把相應(yīng)的目錄位置放到~/.bash_profile里面的PATH中,再source ~/.bash_profile使其生效)
robot login_tests
運(yùn)行CloudBees Jenkins并查看插件
再看看流水線所依賴的兩個(gè)插件是否已安裝。
- 運(yùn)行下述命令來啟動CloudBees Jenkins
java -jar jenkins.war - 打開瀏覽器訪問下面的CloudBees Jenkins鏈接,在頁面上選擇Trial來申請14天的免費(fèi)試用許可證
http://localhost:8080/ - 在首頁上點(diǎn)擊“Jenkins -> Manage Jenkins -> Manage Plugins -> Installed”來查看下面兩個(gè)插件是否已經(jīng)安裝(CloudBees Jenkins Enterprise默認(rèn)已經(jīng)安裝)
- Delivery Pipeline Plugin
- Robot Framework plugin
CI搭建獸的辛苦手工工作
先看看要搭建的流水線的樣子。這個(gè)流水線有兩個(gè)Stage:一個(gè)是COMMIT,用來針對第#53號代碼提交運(yùn)行自動化單元測試;另一個(gè)是ACCEPTANCE,用來在單元測試運(yùn)行通過后,針對同樣的代碼提交運(yùn)行基于Web界面的自動化驗(yàn)收測試。

創(chuàng)建流水線的第一個(gè)Stage:COMMIT
首先創(chuàng)建流水線的第一個(gè)Stage——COMMIT,來運(yùn)行自動化單元測試。
- 在Jenkins首頁點(diǎn)擊“New Item”鏈接
- 在Enter an item name輸入框中輸入這個(gè)item的名字,比如可以叫
mobilebanking-commit - 點(diǎn)擊"Maven project",然后點(diǎn)擊OK按鈕,進(jìn)入配置頁面
- 勾選“Delivery Pipeline configuration”,在下面出現(xiàn)的Stage Name輸入框中,填寫這個(gè)stage的名字,比如叫
COMMIT;在下面的Task Name輸入框中,填寫這個(gè)stage的描述信息,比如叫Build mobilebanking and run unit tests - 在Source Code Management框中,選擇Git,然后在下面的Repository URL輸入框中填入Java源代碼的位置,可以是github的地址,這里填寫了本機(jī)地址
file:///Users/twer/OOR/katas/remote/jenkins-mobile-banking - 在Build Triggers框中,勾選“Poll SCM”,并在Schedule框中填寫
* * * * *,表示每分鐘Jenkins會查看是否有代碼提交進(jìn)而觸發(fā)自動化單元測試;* * * * *這種寫法對這次操練很有用,但在實(shí)際工作環(huán)境中不建議使用,因?yàn)檫@會增大服務(wù)器的負(fù)載。在工作環(huán)境中推薦用類似這樣的格式H/5 * * * *,表示每5分鐘,Jenkins會按Job名字的Hash來分散Poll(輪詢)該Job的SCM,已達(dá)到負(fù)載均衡的目的 - 在Build框中,向Root POM輸入框中填寫pom文件的位置
mobilebanking/pom.xml,向Goals and options框中填寫Maven的命令參數(shù)clean test來運(yùn)行單元測試 - 點(diǎn)擊頁面左下角的“Save”按鈕來保存
讓COMMIT Stage單獨(dú)自動觸發(fā)
咱們需要試一試COMMIT Stage能否隨著代碼提交而自動觸發(fā)單元測試。
- 在IntelliJ里打開Java應(yīng)用程序mobilebanking,隨便加一行無關(guān)緊要的代碼,比如重復(fù)調(diào)用一遍方法
checkUsernameAndPassword(username, password);,但前提是單元測試能夠運(yùn)行通過 - 在命令行中用git命令來提交代碼,然后立即在Jenkins首頁Dashboard上,觀察
mobilebanking-commit這個(gè)Item;它會在1分鐘內(nèi)被自動觸發(fā)
git add .
git commit -m "call method checkUsernameAndPassword() twice"
創(chuàng)建流水線的第二個(gè)Stage:ACCEPTANCE
這個(gè)ACCEPTANCE Stage是用來運(yùn)行Robot Framework Web UI自動化測試的。
- 回到Jenkins的首頁Dashboard,點(diǎn)擊“New Item”鏈接
- 在Enter an item name輸入框中輸入這個(gè)item的名字,比如可以叫
mobilebanking-acceptance - 點(diǎn)擊"Freestyle project",然后點(diǎn)擊OK按鈕,進(jìn)入配置頁面
- 勾選“Delivery Pipeline configuration”,在下面出現(xiàn)的Stage Name輸入框中,填寫這個(gè)stage的名字,比如叫
ACCEPTANCE;在下面的Task Name輸入框中,填寫這個(gè)stage的描述信息,比如叫Run acceptance tests - 在Build框中,點(diǎn)擊“Add build step”,然后選擇“Execute shell”;在隨后出現(xiàn)的Command輸入框中,輸入下面的腳本來運(yùn)行Robot Framework Web UI自動化測試
robot /<完整路徑……>/robot-framework-demo/WebDemo/login_tests - 點(diǎn)擊“Save”保存
將兩個(gè)Stage串起來
現(xiàn)在來把第二個(gè)Stage ACCEPTANCE掛到第一個(gè)Stage COMMIT之后,使得第一個(gè)Stage正常運(yùn)行結(jié)束后,能自動觸發(fā)第二個(gè)Stage繼續(xù)運(yùn)行。
- 回到Jenkins的首頁Dashboard,點(diǎn)擊mobilebanking-commit進(jìn)入這個(gè)Item的頁面,然后點(diǎn)擊左邊的Configure,在頁面底部的Post-build Actions框中,點(diǎn)擊Add post-build action,選擇Trigger parameterized build on other projects,在Projects to build輸入框中輸入
mobilebanking-acceptance,在Trigger when build is輸入框中選擇“Stable”,勾選Trigger build without parameters,點(diǎn)擊Save保存 - 在IntelliJ里mobilebanking這個(gè)Java應(yīng)用程序中,將上述重復(fù)調(diào)用的方法
checkUsernameAndPassword(username, password);刪掉 - 在命令行中用git命令來提交代碼,然后立即在Jenkins首頁Dashboard上,觀察
mobilebanking-commit和mobilebanking-acceptance這兩個(gè)Item;它們先后會在1分鐘內(nèi)被自動觸發(fā)
git add .
git commit -m "delete duplicated method checkUsernameAndPassword()"
創(chuàng)建一個(gè)視圖來可視化Deployment Pipeline
用視圖將上面兩個(gè)Stage串起來的配置進(jìn)行可視化,以便方便地看到誰的代碼提交在哪個(gè)環(huán)節(jié)引起了什么質(zhì)量問題。
- 回到Jenkins的首頁Dashboard,點(diǎn)擊All右邊的"+"號,在View Name輸入框中輸入一個(gè)視圖的名字,比如叫
Deployment Pipeline;再選擇"Delivery Pipeline View",點(diǎn)擊OK; - 在配置頁面中勾選“Show commit messages”來顯示代碼提交描述信息,勾選“Show test results”來顯示單元測試運(yùn)行情況信息
- 在Pipelines框中,Component的Name輸入框中輸入該視圖的名字,比如叫“Deployment Pipeline”,在Initial Job輸入框中選擇mobilebanking-commit來作為初始運(yùn)行的Stage;點(diǎn)擊OK保存
讓視圖可視化一次代碼提交
現(xiàn)在用上面創(chuàng)建的Deployment Pipeline視圖,來可視化一次代碼提交
- 在IntelliJ里打開Java應(yīng)用程序mobilebanking,增加一行代碼來重復(fù)調(diào)用一遍方法
checkUsernameAndPassword(username, password); - 在命令行中用git命令來提交代碼,然后立即在Jenkins首頁Dashboard的Deployment Pipeline視圖中,觀察整個(gè)視圖;它會在1分鐘內(nèi)被自動觸發(fā);另外如果把瀏覽器調(diào)整到僅占據(jù)屏幕的一半,那么在運(yùn)行ACCEPTANCE Stage時(shí),會在屏幕的另一半看到運(yùn)行Web UI自動化測試的界面
git add .
git commit -m "call method checkUsernameAndPassword() twice again"
讓代碼編譯失敗一次
讓代碼編譯失敗一次,看看流水線有什么變化。
- 在IntelliJ里打開Java應(yīng)用程序mobilebanking,增加一行代碼來調(diào)用沒有創(chuàng)建出來的方法,比如調(diào)用方法
abc(); - 在命令行中用git命令來提交代碼,然后立即在Jenkins首頁Dashboard的Deployment Pipeline視圖中,觀察整個(gè)視圖;它會在1分鐘內(nèi)被自動觸發(fā),COMMIT變紅,如下圖所示;
git add .
git commit -m "call method abc()"

讓單元測試運(yùn)行失敗一次
讓單元測試運(yùn)行失敗一次,看看流水線有什么變化。
- 在IntelliJ里打開Java應(yīng)用程序mobilebanking,刪除剛才的代碼
abc();;然后讓方法loginWithUsernameAndPassword()返回false,使得單元測試失敗 - 在命令行中用git命令來提交代碼,然后立即在Jenkins首頁Dashboard的Deployment Pipeline視圖中,觀察整個(gè)視圖;它會在1分鐘內(nèi)被自動觸發(fā),COMMIT變黃,如下圖所示;
git add .
git commit -m "make unit test failed"

讓W(xué)eb UI測試運(yùn)行失敗一次
讓W(xué)eb UI測試運(yùn)行失敗一次,看看流水線有什么變化。
- 在IntelliJ里打開Java應(yīng)用程序mobilebanking,讓方法loginWithUsernameAndPassword()返回
true,使得單元測試能成功 - 在PyCharm里面打開Python項(xiàng)目robotframework-webdemo,將gherkin_login.robot測試文件中的
welcome page should be open改為運(yùn)行失敗,具體改法是將resource.robot文件中的Title Should Be Welcome Page一行,改為Title Should Be Welcome Page 1 - 在命令行中用git命令來提交代碼,然后立即在Jenkins首頁Dashboard的Deployment Pipeline視圖中,觀察整個(gè)視圖;它會在1分鐘內(nèi)被自動觸發(fā),ACCEPTANCE變紅,如下圖所示;
git add .
git commit -m "make web ui test failed"

讓整個(gè)流水線成功運(yùn)行一次
- 在PyCharm里面打開Python項(xiàng)目robotframework-webdemo,將resource.robot文件中的
Title Should Be Welcome Page 1一行,改回Title Should Be Welcome Page - 在命令行中用git命令來提交代碼,然后立即在Jenkins首頁Dashboard的Deployment Pipeline視圖中,觀察整個(gè)視圖;它會在1分鐘內(nèi)被自動觸發(fā),整個(gè)流水線會變綠,如下圖所示;
git add .
git commit -m "make web ui test passed"

10行代碼搞定“CI搭建獸”的全部手工工作
下面看看如何用“流水線即代碼”的實(shí)踐,用10行Groovy代碼搞定“CI搭建獸”的全部手工工作。而這10行代碼都放到一個(gè)名為Jenkinsfile的純文本文件中,下面會配置Jenkins,讓它運(yùn)行這個(gè)文件的Groovy腳本和配置語句。
先在Jenkins的Web UI里面定義一個(gè)流水線
- 在Jenkins首頁點(diǎn)擊“New Item”鏈接
- 在Enter an item name輸入框中輸入這個(gè)item的名字,比如可以叫
pipeline-as-code - 點(diǎn)擊"Pipeline",表示要創(chuàng)建一個(gè)流水線,然后點(diǎn)擊OK按鈕,進(jìn)入配置頁面
- 在Build Triggers框中勾選"Poll SCM",然后在下面的"Schedule"輸入框中輸入
* * * * *,這和前面配置COMMIT Stage一樣,都表示每分鐘Jenkins會查看是否有代碼提交進(jìn)而觸發(fā)流水線 - 在Pipeline框中的Definition選擇框中,選擇"Pipeline script from SCM",表示Jenkins會從版本控制系統(tǒng)來讀取Jenkinsfile;
- 在SCM選擇框中選擇"Git",并在下面的Repository URL輸入框中填寫`file://<完整路徑……>/jenkins-mobile-banking,來指定Jenkinsfile所在的版本控制系統(tǒng)
- 最后在最下方的Script Path輸入框中,填寫
mobilebanking/Jenkinsfile,來指定Jenkinsfile的確切位置,點(diǎn)擊"Save"保存
在Jenkinsfile里面編寫Groovy腳本來定義流水線
- 在Java程序所在的mobilebanking文件夾中,用IntelliJ創(chuàng)建一個(gè)名為Jenkinsfile的純文本文件,并在該文件中插入以下Groovy代碼
node {
stage('COMMIT') {
echo 'The COMMIT stage'
sh 'mvn -f <full path>/jenkins-mobile-banking/mobilebanking/pom.xml clean test'
}
stage('ACCEPTANCE') {
echo 'The ACCEPTANCE stage'
sh 'robot <full path>/jenkins-mobile-banking/robotframework-webdemo/login_tests'
}
}
這里,Jenkins一旦見到了node語句,就要為這個(gè)流水線分配executor和workspace,所以如果沒有node語句,流水線就不會被執(zhí)行。
stage語句指定了Stage;echo語句用來在console上打印一句話,方便查看運(yùn)行結(jié)果;sh語句指定了要在Unix/Linux機(jī)器上運(yùn)行一句腳本,如果是在Windows機(jī)器上,則要用bat語句。
在COMMIT Stage里面的sh語句,執(zhí)行了maven命令,來運(yùn)行單元測試,其中mvn命令指定了pom.xml文件的位置;在ACCEPTANCE Stage里面的sh語句,執(zhí)行了Robot Framework的Web UI 自動化測試。
運(yùn)行一下流水線
- 在運(yùn)行pipeline-as-code流水線前,需要把前面“CI搭建獸”搭建的mobilebanking-commit中Poll SCM里面的
* * * * *改為H/5 * * * *,來讓這個(gè)Job每5分鐘執(zhí)行一次,從而當(dāng)有代碼提交時(shí),不會與后面配置的pipeline-as-code流水線同時(shí)執(zhí)行,以便于單獨(dú)觀察流水線 - 在命令行中用git命令來提交代碼,然后立即在Jenkins首頁Dashboard中點(diǎn)擊"pipeline-as-code",來觀察這個(gè)流水線的視圖;它會在1分鐘內(nèi)被自動觸發(fā),流水線會變綠,如下圖所示;如果把瀏覽器縮小到屏幕的一半,那么就能看到屏幕另一半Web UI自動化測試在打開另一個(gè)瀏覽器來運(yùn)行;
git add .
git commit -m "update Jenkinsfile"

- 再過幾分鐘,“CI搭建獸”搭建的mobilebanking-commit也會被觸發(fā)執(zhí)行
部署流水線與單件流
單件流指的是,正在制作的產(chǎn)品的各個(gè)模塊,能從最初的對其增加價(jià)值的加工步驟,直接傳遞到下一個(gè)增值加工步驟進(jìn)行加工,并最終被傳遞到客戶手中,在這個(gè)過程中,各個(gè)步驟之間沒有發(fā)生等待或者排隊(duì)的現(xiàn)象(參見《豐田套路》)。
而如果在各個(gè)步驟的傳遞過程中發(fā)生了等待或排隊(duì),那就等同于建立了庫存。軟件開發(fā)中常見的庫存包括排隊(duì)等候開發(fā)的需求、排隊(duì)等候測試的代碼、排隊(duì)等待修復(fù)的缺陷、排隊(duì)等待上線的產(chǎn)品特性、甚至Ticket和郵件系統(tǒng);
隱藏很深的庫存可以由諸如那些有固定期限(比如每月一次)的“用戶驗(yàn)收測試”的流程來造成——月初幾天就開發(fā)測試完畢的產(chǎn)品特性必須要被存放近一個(gè)月,等到月底“用戶驗(yàn)收測試”后才能繼續(xù)往下游走。軟件開發(fā)中的上述庫存就是讓項(xiàng)目延期的最大原因。
企業(yè)如果能做到單件流,那么就相當(dāng)于消滅了庫存,讓價(jià)值在不同環(huán)節(jié)之間流動得最快,進(jìn)而實(shí)現(xiàn)了全局優(yōu)化。
這次操練所搭建的部署流水線,可以作為一個(gè)工具來可視化軟件開發(fā)從代碼提交之后的價(jià)值流。如果代碼在各個(gè)Stage之間都無須排隊(duì)且自動化地流動,那么就實(shí)現(xiàn)了Continuous Deployment,也就是在代碼提交之后實(shí)現(xiàn)了軟件開發(fā)最高效的單件流。
操練成就匠藝
全棧軟件開發(fā)者的技術(shù)操練社區(qū):bjdp.org北京設(shè)計(jì)模式學(xué)習(xí)組
QQ群號:235913915,微信訂閱號:bjdp_org,網(wǎng)站:www.bjdp.org