基於GitLab+Jenkins的DevOps賦能實踐

  • 2019 年 10 月 3 日
  • 筆記

    隨着微服務、中台架構的興起,DevOps也變得非常關鍵,畢竟是一些基礎設施層面的建設,如果搞好了對後面的研發工作會有很大的效率提升。關於DevOps本身的概念,網上已經非常多了,在園子里隨便搜索一些都一堆概念,我就不再重複介紹了。下面直接進入正題,怎麼使用GitLab+Jenkins來完成DevOps的建設。

    在開始實戰演練之前,首先用一張圖來展示一下這次實踐所要完成的功能:

    

    在這個流程中,分為3個環境,分別是預覽環境、預發環境和生產環境,普通開發者接受到任務以後,在GitLab中基於feature分支進行開發,然後把開發好的需求申請合併到dev分支,在申請合併的過程中,會觸發構建流水線進行編譯、單元測試、接口測試、發佈環境等系列校驗,當pipeline完成以後,組長就可以在代碼審查後,進行合併到dev分支。這個時候又會觸發dev分支的構建流水線,然後再完成一遍上述的流程,把代碼發佈到預發環境。最後由項目負責人定期把dev合併到master分支,完成生產環境版本發佈。

    首先,在GitLab中創建一個測試項目:

    這個項目是在lizongshenblogs的group下面的applications子group下的一個項目,代表了這是一個源代碼項目。

接下來再為這個項目創建3個流水線配置,主要目的是為了讓代碼和配置分離:

     在3個配置項目中,分別存放了相應的Jenkinsfile,用於Jenkins流水線的構建配置,接下來開始配置Jenkins。首先進行Jenkins的全局配置:在Jenkins的Manage Jenkins – Configure System下面配置Gitlab Connection,如圖:

 

在這裡需要注意,這個connection是需要一個gitlab的訪問令牌,可以在gitlab的個人設置 – 訪問令牌裏面生成,生成完成之後,填入到相應的Credentials裏面:

 

 最後測試一下,連接是否成功,只要顯示success,就可以了。

 

 接下來就可以配置具體pipeline了,首先使用Jenkins的New Item分別創建3個流水線類型的項目:

 

 

    在Jenkins中新建3個流水線類型的項目,分別叫feature-pipeline、dev-pipeline、master-pipeline,然後對這3個項目分別進行配置,先來看feature-pipeline的配置:

    首先要配置的項是Build Triggers,在其中勾選Opened Merge Request Events,並且把Rebuild open Merge Requests設置成On push to source branch:

 

 

 然後點擊Advanced按鈕,進行高級設置:

 

這裡需要注意的地方是原分支是任何分支,目標分支是dev分支,然後生成一個Secret token,這個token在配置gitlan webhooks的時候會用到。

接下來是配置Pipeline項:

 

 

     這個地方需要配置具體的流水線倉儲的地址,在credentials的地方,使用賬號密碼登錄到gitlab即可。

    dev流水線和master流水線配置略有不同,其中dev分支需要配置成accepted merge request events,意思就是當組長接受合併請求的時候觸發:

 

 

     而master分支需要改變的地方是匹配的分支,表示只接受從dev分支到master分支的合併請求:

 

 

     到這裡Jenkins的配置已經配置完成,接下來再回到gitlab進行聯動配置,首先配置項目的webhoos,在項目的Integrations Settings裏面添加一個webhooks:

 

 

    其中URL就填寫Jenkins的Build Triggers項目自動生成的那個URL,secret token是在Build Triggers的高級選項裏面生成的那個token,觸發的選項選擇Merge request events,表示當合併請求的時候進行觸發,點擊保存,gitlab和Jenkins的配置基本上就完成了。

    最後看一下3個項目中的Jenkinsfile:

pipeline {      agent {          label 'slave-pipeline'      }      options {          gitLabConnection('gitlab')      }      stages {          stage('Prepare'){              steps{                  script{                          git branch: '${gitlabSourceBranch}', changelog: false, credentialsId: 'gitlab', poll: false, url: 'https://gitlab.com/lizongshenblogs/applications/devopsdemo.git'                            script {                              GIT_COMMIT_ID = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()                              IMAGE_TAG = "${GIT_COMMIT_ID}-snapshot"                              INGRESS_HOST = "preview-${gitlabSourceRepoName}-${gitlabMergeRequestIid}"                              MR_URL = "${gitlabSourceRepoHomepage}/merge_requests/${gitlabMergeRequestIid}"                                build_tag = "feature-${gitlabSourceRepoName}-${gitlabSourceBranch}-${GIT_COMMIT_ID}"                              currentBuild.displayName = "#" + BUILD_NUMBER + "_" + gitlabMergeRequestIid + "_" + build_tag                          }                  }              }            }            stage('Build') {                when {                  expression {                      "${gitlabTargetBranch}" == 'dev'                  }              }                steps {                  script {                      updateGitlabCommitStatus name: 'Build', state: 'running'                        try {                            echo "開始編譯..."                        } catch(Exception ex){                          updateGitlabCommitStatus name: 'Build', state: 'failed'                          throw ex;                      } finally {                        }                        updateGitlabCommitStatus name: 'Build', state: 'success'                    }              }          }          stage('Test') {              steps {                  script {                      updateGitlabCommitStatus name: 'Test', state: 'running'                        try {                          echo "開始進行單元測試..."                      } catch(Exception ex){                          updateGitlabCommitStatus name: 'Test', state: 'failed'                          throw ex;                      } finally {                        }                        updateGitlabCommitStatus name: 'Test', state: 'success'                    }              }          }          stage('Deploy') {              steps {                  script {                      updateGitlabCommitStatus name: 'Deploy', state: 'running'                        try {                          echo "開始進行發佈..."                        } catch(Exception ex){                          updateGitlabCommitStatus name: 'Deploy', state: 'failed'                          throw ex;                      } finally {                        }                        updateGitlabCommitStatus name: 'Deploy', state: 'success'                      addGitLabMRComment comment: "App ${gitlabSourceRepoName} preview link: <a href='http://localhost'>${gitlabSourceRepoName}</a>"                  }              }          }      }        post {          failure {              updateGitlabCommitStatus name: 'Complete', state: 'failed'          }          success {              updateGitlabCommitStatus name: 'Complete', state: 'success'          }      }  }

    其中updateGitlabCommitStatus就可以實時地把Jenkins的構建狀態發回到gitlab中去:

 

    點擊這些構建狀態,就可以實時地查看到構建日誌。在這裡gitlab和Jenkins的配置基本上就全部完成了,接下來再看一下gitlab中關於代碼管理配置,一般情況下,dev分支和master分支是不允許直接push代碼的,只允許從需求分支中合併代碼,這就需要在gitlab的       Settings – Repository – Protected Branches中把dev和master保護起來:

 

     另外還可以設置只有當流水線成功了以後,才可以進行合併:

 

    通過這樣一些保護措施,就可以讓dev和master分支變得相對穩定。由於篇幅有限,如果有不明白的地方,可以私下找我相互交流。DevOps是一個很廣泛的話題,今天講的GitLab+Jenkins這套流程只是DevOps中的一部分,完全實現DevOps還需要更多的流程配合,以後有機會再為大家分享。