目的
gitlab 推送后,jenkins 可以自动执行流水线。
流程:
用户推送代码到gitlab工程 -》 gitlab 收到推送信息到 jenkins webhook -》Jenkins 收到gitlab的事件 -》根据 Jenkins 流水线工程配置的可执行事件来启动流水线工程 -》 执行pipeline(从配置的scm里获取) -》根据 pipeline 执行代码拉取、代码测试、代码打包、docker镜像包、docker镜像推送。
涉及到的插件
- GitLab Plugin
- ✨安装 gitlab 插件的时候,可能提示 git 插件版本过低,以至于gitlab插件最后安装异常。升级 git 插件后,gitlab插件可用。
- Gitlab Hook Plugin
- Gitlab API Plugin
- ruby-runtime
- git Plugin
- https://plugins.jenkins.io/gitlab-branch-source/
涉及到的账户
- git只读账户,供jenkins使用
- 容器镜像仓库读写账户,供jenkins使用
操作步骤
-
gitlab - 管理中心 - 设置 - 网络 - 外发请求 - 允许Webhook和服务对本地网络的请求(启用)
✨仅当jenkins通过内网访问gitlab的时候需要.
-
在 gitlab 上创建 Jenkins 专属用户 jenkins.
-
生成 jenkins 用户拉取代码的凭证
✨当jenkins通过ssh方式拉取代码:
jenkins 创建证书凭据,并将公钥放在 gitlab 专属用户 jenkins 上
✨当jenkins通过http方式拉取代码:
gitlab 专属用户 jenkins 创建 read_repository 级别的访问令牌
-
生成 jenkins 回传工程执行过程到 gitlab ci 的凭证
✨创建 API级别的访问令牌,且用户必须是 git 项目 Maintainer 用户,否则 jenkins 无法将工程结果回传给 gitlab
-
在 jenkins 上添加拉取代码的凭据
gitlab-jenkins-ro-token
。✨以 http 方式为例,凭据类型 Username with password。password是 gitlab 专属用户 jenkins 的read_repository级别的访问令牌
-
授权 Jenkins 访问 gitlab api。
- jenkins 安装 gitlab 插件,并创建凭据
gitlab-jenkins-api-token
,凭据类型为Gitlab api token,将 gitlab 专属用户 jenkins 的API级别的访问令牌填入。 - jenkins 启用和配置 gitlab 插件。
- jenkins 安装 gitlab 插件,并创建凭据
- 流水线工程启用 gitlab 事件推送,从而获取 jenkins 提供 webhook 地址,便于 gitlab 将代码变更事件推送到 jenkins.
✨截图里黑色区域,即为jenkins为当前流水线工程创建的webhook地址
点击【高级】,即可弹出高级配置,从而可以给 webhook 地址创建 token
-
打开对应的 gitlab 工程 - 设置 - webhooks,将上一步骤里的 webhooks 地址和 token 填入其中。
-
将Pipeline文件Jenkinsfile放入gitlab工程中,并将jenkins工程的pipeline通过scm指向gitlab工程。
✨scm调用
gitlab-jenkins-ro-token
凭据。
配置仓库的Jenkinsfile
pipeline{
agent {
node {
label "agent01"
}
}
options {
gitLabConnection('gitlab') // 链接到 gitlab 上
}
environment {
dockerImage = "" // 空的环境变量名
}
stages {
stage("set custom env"){
steps{
script{
imageName = env.GIT_URL.split("/|\\.")[-2]
imageTag = env.GIT_COMMIT.take(8) // 获取 git commit short id
appVersion = env.BRANCH_NAME + "-" + imageTag // 镜像标签
if (env.BRANCH_NAME == 'master') {
registryCredential = "registry-apps_acr-rw" // 镜像注册表登录凭据名,需提前在jenkins凭据功能里添加
registryUrl = "https://registry-vpc.cn-zhangjiakou.aliyuncs.com/" // 镜像注册表地址
registryNamespace = "pj-zjk-it" // gitlab组/registry命名空间
} else {
registryCredential = "registry-jenkins_harbor-rw"
registryUrl = "https://localhbgg.pengwin.com:10443"
registryNamespace = env.GIT_URL.split("/|\\.")[-3]
}
}
}
}
stage("code: pull") {
steps {
cleanWs()
script {
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'running'
try {
checkout scm
} catch(Exception ex) {
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'failed'
throw ex;
}
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'success'
}
}
}
stage("code: test") {
steps {
script {
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'running'
try {
echo "单元测试"
} catch(Exception ex) {
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'failed'
throw ex;
}
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'success'
}
}
}
stage('docker: build') {
steps {
script {
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'running'
try {
dockerImage = docker.build( registryNamespace + "/" + imageName + ":" +appVersion, "docker")
// build方法接收两个参数,第一个是镜像tag,第二个是dockerfile在代码里的相对目录
} catch(Exception ex) {
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'failed'
throw ex;
}
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'success'
}
}
}
stage('docker: push') {
steps {
script {
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'running'
try {
docker.withRegistry( registryUrl, registryCredential ) { // 登录注册表
dockerImage.push() // dockerImage 即上一阶段的 dockerImage 变量.
}
} catch(Exception ex) {
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'failed'
throw ex;
}
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'success'
}
}
}
}
post {
success{
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'success'
//成功清理工作空间,失败保留现场
cleanWs()
}
failure{
updateGitlabCommitStatus name: env.STAGE_NAME, state: 'failed'
//成功清理工作空间,失败保留现场
cleanWs()
}
}
}