JenkinsPipeline進階:多平臺賬號關聯與自動化部署

新鈦雲服已累計為您分享830篇技術乾貨
引  言

在當今快速迭代的軟體開發環境中,持續整合和持續交付 (CI/CD) 已成為不可或缺的實踐。Jenkins 作為一款開源的自動化伺服器,憑藉其強大的外掛生態系統和靈活的 Pipeline 功能,成為 CI/CD 領域的佼佼者。本文將介紹如何使用 Jenkins Pipeline 實現 CI/CD,並重點講解如何將釘釘賬號與 OpenLDAP 聯動,以及如何實現 Jenkins、GitLab、Harbor 和禪道等多平臺的賬號關聯和整合。

架構圖
Jenkins Pipeline CI/CD
Jenkins Pipeline 是一種將構建、測試和部署等步驟以程式碼形式定義的機制。它使用 Groovy DSL 語法,可以將整個 CI/CD 流程編寫成一個指令碼檔案,方便版本控制和重複使用。
部署
如果還沒有部署Jenkins,可以使用下面的docker-compose.yml配置快速拉起一個Jenkins
docker-compose.yml
version: '3.7'services: jenkins: image: jenkins/jenkins:lts container_name: jenkins environment: - TZ=Asia/Shanghai volumes: - /data/jenkins/jenkins_home:/var/jenkins_home - /var/run/docker.sock:/var/run/docker.sock - /usr/bin/docker:/usr/bin/docker ports: - "8080:8080" - "50000:500000" expose: - "8080" - "50000" privileged: true user: root restart: always

配置共享庫

系統管理 ->系統配置-> Global Trusted Pipeline Libraries

Jenkins Pipeline CI

CI程式碼

下面是使用maven構建linux/win平臺docker映象的流程
@Library('jenkins-shared-library@main') _// 引入 Jenkins 共享庫,名稱為 'jenkins-shared-library',使用 'main' 分支。pipeline { agent any // 指定 Pipeline 在任何可用的 Jenkins Agent 上執行。 parameters { string(name: 'BRANCH_NAME', defaultValue: 'master', description: 'Git分支名稱') // 定義一個字串引數,用於指定 Git 分支名稱,預設值為 'master'。 string(name: 'ZENTAO_REPOPATH', defaultValue: '', description: 'gitlab 專案連結') // 定義一個字串引數,用於指定 GitLab 專案的 URL。 string(name: 'ZENTAO_ACCOUNT', defaultValue: '', description: 'zentao 使用者') // 定義一個字串引數,用於指定禪道(Zentao)的使用者名稱。 choice(name: 'BUILD_PLATFORM', choices: ['linux', 'windows'], description: '構建平臺') // 定義一個選擇引數,用於指定構建平臺,可選值為 'linux' 或 'windows'。 } environment { CURRENT_DATE = sh(script: "date +'%Y%m%d%H%M%S'", returnStdout: true).trim() // 定義一個環境變數 CURRENT_DATE,值為當前日期時間,格式為 'YYYYMMDDHHMMSS'。 BUILD_COMMIT = '' // 定義一個環境變數 BUILD_COMMIT,初始值為空,後續會儲存 Git 提交 ID。 ImageName = '' // 定義一個環境變數 ImageName,初始值為空,後續會儲存 Docker 映象名稱。 GITLAB_GROUP = '' // 定義一個環境變數 GITLAB_GROUP,初始值為空,後續會儲存 GitLab 專案組名稱。 GITLAB_PROJECT = '' // 定義一個環境變數 GITLAB_PROJECT,初始值為空,後續會儲存 GitLab 專案名稱。 } stages { stage("提取專案和專案名") { steps { script { def res = getGitLabGroupAndProject.extractGitLabInfo(params.ZENTAO_REPOPATH) // 呼叫共享庫中的方法 extractGitLabInfo,從 GitLab 專案 URL 中提取專案組和專案名稱。 GITLAB_GROUP = res.group // 將提取到的 GitLab 專案組名稱賦值給環境變數 GITLAB_GROUP。 GITLAB_PROJECT = res.project // 將提取到的 GitLab 專案名稱賦值給環境變數 GITLAB_PROJECT。 } } } stage("構建郵件通知") { steps { script { // 觸發構建訊息。 } } } stage('拉取程式碼並構建') { agent { docker { image "${GlobalVars.MavenBuildImage}" // 使用共享庫中定義的 Maven 構建映象。 args '-v /data/.m2:/root/.m2 -v /data/jar-package:/data/jar-package' // 掛載宿主機的 Maven 快取目錄和構建產物目錄到容器中。 reuseNode true // 重用當前節點,避免切換到其他節點。 } } steps { checkout scmGit( branches: [[name: "*/${params.BRANCH_NAME}"]], // 指定要拉取的分支名稱,使用引數 BRANCH_NAME。 extensions: [], // 無額外的 Git 擴充套件配置。 userRemoteConfigs: [[ credentialsId: "${GlobalVars.GitLabcredentialsId}", // 使用共享庫中定義的 GitLab 憑據 ID。 url: "https://${GlobalVars.GitlabUrl}/${GITLAB_GROUP}/${GITLAB_PROJECT}.git" // 指定 GitLab 專案的 URL。 ]] ) script { sh 'mvn -B -DskipTests clean package' // 使用 Maven 進行構建,跳過測試。 sh "cp ruoyi-admin/target/ruoyi-admin.jar /data/jar-package/${GITLAB_GROUP}-${GITLAB_PROJECT}-${CURRENT_DATE}.jar" // 將構建產物(JAR 檔案)複製到宿主機的指定目錄,並重命名。 } } } stage('儲存構建產物') { steps { stash includes: 'ruoyi-admin/target/ruoyi-admin.jar', name: 'jar-package' // 將構建產物(JAR 檔案)儲存為 Jenkins 的 stash,供後續階段使用。 } } stage('構建資訊') { steps { script { wrap([$class: 'BuildUser']) { // 使用 Jenkins 的 BuildUser 外掛獲取構建使用者資訊。 BUILD_USER = "${ZENTAO_ACCOUNT}" // 將禪道使用者賦值給環境變數 BUILD_USER。 BUILD_NAME = "${GITLAB_GROUP}/${GITLAB_PROJECT}" // 將專案組和專案名稱拼接後賦值給環境變數 BUILD_NAME。 BUILD_BRANCH = "${BRANCH_NAME}" // 將分支名稱賦值給環境變數 BUILD_BRANCH。 BUILD_COMMIT = "${getCommitId()}" // 呼叫共享庫中的方法 getCommitId,獲取當前 Git 提交 ID,並賦值給環境變數 BUILD_COMMIT。 BUILD_PLATFORM = "${params.BUILD_PLATFORM}" // 將構建平臺賦值給環境變數 BUILD_PLATFORM。 } currentBuild.description = """構建使用者: ${BUILD_USER}\n構建專案: ${BUILD_NAME}\n構建分支: ${BUILD_BRANCH}\n構建平臺: ${BUILD_PLATFORM}\nMessageID: ${BUILD_COMMIT} """ // 設定當前構建的描述資訊,包含構建使用者、專案、分支、平臺和提交 ID。 } } } stage('構建映象名稱') { steps { script { ImageName = "${GlobalVars.HarborUrl}/${GITLAB_GROUP}/${GITLAB_PROJECT}:${CURRENT_DATE}-${BUILD_COMMIT}-${params.BUILD_PLATFORM}" // 根據 Harbor 地址、專案組、專案名稱、日期、提交 ID 和構建平臺生成 Docker 映象名稱。 } } } stage('打包映象') { steps { script { if (params.BUILD_PLATFORM == 'linux') { buildDockerImage.LinuxRuoyiBuild(ImageName) // 如果構建平臺是 Linux,呼叫共享庫中的方法 LinuxRuoyiBuild 打包 Docker 映象。 } else if (params.BUILD_PLATFORM == 'windows') { node('win-agent') { unstash 'jar-package' // 如果構建平臺是 Windows,切換到 Windows 節點,並恢復之前儲存的構建產物。 buildDockerImage.WindowsRuoyiBuild(ImageName) // 呼叫共享庫中的方法 WindowsRuoyiBuild 打包 Docker 映象。 } } } } } stage('推送映象') { steps { script { if (params.BUILD_PLATFORM == 'linux') { withDockerRegistry(credentialsId: "${GlobalVars.HarborcredentialsId}", url: "https://${GlobalVars.HarborUrl}") { sh "docker push ${ImageName}" // 如果構建平臺是 Linux,推送 Docker 映象到 Harbor。 sh "docker rmi ${ImageName}" // 刪除本地 Docker 映象以釋放空間。 } } else if (params.BUILD_PLATFORM == 'windows') { node('win-agent') { withDockerRegistry(credentialsId: "${GlobalVars.HarborcredentialsId}", url: "https://${GlobalVars.HarborUrl}") { bat "docker push ${ImageName}" // 如果構建平臺是 Windows,推送 Docker 映象到 Harbor。 bat "docker rmi ${ImageName}" // 刪除本地 Docker 映象以釋放空間。 } } } } } } } post { always { cleanWs() // 無論構建成功與否,始終清理工作空間。 } success { script { // 構建成功時傳送通知。 } } unsuccessful { script { // 構建失敗時傳送通知。 } } }}
該 Jenkins Pipeline 指令碼實現了一個完整的 CI 流程
  1. 引數化構建:支援動態指定 Git 分支、GitLab 專案、禪道使用者和構建平臺。
  2. 程式碼拉取與構建:從 GitLab 拉取程式碼並使用 Maven 進行構建。
  3. 映象打包與推送:根據構建平臺(Linux 或 Windows)打包 Docker 映象並推送到 Harbor。
  4. 構建資訊記錄:記錄構建使用者、專案、分支、平臺和提交 ID 等資訊。
  5. 通知與清理:支援構建成功或失敗時傳送通知,並始終清理工作空間。
透過共享庫的使用,指令碼的邏輯更加簡潔和模組化,便於維護和擴充套件。

建立構建流程

寫好CI流程,下面在Jenkins新增構建流程

基於禪道關聯Jenkins任務

禪道平臺配置關聯jenkins,方便使用者呼叫觸發

Jenkins Pipeline CD

CD 程式碼

@Library('jenkins-shared-library@main') _// 引入 Jenkins 共享庫,名稱為 'jenkins-shared-library',使用 'main' 分支。pipeline { agent any // 指定 Pipeline 在任何可用的 Jenkins Agent 上執行。 parameters { string(name: 'SSH_USER', defaultValue: 'root', description: 'SSH User') // 定義一個字串引數,用於指定 SSH 使用者,預設值為 'root'。 string(name: 'TARGET_IP', defaultValue: '', description: '部署機器 IP 地址') // 定義一個字串引數,用於指定目標機器的 IP 地址。 string(name: 'DOCKER_IMAGE', defaultValue: '', description: '執行服務映象地址') // 定義一個字串引數,用於指定 Docker 映象的名稱。 string(name: 'IMAGE_VERSION', defaultValue: '', description: '執行服務映象版本號') // 定義一個字串引數,用於指定 Docker 映象的版本號。 string(name: 'SERVICE_NAME', defaultValue: '', description: '容器執行名稱') // 定義一個字串引數,用於指定 Docker 容器的名稱。 string(name: 'PORT_MAPPING', defaultValue: '', description: '埠對映,eg: 8080:8080') // 定義一個字串引數,用於指定 Docker 容器的埠對映。 string(name: 'ENV_VARIABLES', defaultValue: '', description: '環境變數,eg: KEY="VALUE",KEY2="VALUE2"') // 定義一個字串引數,用於指定 Docker 容器的環境變數。 } environment { SEND_BUILD_USER_EMAIL = '' // 定義一個環境變數 SEND_BUILD_USER_EMAIL,初始值為空,後續會儲存構建使用者的郵箱。 } stages { stage('獲取引數') { steps { script { withBuildUser { SEND_BUILD_USER_EMAIL = env.BUILD_USER_EMAIL // 使用 Jenkins 的 BuildUser 外掛獲取構建使用者的郵箱,並賦值給環境變數 SEND_BUILD_USER_EMAIL。 } // 檢查引數是否為空 if (!params.SSH_USER?.trim()) { error "SSH 使用者不能為空!" // 如果 SSH 使用者為空,丟擲錯誤並終止 Pipeline。 } if (!params.TARGET_IP?.trim()) { error "目標機器 IP 地址不能為空!" // 如果目標機器 IP 地址為空,丟擲錯誤並終止 Pipeline。 } if (!params.SERVICE_NAME?.trim()) { error "服務名稱不能為空!" // 如果服務名稱為空,丟擲錯誤並終止 Pipeline。 } if (!params.DOCKER_IMAGE?.trim()) { error "Docker 映象名稱不能為空!" // 如果 Docker 映象名稱為空,丟擲錯誤並終止 Pipeline。 } if (!params.IMAGE_VERSION?.trim()) { error "Docker 映象版本號不能為空!" // 如果 Docker 映象版本號為空,丟擲錯誤並終止 Pipeline。 } if (!params.PORT_MAPPING?.trim()) { error "埠對映不能為空!" // 如果埠對映為空,丟擲錯誤並終止 Pipeline。 } } } } stage('部署服務') { steps { script { def envVariables = "" if (params.ENV_VARIABLES?.trim()) { def envList = params.ENV_VARIABLES.split(',') envVariables = envList.collect { envVar -> "-e ${envVar.trim()}" }.join(' ') // 如果環境變數引數不為空,將其拆分為多個環境變數,並格式化為 Docker 命令中的 `-e` 引數。 } def dockerCommand = "" withCredentials([usernamePassword(credentialsId: 'devops_harbor', passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USERNAME')]) { // 使用 Jenkins 的 Credentials 外掛獲取 Harbor 倉庫的使用者名稱和密碼。 dockerCommand = """ docker login harbor.wuyinit.com -u $HARBOR_USERNAME -p $HARBOR_PASSWORD && docker pull ${params.DOCKER_IMAGE}:${params.IMAGE_VERSION} && docker stop ${params.SERVICE_NAME} || true && docker rm ${params.SERVICE_NAME} || true && docker run -itd --name ${params.SERVICE_NAME} -p ${params.PORT_MAPPING} ${envVariables} ${params.DOCKER_IMAGE}:${params.IMAGE_VERSION} """ // 構建 Docker 命令,包括: // 1. 登入 Harbor 倉庫。 // 2. 拉取指定版本的 Docker 映象。 // 3. 停止並刪除已存在的同名容器。 // 4. 啟動新的容器,並指定名稱、埠對映和環境變數。 } sshagent(['deploy-agent-key']) { // 使用 Jenkins 的 SSH Agent 外掛,載入指定的 SSH 私鑰('deploy-agent-key')。 sh """ ssh -o StrictHostKeyChecking=no ${params.SSH_USER}@${params.TARGET_IP} << 'EOF' ${dockerCommand}EOF """ // 透過 SSH 連線到目標機器,並執行 Docker 命令。 } } } } } post { success { script { // 構建成功時傳送郵件通知。 } } unsuccessful { script { // 構建失敗時傳送郵件通知。 } } }}
該 Jenkins Pipeline 指令碼實現了一個完整的服務部署流程,包括:
  1. 引數化構建:支援動態指定 SSH 使用者、目標機器 IP、Docker 映象、版本號、服務名稱、埠對映和環境變數。
  2. 引數檢查:確保所有必填引數不為空。
  3. 服務部署
    • 登入 Harbor 倉庫並拉取 Docker 映象。
    • 停止並刪除已存在的同名容器。
    • 啟動新的容器,並指定名稱、埠對映和環境變數。
  4. 通知與清理:支援構建成功或失敗時傳送通知。
透過共享庫和 Jenkins 外掛(如 Credentials、SSH Agent)的使用,指令碼的邏輯更加簡潔和模組化,便於維護和擴充套件。
釘釘與OpenLDAP的聯動
為了實現統一身份認證,並基於現有的釘釘組織和人員資訊,將Jenkins、GitLab、Harbor 和禪道等多平臺的賬號關聯,我們可以使用 OpenLDAP 作為統一的使用者認證中心。具體步驟如下:
  1. 配置 Go-Ldap-Admin 等服務:將釘釘通訊錄中的使用者和組織結構同步到 OpenLDAP 伺服器。
  2. 配置各平臺使用 OpenLDAP 認證: 在 Jenkins、GitLab、Harbor 和禪道的系統設定中,配置 LDAP 認證,並指定 OpenLDAP 伺服器的地址、埠、Base DN 等資訊。
docker-compose參考,詳情檢視
version: '3'networks: go-ldap-admin: driver: bridgeservices: go-ldap-admin: image: registry.cn-hangzhou.aliyuncs.com/eryajf/go-ldap-admin container_name: go-ldap-admin hostname: go-ldap-admin restart: always environment: WAIT_HOSTS: openldap:389 configs: - source: go_ldap_admin_config target: /app/config.yml ports: - 8888:8888 volumes: - ./data/go-ldap-admin:/app/data depends_on: - openldap links: - openldap:go-ldap-admin-openldap networks: - go-ldap-admin openldap: image: registry.cn-hangzhou.aliyuncs.com/eryajf/openldap:1.4.1 container_name: go-ldap-admin-openldap hostname: go-ldap-admin-openldap restart: always environment: TZ: Asia/Shanghai LDAP_ORGANISATION: "eryajf.net" LDAP_DOMAIN: "eryajf.net" LDAP_ADMIN_PASSWORD: "123456" command: [ '--copy-service' ] volumes: - ./data/openldap/database:/var/lib/ldap - ./data/openldap/config:/etc/ldap/slapd.d ports: - 389:389 networks: - go-ldap-admin
總  結
透過 Jenkins Pipeline、OpenLDAP 和釘釘的整合,我們可以實現高效的 CI/CD 流程,並統一管理多平臺的使用者賬號。本文提供的方案只是一個簡單的示例,實際應用中可以根據具體需求進行調整和擴充套件。
未來展望
未來,我們可以探索以下方向:
  • 使用 Kubernetes 進行容器化部署,進一步提高資源利用率和部署效率。
  • 引入程式碼質量分析工具,例如 SonarQube,提升程式碼質量。
  • 實現自動化測試,例如使用 Selenium 進行 UI 自動化測試。
如有相關問題,請在文章後面給小編留言,小編安排作者第一時間和您聯絡,為您答疑解惑。
參考文獻
  • Jenkins 官方文件: https://www.jenkins.io/doc/
  • OpenLDAP 官方文件:https://www.openldap.org/doc/
  • 釘釘開發者文件:https://open.dingtalk.com/document/orgapp-server
  • Go-Ldap-Admin: https://ldapdoc.eryajf.net/
    推薦閱讀   

    推薦影片    

相關文章