Jenkins运维之路(Jenkins流水线改造Day02-1-容器项目)
这回对线上容器服务器的流水线进行了一定的改造来满足目前线上的需求,还是会将所有的自动化脚本都放置到代码库中统一管理,我感觉一章不一定写的完,所以先给标题加了个-1,话不多说开干
1.本次流水线的流程设计
对于这个流水线我想说每家公司的流程和制度都不一样,我的流水线也只是个参考。我先用2张图来将我这次流水线的部署流程给做下解释
image-20250912164347373
image-20250912164127499
2. 运维的代码库结构
image-20250912165146765
3. Jenkinsfile
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'//运维代码库,在部署和回滚时会拉取代码库中信息,在执行不同的playbookOPS_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', //这里是同时显示分支和tagdefaultValue: '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_URLcheckout([$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') { // 启用 AnsiColorecho "${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') { // 启用 AnsiColorecho "${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"// 登录harborwithCredentials([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_TAGdef 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一起发出来}
}