沪江基于容器编排的Dev/Ops流程
生活随笔
收集整理的這篇文章主要介紹了
沪江基于容器编排的Dev/Ops流程
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
【編者的話】我們整個 DevOps 流程是建立在容器編排的基礎(chǔ)上的,目的是簡化流程和實現(xiàn)自動化 CI/CD 和自動化運維。當(dāng)中會有很多沒有想到的地方,可能也不太適用于復(fù)雜場景。
本文講的是滬江基于容器編排的Dev/Ops流程隨著 DevOps 和 SRE 概念的流行,越來越多的 developer 和 operater 們摒棄傳統(tǒng)的開發(fā)部署流程,轉(zhuǎn)向了如下圖所示的無線循環(huán)模式:
在我理解 DevOps 包含三個大塊:敏捷開發(fā)(Agile)、持續(xù)集成與交付(CI/CD)、自動運維(ITSM)。?
在容器化的時代,我們是如何實現(xiàn) DepOps 或者 SRE 的呢?下面我就來分享一下滬江學(xué)習(xí)產(chǎn)品線團(tuán)隊基于容器編排的 DevOps 流程。
FROM?java:8-jre-alpine
使用上述 Dockerfile 生成的鏡像平均只有 80 多 MB,啟動時間幾乎在 5 秒內(nèi)。使用 Alpine 鏡像雖然減小了體積,但缺少一些工具命令,例如 curl 等,可以根據(jù)需要酌情安裝。另外遇到的一個坑是時區(qū)問題:由于 Docker 鏡像內(nèi)的時區(qū)是 UTC 時間,和宿主機(jī)的東 8 區(qū)不一致,所以必須安裝 timezone 工具并設(shè)置 TZ,才能使容器內(nèi)時間和宿主機(jī)保持一致,對數(shù)據(jù)庫的寫入和日志的輸出都是非常必要的一環(huán)。
這里我們還要介紹一個概念,要讓制作的鏡像,能在所有安裝了 Docker 的服務(wù)器上運行,而不在乎宿主機(jī)的操作系統(tǒng)及環(huán)境。借用 Java 的一句話來說:一次制作,多平臺運行。所以,我們還會把所有環(huán)境的配置文件,以不同的文件名全部放入鏡像中,通過參數(shù)來選擇 Docker 啟動時使用的環(huán)境配置文件。
值得注意的是,如果開發(fā)的應(yīng)用是基于 Spring 框架的話,這個功能很好實現(xiàn)。但如果是其他語言開發(fā),會有一定的開發(fā)量。
本文以默認(rèn) Java 開發(fā)當(dāng)所有的開發(fā)工作完成后,推薦程序目錄結(jié)構(gòu)是這樣的:
│???├──?main │???│???├──?java │???│???├──?resources │???│???│???├──?application.yaml │???│???│???├──?application-dev.yaml │???│???│???├──?application-qa.yaml │???│???│???├──?application-yz.yaml │???│???│???├──?application-prod.yaml │???│???│???├──?logback.xml │???├──?test ├──?scripts │???├──?Dockerfile │???├──?InitDB.sql ├──?pom.xml?
如何減少人工參與到持續(xù)集成與持續(xù)交付呢?我們最希望的開發(fā)過程是:對著計算機(jī)說出我們的想要的功能,計算機(jī)按照套路,自動編碼,自動發(fā)布到測試環(huán)境,自動運行測試腳本,自動上線。當(dāng)然,目前時代要實現(xiàn)自動編碼的過程還需要發(fā)明那只「貓」。
但只要對測試有足夠信心,我們完全可以實現(xiàn)一種境界:在炎熱的下午,輕松地提交自己編寫的代碼,去休息室喝杯咖啡,回來后看見自己的代碼已經(jīng)被應(yīng)用在生產(chǎn)環(huán)境上了。在容器時代,我們可以很快速的實現(xiàn)這一夢想,其具體步驟如下圖:
Gitflow 給我們展示了復(fù)雜團(tuán)隊在處理不通代碼版本的優(yōu)雅解決方案,它需要feature、develop、release、hotfix、master 5 條分支來處理不同時段的并行開發(fā)。但這真的合適于一個不超過 20 人的本地合作團(tuán)隊開發(fā)嗎?我們的開發(fā)團(tuán)隊不足 6 人,每個人負(fù)責(zé) 3 個以上的微服務(wù),幾乎不可能在同個項目上安排兩個以上的同學(xué)并行開發(fā)。
在初期我們準(zhǔn)守規(guī)定并使用標(biāo)準(zhǔn)的 Gitflow 流程,開發(fā)人員立刻發(fā)現(xiàn)一個問題,他們需要在至少 3 條分支上來回的 merge 代碼,且不會有任何代碼沖突(因為就一個人開發(fā)),降低了開發(fā)的效率。這讓我意識到,Gitflow 模式也許并不適合于小團(tuán)隊微服務(wù)的世界,一種反 Gitflow 模式的想法出現(xiàn)在腦海中。我決定對Gitflow 進(jìn)行瘦身,化繁至簡。
我們把 5 條分支簡化為 3 條分支,其中 Master 分支的作用只是維護(hù)了最新的線上版本的作用,Dev 分支為開發(fā)的主要分支,所有的鏡像是以此分支的代碼為源頭生成的。這時開發(fā)的過程變?yōu)?#xff1a;
如此一來,只有從 Feature 把代碼 merge 到 Dev 分支的一次 merge 動作,大大提升可開發(fā)效率。
工欲善其事必先利其器,首先我們必須要在 Jenkins 上安裝插件 :
Pipeline Plugin(如果使用Jenkins2.0默認(rèn)安裝) Git Sonar Scaner Docker Pipeline Plugin Marathon
如果你第一次接觸 Jenkins Pipeline,可以從https://github.com/jenkinsci/p ... AL.md找到幫助。
現(xiàn)在,我們開始編寫 Groove 代碼。基于容器編排的 Pipeline 分為如下幾個步驟:
1、檢出代碼
這個步驟使用 Git 插件,把開發(fā)好的代碼檢出。
stage('Check?out') gitUrl?=?"git@gitlab.xxxx.com:xxx.git" git?branch:?"dev",?changelog:?false,?credentialsId:?"deploy-key",?url:?gitUrl?
2、Maven 構(gòu)建 Java 代碼
由于我們使用的是 Spring Boot 框架,生成物應(yīng)該是一個可執(zhí)行的 jar 包。
stage('Build') sh?"${mvnHome}/bin/mvn?-U?clean?install"
3、靜態(tài)代碼分析
通過 Sonar Scaner 插件,通知 Sonar 對代碼庫進(jìn)行靜態(tài)掃描。
stage('SonarQube?analysis') //?requires?SonarQube?Scanner?2.8+ def?scannerHome?=?tool?'SonarQube.Scanner-2.8'; withSonarQubeEnv('SonarQube-Prod')?{sh?"${scannerHome}/bin/sonar-scanner?-e?-Dsonar.links.scm=${gitUrl}?-Dsonar.sources=.?-Dsonar.test.exclusions=file:**/src/test/java/**?-Dsonar.exclusions=file:**/src/test/java/**?-Dsonar.language=java?-Dsonar.projectVersion=1.${BUILD_NUMBER}?-Dsonar.projectKey=lms-barrages?-Dsonar.projectDescription=0000000-00000?-Dsonar.java.source=8?-Dsonar.projectName=xxx" }?
4、制作 Docker 鏡像
此步驟會調(diào)用 Docker Pipeline 插件通過預(yù)先寫好的 Dockerfile,把 jar 包和配置文件、三方依賴包一起打入 Docker 鏡像中,并上傳到私有 Docker 鏡像倉庫中。
stage('Build?image') docker.withRegistry('https://dockerhub.xxx.com',?'dockerhub-login')?{ docker.build('dockerhub.xxx.com/xxxx').push('test')?//test是tag名稱 }?
5、部署測試環(huán)境
通過事先寫好的部署文件,用 Marathon 插件通知 Marathon 集群,在測試環(huán)境中部署生成好的鏡像。
stage('Deploy?on?Test') sh?"mkdir?-pv?deploy" dir("./deploy")?{git?branch:?'dev',?changelog:?false,?credentialsId:?'deploy-key',?url:?'git@gitlab.xxx.com:lms/xxx-deploy.git'//Get?the?right?marathon?urlmarathon_url="http://marathon-qa"marathon?docker:?imageName,?dockerForcePull:?true,?forceUpdate:?true,?url:?marathon_url,?filename:?"qa-deploy.json" }?
6、自動化測試
運行事先測試人員寫好的自動化測試腳本來檢驗程序是否運行正常。
stage('Test') //?下載測試用例代碼 git?branch:?'dev',?changelog:?false,?credentialsId:?'deploy-key',?url:?'git@gitlab.xxx.com:lms/xxx-test.git' parallel(autoTests:?{//?使用nosetests?運行測試用例sh?"docker?run?-it?--rm?-v?$PWD:/code?nosetests?nosetests?-s?-v?-c?conf\run\api_test.cfg?--attr?safeControl=1" },manualTests:{sleep?30000 })?
7、人工測試
如果對自動化測試不放心,此時可選擇結(jié)束 Pipeline,進(jìn)行人工測試。為了說明整個流程,我們這里選擇跳過人工測試環(huán)節(jié)。
8、部署生產(chǎn)環(huán)境
當(dāng)所有測試通過后,Pipeline 自動發(fā)布生產(chǎn)環(huán)境。
stage('Deploy?on?Prod') input?"Do?tests?OK?" dir("./deploy")?{//Get?the?right?marathon?urlmarathon_url="http://marathon-prod"marathon?docker:?imageName,?dockerForcePull:?true,?forceUpdate:?true,?url:?marathon_url,?filename:?"prod-deploy.json" }?
最后我們來看看整個 Pipeline 的過程:
下面我們就來介紹一下容器編排的配置文件。由于我們使用 Mesos+Marathon的容器編排方式,部署的重任從以前的寫部署腳本變成了寫一個 Marathon 的配置,其內(nèi)容如下:
{ "id":?"/appName", "cpus":?2, "mem":?2048.0, "instances":?2, "args":?[ "--spring.profiles.active=qa" ], "labels":?{ "HAPROXY_GROUP":?"external", "HAPROXY_0_VHOST":?"xxx.hujiang.com" }, "container":?{ "type":?"DOCKER", "docker":?{"image":?"imageName","network":?"USER","forcePullImage":?true,"portMappings":?[{"containerPort":?12345,"hostPort":?0,"protocol":?"tcp","servicePort":?12345}] }, "volumes":?[{"containerPath":?"/app/log","hostPath":?"/home/logs/appName","mode":?"RW"} ] }, "ipAddress":?{ "networkName":?"calico-net" }, "healthChecks":?[ {"gracePeriodSeconds":?300,"ignoreHttp1xx":?true,"intervalSeconds":?20,"maxConsecutiveFailures":?3,"path":?"/health_check","portIndex":?0,"protocol":?"HTTP","timeoutSeconds":?20 } ], "uris":?[ "file:///etc/docker.tar.gz" ] }?
我們把這個配置內(nèi)容保存為不同的 Json 文件,每個對應(yīng)的環(huán)境都有一套配置文件。例如 Marathon-qa.json,Marathon-prod.json。當(dāng) Pipeline 部署時,可以通過Jenkins Marathon 插件,根據(jù)選擇不同的環(huán)境,調(diào)用部署配置,從而達(dá)到自動部署的目的。
但風(fēng)險過大,我們并不是每個人都能像 Rambo 一樣 bug 的存在,大多數(shù)的情況還需要使用規(guī)范和流程來約束。就像自動化測試取代不了人工黑盒測試一樣,部署測試后也不能直接上生產(chǎn)環(huán)境,在測試通過后還是需要有個人工確認(rèn)和部署生產(chǎn)的過程。
所以我們需要把自動化流程和最后的部署上線工作分開來,分別變成兩個 Job,并給后者單獨分配權(quán)限,讓有權(quán)限的人來做最后的部署工作。這個人可以是 Team leader、開發(fā)經(jīng)理,也可以是運維伙伴,取決于公司的組織結(jié)構(gòu)。
那這個部署的 Job 具體干什么呢?在容器編排時代,結(jié)合鏡像既構(gòu)建物的思想,部署 Job 不會從代碼編譯開始工作,而是把一個充分測試且通過的鏡像版本,通過 Marathon Plugin 部署到產(chǎn)線環(huán)境中去。這里是 Deploy_only 的例子:
node('docker-qa'){ if?(ReleaseVersion?==""){echo?"發(fā)布版本不能為空"return } stage?"Prepare?image"def?moduleName?=?"${ApplicationModule}".toLowerCase()def?resDockerImage?=?imageName?+?":latest"def?desDockerImage?=?imageName?+?":${ReleaseVersion}"if?(GenDockerVersion?=="true"){sh?"docker?pull?${resDockerImage}"sh?"docker?tag?${resDockerImage}?${desDockerImage}"sh?"docker?push?${desDockerImage}"sh?"docker?rmi?-f?${resDockerImage}?${desDockerImage}"}stage?"Deploy?on?Mesos"git?branch:?'dev',?changelog:?false,?credentialsId:?'deploy-key',?url:?'git@gitlab.xxx.com:lms/xxx-test.git'??//Get?the?right?marathon?urlecho?"DeployDC:?"?+?DeployDCmarathon_url?=?""if?(DeployDC=="AA")?{if?(DeployEnv?==?"prod"){input?"Are?you?sure?to?deploy?to?production?"marathon_url?=?"${marathon_AA_prod}"}else?if?(DeployEnv?==?"yz")?{marathon_url?=?"${marathon_AA_yz}"}}else?if?("${DeployDC}"=="BB"){if?("${DeployEnv}"?==?"prod"){input?"Are?you?sure?to?deploy?to?production?"marathon_url?=?"${marathon_BB_prod}"}else?if?("${DeployEnv}"?==?"yz")?{marathon_url?=?"${marathon_BB_yz}"}}marathon?docker:?imageName,?dockerForcePull:?true,?forceUpdate:?true,?url:?marathon_url,?filename:?"${DeployEnv}-deploy.json" }?
為什么不把這個文件跟隨應(yīng)用項目一起放到 scripts 下呢?因為把部署和應(yīng)用分開后,可以由兩撥人進(jìn)行維護(hù),兼顧公司的組織架構(gòu)。
在我們團(tuán)隊目前使用 cAdvisor + InfluxDB + Grafana 的組合套件實現(xiàn)對容器的監(jiān)控。
首先需要在 Mesos 集群中所有的 Agent 安裝 cAdvisor 。他負(fù)責(zé)把宿主機(jī)上所有運行中的容器數(shù)據(jù)以數(shù)據(jù)點(data point)形式發(fā)送給時序數(shù)據(jù)庫(InfluxDB),下面是 cAdvisor 監(jiān)控的一些數(shù)據(jù)點:
這些數(shù)據(jù)點經(jīng)過 Grafana 整理,展示在界面上,這樣我們就能掌握具體容器的性能指標(biāo)了。下面是一個 Grafana 的截圖:
除了對容器本身的監(jiān)控,宿主機(jī)的監(jiān)控也是必不可少的。由于監(jiān)控的點有很多,這里不一一例舉。
本文講的是滬江基于容器編排的Dev/Ops流程隨著 DevOps 和 SRE 概念的流行,越來越多的 developer 和 operater 們摒棄傳統(tǒng)的開發(fā)部署流程,轉(zhuǎn)向了如下圖所示的無線循環(huán)模式:
在我理解 DevOps 包含三個大塊:敏捷開發(fā)(Agile)、持續(xù)集成與交付(CI/CD)、自動運維(ITSM)。?
在容器化的時代,我們是如何實現(xiàn) DepOps 或者 SRE 的呢?下面我就來分享一下滬江學(xué)習(xí)產(chǎn)品線團(tuán)隊基于容器編排的 DevOps 流程。
敏捷開發(fā)
大道至簡,所有血的教訓(xùn)告訴我們,不要把簡單的事情復(fù)雜化。換句話說,不要用復(fù)雜的方法處理簡單的事情。我對敏捷的理解是「快」和「微」??熘傅?#xff0c;開發(fā)快,上線快,性能快。微指微服務(wù)、微鏡像。圍繞這兩點,在開發(fā)階段我們需要做以下幾件事:應(yīng)用微服務(wù)化
這是個比較大的概念,不在這里討論,有興趣可以參考我的其他文章。但只有應(yīng)用小了,才有可能快起來。給 Docker 鏡像瘦身
為了讓 Docker 啟動和運行得快,首先就是要對 Docker 瘦身。由于所有的應(yīng)用全部會統(tǒng)一為 Java 語言開發(fā),所以我們以 Java 為例,選用了 jre-alpine 作為我們的基礎(chǔ)鏡像,下面是 Dockerfile 的例子:FROM?java:8-jre-alpine
add timezone and default it to Shanghai
RUN?apk?--update?add?--no-cache?tzdata ENV?TZ=Asia/Shanghai RUN?mkdir?-p?/app/log COPY??./target/xxx.jar??/app/xxx.jar EXPOSE?9999 VOLUME?["/app/log"] WORKDIR?/app/ ENTRYPOINT?["java","-Xms2048m",?"-Xmx2048m",?"-Xss512k",?"-jar","xxx.jar"] CMD?[]?使用上述 Dockerfile 生成的鏡像平均只有 80 多 MB,啟動時間幾乎在 5 秒內(nèi)。使用 Alpine 鏡像雖然減小了體積,但缺少一些工具命令,例如 curl 等,可以根據(jù)需要酌情安裝。另外遇到的一個坑是時區(qū)問題:由于 Docker 鏡像內(nèi)的時區(qū)是 UTC 時間,和宿主機(jī)的東 8 區(qū)不一致,所以必須安裝 timezone 工具并設(shè)置 TZ,才能使容器內(nèi)時間和宿主機(jī)保持一致,對數(shù)據(jù)庫的寫入和日志的輸出都是非常必要的一環(huán)。
把所有環(huán)境配置包含在鏡像中
早在虛擬機(jī)時代,我們已經(jīng)做到了使用包含依賴的虛擬機(jī)鏡像來加速部署,那么為什么要止步于此呢?我們可以更進(jìn)一步,把服務(wù)本身也包含在鏡像中,Docker 用了更輕量的方式已經(jīng)實現(xiàn)了這一點。這里我們還要介紹一個概念,要讓制作的鏡像,能在所有安裝了 Docker 的服務(wù)器上運行,而不在乎宿主機(jī)的操作系統(tǒng)及環(huán)境。借用 Java 的一句話來說:一次制作,多平臺運行。所以,我們還會把所有環(huán)境的配置文件,以不同的文件名全部放入鏡像中,通過參數(shù)來選擇 Docker 啟動時使用的環(huán)境配置文件。
值得注意的是,如果開發(fā)的應(yīng)用是基于 Spring 框架的話,這個功能很好實現(xiàn)。但如果是其他語言開發(fā),會有一定的開發(fā)量。
本文以默認(rèn) Java 開發(fā)當(dāng)所有的開發(fā)工作完成后,推薦程序目錄結(jié)構(gòu)是這樣的:
│???├──?main │???│???├──?java │???│???├──?resources │???│???│???├──?application.yaml │???│???│???├──?application-dev.yaml │???│???│???├──?application-qa.yaml │???│???│???├──?application-yz.yaml │???│???│???├──?application-prod.yaml │???│???│???├──?logback.xml │???├──?test ├──?scripts │???├──?Dockerfile │???├──?InitDB.sql ├──?pom.xml?
持續(xù)集成與交付
自動化的持續(xù)集成和交付在整個 DevOps 流中起了重要的角色,他是銜接開發(fā)和運維的橋梁。如果這一環(huán)做的不好,無法支撐大量微服務(wù)的快速的迭代和高效運維。在這一環(huán)節(jié),我們需要靈活的運用工具,盡量減少人參與,當(dāng)然仍然需要圍繞「快」和「微」做文章。如何減少人工參與到持續(xù)集成與持續(xù)交付呢?我們最希望的開發(fā)過程是:對著計算機(jī)說出我們的想要的功能,計算機(jī)按照套路,自動編碼,自動發(fā)布到測試環(huán)境,自動運行測試腳本,自動上線。當(dāng)然,目前時代要實現(xiàn)自動編碼的過程還需要發(fā)明那只「貓」。
但只要對測試有足夠信心,我們完全可以實現(xiàn)一種境界:在炎熱的下午,輕松地提交自己編寫的代碼,去休息室喝杯咖啡,回來后看見自己的代碼已經(jīng)被應(yīng)用在生產(chǎn)環(huán)境上了。在容器時代,我們可以很快速的實現(xiàn)這一夢想,其具體步驟如下圖:
Gitfolw 與 Anti-Gitflown
持續(xù)集成的第一步是提交代碼(Code Commit),VCS 也由 CVS,SVN 進(jìn)化到如今的 Git,自然不得不說一下 Gitflow。談起無人不曉的 Gitflow,大家一定會大談其優(yōu)點:支持多團(tuán)隊,設(shè)置多國家的開發(fā)人員并行開發(fā),減小代碼沖突或臟代碼的上線概率。它的大致流程如下:Gitflow 給我們展示了復(fù)雜團(tuán)隊在處理不通代碼版本的優(yōu)雅解決方案,它需要feature、develop、release、hotfix、master 5 條分支來處理不同時段的并行開發(fā)。但這真的合適于一個不超過 20 人的本地合作團(tuán)隊開發(fā)嗎?我們的開發(fā)團(tuán)隊不足 6 人,每個人負(fù)責(zé) 3 個以上的微服務(wù),幾乎不可能在同個項目上安排兩個以上的同學(xué)并行開發(fā)。
在初期我們準(zhǔn)守規(guī)定并使用標(biāo)準(zhǔn)的 Gitflow 流程,開發(fā)人員立刻發(fā)現(xiàn)一個問題,他們需要在至少 3 條分支上來回的 merge 代碼,且不會有任何代碼沖突(因為就一個人開發(fā)),降低了開發(fā)的效率。這讓我意識到,Gitflow 模式也許并不適合于小團(tuán)隊微服務(wù)的世界,一種反 Gitflow 模式的想法出現(xiàn)在腦海中。我決定對Gitflow 進(jìn)行瘦身,化繁至簡。
我們把 5 條分支簡化為 3 條分支,其中 Master 分支的作用只是維護(hù)了最新的線上版本的作用,Dev 分支為開發(fā)的主要分支,所有的鏡像是以此分支的代碼為源頭生成的。這時開發(fā)的過程變?yōu)?#xff1a;
- 開發(fā)人員從 Dev 分支中 checkout 新的 feature 分支,在 feature 分支上進(jìn)行開發(fā)
- 當(dāng)開發(fā)完成后 merge 回 Dev 分支中,根據(jù) Dev 分支的代碼打成鏡像,部署 QA 環(huán)境交給 QA 人員測試
- 測試中如有 bug 便在新分支中修復(fù)問題循環(huán)步驟 2
- 測試完成 merge 回 Master 分支
如此一來,只有從 Feature 把代碼 merge 到 Dev 分支的一次 merge 動作,大大提升可開發(fā)效率。
使用Jenkins Pipeline
Jenkins 作為老牌 CI/CD 工具,能夠幫我們自動化完成代碼編譯、上傳靜態(tài)代碼分析、制作鏡像、部署測試環(huán)境、冒煙測試、部署上線等步驟。尤其是Jenkins 2.0 引入 Pipeline 概念后,以上步驟變的如此行云流水。它讓我們從步驟 3 開始,完全可以無人值守完成整個集成和發(fā)布過程。工欲善其事必先利其器,首先我們必須要在 Jenkins 上安裝插件 :
如果你第一次接觸 Jenkins Pipeline,可以從https://github.com/jenkinsci/p ... AL.md找到幫助。
現(xiàn)在,我們開始編寫 Groove 代碼。基于容器編排的 Pipeline 分為如下幾個步驟:
1、檢出代碼
這個步驟使用 Git 插件,把開發(fā)好的代碼檢出。
stage('Check?out') gitUrl?=?"git@gitlab.xxxx.com:xxx.git" git?branch:?"dev",?changelog:?false,?credentialsId:?"deploy-key",?url:?gitUrl?
2、Maven 構(gòu)建 Java 代碼
由于我們使用的是 Spring Boot 框架,生成物應(yīng)該是一個可執(zhí)行的 jar 包。
stage('Build') sh?"${mvnHome}/bin/mvn?-U?clean?install"
3、靜態(tài)代碼分析
通過 Sonar Scaner 插件,通知 Sonar 對代碼庫進(jìn)行靜態(tài)掃描。
stage('SonarQube?analysis') //?requires?SonarQube?Scanner?2.8+ def?scannerHome?=?tool?'SonarQube.Scanner-2.8'; withSonarQubeEnv('SonarQube-Prod')?{sh?"${scannerHome}/bin/sonar-scanner?-e?-Dsonar.links.scm=${gitUrl}?-Dsonar.sources=.?-Dsonar.test.exclusions=file:**/src/test/java/**?-Dsonar.exclusions=file:**/src/test/java/**?-Dsonar.language=java?-Dsonar.projectVersion=1.${BUILD_NUMBER}?-Dsonar.projectKey=lms-barrages?-Dsonar.projectDescription=0000000-00000?-Dsonar.java.source=8?-Dsonar.projectName=xxx" }?
4、制作 Docker 鏡像
此步驟會調(diào)用 Docker Pipeline 插件通過預(yù)先寫好的 Dockerfile,把 jar 包和配置文件、三方依賴包一起打入 Docker 鏡像中,并上傳到私有 Docker 鏡像倉庫中。
stage('Build?image') docker.withRegistry('https://dockerhub.xxx.com',?'dockerhub-login')?{ docker.build('dockerhub.xxx.com/xxxx').push('test')?//test是tag名稱 }?
5、部署測試環(huán)境
通過事先寫好的部署文件,用 Marathon 插件通知 Marathon 集群,在測試環(huán)境中部署生成好的鏡像。
stage('Deploy?on?Test') sh?"mkdir?-pv?deploy" dir("./deploy")?{git?branch:?'dev',?changelog:?false,?credentialsId:?'deploy-key',?url:?'git@gitlab.xxx.com:lms/xxx-deploy.git'//Get?the?right?marathon?urlmarathon_url="http://marathon-qa"marathon?docker:?imageName,?dockerForcePull:?true,?forceUpdate:?true,?url:?marathon_url,?filename:?"qa-deploy.json" }?
6、自動化測試
運行事先測試人員寫好的自動化測試腳本來檢驗程序是否運行正常。
stage('Test') //?下載測試用例代碼 git?branch:?'dev',?changelog:?false,?credentialsId:?'deploy-key',?url:?'git@gitlab.xxx.com:lms/xxx-test.git' parallel(autoTests:?{//?使用nosetests?運行測試用例sh?"docker?run?-it?--rm?-v?$PWD:/code?nosetests?nosetests?-s?-v?-c?conf\run\api_test.cfg?--attr?safeControl=1" },manualTests:{sleep?30000 })?
7、人工測試
如果對自動化測試不放心,此時可選擇結(jié)束 Pipeline,進(jìn)行人工測試。為了說明整個流程,我們這里選擇跳過人工測試環(huán)節(jié)。
8、部署生產(chǎn)環(huán)境
當(dāng)所有測試通過后,Pipeline 自動發(fā)布生產(chǎn)環(huán)境。
stage('Deploy?on?Prod') input?"Do?tests?OK?" dir("./deploy")?{//Get?the?right?marathon?urlmarathon_url="http://marathon-prod"marathon?docker:?imageName,?dockerForcePull:?true,?forceUpdate:?true,?url:?marathon_url,?filename:?"prod-deploy.json" }?
最后我們來看看整個 Pipeline 的過程:
容器編排配置文檔化
在介紹敏捷開發(fā)時,曾介紹過根據(jù)不同環(huán)境的配置參數(shù)部署到不同的環(huán)境。如何告知部署程序用什么樣的配置文件啟動服務(wù),每個環(huán)境又用多少 CPU,內(nèi)存和 instance 呢?下面我們就來介紹一下容器編排的配置文件。由于我們使用 Mesos+Marathon的容器編排方式,部署的重任從以前的寫部署腳本變成了寫一個 Marathon 的配置,其內(nèi)容如下:
{ "id":?"/appName", "cpus":?2, "mem":?2048.0, "instances":?2, "args":?[ "--spring.profiles.active=qa" ], "labels":?{ "HAPROXY_GROUP":?"external", "HAPROXY_0_VHOST":?"xxx.hujiang.com" }, "container":?{ "type":?"DOCKER", "docker":?{"image":?"imageName","network":?"USER","forcePullImage":?true,"portMappings":?[{"containerPort":?12345,"hostPort":?0,"protocol":?"tcp","servicePort":?12345}] }, "volumes":?[{"containerPath":?"/app/log","hostPath":?"/home/logs/appName","mode":?"RW"} ] }, "ipAddress":?{ "networkName":?"calico-net" }, "healthChecks":?[ {"gracePeriodSeconds":?300,"ignoreHttp1xx":?true,"intervalSeconds":?20,"maxConsecutiveFailures":?3,"path":?"/health_check","portIndex":?0,"protocol":?"HTTP","timeoutSeconds":?20 } ], "uris":?[ "file:///etc/docker.tar.gz" ] }?
我們把這個配置內(nèi)容保存為不同的 Json 文件,每個對應(yīng)的環(huán)境都有一套配置文件。例如 Marathon-qa.json,Marathon-prod.json。當(dāng) Pipeline 部署時,可以通過Jenkins Marathon 插件,根據(jù)選擇不同的環(huán)境,調(diào)用部署配置,從而達(dá)到自動部署的目的。
自動化流程和部署上線分離與管理
開發(fā)部署如此的簡單快捷,是不是每個人都能方便的使用呢?答案是否定的,并不是因為技術(shù)上有難度,而是在于權(quán)限。在理想的情況下,通過這套流程的確可以做到在提交代碼后,喝杯咖啡的時間就能看見自己的代碼已經(jīng)被千萬用戶使用了。但風(fēng)險過大,我們并不是每個人都能像 Rambo 一樣 bug 的存在,大多數(shù)的情況還需要使用規(guī)范和流程來約束。就像自動化測試取代不了人工黑盒測試一樣,部署測試后也不能直接上生產(chǎn)環(huán)境,在測試通過后還是需要有個人工確認(rèn)和部署生產(chǎn)的過程。
所以我們需要把自動化流程和最后的部署上線工作分開來,分別變成兩個 Job,并給后者單獨分配權(quán)限,讓有權(quán)限的人來做最后的部署工作。這個人可以是 Team leader、開發(fā)經(jīng)理,也可以是運維伙伴,取決于公司的組織結(jié)構(gòu)。
那這個部署的 Job 具體干什么呢?在容器編排時代,結(jié)合鏡像既構(gòu)建物的思想,部署 Job 不會從代碼編譯開始工作,而是把一個充分測試且通過的鏡像版本,通過 Marathon Plugin 部署到產(chǎn)線環(huán)境中去。這里是 Deploy_only 的例子:
node('docker-qa'){ if?(ReleaseVersion?==""){echo?"發(fā)布版本不能為空"return } stage?"Prepare?image"def?moduleName?=?"${ApplicationModule}".toLowerCase()def?resDockerImage?=?imageName?+?":latest"def?desDockerImage?=?imageName?+?":${ReleaseVersion}"if?(GenDockerVersion?=="true"){sh?"docker?pull?${resDockerImage}"sh?"docker?tag?${resDockerImage}?${desDockerImage}"sh?"docker?push?${desDockerImage}"sh?"docker?rmi?-f?${resDockerImage}?${desDockerImage}"}stage?"Deploy?on?Mesos"git?branch:?'dev',?changelog:?false,?credentialsId:?'deploy-key',?url:?'git@gitlab.xxx.com:lms/xxx-test.git'??//Get?the?right?marathon?urlecho?"DeployDC:?"?+?DeployDCmarathon_url?=?""if?(DeployDC=="AA")?{if?(DeployEnv?==?"prod"){input?"Are?you?sure?to?deploy?to?production?"marathon_url?=?"${marathon_AA_prod}"}else?if?(DeployEnv?==?"yz")?{marathon_url?=?"${marathon_AA_yz}"}}else?if?("${DeployDC}"=="BB"){if?("${DeployEnv}"?==?"prod"){input?"Are?you?sure?to?deploy?to?production?"marathon_url?=?"${marathon_BB_prod}"}else?if?("${DeployEnv}"?==?"yz")?{marathon_url?=?"${marathon_BB_yz}"}}marathon?docker:?imageName,?dockerForcePull:?true,?forceUpdate:?true,?url:?marathon_url,?filename:?"${DeployEnv}-deploy.json" }?
為什么不把這個文件跟隨應(yīng)用項目一起放到 scripts 下呢?因為把部署和應(yīng)用分開后,可以由兩撥人進(jìn)行維護(hù),兼顧公司的組織架構(gòu)。
自動化運維
在 DevOps 的最后階段是運維階段。在容器時代,如何對龐大的鏡像制品進(jìn)行運維呢?我們的目標(biāo)是盡量實現(xiàn)自動化運維,這里主要講述兩點:容器的監(jiān)控
容器的監(jiān)控大致有兩種方式:物理機(jī)上安裝其他服務(wù)監(jiān)控本機(jī)上的所有容器;通過 Mesos 或 Kubernates 自帶 API 監(jiān)控容器狀態(tài)。兩種方式其實都需要在物理機(jī)上安裝相應(yīng)的監(jiān)控軟件或 Agent。在我們團(tuán)隊目前使用 cAdvisor + InfluxDB + Grafana 的組合套件實現(xiàn)對容器的監(jiān)控。
首先需要在 Mesos 集群中所有的 Agent 安裝 cAdvisor 。他負(fù)責(zé)把宿主機(jī)上所有運行中的容器數(shù)據(jù)以數(shù)據(jù)點(data point)形式發(fā)送給時序數(shù)據(jù)庫(InfluxDB),下面是 cAdvisor 監(jiān)控的一些數(shù)據(jù)點:
這些數(shù)據(jù)點經(jīng)過 Grafana 整理,展示在界面上,這樣我們就能掌握具體容器的性能指標(biāo)了。下面是一個 Grafana 的截圖:
除了對容器本身的監(jiān)控,宿主機(jī)的監(jiān)控也是必不可少的。由于監(jiān)控的點有很多,這里不一一例舉。
自動伸縮
有了監(jiān)控指標(biāo)只是實現(xiàn)了自動化運維的第一步,當(dāng)業(yè)務(wù)請求發(fā)生大量增加或減少,通過人工監(jiān)測是不能及時的進(jìn)行相應(yīng)的,況且還不一定有那么多的人,7×24 小時的監(jiān)控。一定需要有一套根據(jù)監(jiān)控數(shù)據(jù)自行伸縮容的機(jī)制。在學(xué)習(xí)產(chǎn)品線,我們針對容器編排的 Mesos+Marathon 框架,開發(fā)了一套針對應(yīng)用本身的自動擴(kuò)容微服務(wù)。其原理如下:- 通過 Restful 的接口通知 AutoScaler 程序需要監(jiān)控的應(yīng)用服務(wù)。
- AutoScaler 程序開始讀取每臺 Agent 上部署相關(guān)應(yīng)用的 Metrics 數(shù)據(jù),其中包括 CPU,內(nèi)存的使用狀況。
- 當(dāng)發(fā)現(xiàn)有應(yīng)用過于繁忙(其表現(xiàn)形式大多是 CPU 占用過高或內(nèi)存占用過大)時調(diào)用 Marathon API 將其擴(kuò)容
- Marathon 收到消息后,立刻通知 Mesos 集群發(fā)布新的應(yīng)用,從而緩解當(dāng)前的繁忙狀況。
結(jié)束語
DevOps 和 SRE 并不是一個渴望而不可及的概念,它們需要在不同的環(huán)境中落地。我們整個 DevOps 流程是建立在容器編排的基礎(chǔ)上的,目的是簡化流程和實現(xiàn)自動化 CI/CD 和自動化運維。當(dāng)中會有很多沒有想到的地方,可能也不太適用于復(fù)雜場景。其次,本文中的例子也做了相應(yīng)的隱私處理,可能無法直接使用。希望大家能通過我們在實踐中產(chǎn)生的成功和遇到的問題,提煉出適合自己的 DevOps 流程。原文鏈接:基于容器編排的Dev/Ops流程(作者:黃凱)
原文發(fā)布時間為:2017-10-01
本文作者:黃凱
本文來自云棲社區(qū)合作伙伴Dockerone.io,了解相關(guān)信息可以關(guān)注Dockerone.io。
原文標(biāo)題:滬江基于容器編排的Dev/Ops流程
總結(jié)
以上是生活随笔為你收集整理的沪江基于容器编排的Dev/Ops流程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 保证java的jar包在后台运行
- 下一篇: 解决github push错误The r