隨著敏捷軟件開發(fā)方法論的普及,越來越多的原來由開發(fā)者完成的重復(fù)性工作都自動化讓機器來執(zhí)行了,對于一個開發(fā)團隊來說,如果沒有自動化的pipeline,開發(fā)人員的痛苦相信大家都深有體會,隨著.NET Core 2.0 的發(fā)布,在.NET Core平臺上的所有開發(fā)工具都跨平臺化了,這也給團隊在Linux環(huán)境上部署持續(xù)集成(CI),持續(xù)發(fā)布(CD)工作帶來方便,整套環(huán)境對團隊的開發(fā)效率的提升可以說是指數(shù)級別的,以下以 Ubuntu 16.0.4 服務(wù)器為例,演示CI CD搭建獸如何搭建整個環(huán)境,當(dāng)你征服了手動搭建過程后,就可以借助 ansible docker 等工具將這一過程自動化,后續(xù)文章將演示這一自動化搭建的過程。
一,服務(wù)器環(huán)境的準(zhǔn)備
- 因為家里的文件服務(wù)器是 Ubuntu 16.0.4 Server 版,為了方便,就選這個作為服務(wù)器了。
- 按照 .NET Core 2.0 SDK安裝指南 安裝.NET Core SDK,.NET Core 項目需要 dotnet 工具來編譯,發(fā)布,測試項目。
- 按照 Docker官方安裝指南安裝 docker 服務(wù)。
- 通過
sudo apt install nginx安裝 Nginx 服務(wù)器,后面會用到該服務(wù)來反向代理內(nèi)部所有的服務(wù),這樣 HTTPS 的問題都交給 Nginx 來解決。
二,安裝并配置Jenkins CD 服務(wù)
目前比較流行的CD工具有 Jenkins 和 GoCD,此處我們選擇Jinkins,當(dāng)然 Jenkins 服務(wù)可以使用docker來運行,但考慮到簡單性,我們此處直接安裝為本地服務(wù)運行。
參照官方安裝指南安裝 Jenkins 服務(wù), 默認服務(wù)端口為:8080,為了服務(wù)的安全性,我們在服務(wù)器上安裝了nginx 服務(wù)通過 https
反向代理 Jenkins 服務(wù),最后的服務(wù)地址為:https://home.freemanke.com:28080,當(dāng)然如果你的服務(wù)器主機是云主機有固定IP可以直接使用443端口,我的服務(wù)器在家里,通過DDNS暴露到外網(wǎng),而家里的路由器又把443和80端口占用了,所以服務(wù)需要用其他端口(28080等)來服務(wù),SSL證書可以通過騰訊云SSL證書服務(wù)申請個人免費證書(當(dāng)然前提是你要有一個你自己的域名),有效期一年。
Nginx 反向代理配置如下:
####################################################################
## jenkins service - https://home.freemanke.com:28080
server {
listen 28080;
server_name home.freemanke.com;
ssl on;
ssl_certificate /home/freeman/ssl/home.freemanke.com.crt;
ssl_certificate_key /home/freeman/ssl/home.freemanke.com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:8080/;
proxy_redirect default;
}
}
搭建完成后訪問服務(wù)地址https://home.freemanke.com:28080

默認安裝完成是樸素的默認theme,我們可以參照 jenkins-material-theme 來自定義界面,此處我選擇了綠色的 Material
主題。
安裝完成后的配置步驟比較多,不一一列舉,根據(jù)每個人團隊的需要不同而不同,一下列舉本次用到插件和配置
- Bitbucket Plugin 因為我使用的git庫是 https://bitbucket.org/,所以需要安裝 Bitbucket Plugin 來支持 Webhook,當(dāng)git又提交時,bitbucket通過 Webhook 功能發(fā)送請求到 Jenkins 服務(wù)器,服務(wù)器將實時觸發(fā)指定 pipeline 的構(gòu)建,這樣才能具有持續(xù)構(gòu)建的能力。
- Email,Slack 擴展,將pipeline通過 Email 或 Slack 通知到開發(fā)者,實時反饋構(gòu)建的過程和結(jié)果。
三,安裝 docker registry 本地服務(wù)
Pipeline的每次成功構(gòu)建,都將產(chǎn)生一個軟件的新的可發(fā)性版本,如果我們使用文件存儲,我們后續(xù)的部署起來非常繁瑣,此處我們使用 docker 來部署服務(wù),所以需要一個 docker registry 服務(wù)來保存托管這些新的鏡像,官方的速度太慢,所以需要本地服務(wù),此處直接使用 docker 來啟動服務(wù),腳本如下。
docker stop registry
docker rm -f registry
docker run \
--detach \
--name registry \
--publish 5000:5000 \
--volume /home/freeman/docker-registry:/var/lib/registry \
registry:2.6.2
同樣我們需要通過 Nginx 反向代理服務(wù),最后的服務(wù)地址為https://home.freemanke.com:25000/v2/_catalog,反向代理配置如下,注意此處的配置client_max_body_size 1000M; 因為 image 包都很大,而默認的 body size 為 2M,無法提交大的 Image 文件。
####################################################################
## docker-registry service - https://home.freemanke.com:25000
server {
listen 25000;
server_name home.freemanke.com;
ssl on;
ssl_certificate /home/freeman/ssl/home.freemanke.com.crt;
ssl_certificate_key /home/freeman/ssl/home.freemanke.com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "HIGH:!aNULL:!MD5 or HIGH:!aNULL:!MD5:!3DES";
ssl_prefer_server_ciphers on;
client_max_body_size 1000M;
location / {
proxy_pass http://127.0.0.1:5000/;
proxy_redirect default;
}
}
四,準(zhǔn)備項目和 Pipeline
此處以我最近正在做的項目為例,項目的產(chǎn)出物是一個web站點和一個windows平臺的winform客戶端,此處先為 web 站點的開發(fā)構(gòu)建準(zhǔn)備一個自動化的CI CD Pipeline。

配置過程如下
- 在 Jenkins 服務(wù)上新建一個 pipeline 名為 telemetry
- 將項目的git地址添加到git源,并配置好授權(quán)信息
- 在項目的根目錄下創(chuàng)建 Jenkinsfile,此處是 Pipeline as Code 的關(guān)鍵,如果你直接在 Jenkins 上配置構(gòu)建腳本的話,你將無法跟蹤到腳本的任何改動,如果我們使用文件作為配置,并放入到git管理中,我們每一個開發(fā)者都能實時修改并跟蹤 pipeline 配置變化。此處的腳本內(nèi)容如下,具體的內(nèi)容定義請參見官方文檔,此處的主要流程是:
- 恢復(fù)nuget包應(yīng)用
- 構(gòu)建解決方案
- 運行測試
- 發(fā)布 web 項目到指定環(huán)境,此處為 ubuntu.16.10-x64 環(huán)境
- 構(gòu)建 docker 鏡像
- 推送 docker 鏡像到 docker registry
- 在 dev 環(huán)境上部署最新的代碼版本
#!/usr/bin/env groovy
pipeline {
agent any
stages {
stage('build-test') {
steps {
sh 'dotnet restore ./src/Telemetry.sln'
sh 'dotnet build ./src/Telemetry.sln'
sh 'dotnet test --no-build --no-restore ./src/Telemetry.Data.Tests/'
}
}
stage('build-push-image') {
steps {
sh 'dotnet publish ./src/Telemetry.Web/Telemetry.Web.csproj -c Release -r ubuntu.16.10-x64 -o ./bin/publish'
sh 'dotnet publish ./src/Telemetry.Web/Telemetry.Web.csproj -c Release -r win7-x86 -o ./bin/win7/publish'
sh 'docker build -t home.freemanke.com:25000/freemanke/telemetry:${BUILD_NUMBER} ./src/Telemetry.Web/'
sh 'docker push home.freemanke.com:25000/freemanke/telemetry:${BUILD_NUMBER}'
}
}
stage('deploy-dev') {
steps {
sh 'docker stop telemetry || true && docker rm -f telemetry || true'
sh 'docker run -d --name telemetry -p 45000:45000 home.freemanke.com:25000/freemanke/telemetry:${BUILD_NUMBER}'
}
}
}
}
五,驗證 Pipeline
git上的每次提交都自動觸發(fā) pipeline 的一次構(gòu)建,構(gòu)建結(jié)果如下

對于每次的構(gòu)建,我們可以跟蹤 Console 的輸出來定位是否有問題發(fā)生,一個典型的 Console 輸出結(jié)果如下圖,我們可以很清晰的跟蹤到每一個步驟的執(zhí)行情況。

自動構(gòu)建發(fā)布如果成功完成,將直接將最新的代碼部署到dev環(huán)境中,下圖是本站點的dev環(huán)境訪問截圖。

后續(xù)文章將把此次的手動搭建工作通過 ansible 和 docker 等輔助工具自動化,敬請期待!