窮人的持續集成與持續交付
基于免費服務的持續交付環境
本文將使用一些免費的服務來為你的項目搭建 持續交付 平臺,這些服務包括
- 持續集成環境
- 持續部署環境
- 服務端應用托管
以及一些可以用于本地開發使用的開源工具如:
除此之外,我們在過程中編寫的腳本還可以用以本地構建,如果你的團隊中正好已經有CI工具/CD工具,將這些腳本集成進去也是一件非常容易的事情。
背景知識
軟件的度量
傳統的管理方法論,在軟件開發這個領域來說基本上是不工作的。軟件項目的不確定性使得人們畏懼,管理者希望通過一些數字,指標來讓自己感到某種虛幻的“掌控感”。軟件行數,測試覆蓋率,代碼故障率等數字的名聲基本上已經很糟了,經常有人拿來諷刺那些追求虛幻掌控感的“領導”。
但是有一個數字,即使最頑固的“自由主義者”也會認為是有意義的,那就是周期時間(cycle time)。簡而言之,就是一個需求從產生到最終上線所需要的時間。其中包括了需求分析,設計,編碼,測試,部署,運維等活動,可能還會包含后續的監控。
其實不論是瀑布模型,還是迭代開發的方式,或者其他的方法論,周期時間的縮短都是至關重要的。而具體到周期內,單純的開發時間變長或者測試時間變長都無關緊要。比如項目A的開發時間是測試時間的2倍,項目B則恰恰反過來,這并不能說A做的比B好,真正有意義的是A的周期時間是否比B更短。
單純改善項目過程中的某一個階段的時間,可能并不能達到預期的目的。局部優化并不一定會帶來全局的優化。換言之, 通過某些策略來提高軟件測試的效率未必能減少周期時間! 。
持續交付
傳統情況下,企業要進行軟件開發,從用戶研究到產品上線,其中會花費數月,甚至數年(我的一位印度同事給我聊起過,他的上家公司做產品,從版本啟動到版本上線需要整整兩年時間!)。而且一旦軟件需求發生變更,又有需要數月才能將變更發布上線。除了為變更提交代碼外,還有很多額外的回歸測試,發布計劃,運維部門的進度等等。而市場機會千變萬化,在特定的時間窗口中,企業的競爭者可能早已發布并占領了相當大的市場份額。
在軟件工程領域,人們提出了持續交付(continuous delivery)的概念,它旨在減少周期時間,強調在任何時刻軟件都處于可發布狀態。采用這種實踐,我們可以頻繁,快速,安全的將需求的變化發布出來,交由真實世界的用戶來使用,在為用戶帶來價值的同時,我們也可以快速,持續的得到反饋,并激勵新的變化產生(新的商業創新,新的模式等)。
持續交付包含了自動化構建,自動化測試以及自動化部署等過程,持續改進開發流程中的問題,并促進開發人員,測試人員,運維人員之間的協作,團隊可以在分鐘級別將變更發布上線。
持續交付相關技術及實踐
- 版本控制(配置管理)
- 持續集成CI
- 自動化測試
- 構建工具及構建腳本
- 部署流水線
團隊通過版本控制來進行協作,所有的代碼會在持續集成環境中編譯,代碼靜態檢查/分析,自動化測試(還可能產生報告等)。除此之外,CI還還需要有自動化驗收測試,自動化回歸測試等。
持續交付則更進一步,它將環境準備,持續集成,自動化部署等放在了一起。通過全自動(有些過程可以設置為手動,比如發布到產品環境)的方式,使得軟件可以一鍵發布。如果上線后發現嚴重defect,還支持一鍵回滾的機制(其實就是將之前的一個穩定版本做一次發布,由于發布流程已經經過千錘百煉,所以發布本身就變得非常輕松,安全)
實例
我在 《前后端分離了,然后呢?》 這篇文章中,提到了一個叫做 bookmarks 的應用,這個應用是一個前后端分離的非常徹底的應用。
我們這里會再次使用這個應用作為實例,并采用不同的兩個免費服務( travis-ci 和 snap-ci )來完成 持續部署 環境的搭建。
服務器端
bookmarks-server 是一個基于 spring-boot 的純粹的 API ,它可以被打包成一個 jar 包,然后通過命令行啟動運行。在本文中,我們我們將會將這個server部署到 heroku 平臺上。
首先需要定義一個 Procfile ,這個是我們應用的入口, heroku 根據這個文件來明確以何種方式來啟動我們的應用:
web: java -Dserver.port=$PORT -jar build/libs/bookmarks-server-0.1.0.jar --spring.profiles.active=staging
由于我們在本地使用的使用 mysql ,而 heroku 默認的是 postgres 數據庫,因此需要在 application.yml 中額外配置
spring: profiles: staging datasource: driverClassName: org.postgresql.Driver url: ${JDBC_DATABASE_URL} username: ${DATABASE_USER} password: ${DATABASE_PASS} jpa: database_platform: org.hibernate.dialect.PostgreSQLDialect hibernate: ddl-auto: update
有了這些配置后,我們需要創建一個 heroku 應用:
$ heroku create Created http://quiet-atoll-8237.herokuapp.com/ | git@heroku.com:quiet-atoll-8237.git
創建之后,我們可以在界面上對這個應用進行一些配置(當然,也可以通過命令行,具體參看 heroku help )。為了支持數據庫,需要為我們的應用添加一個 postgres 的AddOn。添加之后, heroku 會為我們提供一個 postgres 的連接地址,格式大概是這樣:
postgres://username:password@host:port/database
然后我們需要在 Heroku 的配置界面中配置一些環境變量:
這樣,當應用部署到 Heroku 上之后,我們的應用就可以讀到這些配置了(注意 application.yml 中的環境變量 JDBC_DATABASE_URL )。
搭建持續集成環境
持續集成環境,這里我們選用最簡單的 travis-ci ,它可以很容易的與 github 集成。
- 在項目X中定義一個 .travis.yml 的文件
- 將你的代碼push到github上
- 綁定github帳號到 travis
- 在 travis 中啟用項目X
這個 .travis.yml 因項目而異,我們這里的項目是 spring-boot ,所以只需要指定 java 即可:
language: java
如果是 java 項目,并且項目中有 build.gradle , travis-ci 會自動執行 gradle check 任務。
自動化部署
當CI運行成功之后,我們需要 travis-ci 幫我們將應用程序發布到 heroku 上,這時候需要做一些修改。最簡單的方式是直接安裝 travis-ci 的命令行工具到本地:
$ gem install travis -v 1.8.0 --no-rdoc --no-ri
然后通過 heroku 的 auth:token 命令獲得 heroku 的token,在加密并寫入 .travis.yml :
$ heroku auth:token 00xxxxxxxxxxxxx55d11dbd0cxxxxxxxxxxfe067 $ travis encrypt 00xxxxxxxxxxxxx55d11dbd0cxxxxxxxxxxfe067 --add
當然可以合并為一條命令:
$ travis encrypt $(heroku auth:token) --add
將加密過的token存入 .travis.yml 文件。最后的結果大致如下:
language: java deploy: provider: heroku api_key: secure: ... app: quiet-atoll-8237
注意此處的 app ,正是我們的App的名字。另外,還需要給 build.gradle 添加一個名叫 stage 的task, travis 在deploy時需要這個 task :
task stage { dependsOn build }
這樣,我們只需要在本地的一個提交,一切都會自動化起來:
- travis會執行 gradle check
- gradle check 會編譯并運行自動化測試
- travis 會部署應用到 heroku 上
- heroku 會自動重啟服務
我們可以在本地進行簡單的測試(注意此處我們的 staging 環境的URL):
$ curl https://quiet-atoll-8237.herokuapp.com/api/feeds -s | jq . [ { "id": 1, "url": "http://icodeit.org/2016/01/how-to-summarize-privious-project/", "title": "如何持久化你的項目經歷", "author": "icodit.org", "summary": "通常來說,下項目總是一件比較高興的事(大部分團隊還會一起吃個飯慶祝一下)。", "publishDate": "2016-01-07" }, { "id": 2, "url": "http://icodeit.org/2015/11/get-started-with-reflux/", "title": "你為什么應該試一試Reflux?", "author": "icodit.org", "summary": "React在設計之初就只關注在View本身上,其余部分如數據的獲取,事件處理等,全然不在考慮之內。", "publishDate": "2016-01-09" } ]
完整的 代碼在這里 。
其他
CI monitor
node-build-monitor 是一個非常容易配置,使用的CI monitor,我們只需要進行簡單地配置,就可以將 travis 的狀態可視化出來
{ "monitor": { "interval": 2000, "numberOfBuilds": 12, "debug": true }, "services": [ { "name": "Travis", "configuration": { "slug": "abruzzi/bookmarks-server" } } ] }
不過這個工具會在有網絡異常時自動終止,我們可以通過一個簡單的腳本來在它終止時自動重啟:
#!/bin/bash until node app/app.js do echo "restarting..." done
小結
通過 travis 和 heroku 這樣的免費服務,我們就可以輕松的將自己的項目做到持續集成+持續交付。我們后端的服務相對來說是比較容易的,但是涉及到一個前后端分離的架構,如何做到靜態內容的托管,打包,部署,并和后端API集成起來,我會在下一篇文章中詳細解釋。