.NET項目從CI到CD-Jenkins_Pipeline的應用

1、羅裏吧嗦

最近遷移了服務器,順道完善下服役了一兩年的Jenkins服務,主要是把Slave搭建起來,還有等等。本文只是我對Jenkins Pipeline的一些本身的理解與應用,歡迎指出錯誤,歡迎交流高級應用html

2、運行環境

Jenkins:

  1. master:阿里雲Windows_2016_x64
  2. Slave1:京東雲Windows_2008_r2_x64
  3. Slave2:阿里雲Windows_2008_r2_x86

版本管理器:自建的git服務器,使用gogs

.NET項目:使用VS2017新建的一個web mvc項目與一個windows service項目,項目上傳至git服務器

一些輔助工具:

  1. 7-zip:做爲壓縮 解壓
  2. ossutil:阿里雲oss服務工具
  3. nuget:還原解決方案引用包
  4. MSBuild:編譯項目

3、開始

首先新建.NET項目,新建一個web項目與windows service項目,步驟略node

其次,在自行安裝Jenkins,步驟略linux


新建Jenkins項目,類型選擇Pipeline,命名爲JenkinsPipelineProjectgit

總體流程以下web

start->檢出代碼->還原引用包->編譯->打包->上傳OSS->分發slave->發佈web->發佈Service->endwindows

各步驟:

檢出代碼:使用內置的工具進行代碼的檢出,如我使用的是git服務器

還原引用包:使用nuget.exe對解決方案進行引用包還原,包源可選國內節點,國內節點下載速度框mvc

編譯:此處進行了兩次編譯,一次編譯web,一次編譯Serviceapp

打包:並行進行,對編譯步驟獲得的文件進行打包(使用7zip),存放於本地路徑上,打包時,會刪除相關配置文件,配置文件爲手動更新工具

上傳OSS:對剛打包好的更新包進行上傳,因兩臺服務器處於阿里雲內網,因此採用阿里雲的OSS,更新速度快

分發Slave:根據配置的節點,進行更新web和service操做

發佈web:首先從OSS下載文件下來, 中止站點(非中止IIS),使用7zip進行解壓文件,更新文件,更新完畢後啓動站點,若有多臺服務器須要更新,則並行執行,互不干擾

發佈Service:首先從OSS下載文件下來,中止對應的windows服務,卸載對應的windows服務,如若失敗,則進行強制刪除windows服務,以後使用7zip進行文件的解壓更新,更新完畢後安裝服務,並啓動服務

如下爲具體的Pipeline代碼

注:

  1. 代碼中所需的配置爲我本身自己項目須要,如若更改,可根據本身項目進行定製
  2. 代碼中一些敏感的配置已用xxxx代替
  3. 僅用於參考

 

//編譯服務器設置start
def buildNodeSettings = [:]
buildNodeSettings.node = '阿里雲Windows_2008_r2_x86'//編譯服務器節點設置
buildNodeSettings.gitUrl = 'https://xxx/JenkinsPipelineProject.git'//git地址
buildNodeSettings.gitBarnches = '*/master' //分支
buildNodeSettings.slnFile = 'JenkinsPipelineProject.sln' //Nuget還原解決方案名

buildNodeSettings.buildFileForWeb ='JenkinsPipelineProjectWeb\\JenkinsPipelineProjectWeb.csproj' //msbulid編譯文件名 web
buildNodeSettings.msbuildArgForWeb = '/t:Rebuild /p:Configuration=Release;PublishProfile=FolderProfile;DeployOnBuild=true' //msbulid參數 web
buildNodeSettings.publishOutputForWeb = '\\JenkinsPipelineProjectWeb\\bin\\Release\\PublishOutput' //編譯後發佈的路徑 web
buildNodeSettings.publishFileNameForWeb = env.JOB_NAME + '/Build-Web-' +env.BUILD_NUMBER + '.7z'  //文件名 
buildNodeSettings.delFilesForWeb = ["Web.config","Web.Debug.config","Web.Release.config"] as String[]  //須要刪除的文件

buildNodeSettings.buildFileForService ='JenkinsPipelineProject.sln' //msbulid編譯文件名 Service
buildNodeSettings.msbuildArgForService = '/t:Rebuild /p:Configuration=Release' //msbulid參數 Service
buildNodeSettings.publishOutputForService = '\\JenkinsPipelineProjectWindowsService\\bin\\Release' //編譯後發佈的路徑 Service
buildNodeSettings.publishFileNameForService = env.JOB_NAME + '/Build-Service-' +env.BUILD_NUMBER + '.7z'  //文件名 
buildNodeSettings.delFilesForService = ["*.config"] as String[]  //須要刪除的文件

buildNodeSettings.updateServerPath = 'D:\\WebRoot\\update\\public_html\\'//更新服務器存放包地址
//編譯服務器設置end

def webNodeSetting = [:]
webNodeSetting.node = '阿里雲Windows_2008_r2_x86' //Web服務器節點
webNodeSetting.downloadPath = 'C:\\Jenkins\\download\\'//更新包下載地址
webNodeSetting.publishPath = 'D:\\WebRoot\\JenkinsPipelinePorject\\Web' //web服務器網站根目錄
webNodeSetting.webApplicationName = 'JenkinsPipelinePorject'//web站點名稱

def webNodeSetting2 = [:]
webNodeSetting2.node = 'master' //Web服務器節點
webNodeSetting2.downloadPath = 'C:\\JenkinsDownload\\'//更新包下載地址
webNodeSetting2.publishPath = 'D:\\WebRoot\\JenkinsPipelinePorject\\Web' //web服務器網站根目錄
webNodeSetting2.webApplicationName = 'JenkinsPipelinePorject'//web站點名稱

def webNodeSetting3 = [:]
webNodeSetting3.node = '京東雲Windows_2008_r2_x64' //Web服務器節點
webNodeSetting3.downloadPath = 'C:\\Jenkins\\download\\'//更新包下載地址
webNodeSetting3.publishPath = 'C:\\WebRoot\\JenkinsPipelinePorject\\Web' //web服務器網站根目錄
webNodeSetting3.webApplicationName = 'JenkinsPipelinePorject'//web站點名稱

def serviceNodeSetting = [:]
serviceNodeSetting.node = '阿里雲Windows_2008_r2_x86'
serviceNodeSetting.downloadPath = 'C:\\Jenkins\\download\\'//更新包下載地址
serviceNodeSetting.publishPath = 'D:\\WebRoot\\JenkinsPipelinePorject\\Service' //Service Windows Service存放路徑
serviceNodeSetting.serviceName = 'JenkinsPipelineProject'//服務名稱
serviceNodeSetting.serviceFileName = 'JenkinsPipelineProjectWindowsService.exe' //服務的文件名,相對publishPath的路徑

def serviceNodeSetting2 = [:]
serviceNodeSetting2.node = 'master'
serviceNodeSetting2.downloadPath = 'C:\\Jenkins\\download\\'//更新包下載地址
serviceNodeSetting2.publishPath = 'D:\\WebRoot\\JenkinsPipelinePorject\\Service' //Service Windows Service存放路徑
serviceNodeSetting2.serviceName = 'JenkinsPipelineProject'//服務名稱
serviceNodeSetting2.serviceFileName = 'JenkinsPipelineProjectWindowsService.exe' //服務的文件名,相對publishPath的路徑

def serviceNodeSetting3 = [:]
serviceNodeSetting3.node = '京東雲Windows_2008_r2_x64'
serviceNodeSetting3.downloadPath = 'C:\\Jenkins\\download\\'//更新包下載地址
serviceNodeSetting3.publishPath = 'C:\\WebRoot\\JenkinsPipelinePorject\\Service' //Service Windows Service存放路徑
serviceNodeSetting3.serviceName = 'JenkinsPipelineProject'//服務名稱
serviceNodeSetting3.serviceFileName = 'JenkinsPipelineProjectWindowsService.exe' //服務的文件名,相對publishPath的路徑

node(buildNodeSettings.node) {

    def msbuild=tool name: 'MSBuildTool V14.0', type: 'msbuild' //編譯工具名稱與地址
    buildNodeSettings.publishOutputForWeb  = env.WORKSPACE + buildNodeSettings.publishOutputForWeb 
    buildNodeSettings.publishOutputForService  = env.WORKSPACE + buildNodeSettings.publishOutputForService 
    
    stage('Check Out')
    {
        echo '檢出項目'
        checkout([$class: 'GitSCM', branches: [[name: buildNodeSettings.gitBarnches]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: 'xxxxxx', url: buildNodeSettings.gitUrl]]])
    }
    
    stage('Nuget Restore')
    {
        echo ' 還原nuget '
        echo '${env.nuget} restore "' + env.WORKSPACE + '/' + buildNodeSettings.slnFile + '" -ConfigFile "' + env.config + '" -NoCache'
        bat env.nuget + ' restore "' + env.WORKSPACE + '/' + buildNodeSettings.slnFile + '" -ConfigFile "' + env.config + '" -NoCache'
    }
        
    stage('Bulid')
    {
        echo ' 編譯項目'
        echo 'Bulid Web'
        bat '"' + msbuild + '" ' + buildNodeSettings.msbuildArgForWeb + ' "' + env.WORKSPACE + '/' + buildNodeSettings.buildFileForWeb + '"'
        echo 'Bulid Service'
        bat '"' + msbuild + '" ' + buildNodeSettings.msbuildArgForService + ' "' + env.WORKSPACE + '/' + buildNodeSettings.buildFileForService + '"'
    }

    stage('Pack') {
        parallel PackWeb:{
            echo '刪除相關配置文件'
            buildNodeSettings.delFilesForWeb.each{
                echo '刪除文件:' + it
                def filepath ='"' + buildNodeSettings.publishOutputForWeb.replace("/","\\") + '\\' + it + '"'
                bat 'if exist '+ filepath +' del ' + filepath
            }
            echo ' 發佈到更新系統'
            bat 'if not exist "' + buildNodeSettings.updateServerPath + env.JOB_NAME + '" md "' + buildNodeSettings.updateServerPath + env.JOB_NAME + '"'
            bat '"' + env.zip + '"'+ ' a -r "' + buildNodeSettings.updateServerPath + buildNodeSettings.publishFileNameForWeb + '" "' + buildNodeSettings.publishOutputForWeb + '\\*"'
            echo '壓縮完成'
            echo '上傳oss'
            bat env.oss + ' -c ' + env.ossconfig + ' cp "' + buildNodeSettings.updateServerPath + buildNodeSettings.publishFileNameForWeb + '" "oss://xxxx/' + buildNodeSettings.publishFileNameForWeb +'"'
        },
        PackService:{
            echo '刪除相關配置文件'
            buildNodeSettings.delFilesForService.each{
                echo '刪除文件:' + it
                def filepath ='"' + buildNodeSettings.publishOutputForService.replace("/","\\") + '\\' + it + '"'
                bat 'if exist '+ filepath +' del ' + filepath
            }
            echo ' 發佈到更新系統'
             bat 'if not exist "' + buildNodeSettings.updateServerPath + env.JOB_NAME + '" md "' + buildNodeSettings.updateServerPath + env.JOB_NAME + '"'
            bat '"' + env.zip + '"'+ ' a -r "' + buildNodeSettings.updateServerPath + buildNodeSettings.publishFileNameForService + '" "' + buildNodeSettings.publishOutputForService + '\\*"'
            echo '壓縮完成'
            echo '上傳oss'
            bat env.oss + ' -c ' + env.ossconfig + ' cp "' + buildNodeSettings.updateServerPath + buildNodeSettings.publishFileNameForService + '" "oss://xxxx/' + buildNodeSettings.publishFileNameForService +'"'
        }
    }

    stage('Clear')
    {
        echo '清理工做目錄'
        deleteDir()
    }
}

stage('Publish Web')
{
    parallel publishWeb1:{
        node(webNodeSetting.node)
        {
            echo '發佈web'
            echo '更新文件'
            echo '更新文件下載地址爲:http://xxxx/' + buildNodeSettings.publishFileNameForWeb
            echo '下載文件'
            bat env.oss + ' -c ' + env.ossconfig + ' cp  "oss://xxxx/' + buildNodeSettings.publishFileNameForWeb + '" ' + webNodeSetting.downloadPath
            echo '文件下載完成'
            echo '中止站點'
            bat 'C:\\Windows\\System32\\inetsrv\\appcmd.exe stop site "' + webNodeSetting.webApplicationName + '"'
            bat '"' + env.zip + '" x "'+ webNodeSetting.downloadPath + buildNodeSettings.publishFileNameForWeb + '" -y -o"' + webNodeSetting.publishPath + '"'
            echo '啓動站點'
            bat 'C:\\Windows\\System32\\inetsrv\\appcmd.exe start site "' + webNodeSetting.webApplicationName+ '"'
        }
    },
    publishWeb2:{
        node(webNodeSetting2.node)
        {
            echo '發佈web'
            echo '更新文件'
            echo '更新文件下載地址爲:http://xxxx/' + buildNodeSettings.publishFileNameForWeb
            echo '下載文件'
            bat env.oss + ' -c ' + env.ossconfig + ' cp  "oss://xxxx/' + buildNodeSettings.publishFileNameForWeb + '" ' + webNodeSetting2.downloadPath
            echo '文件下載完成'
            echo '中止站點'
            bat 'C:\\Windows\\System32\\inetsrv\\appcmd.exe stop site "' + webNodeSetting2.webApplicationName + '"'
            bat '"' + env.zip + '" x "'+ webNodeSetting2.downloadPath + buildNodeSettings.publishFileNameForWeb + '" -y -o"' + webNodeSetting2.publishPath + '"'
            echo '啓動站點'
            bat 'C:\\Windows\\System32\\inetsrv\\appcmd.exe start site "' + webNodeSetting2.webApplicationName+ '"'
        }
    },
    publishWeb3:{
        node(webNodeSetting3.node)
        {
            withEnv(['oss=C:\\Tools\\oss\\ossutil.exe', 'ossconfig=C:\\Tools\\oss\\config']) {//須要手動設置變量
                echo '發佈web'
                echo '更新文件'
                echo '更新文件下載地址爲:http://xxxx/' + buildNodeSettings.publishFileNameForWeb
                echo '下載文件'
                bat env.oss + ' -c ' + env.ossconfig + ' cp  "oss://xxxx/' + buildNodeSettings.publishFileNameForWeb + '" ' + webNodeSetting3.downloadPath
                echo '文件下載完成'
                echo '中止站點'
                bat 'C:\\Windows\\System32\\inetsrv\\appcmd.exe stop site "' + webNodeSetting3.webApplicationName + '"'
                bat '"' + env.zip + '" x "'+ webNodeSetting3.downloadPath + buildNodeSettings.publishFileNameForWeb + '" -y -o"' + webNodeSetting3.publishPath + '"'
                echo '啓動站點'
                bat 'C:\\Windows\\System32\\inetsrv\\appcmd.exe start site "' + webNodeSetting3.webApplicationName+ '"'
            }
        }
    }
}

stage('Publish Service')
{
    parallel publishService1:
    {
        node(serviceNodeSetting.node){
            
            //發佈windows service
            echo '發佈Service'
            echo '下載文件'
            bat env.oss + ' -c ' + env.ossconfig + ' cp  "oss://xxxx/' + buildNodeSettings.publishFileNameForService + '" ' + serviceNodeSetting.downloadPath
            echo '卸載Windows Services'
            try{
                bat 'net stop ' + serviceNodeSetting.serviceName
                bat env.InstallUtil + ' -u ' + serviceNodeSetting.serviceName
            }catch(ex)
            {
                echo '卸載失敗:' + ex
                try{
                    bat 'sc delete ' + serviceNodeSetting.serviceName
                }catch(ex2)
                {
                    echo '強制刪除失敗:' +ex2
                }
            }
            echo '解壓文件'
            bat '"' + env.zip + '" x "'+ serviceNodeSetting.downloadPath + buildNodeSettings.publishFileNameForService + '" -y -o"' + serviceNodeSetting.publishPath + '"'
            echo '服務安裝'
            bat env.InstallUtil + ' ' + serviceNodeSetting.publishPath + '\\' + serviceNodeSetting.serviceFileName + ' /name='+ serviceNodeSetting.serviceName + ' /display=' + serviceNodeSetting.serviceName + ' /desc=' + serviceNodeSetting.serviceName
            echo '啓動服務'
            bat 'net start ' + serviceNodeSetting.serviceName
            
        }
    },
    publishService2:
    {
        node(serviceNodeSetting2.node){
            
            //發佈windows service
            echo '發佈Service'
            echo '下載文件'
            bat env.oss + ' -c ' + env.ossconfig + ' cp  "oss://xxxx/' + buildNodeSettings.publishFileNameForService + '" ' + serviceNodeSetting2.downloadPath
            echo '卸載Windows Services'
            try{
                bat 'net stop ' + serviceNodeSetting2.serviceName
                bat env.InstallUtil + ' -u ' + serviceNodeSetting2.serviceName
            }catch(ex)
            {
                echo '卸載失敗:' + ex
                try{
                    bat 'sc delete ' + serviceNodeSetting2.serviceName
                }catch(ex2)
                {
                    echo '強制刪除失敗:' +ex2
                }
            }
            echo '解壓文件'
            bat '"' + env.zip + '" x "'+ serviceNodeSetting2.downloadPath + buildNodeSettings.publishFileNameForService + '" -y -o"' + serviceNodeSetting2.publishPath + '"'
            echo '服務安裝'
            bat env.InstallUtil + ' ' + serviceNodeSetting2.publishPath + '\\' + serviceNodeSetting2.serviceFileName + ' /name='+ serviceNodeSetting2.serviceName + ' /display=' + serviceNodeSetting2.serviceName + ' /desc=' + serviceNodeSetting2.serviceName
            echo '啓動服務'
            bat 'net start ' + serviceNodeSetting2.serviceName
            
        }
    },
    publishService3:
    {
        node(serviceNodeSetting3.node){
            withEnv(['oss=C:\\Tools\\oss\\ossutil.exe', 'ossconfig=C:\\Tools\\oss\\config']) {//須要手動設置變量
                //發佈windows service
                echo '發佈Service'
                echo '下載文件'
                bat env.oss + ' -c ' + env.ossconfig + ' cp  "oss://xxxx/' + buildNodeSettings.publishFileNameForService + '" ' + serviceNodeSetting3.downloadPath
                echo '卸載Windows Services'
                try{
                    bat 'net stop ' + serviceNodeSetting3.serviceName
                    bat env.InstallUtil + ' -u ' + serviceNodeSetting3.serviceName
                }catch(ex)
                {
                    echo '卸載失敗:' + ex
                    try{
                        bat 'sc delete ' + serviceNodeSetting3.serviceName
                    }catch(ex2)
                    {
                        echo '強制刪除失敗:' +ex2
                    }
                }
                echo '解壓文件'
                bat '"' + env.zip + '" x "'+ serviceNodeSetting3.downloadPath + buildNodeSettings.publishFileNameForService + '" -y -o"' + serviceNodeSetting3.publishPath + '"'
                echo '服務安裝'
                bat env.InstallUtil + ' ' + serviceNodeSetting3.publishPath + '\\' + serviceNodeSetting3.serviceFileName + ' /name='+ serviceNodeSetting3.serviceName + ' /display=' + serviceNodeSetting3.serviceName + ' /desc=' + serviceNodeSetting3.serviceName
                echo '啓動服務'
                bat 'net start ' + serviceNodeSetting3.serviceName
            }
        }
    }
}

 

以上代碼對三臺服務器上的web和service進行了更新操做,兩臺阿里雲內網,一臺京東雲

代碼說明:

node:節點,Slave,表示在哪一個節點中運行
stage:階段,表示當前階段,可定義階段名稱
checkout:代碼檢出
echo:輸出信息
bat:執行cmd命令,linux下命令爲sh
env:環境變量,有系統定義變量和自定義變量兩部分
parallel:表示並行執行步驟
更多詳細解釋請查看官方文檔https://jenkins.io/doc/book/pipeline/

4、看看效果

咱們開始構建剛纔新建的項目

從gif能夠看出,整個流程只耗費了一分鐘不到,咱們去看看這三臺服務器

三臺服務器的文件都已更新,而且服務已經啓動,證實咱們的pipeline代碼是可行的

5、補充和改進

  • 這篇文章的代碼量過大,語言組織能力有待改進
  • Slave節點的環境變量不能正確讀取到,目前只能使用withEnv進行更改環境變量,具體狀況publishService3
  • 項目的耦合性過高了,目前編譯、打包、發佈都在同一個項目中,須要進行項目的拆分
  • 沒有加劇試機制,一旦某一階段失敗,只能從新運行,有待改進
  • 失敗郵件通知,這個目前沒有加入

如如有寫的很差的地方,請指出

如如有更好的方案,歡迎一塊兒交流

如如有不懂,歡迎諮詢,我會告訴你我知道的

本文已同步我的博客,歡迎轉載

相關文章
相關標籤/搜索