这回对线上容器服务器的流水线进行了一定的改造来满足目前线上的需求,还是会将所有的自动化脚本都放置到代码库中统一管理,我感觉一章不一定写的完,所以先给标题加了个-1,话不多说开干
1.本次流水线的流程设计
对于这个流水线我想说每家公司的流程和制度都不一样,我的流水线也只是个参考。我先用2张图来将我这次流水线的部署流程给做下解释

image-20250912164347373

image-20250912164127499
2. 运维的代码库结构

image-20250912165146765
3. Jenkinsfile
kotlin
def createVersion() {
// 创建了一个方法createVersion()
// 定义一个时间戳+构建ID作为版本号,为tag使用
return new Date().format('yyyyMMddHHmmss') + "_${env.BUILD_ID}"
}
pipeline {
//执行构建的节点名称
agent { node { label "node47"}}
//指定构建工具集
tools {
git 'Git'
maven 'maven399'
jdk 'jdk2102'
}
// 定义环境变量
environment {
//代码库地址
GIT_REPO_URL = 'git@xxxxx/spring-boot-3-hello-world-jdk21.git'
//有些字体要有颜色输出 这里定义为变量,后期输出时直接引用
RED = "\u001B[31m"
GREEN = "\u001B[32m" // 绿色
PURPLE = "\u001B[35m" // 紫色
RESET = "\u001B[0m" // 重置
//harbor地址
REGISTRY_URL = 'harbor.xxx.top'
//docker登录时需要使用到https协议所以这里还需要在定义一个有协议的harbor地址
HARBOR_URL = 'https://harbor.xxxx.top'
//harbor 容器组
PROJECT_GROUP = 'devops'
//harbor的项目名
PROJECT_NAME = 'hello-world-jdk21'
//在服务器上的服务容器名,这个用来部署时搜索或者删除
CONTAINER_NAME = 'hello-world-jdk21'
//运维代码库,在部署和回滚时会拉取代码库中信息,在执行不同的playbook
OPS_SHARE_LIBRARY = 'xxxxx/ops-share-librarya.git'
}
options {
//整个项目的超时时间
timeout(time: 10, unit: 'MINUTES')
// 不允许同时执行流水线
disableConcurrentBuilds()
// 构建时的时间信息
timestamps()
}
// 参数话构建中的参数
parameters {
// git参数
gitParameter(
name: 'BRANCH_TAG', //这个作为后面调用的变量名
type: 'PT_BRANCH_TAG', //这里是同时显示分支和tag
defaultValue: 'master',
description: '请选择你要部署的分支或Tag',
useRepository: 'xxxx/spring-boot-3-hello-world-jdk21.git', // 这里要使用完整的Git仓库地址
quickFilterEnabled: true // 启用快速筛选
)
// 定义回滚条件
booleanParam(defaultValue: false, description: '是否进行项目回滚?', name: 'ROLLBACK_TAG')
}
stages {
// 校验发布条件
stage('Check requirement') {
steps {
script {
if (params.ROLLBACK_TAG && env.BRANCH_TAG.startsWith('rel-')) {
echo "条件满足:ROLLBACK_TAG 为真,且 BRANCH_TAG 以 rel- 开头,继续执行后续步骤。"
} else if (!params.ROLLBACK_TAG && !env.BRANCH_TAG.startsWith('rel-')) {
echo "条件满足:ROLLBACK_TAG 为假,且BRANCH_TAG 不以 rel- 开头,继续执行后续步骤。"
} else {
echo "条件不满足,终止整个流程。"
error("构建中止:条件不满足。")
}
}
}
}
// 将需要的构建人等显示信息抓取并显示,部署(执行人,发布分支,成功后代码库tag信息),回滚(执行人,回滚容器的rel-id,显示项目回滚)
stage('Show info') {
steps {
script {
if (params.ROLLBACK_TAG) {
wrap([$class: 'BuildUser']) {
echo "Built by: ${env.BUILD_USER_ID}"
currentBuild.description = "Built by: ${env.BUILD_USER_ID}, Rollback: ${env.BRANCH_TAG}, tag: '项目回滚'}"
}
} else {
// 如果你选择了发布master那么在这里会调用上面定义的全局时间戳方法createVersion来生成数字作为代码和容器的tag
// 生成tag并给与变量_tag
_tag = createVersion()
wrap([$class: 'BuildUser']) {
echo "Built by: ${env.BUILD_USER_ID}"
currentBuild.description = "Built by: ${env.BUILD_USER_ID}, Branch: ${env.BRANCH_TAG}, tag: ${_tag}"
}
}
}
}
}
// 清理工作空间,很多人会在构建后清理,我习惯在构建前清理下,个人习惯不同
stage('CleanWorkDir') {
steps {
cleanWs()
}
}
// 拉取代码
stage('Checkout') {
steps {
script {
//这里引用的环境变量 GIT_REPO_URL
checkout([$class: 'GitSCM',
branches: [[name: params.BRANCH_TAG]],
userRemoteConfigs: [[url: "${GIT_REPO_URL}", credentialsId: "GiteeKey"]]])
}
}
}
// 开始进行构建这里也会做个判断,如果你前面选择的master分支那么进行代码打包,如果你选择的是rel-开头的tag和回滚则跳过打包
stage('Build') {
// when 当达到什么条件时执行下面的步骤
when {
expression {
// 检查 BRANCH_TAG 是否不以 'rel-' 开头
return !params.BRANCH_TAG.startsWith('rel-')
}
}
steps {
ansiColor('xterm') { // 启用 AnsiColor
echo "${GREEN}项目开始构建${RESET}"
echo "Building"
}
sh 'mvn clean package -Dmaven.test.skip=true'
}
}
// 构建容器镜像并推送到私有库中,同样如果选择的是rel-开头的tag和回滚跳过
stage('Dockerbuild') {
when {
expression {
// 检查 BRANCH_TAG 是否不以 'rel-' 开头
return !params.BRANCH_TAG.startsWith('rel-')
}
}
steps {
ansiColor('xterm') { // 启用 AnsiColor
echo "${GREEN}构建容器${RESET}"
echo "Docker Building"
}
script {
sh 'pwd' // 打印当前工作目录(调试用)
sh 'ls -la' // 列出当前目录中的文件(调试用)
// 构建镜像(这里用上面的环境变量进行拼接)
def image = docker.build("${REGISTRY_URL}/${PROJECT_GROUP}/${PROJECT_NAME}:${_tag}",".")
// 推送镜像
docker.withRegistry("${HARBOR_URL}", "Harbor") {
image.push() // 推送镜像到注册表
}
// 删除本地镜像
sh "docker rmi ${REGISTRY_URL}/${PROJECT_GROUP}/${PROJECT_NAME}:${_tag}"
}
}
}
//使用Ansible进行部署或回滚
stage('Ansible Deploy') {
steps {
ansiColor('xterm') { // 启用 AnsiColor
//sh "ansible 192.168.1.98 -m shell -a 'ls /root'" 测试的时候用了一下
echo "${PURPLE}Ansible 部署项目${RESET}"
}
script {
// 下载运维代码
sh "git clone ${OPS_SHARE_LIBRARY} ./ops-share-librarya"
// 登录harbor
withCredentials([usernamePassword(credentialsId: 'Harbor', usernameVariable: 'HARBOR_USERNAME', passwordVariable: 'HARBOR_PASSWORD')]) {
// 定义一个通用的 Map,这里是传入Ansible中使用的参数,有一个变量因为会涉及到部署和回滚所以在下面的判断中在进行加入
def extraVars = [
container_name: "${CONTAINER_NAME}",
docker_registry: "${HARBOR_URL}",
docker_username: "${HARBOR_USERNAME}",
docker_password: "${HARBOR_PASSWORD}"
].collectEntries { [(it.key): it.value] }
// 根据 BRANCH_TAG 的值添加特定的参数
if (!env.BRANCH_TAG.startsWith('rel-')) {
// 当你选择正常部署master的时候会追加参数,这个参数是容器名称
extraVars['new_image_name'] = "${REGISTRY_URL}/${PROJECT_GROUP}/${PROJECT_NAME}:${_tag}"
// 执行正常的部署
ansiblePlaybook(
playbook: "./ops-share-librarya/Ansible/HelloWorld-Pipeline/deploy.yml",
inventory: "./ops-share-librarya/Ansible/HelloWorld-Pipeline/hosts",
extraVars: extraVars
)
} else {
// 当你选择的是回滚的时候 会对你选择的回滚tag进行tag处理去除rel- 只要后面的数字
def branchTag = env.BRANCH_TAG
def extractedValue = branchTag.replaceFirst(/^rel-/, '') // 去掉前缀 "rel-"
println "Extracted Value: ${extractedValue}" // 输出提取的值
// 回滚时添加的参数
extraVars['rel_image_name'] = "${REGISTRY_URL}/${PROJECT_GROUP}/${PROJECT_NAME}:${extractedValue}"
// 执行回滚
ansiblePlaybook(
playbook: "./ops-share-librarya/Ansible/HelloWorld-Pipeline/rollback.yml",
inventory: "./ops-share-librarya/Ansible/HelloWorld-Pipeline/hosts",
extraVars: extraVars
)
}
}
}
}
}
}
post {
//通知和告警,这个下章在和Ansible一起发出来
}
}