背景
在现代软件开发过程中,持续集成和持续部署(CI/CD)已成为提升开发效率和交付质量的关键环节。Jenkins作为广泛应用的CI/CD工具,经常用于自动化构建、测试和部署流程。随着项目的复杂性增加,开发者面临同时管理和构建多个Git仓库的需求,而如何在Jenkinsfile中有效地实现这一点成为了挑战。本文将探讨如何在Kubernetes环境下,通过Jenkinsfile并行检出多个Git仓库,提升构建效率。
概要
本文将详细介绍如何在Jenkinsfile中拉取多个Git仓库,首先从基本的单仓库检出开始,逐步深入探讨如何将仓库检出到指定文件夹,并最终通过并行操作实现多个仓库的同时检出。文章中提供了完整的代码示例,并针对如何在Kubernetes环境下运行这些Jenkins Pipeline进行了实际操作指导。
通常,在 Jenkinsfile 中使用 Git 仓库是这样的:
stage('Checkout git repo') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: 'develop']],
userRemoteConfigs: [[
credentialsId: 'ssh-private-key-id',
url: 'project-git-repo-url'
]]
])
}
}
以上写法的结果是远程 Git 仓库中所有内容都被检出到当前工作目录下,因此我们才能像下面这样(不用切换目录而)直接运行一些命令:
stage('generate version number') {
steps {
VERSION_NUBMER = sh(script: "git describe --always", returnStdout: true).trim()
}
}
将 Git 仓库检出到指定文件夹
如果要同时拉取多个 Git 仓库,面临的首要问题是如何将 Git 仓库检出到指定文件夹。对此,可以通过使用 $class: 'RelativeTargetDirectory' 解决,具体代码如下:
stage('Checkout git repo') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: 'develop']],
userRemoteConfigs: [[
credentialsId: 'ssh-private-key-id',
url: 'project-git-repo-url'
]],
extensions: [[
$class: 'RelativeTargetDirectory',
relativeTargetDir: 'repo-folder'
]]
])
}
}
这样一来,远程 Git 仓库中所有内容将被检出到 ./repo-folder 文件夹下;后续我们若想在 Git 项目目录下操作,就需要先 cd ./repo-folder 了,例如:
stage('generate version number') {
steps {
VERSION_NUBMER = sh(script: "cd ./repo-folder && git describe --always", returnStdout: true).trim()
}
}
检出多 Git 仓库
在解决了将 Git 仓库检出到指定文件夹这一问题后,检出多 Git 仓库这一需求就稍显容易,下面以检出repoA、repoB、repoC三个仓库为例:
stage('Checkout multiple git repos') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: 'develop']],
userRemoteConfigs: [[
credentialsId: 'ssh-private-key-id',
url: 'project-git-repoA-url'
]],
extensions: [[
$class: 'RelativeTargetDirectory',
relativeTargetDir: 'repoA-folder'
]]
])
checkout([
$class: 'GitSCM',
branches: [[name: 'develop']],
userRemoteConfigs: [[
credentialsId: 'ssh-private-key-id',
url: 'project-git-repoB-url'
]],
extensions: [[
$class: 'RelativeTargetDirectory',
relativeTargetDir: 'repoB-folder'
]]
])
checkout([
$class: 'GitSCM',
branches: [[name: 'develop']],
userRemoteConfigs: [[
credentialsId: 'ssh-private-key-id',
url: 'project-git-repoC-url'
]],
extensions: [[
$class: 'RelativeTargetDirectory',
relativeTargetDir: 'repoC-folder'
]]
])
}
}
上面👆代码的结果就是工作目录下有了 repoA-folder、repoB-folder、repoC-folder 三个文件夹。
同时
截止现在,还没有达到我们的最终目的。本文标题叫做“Jenkinsfile 同时检出多个 Git 仓库”,但现在我们仅仅做到了“多个”还没有做到“同时”,为此我们需要使用Jenkins Pipeline语法中的 parallel 关键字。
stage('Checkout multiple git repos at the same time') {
parallel {
stage('repoA') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: 'develop']],
userRemoteConfigs: [[
credentialsId: 'ssh-private-key-id',
url: 'project-git-repoA-url'
]],
extensions: [[
$class: 'RelativeTargetDirectory',
relativeTargetDir: 'repoA-folder'
]]
])
}
}
stage('repoB') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: 'develop']],
userRemoteConfigs: [[
credentialsId: 'ssh-private-key-id',
url: 'project-git-repoB-url'
]],
extensions: [[
$class: 'RelativeTargetDirectory',
relativeTargetDir: 'repoB-folder'
]]
])
}
}
stage('repoC') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: 'develop']],
userRemoteConfigs: [[
credentialsId: 'ssh-private-key-id',
url: 'project-git-repoC-url'
]],
extensions: [[
$class: 'RelativeTargetDirectory',
relativeTargetDir: 'repoC-folder'
]]
])
}
}
}
}
k8s 上跑jenkins pipeline 示例
pipeline {
agent {
node {
label 'maven'
}
}
stages {
stage('Initialization') {
agent none
steps {
script {
echo "SONAR_PROJECT value: ${env.SONAR_PROJECT}"
// 如果SONAR_PROJECT为空,则尝试从SOURCE_URL中提取
if (env.SONAR_PROJECT == null || env.SONAR_PROJECT.trim() == '') {
env.SONAR_PROJECT_NAME = sh(script :"basename -s .git ${env.SOURCE_URL}",returnStdout: true).trim()
echo "reponame:${env.SONAR_PROJECT_NAME}"
}
// 如果harbor 项目名称为空,则尝试从SOURCE_URL中提取group
echo "HARBOR_PROJECT value: ${env.HARBOR_PROJECT}"
if (env.HARBOR_PROJECT == null || env.HARBOR_PROJECT.trim() == '') {
def group = env.SOURCE_URL =~ /^https?://[^/]+/([^/]+)/
env.TEMP_HARBOR_PROJECT = group ? group[0][1].toLowerCase() + '-aswatson' : ''
echo "TEMP_HARBOR_PROJECT: ${env.TEMP_HARBOR_PROJECT}"
}
// 如果镜像名为空,则尝试从SOURCE_URL中提取repoName
echo "HARBOR_PROJECT_IMAGE_NAME value: ${env.HARBOR_PROJECT_IMAGE_NAME}"
if (env.HARBOR_PROJECT_IMAGE_NAME == null || env.HARBOR_PROJECT_IMAGE_NAME.trim() == '') {
env.TEMP_HARBOR_PROJECT_IMAGE_NAME = sh(script :"basename -s .git ${env.SOURCE_URL}",returnStdout: true).trim()
}
}
}
}
stage('Checkout git repo cae-authorization') {
agent none
steps {
container('maven') {
checkout([
$class: 'GitSCM',
branches: [[name: 'dev_keycloak_jdk17']],
userRemoteConfigs: [[
credentialsId: "${SOURCE_CERT}",
url: 'https://www.example.com/rbac/cae-authorization.git'
]],
extensions: [[
$class: 'RelativeTargetDirectory',
relativeTargetDir: 'repo-cae-authorization'
]]
])
}
}
}
stage('Checkout git repo keycloak') {
agent none
steps {
container('maven') {
checkout([
$class: 'GitSCM',
branches: [[name: 'develop/1.1.0']],
userRemoteConfigs: [[
credentialsId: 'gitlab-admin',
url: 'https://www.example.com/rbac/keycloak.git'
]],
extensions: [[
$class: 'RelativeTargetDirectory',
relativeTargetDir: 'repo-keycloak'
]]
])
sh "ls -al ./"
}
}
}
stage('Docker Build & Push') {
agent none
steps {
container('maven') {
withCredentials([usernamePassword(credentialsId:'admin',passwordVariable:'password',usernameVariable:'username')]) {
sh '''
set -ex
cd ./repo-cae-authorization
sed -i 's/10.32.224.129/bootstrap.ms-ctl.aswatson.net/g' ${DOCKERFILE}
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
GIT_COMMIT=$(git rev-parse --short HEAD)
export IMG=${REGISTRY_URL}/${TEMP_HARBOR_PROJECT}/${TEMP_HARBOR_PROJECT_IMAGE_NAME}:${GIT_BRANCH}-${GIT_COMMIT}
docker build -f ${DOCKERFILE} ${DOCKER_CONTEXT} -t ${IMG} --tls-verify=false
docker login harbor.example.com -u $username -p $password --tls-verify=false
docker push $IMG --tls-verify=false
echo ${IMG} > img
'''
}
}
}
}
}
environment {
REGISTRY_URL = 'harbor.example.com'
REGISTRY_CERT = 'admin'
HARBOR_PROJECT = ''
HARBOR_PROJECT_IMAGE_NAME = ''
SOURCE_URL = 'https://www.example.com/rbac/cae-authorization.git'
SOURCE_REVISION = 'dev_keycloak_jdk17'
THEME_SOURCE_URL = 'https://www.example.com/rbac/keycloak.git'
THEME_SOURCE_REVISION = 'develop/1.1.0'
SOURCE_CERT = 'gitlab-admin'
DOCKER_CONTEXT = '.'
DOCKERFILE = 'docker-keycloak/Dockerfile'
SONAR_PROJECT = ''
SONAR_SOURCE = './src/main'
SONAR_QUALITYGATE = 'true'
MAVEN_OPTS = '-XX:CICompilerCount=2 -Xms2048m -Xmx2048m -Xss4m'
}
parameters {
string(name: 'GIT_BRANCHES', defaultValue: 'dev_keycloak_jdk17', description: '分支')
booleanParam(name: 'WAIT_FOR_QUALITY_GATE', defaultValue: true, description: '是否等待SonarQube分析完成并返回质量门状态')
}
}
结束语
通过以上方法,我们成功实现了在Kubernetes中通过Jenkinsfile同时拉取多个Git仓库的需求。这不仅优化了CI/CD流程,还提升了项目的构建效率。当然,随着项目的复杂度增加,可能还会面临更多挑战。未来,我将继续分享更多与Kubernetes、Jenkinsfile和DevOps相关的技巧和经验,帮助你在持续集成和持续交付的实践中更加游刃有余。敬请期待!