咱們工做中經常使用Jenkins部署Java代碼,因其靈活的插件特性,例如jdk,maven,ant等使得java項目編譯後上線部署一鼓作氣,一樣對於腳本語言類型如Python上線部署,利用Jenkins強大的插件功能,輕鬆實現CI/CD,但若是部署多項目到同一臺服務器涉及環境一致性問題,對此能夠利用容器技術Docker解決,也能夠利用Python虛擬環境例如virutalenv或conda等優秀等工具解決,在此因爲後期根據requirements來安裝依賴包比較慢,且後期須要將Python整個環境打包,利用conda工具來對項目環境進行管理,方便快速移植。css
本文較系統的記錄下部署一個具體的Django項目,包括利用conda工具來實現Python多環境管理,Pylint工具來實現代碼檢查,使用nose框架作單元測試和覆蓋率。html
因爲conda包較大,一般狀況下可使用Miniconda
官網下載地址:https://conda.io/en/latest/miniconda.html
基礎使用命令:node
1、工具包管理命令 1.更新工具包 conda update conda conda upgrade --all 2.安裝包(進入虛擬環境,也可用pip安裝) conda install package_name 能夠指定版本 conda install package_name=1.10 3.移除包 conda remove package_name 4.查看包 conda list 5.查詢包 conda search package_name 6.源配置 由於anaconda的服務器在國外,所以有時候速度會比較慢,能夠換到國內源,好比清華的TUNA。 conda config --show-sources #查看當前使用源 conda config --remove channels 源名稱或連接 #刪除指定源 conda config --add channels 源名稱或連接 #添加指定源 # 添加Anaconda的TUNA鏡像 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ # TUNA的help中鏡像地址加有引號,須要去掉 # 設置搜索時顯示通道地址 conda config --set show_channel_urls yes 2、Python環境管理命令 1.顯示建立的全部環境 conda env list 2.建立python環境: conda create -n env_name list of packages eg:conda create -n py2 python=2.7 request 3.進入python環境:(windows 環境不須要加source) source activate env_name eg:source activate py2 4.退出環境: source deactivate 5.查看某環境下已安裝的包 conda list -n python2 6.複製環境: conda create --name <new_env_name> --clone <copied_env_name> 7.刪除環境: conda remove -n go2cloud-api-env --all 8.環境生成爲YAML文件 當分享代碼的時候,同時也須要將運行環境分享給你們,首先進入到環境中,執行以下命令能夠將當前環境下的 package 信息存入名爲 environment 的 YAML 文件中。
一樣,當執行他人的代碼時,也須要配置相應的環境。這時你能夠用對方分享的 YAML 文件來建立一摸同樣的運行環境python
conda env create -f environment.yamlgit
Shell基礎docker
可參考:https://blog.51cto.com/kaliarch/2049724windows
主題工具生成連接:http://afonsof.com/jenkins-material-theme/
在主題工具生成喜歡的顏色已經上傳logo下載生成的主題到Jenkins服務器的jenkins 家目錄,通常爲安裝啓動jenkins系統用戶的家目錄下.jenkins/userContent/material/,若是沒有此目錄須要新建目錄,css文件移動到目錄下,例如/root/.jenkins/userContent/material/blue.css。api
a.Install Jenkins Simple Theme Plugin
b.點擊Manage Jenkins
c.點擊Configure System
d.找到 Theme 配置
e.填寫本地主題cssurl,例如:http://localhost:8080/jenkins/userContent/material/blue.css
f.Click Save
g.保存可查看效果。
本地css的url能夠瀏覽器打開測試訪問,若是訪問不到會默認加載Jenkins默認主題
後期遷移url最好寫成localhost,若是寫公網IP,css文件不存在404,Jenkins頁面會很卡。
名稱 | IP | 軟件 | 備註 |
---|---|---|---|
Jenkins-server | 10.57.61.138 | miniconda | Jenkins服務器 |
Des-server | 172.21.0.10 | miniconda | 項目部署服務器 |
對依賴包須要在Jenkins-server服務器進行安裝,首先根據項目裏面conda建立對應項目對虛擬環境conda create -n <project_name> python=3.6
,建立完成利用conda env list
查看環境,爲避免環境污染,在項目環境內利用pip工具安裝軟件pip install pylint mock nose coverage
。
New任務->構建一個自由風格的軟件項目,填寫描述,在此因爲後期會利用pylint進行代碼檢查,給出了代碼檢查的消息類型,能夠根據消息類型進行相應修復處理。
配置選擇git具體branch進行構建,和能夠自定義端口,在此須要注意參數化構建的變了Name,在後續須要用到。
在此須要選擇源碼倉庫,選擇gitlab已經認證方式,須要注意因爲參數化構建選擇了branch,在Branches to build須要引用上面的變量$branch。
執行shell此shell爲在Jenkins服務器上執行,因此須要預先在其上配置Python虛擬環境,在其上進行代碼審查,單元測試生成nosetests.xml文件,已經代碼覆蓋率測試生成coverage.xml文件,pylint測試生成pylint.xml文件。
如下爲此項目示例shell腳本,此腳本須要根據本身的實際狀況來修改,須要注意Python項目結構與須要代碼檢查的標識符。
base_dir=/root/.jenkins/workspace/ project=go2cloud-api-deploy-prod/ project_env=go2cloud-api-env # 切換python環境 source activate ${project_env} $(which python) -m pip install mock nose coverage # 更新python環境 echo "+++更新Python環境+++" if [ -f ${base_dir}${project}requirements.txt ];then $(which python) -m pip install -r ${base_dir}${project}requirements.txt && echo 0 || echo 0 fi # 代碼檢查/單元測試/代碼測試覆蓋率 echo "+++代碼檢查+++" cd ${base_dir} # 生成pylint.xml $(which pylint) -f parseable --disable=C0103,E0401,C0302 $(find ${project}/* -name *.py) >${base_dir}pylint.xml || echo 0 echo "+++單元測試+++" # 生成nosetests.xml #$(which nosetests) --with-xunit --all-modules --traverse-namespace --with-coverage --cover-package=go2cloud-api-deploy-prod --cover-inclusive || echo 0 $(which nosetests) --with-xunit --all-modules --traverse-namespace --with-coverage --py3where=go2cloud-api-deploy-prod --cover-package=go2cloud-api-deploy-prod --cover-inclusive || echo 0 echo "+++代碼覆蓋率+++" # 生成coverage.xml
python -m coverage xml --include=go2cloud-api-deploy-prod* || echo 0
在此須要制定發佈到目標到的服務器遠端目錄,已經執行的命令,在此執行重啓腳本。
因爲在此項目爲Django項目,須要制定虛擬環境/入口啓動文件/啓動端口。
將以前參數化構建的端口看成變量傳遞給腳本,啓動相應的端口
#!/usr/bin/env bash # 當前目錄 BASEPATH=$(cd `dirname $0`;pwd) # python解釋器具體路徑 PYTHON_BIN=$1 # mananger文件路徑 MAIN_APP=$2 # python SERVER_PORT=$3 [ $# -lt 3 ] && echo "缺乏參數" && exit 1 LOG_DIR=${BASEPATH}/logs/ [ ! -d ${LOG_DIR} ] && mkdir ${LOG_DIR} OLD_PID=`netstat -lntup | awk -v SERVER_PORT=${SERVER_PORT} '{if($4=="0.0.0.0:"SERVER_PORT) print $NF}'|cut -d/ -f1` [ -n "${OLD_PID}" ] && kill -9 ${OLD_PID} echo "---------$0 $(date) excute----------" >> ${LOG_DIR}server-$(date +%F).log # 啓動服務 nohup ${PYTHON_BIN} -u ${MAIN_APP} runserver 0.0.0.0:${SERVER_PORT} &>> ${LOG_DIR}server-$(date +%F).log 2>&1 &
須要注意文件路徑爲jenkins服務器pylint.xml,以及對應生成文件的編碼。
選擇郵件內容爲Content Type爲HTML,這樣能夠編寫郵件HTML模版,生成較爲好看的郵件通知模版。
注意選擇觸發告警能夠選擇類型,失敗幾回或不管構建成功失敗都發送,可根據具體需求配置。
郵件HTML模版
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>${ENV, var="JOB_NAME"}-第${BUILD_NUMBER}次構建日誌</title> </head> <body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0"> <table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif"> <tr> <td>(本郵件是程序自動下發的,請勿回覆!)</td> </tr> <tr> <td><h2> <font color="#0000FF">構建結果 - ${BUILD_STATUS}</font> </h2></td> </tr> <tr> <td><br /> <b><font color="#0B610B">構建信息</font></b> <hr size="2" width="100%" align="center" /></td> </tr> <tr> <td> <ul> <li>項目名稱 : ${PROJECT_NAME}</li> <li>構建編號 : 第${BUILD_NUMBER}次構建</li> <li>SVN 版本: ${SVN_REVISION}</li> <li>觸發緣由: ${CAUSE}</li> <li>構建日誌: <a href="${BUILD_URL}console">${BUILD_URL}console</a></li> <li>構建 Url : <a href="${BUILD_URL}">${BUILD_URL}</a></li> <li>工做目錄 : <a href="${PROJECT_URL}ws">${PROJECT_URL}ws</a></li> <li>項目 Url : <a href="${PROJECT_URL}">${PROJECT_URL}</a></li> </ul> </td> </tr> <tr>
<td><a href="http://www.yxccc.com" target="_blank">電動叉車</a></td> <td><b><font color="#0B610B">Changes Since Last Successful Build:</font></b> <hr size="2" width="100%" align="center" /></td> </tr> <tr> <td> <ul> <li>歷史變動記錄 : <a href="${PROJECT_URL}changes">${PROJECT_URL}changes</a></li> </ul> ${CHANGES_SINCE_LAST_SUCCESS,reverse=true, format="Changes for Build #%n:<br />%c<br />",showPaths=true,changesFormat="<pre>[%a]<br />%m</pre>",pathFormat=" %p"} </td> </tr> <tr> <td><b>Failed Test Results</b> <hr size="2" width="100%" align="center" /></td> </tr> <tr> <td><pre style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">$FAILED_TESTS</pre> <br /></td> </tr> <tr> <td><b><font color="#0B610B">構建日誌 (最後 100行):</font></b> <hr size="2" width="100%" align="center" /></td> </tr> <!-- <tr> <td>Test Logs (if test has ran): <a href="${PROJECT_URL}ws/TestResult/archive_logs/Log-Build-${BUILD_NUMBER}.zip">${PROJECT_URL}/ws/TestResult/archive_logs/Log-Build-${BUILD_NUMBER}.zip</a> <br /> <br /> </td> </tr> --> <tr> <td><textarea cols="80" rows="30" readonly="readonly" style="font-family: Courier New">${BUILD_LOG, maxLines=100}</textarea> </td> </tr> </table> </body> </html>
選擇對應的branch與端口
查看具體不合規代碼
在源代碼分析結束後面,會有一系列的報告,每一個報告關注於項目的某些方面,如每種類別的 message 的數目,模塊的依賴關係等等。具體來講,報告中會包含以下的方面:
MESSAGE_TYPE 有以下幾種:
(C) convention 慣例。違反了編碼風格標準 (R) refactor 重構。寫得很是糟糕的代碼。 (W) warning 警告。某些 Python 特定的問題。 (E) error 錯誤。極可能是代碼中的錯誤。 (F) fatal 致命錯誤。阻止 Pylint 進一步運行的錯誤。
pipeline爲運行於Jenkins上的工做流框架,project中的配置信息以steps的方式放在一個腳本里將本來獨立運行於單個或者多個節點的任務鏈接起來,實現單個任務難以完成的複雜流程編排與可視化。
Pipeline提供了一組可擴展的工具,經過Pipeline Domain Specific Language(DSL)syntax能夠達到Pipeline as Code(Jenkinsfile存儲在項目的源代碼庫)的目的。
基於 Jenkins Pipeline,用戶能夠在一個 JenkinsFile 中快速實現一個項目的從構建、測試以到發佈的完整流程,而且能夠保存這個流水線的定義。
pipeline { /* insert Declarative Pipeline here */ }
在聲明式流水線中有效的基本語句和表達式遵循與 Groovy的語法一樣的規則, 有如下例外:
示例:
Jenkinsfile (Declarative Pipeline) pipeline { agent none stages { stage('Example Build') { agent { docker 'maven:3-alpine' } steps { echo 'Hello, Maven' sh 'mvn --version' } } stage('Example Test') { agent { docker 'openjdk:8-jre' } steps { echo 'Hello, JDK' sh 'java -version' } } } }
腳本化流水線, 與[declarative-pipeline]同樣的是, 是創建在底層流水線的子系統上的與聲明式不一樣的是, 腳本化流水線其實是由 Groovy構建的通用 DSL [2]。 Groovy 語言提供的大部分功能均可以用於腳本化流水線的用戶。這意味着它是一個很是有表現力和靈活的工具,能夠經過它編寫持續交付流水線。
示例:
node('master') { //master節點運行,如下stage也可指定節點 stage 'Prepare' //清空發佈目錄 bat '''if exist D:\\publish\\LoginServiceCore (rd/s/q D:\\publish\\LoginServiceCore) if exist C:\\Users\\Administrator\\.nuget (rd/s/q C:\\Users\\Administrator\\.nuget) exit''' //拉取git代碼倉庫 stage 'Checkout' checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'c6d98bbd-5cfb-4e26-aa56-f70b054b350d', url: 'http://xxx/xxx/xxx']]]) //構建 stage 'Build' bat '''cd "D:\\Program Files (x86)\\Jenkins\\workspace\\LoginServiceCore\\LoginApi.Hosting.Web" dotnet restore dotnet build dotnet publish --configuration Release --output D:\\publish\\LoginServiceCore''' //部署 stage 'Deploy' bat ''' cd D:\\PipelineScript\\LoginServiceCore python LoginServiceCore.py ''' //自動化測試(python代碼實現) stage 'Test' bat''' cd D:\\PipelineScript\\LoginServiceCore python LoginServiceCoreApitest.py ''' }
在此將上面的項目利用pipeline進行發佈
New任務->流水線,填寫任務描述。
若是對Pipeline語法不熟悉,能夠利用工具生成
在此的pipeline
pipeline { agent any parameters { gitParameter branchFilter: 'origin/(.*)', defaultValue: 'master', name: 'BRANCH', type: 'PT_BRANCH' } stages { stage('checkout src code') { steps { echo "checkout src code" git branch: "${params.BRANCH}",'http://123.206.xxx.xxx/xuel/go2cloud_platform.git' } } stage('exec shell'){ steps{ echo "pylint,Unit test" sh '''# jenkins 服務器項目workspace目錄 base_dir=/root/.jenkins/workspace/ # 項目名稱 project=go2cloud_platform # 項目環境python環境 project_env=go2cloud_platform_pipeline # 切換python環境 source /data/miniconda3/bin/activate ${project_env} $(which python) -m pip install mock nose coverage pylint # 更新python環境 echo "++++++更新Python環境+++" if [ -f ${base_dir}${project}requirements/requirements.txt ];then $(which python) -m pip install -r ${base_dir}${project}/requirements/requirements.txt && echo 0 || echo 0 fi # 代碼檢查/單元測試/代碼測試覆蓋率 echo "++代碼檢查++" cd ${base_dir} # 生成pylint.xml $(which pylint) -f parseable --disable=C0103,E0401,C0302 $(find ${project}/* -name *.py) >${base_dir}${project}_pylint.xml || echo 0 echo "++單元測試++" # 生成nosetests.xml #$(which nosetests) --with-xunit --all-modules --traverse-namespace --with-coverage --cover-package=go2cloud-api-deploy-prod --cover-inclusive || echo 0 $(which nosetests) --with-xunit --all-modules --traverse-namespace --with-coverage --py3where=${project} --cover-package=${project} --cover-inclusive || echo 0 echo "++代碼覆蓋率+++" # 生成coverage.xml python -m coverage xml --include=${project_env}* || echo 0''' } } stage("deploy") { steps { echo "send file" sshPublisher(publishers: [sshPublisherDesc(configName: 'go2cloud_platform_host', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''base_dir=/devops_pipeline project_src=go2cloud_platform project_env=/data/miniconda3/envs/go2cloud_platform_pipeline/bin/python echo "+++++++更新部署服務器python環境++++++++++" if [ -f ${base_dir}/requirements/requirements.txt ];then ${project_env} -m pip install -r ${base_dir}/requirements/requirements.txt && echo 0 || echo 0 fi echo -e "\\033[32m 啓動服務腳本 \\033[0m" $(which bash) ${base_dir}/run_server.sh ${project_env} ${base_dir}/apps/manage.py ${port} ''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/devops_pipeline', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '**/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) } } } }