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

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

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 流程
-
引數化構建:支援動態指定 Git 分支、GitLab 專案、禪道使用者和構建平臺。
-
程式碼拉取與構建:從 GitLab 拉取程式碼並使用 Maven 進行構建。
-
映象打包與推送:根據構建平臺(Linux 或 Windows)打包 Docker 映象並推送到 Harbor。
-
構建資訊記錄:記錄構建使用者、專案、分支、平臺和提交 ID 等資訊。
-
通知與清理:支援構建成功或失敗時傳送通知,並始終清理工作空間。
透過共享庫的使用,指令碼的邏輯更加簡潔和模組化,便於維護和擴充套件。
建立構建流程
寫好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 指令碼實現了一個完整的服務部署流程,包括:
-
引數化構建:支援動態指定 SSH 使用者、目標機器 IP、Docker 映象、版本號、服務名稱、埠對映和環境變數。
-
引數檢查:確保所有必填引數不為空。
-
服務部署:
-
登入 Harbor 倉庫並拉取 Docker 映象。
-
停止並刪除已存在的同名容器。
-
啟動新的容器,並指定名稱、埠對映和環境變數。
-
通知與清理:支援構建成功或失敗時傳送通知。
透過共享庫和 Jenkins 外掛(如 Credentials、SSH Agent)的使用,指令碼的邏輯更加簡潔和模組化,便於維護和擴充套件。

為了實現統一身份認證,並基於現有的釘釘組織和人員資訊,將Jenkins、GitLab、Harbor 和禪道等多平臺的賬號關聯,我們可以使用 OpenLDAP 作為統一的使用者認證中心。具體步驟如下:
-
配置 Go-Ldap-Admin 等服務:將釘釘通訊錄中的使用者和組織結構同步到 OpenLDAP 伺服器。
-
配置各平臺使用 OpenLDAP 認證: 在 Jenkins、GitLab、Harbor 和禪道的系統設定中,配置 LDAP 認證,並指定 OpenLDAP 伺服器的地址、埠、Base DN 等資訊。
docker-compose參考,詳情檢視
version: '3'
networks:
go-ldap-admin:
driver: bridge
services:
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/
推薦閱讀


推薦影片