基于Jenkins+K8S的微服务CI/CD全链路实践:从代码提交到生产部署、邮件通知

系列文章目录

Jenkins声明式Pipeline流水线入门



前言

在云原生与微服务架构日益普及的今天,高效的CI/CD流水线已成为企业提升交付效率的核心引擎。本文将分享一套基于Jenkins与Kubernetes深度集成的企业级CI/CD实践方案,通过 动态Slave Pod调度 、 Harbor镜像全生命周期管理 和 参数化多环境部署 三大核心设计,结合Pipeline+Groovy脚本实现从代码提交到生产部署的分钟级自动化流水线。


架构图

在这里插入图片描述

一、环境准备

在这里插入图片描述

ip部署
192.168.56.101k8s-master(1.28)、harbor、docker、docker-compose
192.168.56.102k8s-node(1.28)、Jenkins(非容器化master)

二、静态slave

在这里插入图片描述

三、动态slave

在这里插入图片描述

四、Jenkins部署及相关插件下载

可参考https://blog.csdn.net/weixin_50902636/article/details/143600389 文章进行docker部署

1.部署jenkins2.462+jdk17

登录192.168.56.102机器

代码如下(示例):

[root@k8s-node ~]# wget  https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat/jenkins-2.462-1.1.noarch.rpm
[root@k8s-node ~]# rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
[root@k8s-node ~]# rpm -ivh jenkins-2.462-1.1.noarch.rpm
[root@k8s-node ~]# systemctl enable --now jenkins
[root@k8s-node ~]#  wget  https://download.java.net/java/GA/jdk17.0.2/dfd4a8d0985749f896bed50d7138ee7f/8/GPL/openjdk-17.0.2_linux-x64_bin.tar.gz
[root@k8s-node ~]# tar xf openjdk-17.0.2_linux-x64_bin.tar.gz -C /export/
[root@k8s-node ~]# cd /export/jdk-17.0.2/bin/
[root@k8s-node ~]# ln -s /export/jdk-17.0.2/bin/java /usr/bin/java
[root@k8s-node ~]# java -version
openjdk version "17.0.2" 2022-01-18
OpenJDK Runtime Environment (build 17.0.2+8-86)
OpenJDK 64-Bit Server VM (build 17.0.2+8-86, mixed mode, sharing)

2.访问Jenkins下载插件

可参考这篇文章进行插件下载https://blog.csdn.net/weixin_50902636/article/details/143508066
在这里插入图片描述
在这里插入图片描述

下载汉化插件

在这里插入图片描述

下载kubernetes插件

在这里插入图片描述
其余pipeline插件、选项插件、git插件等就不一一展示了,详情见这个资源插件包,导入即可

五、Jenkins配置Cloud

1.Jenkins开启agent节点通道

在这里插入图片描述

2.配置Cloud

2.1创建凭据

在这里插入图片描述
在这里插入图片描述

2.2.配置cloud

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3.配置pod Template

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
添加harbor仓库凭据
在这里插入图片描述
选择对应的凭据

#同时要在k8s集群的default命名空间下创建这个secret
[root@k8s-master ~]# kubectl create secret generic registy-secert --from-file=.dockerconfigjson=/root/.docker/config.json --type=kubernetes.io/dockerconfigjson -n default

在这里插入图片描述

2.4.创建流水线项目测试

在这里插入图片描述
在这里插入图片描述

pipeline {
    // 使用k8s拉起slave
    agent {
        kubernetes {
            cloud 'kubernetes'             ##此处的名称必须和Jenkins凭据中定义的k8s config 的名称一致
            inheritFrom 'jenkins-slave'    ##此处必须和cloud配置的名称一致
            namespace 'default'            ##此处必须和cloud配置的名称一致
        }
    }

    stages {
        stage('输出pods名称') {
            steps {
                sh 'hostname'
            }
        }

        stage('等待时间') {
            steps {
                sh 'sleep 3'
            }
        }
    }
}

结果示例
在这里插入图片描述
在这里插入图片描述
至此cloud配置完成

六、Jenkins集成Gitee

gitee插件在插件包中有,此处不再掩饰插件安装过程

1.gitee创建API令牌

在这里插入图片描述
在这里插入图片描述
提交后,输入gitee密码,然后将显示出来的凭据复制一份

2.Jenkins添加Gitee API凭据

在这里插入图片描述
在这里插入图片描述

3.Jenkins中配置Gitee

测试出现成功即可
在这里插入图片描述

七、Jenkins集成共享库(jenkinslib)

共享库是配合pipeline实现整个构建过程,其封装了pipeline中的一些操作。它以仓库的形式保存于gitee中

1.Jenkins结合gitee配置ssh密钥

在Jenkins服务器的工作目录下创建ssh密钥。默认工作目录是/var/lib/jenkins/
在这里插入图片描述

[root@k8s-node .ssh]# ssh-keygen -t ed25519 -C "Gitee SSH Key"
[root@k8s-node .ssh]# ls -l
total 16
-rw------- 1 root root 390 Jan 12 21:25 authorized_keys
-rw------- 1 root root 399 Apr 30 15:09 id_ed25519
-rw-r--r-- 1 root root  95 Apr 30 15:09 id_ed25519.pub
-rw-r--r-- 1 root root 521 Apr 30 17:42 known_hosts

复制公钥id_ed25519.pub内容到gitee
在这里插入图片描述
在这里插入图片描述
在Jenkins服务验证能否连接到gitee
在这里插入图片描述
jenkins凭据添加ssh密钥
在这里插入图片描述

2.Jenkins集成共享库配置

在这里插入图片描述
在这里插入图片描述

八、Jenkins集成邮件通知

1. 创建邮箱服务凭据

授权码获取自行百度
在这里插入图片描述

2.Jenkins配置邮箱通知

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
至此邮件配置完成

九、共享库groovy脚本封装

在这里插入图片描述

1.k8s.groovy脚本示例

1、该脚本的作用是在启动构建时,动态的在k8s集群中启动一个pod
2、该脚本封装了maven、jdk、kubectl、docker、nodejs镜像,满足前后端服务打包构建、推送构建好的镜像到harbor仓库
class K8s {
    def call(Map config = [:]) {
        return """
apiVersion: v1
kind: Pod
metadata:
  labels:
    jenkins: slave
spec:
  securityContext:
    runAsUser: 0
  containers:
  - name: mvn
    image: ${config.mavenImage ?: 'harbor.jdicity.local/registry/maven:3.8.6-jdk-8'}
    imagePullPolicy: IfNotPresent
    command: ["cat"]
    tty: true
    env:
    - name: TZ
      value: Asia/Shanghai
    - name: LANG
      value: en_US.UTF-8
    - name: LC_ALL
      value: en_US.UTF-8
    volumeMounts:
    - name: jenkins-home
      mountPath: /root/.m2
    - name: time
      mountPath: /etc/localtime
      readOnly: true
  - name: docker
    image: ${config.mavenImage ?: 'harbor.jdicity.local/registry/docker:19.03'}
    imagePullPolicy: IfNotPresent
    command: ["cat"]
    tty: true
    env:
    - name: TZ
      value: Asia/Shanghai
    - name: LANG
      value: en_US.UTF-8
    - name: LC_ALL
      value: en_US.UTF-8
    volumeMounts:
    - name: dockersocket
      mountPath: /run/docker.sock
    - name: docker-config
      mountPath: /etc/docker/daemon.json
  - name: npm
    image: ${config.nodejsImage ?: 'harbor.jdicity.local/registry/nodejs:v18'}
    imagePullPolicy: IfNotPresent
    command: ["cat"]
    tty: true
    env:
    - name: TZ
      value: Asia/Shanghai
    - name: LANG
      value: en_US.UTF-8
    - name: LC_ALL
      value: en_US.UTF-8
  // - name: sonar  --环境资源不足
  //   image: ${config.sonarImage ?: 'harbor.jdicity.local/registry/sonar-scanner:2.3.0'}
  //   imagePullPolicy: IfNotPresent
  //   command: ["cat"]
  //   tty: true
  //   env:
  //   - name: TZ
  //     value: Asia/Shanghai
  //   - name: LANG
  //     value: en_US.UTF-8
  //   - name: LC_ALL
  //     value: en_US.UTF-8
  - name: kubectl
    image: ${config.kubectlImage ?: 'harbor.jdicity.local/registry/kubectl:1.28.2'}
    imagePullPolicy: IfNotPresent
    tty: true
    command: ["sleep"]
    args: ["infinity"]
    env:
    - name: TZ
      value: Asia/Shanghai
    - name: LANG
      value: en_US.UTF-8
    - name: LC_ALL
      value: en_US.UTF-8
  volumes:
  - name: jenkins-home
    persistentVolumeClaim:
      claimName: jenkins-home-pvc
  - name: dockersocket
    hostPath:
      path: /run/docker.sock
  - name: docker-config
    hostPath:
      path: /etc/docker/daemon.json
      type: File
  - name: time
    hostPath:
      path: /usr/share/zoneinfo/Asia/Shanghai
"""
    }
}

pipeline中调用

#!groovy
@Library("jenkinslib") _  #引入共享库
def k8s=new org.devops.k8s()

    agent {
        kubernetes { 
            cloud 'kubernetes'              // 对应 Jenkins 中配置的 Kubernetes 云名称
            inheritFrom 'jenkins-slave'  // 继承的 Pod 模板(可选)
            namespace 'default'      // 命名空间
            yaml k8s()            // 调用共享库生成 Pod YAML
        }
    }

十、CI阶段流程

因为电脑资源不足,就省略了代码扫描阶段

1.拉取代码

1.1.k8s集群配置gitee域名解析

[root@k8s-master ~]# kubectl edit configmaps -n kube-system coredns	
添加如下部分
gitee.com:53 {
   forward . /etc/reslove.conf
}

1.2. 声明式stage

在这里插入图片描述
在这里插入图片描述

#结合Jenkins流水线项目中的参数化构建-->设置字符参数 SrcURL 然后将其赋值给srcURL参数,实现在pipeline中调用
String srcURL="${env.SrcURL}"
#结合Jenkins流水线项目中的参数化构建-->设置git参数 branchName 然后将其赋值给branch参数,实现在pipeline中调用
String branch="${env.branchName}"
#结合Jenkins流水线项目中的参数化构建-->设置布尔值参数 rollback 然后再pipeline中调用,用于判断是否回滚
Boolean rollback = (env.rollback == 'true')
        stage("CheckOut"){
            when { expression { !rollback } }  #非回滚时执行
            steps{
                script{
                    tools.PrintMsg("获取分支: ${branch}","checkout")
                    checkout([$class: 'GitSCM', branches: [[name: "${branch}"]], 
                        extensions: [], 
                        userRemoteConfigs: [[credentialsId: 'gitee_registry_ssh', url: "${srcURL}"]]])
                        #此处的'gitee_registry_ssh'就是Jenkins凭据创建的SSH密钥名称
                }
            }
        }

在这里插入图片描述

2.代码编译

2.1.声明式stage

build.groovy脚本示例
在这里插入图片描述
在这里插入图片描述

#调用共享库,实现命令传参调用
def build=new org.devops.build()

#结合Jenkins流水线项目中的参数化构建-->设置字符参数 buildType 然后将其赋值给buildType参数,实现在pipeline中调用
String buildType="${env.buildType}"

#结合Jenkins流水线项目中的参数化构建-->设置字符参数 buildshell 然后将其赋值给buildshell参数,实现在pipeline中调用
String buildshell="${env.buildshell}"

#结合Jenkins流水线项目中的参数化构建-->设置布尔值参数 rollback 然后再pipeline中调用,用于判断是否回滚
Boolean rollback = (env.rollback == 'true')
        stage("Build"){
            when { expression { !rollback } }  // 非回滚时执行
            steps{
                script{
                    //集成构建工具
                    container('mvn') {
                        tools.PrintMsg("代码打包","build")
                        build.Build(buildType,buildshell)
                    }
                }
            }
        }

在这里插入图片描述

3.生成镜像tag

3.1.声明式stage

#结合Jenkins流水线项目中的参数化构建-->设置字符参数 imageTag 然后将其赋值给imageTag参数,实现在pipeline中调用
String imageTag="${env.imageTag}" 

        stage("镜像tag"){
            when { expression { !rollback } }  // 非回滚时执行
            steps{
                script{
                    tools.PrintMsg("获取镜像tag","image_tag")
                    // 使用 Jenkins 主机的本地时间(绕过容器时钟问题)
                    TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"))
                    env.BUILD_TIME = new Date().format("yyyyMMdd_HHmmss")
                        // 组合完整Tag
                    env.FULL_IMAGE_TAG = "${imageTag}"+"_"+env.BUILD_TIME
                    env.FULL_IMAGE_NAME="${env.HARBOR_URL}/${env.PROJECT_GROUP}/${imageName}:${env.FULL_IMAGE_TAG}"
                    tools.PrintMsg("生成镜像Tag: ${FULL_IMAGE_TAG}", "image_tag")
                    tools.PrintMsg("生成镜像地址: ${FULL_IMAGE_NAME}", "image_tag")
                   
                }    
            }
        }

在这里插入图片描述

在这里插入图片描述

4.镜像构建

Harbor.groovy脚本示例

1、通过获取凭据登录harbor仓库
2、通过docker、Dockerfile构建服务镜像
3、推送镜像到harbor仓库中
4、从本地删除镜像,释放服务器资源

在这里插入图片描述

4.1.声明式stage

String imageName="${env.imageName}"

        stage("镜像构建"){
            when { expression { !rollback } }  // 非回滚时执行
            steps{
                script{
                    container('docker') {
                        tools.PrintMsg("代码打包","image_build")
                        harbor.BuildImage(
                        this,
                        'HARBOR_ID',
                        env.HARBOR_URL,
                        env.PROJECT_GROUP,
                        "${imageName}",
                        env.FULL_IMAGE_TAG)
                    }
                }
            }
        }

在这里插入图片描述

在这里插入图片描述

5.生成部署模板

支持deployment、statefulset两种类型

5.1.声明式stage

String controllerType="${env.controllerType}"

        stage("生成部署模板") {
            when { expression { !rollback } }
            steps {
                container('kubectl') {
                    script {
                        tools.PrintMsg("并行生成部署模板","template")
                                // 根据资源类型选择模板
                        def templateFile = ("${controllerType}".toLowerCase() == 'statefulset') ? 
                            'statefulset-template.yaml' : 'deployment-template.yaml'
                        // 并行生成不同类型的模板
                        parallel(
                            "生成${controllerType}模板": {
                                // 读取模板内容
                                String template = readFile("${templateFile}")
                                
                                // 定义替换变量
                                def replacements = [
                                    '${imageName}'       : "${imageName}",
                                    '${replicas}'       : "${replicas}",
                                    '${FULL_IMAGE_NAME}': env.FULL_IMAGE_NAME,
                                    '${containerPort}'   : "${containerPort}",
                                    '${k8s_ns}'         : "${k8s_ns}"
                                ]
                                
                                // 执行替换并写入文件
                                String yamlContent = replacements.inject(template) { 
                                    content, entry -> content.replace(entry.key, entry.value) 
                                }
                                writeFile file: "${WORKSPACE}/${controllerType}.yaml", text: yamlContent
                            },
                            
                            "生成Service模板": {
                                // 读取模板内容
                                String templatesvc = readFile('svc-template.yaml')
                                
                                // 定义替换变量
                                def replacements = [
                                    '${imageName}'    : "${imageName}",
                                    '${containerPort}': "${containerPort}",
                                    '${k8s_ns}'       : "${k8s_ns}"
                                ]
                                
                                // 执行替换并写入文件
                                String svcYaml = replacements.inject(templatesvc) { 
                                    content, entry -> content.replace(entry.key, entry.value) 
                                }
                                writeFile file: "${WORKSPACE}/svc.yaml", text: svcYaml
                            },
                            
                            failFast: true  // 任一失败则立即终止
                        )
                    }
                }
            }
        }

在这里插入图片描述

在这里插入图片描述

十一、CD阶段

1.发布服务

k8sdeploy.groovy脚本示例

1、根据选择的发布环境,获取到凭据中导入的k8s config 名称

2、根据选择的发布环境和发布的类型、命名空间等信息,发布对应的yaml文件和svc.yaml文件

3、通过rollout 观察pod 状态running时 继续执行下一步操作,否则返回发布失败

在这里插入图片描述

1.1.声明式stage

#!groovy
@Library("jenkinslib") _

//func from sharelibrary调用共享库
def k8sdeploy=new org.devops.k8sdeploy()
def tools=new org.devops.tools()
//调用Jenkins中定义的选项参数 名称和Jenkins中定义的保持一致
//from jenkins 参数化构建变量
String Tenv="${env.Tenv}"
String k8s_ns="${env.k8s_ns}"
String controllerType="${env.controllerType}"
String imageName="${env.imageName}"
String imageTag="${env.imageTag}" 
String replicas="${env.replicas}"
String containerPort="${env.ContainerPort}"
Boolean rollback = (env.rollback == 'true')

        stage("部署到k8s"){
            when { expression { !rollback } }  // 非回滚时执行
            steps{
                script{
                    container('kubectl') {
                        tools.PrintMsg("部署到${Tenv}环境","deploy")
                        k8sdeploy.Deploy(
                        this,
                        "${Tenv}",
                        "${controllerType}",
                        "${imageName}",
                        "${env.FULL_IMAGE_NAME}",
                        "${k8s_ns}"
                        )
                        // 在发布阶段将镜像名称保存到 env.FULL_IMAGE_NAME
                        env.FULL_IMAGE_NAME = "${env.FULL_IMAGE_NAME}"
                        currentBuild.description = "${env.FULL_IMAGE_NAME}"
                    }
                }
            }
        }

在这里插入图片描述

在这里插入图片描述
在下方添加此groovy脚本,实现动态获取k8s集群中的命名空间

try {
    // 指定 kubectl 和 kubeconfig 的绝对路径
    def cmd = "/usr/bin/kubectl --kubeconfig=/var/lib/jenkins/.kube/config get namespaces -o jsonpath='{.items[*].metadata.name}'"
    def process = cmd.execute()
    
    // 捕获标准错误输出
    def errorStream = new StringBuffer()
    process.consumeProcessErrorStream(errorStream)
    
    // 等待命令完成
    process.waitFor()
    
    // 处理输出
    def output = process.text.trim().replaceAll("'", "")  // 清理单引号
    def error = errorStream.toString().trim()
    
    println "✅ 标准输出: ${output}"
    println "❌ 错误输出: ${error}"
    
    if (process.exitValue() == 0) {
        def namespaces = output.split(/\s+/)
        namespaces = namespaces.findAll { !it.startsWith('kube-') && it != 'default' }
        return namespaces
    } else {
        return ["Error: ${error}"]
    }
} catch (Exception e) {
    return ["Error: ${e.message}"]
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.回滚服务

回滚groovy脚本示例
在这里插入图片描述

2.1.声明式stage

        stage("是否回滚"){
            when { expression { rollback } }  // 回滚时执行
            steps{
                script{
                    container('kubectl') {
                        tools.PrintMsg("回滚到${Tenv}环境","rollback")
                        def  rollbackImage = k8sdeploy.Rollback(
                            this,
                            "${Tenv}",
                            "${controllerType}",
                            // 'k8s',
                            "${imageName}",
                            "${k8s_ns}"
                        )
                        // 保存回滚镜像信息
                        env.FULL_IMAGE_NAME = rollbackImage  // 使用回滚镜像替换 FULL_IMAGE_NAME
                        // 保存回滚镜像信息
                        currentBuild.description = "${rollbackImage}"
                    }
                }
            }
        }
    }

在这里插入图片描述

在这里插入图片描述

十二、构建结果使用邮件发送

toemail.grovy脚本示例

1、不管Jenkins构建成功还是失败,回滚成功/失败,都发送邮件

2、邮件内容包含构建结果、微服务名称、发布的镜像地址(回滚的镜像地址)、仓库地址、仓库分支、构建用户、构建URL、构建日志URL、构建编号、构建时间、构建持续时间

在这里插入图片描述

声明式stage

    post {
        always {
            script {
                // 获取构建时间和持续时间
                TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"))
                env.BUILD_TIME = new Date().format("yyyyMMdd_HHmmss")
                def buildTime = env.BUILD_TIME ?: "N/A"
                def buildDuration = currentBuild.durationString ?: "N/A"
                // 发送邮件
                toemail.Email(
                    currentBuild.currentResult,
                    "${env.emailUser}",
                    "${imageName}",
                    "${branch}",
                    "${env.BUILD_USER}",
                    buildTime,
                    buildDuration,
                    rollback,
                    "${k8s_ns}",
                    currentBuild.description,
                    "${srcURL}"
                )
            }
        }
    }

在这里插入图片描述

在这里插入图片描述

注意事项

如果需要获取到构建用户名称和时间,则需要安装build user vars plugin插件.然后选择开启
在这里插入图片描述

十三、实战:构建springboot项目

1.准备工作

编写一个springboot项目,上传到igtee仓库,如下所示
在这里插入图片描述

2.jenkins构建

2.1.构建参数设置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2.发布构建过程

引入共享库Jenkinslib
在这里插入图片描述
创建动态pod
在这里插入图片描述
在这里插入图片描述
拉代码
在这里插入图片描述
代码编译
在这里插入图片描述
生成镜像tag
在这里插入图片描述
代码构建、推送到harbor仓库
在这里插入图片描述
生成部署模板
在这里插入图片描述
发布到指定环境
在这里插入图片描述
邮件通知
在这里插入图片描述
在这里插入图片描述

2.3.回滚过程

只需要勾选rollabck、选择k8s命名空间、回滚环境、服务类型、服务名称即可
在这里插入图片描述
回滚示例
在这里插入图片描述
回滚邮件发送
在这里插入图片描述在这里插入图片描述

十四、完整jenkinsfile示例

groovy脚本+声明式pipeline组合而成

#!groovy
@Library("jenkinslib") _

//func from sharelibrary调用共享库
def build=new org.devops.build()
def k8s=new org.devops.k8s()
def k8sdeploy=new org.devops.k8sdeploy()
def tools=new org.devops.tools()
def harbor=new org.devops.Harbor()
def toemail=new org.devops.toemail()
//调用Jenkins中定义的选项参数 名称和Jenkins中定义的保持一致
//from jenkins 参数化构建变量
String Tenv="${env.Tenv}"
String buildType="${env.buildType}"
String buildshell="${env.buildshell}"
String deployHosts="${env.deployHosts}"
String srcURL="${env.SrcURL}"
String branch="${env.branchName}"
String k8s_ns="${env.k8s_ns}"
String controllerType="${env.controllerType}"
String imageName="${env.imageName}"
String imageTag="${env.imageTag}" 
String replicas="${env.replicas}"
String containerPort="${env.ContainerPort}"
Boolean rollback = (env.rollback == 'true')

// 固定配置
env.HARBOR_URL = "harbor.jdicity.local"
env.PROJECT_GROUP = "registry"

pipeline{
    agent {
        kubernetes { 
            cloud 'kubernetes'              // 对应 Jenkins 中配置的 Kubernetes 云名称
            inheritFrom 'jenkins-slave'  // 继承的 Pod 模板(可选)
            namespace 'default'      // 命名空间
            yaml k8s()            // 调用共享库生成 Pod YAML
        }
    }
    options {
        timestamps()
        skipDefaultCheckout()  // 禁用隐式 Checkout
        timeout(time: 1, unit: 'HOURS') //设置流水线超时
    }
    stages{
        stage("CheckOut"){
            when { expression { !rollback } }  // 非回滚时执行
            steps{
                script{
                    tools.PrintMsg("获取分支: ${branch}","checkout")
                    tools.PrintMsg("获取代码","checkout")
                    checkout([$class: 'GitSCM', branches: [[name: "${branch}"]], 
                        extensions: [], 
                        userRemoteConfigs: [[credentialsId: 'gitee_registry_ssh', url: "${srcURL}"]]])
                }
            }
        }
        stage("Build"){
            when { expression { !rollback } }  // 非回滚时执行
            steps{
                script{
                    //集成构建工具
                    container('mvn') {
                        tools.PrintMsg("代码打包","build")
                        build.Build(buildType,buildshell)
                    }
                }
            }
        }
        stage("镜像tag"){
            when { expression { !rollback } }  // 非回滚时执行
            steps{
                script{
                    tools.PrintMsg("获取镜像tag","image_tag")
                    // 使用 Jenkins 主机的本地时间(绕过容器时钟问题)
                    TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"))
                    env.BUILD_TIME = new Date().format("yyyyMMdd_HHmmss")
                        // 组合完整Tag
                    env.FULL_IMAGE_TAG = "${imageTag}"+"_"+env.BUILD_TIME
                    env.FULL_IMAGE_NAME="${env.HARBOR_URL}/${env.PROJECT_GROUP}/${imageName}:${env.FULL_IMAGE_TAG}"
                    tools.PrintMsg("生成镜像Tag: ${FULL_IMAGE_TAG}", "image_tag")
                    tools.PrintMsg("生成镜像地址: ${FULL_IMAGE_NAME}", "image_tag")
                   
                }    
            }
        }
        stage("镜像构建"){
            when { expression { !rollback } }  // 非回滚时执行
            steps{
                script{
                    container('docker') {
                        tools.PrintMsg("代码打包","image_build")
                        harbor.BuildImage(
                        this,
                        'HARBOR_ID',
                        env.HARBOR_URL,
                        env.PROJECT_GROUP,
                        "${imageName}",
                        env.FULL_IMAGE_TAG)
                    }
                }
            }
        }
        stage("生成部署模板") {
            when { expression { !rollback } }
            steps {
                container('kubectl') {
                    script {
                        tools.PrintMsg("并行生成部署模板","template")
                                // 根据资源类型选择模板
                        def templateFile = ("${controllerType}".toLowerCase() == 'statefulset') ? 
                            'statefulset-template.yaml' : 'deployment-template.yaml'
                        // 并行生成不同类型的模板
                        parallel(
                            "生成${controllerType}模板": {
                                // 读取模板内容
                                String template = readFile("${templateFile}")
                                
                                // 定义替换变量
                                def replacements = [
                                    '${imageName}'       : "${imageName}",
                                    '${replicas}'       : "${replicas}",
                                    '${FULL_IMAGE_NAME}': env.FULL_IMAGE_NAME,
                                    '${containerPort}'   : "${containerPort}",
                                    '${k8s_ns}'         : "${k8s_ns}"
                                ]
                                
                                // 执行替换并写入文件
                                String yamlContent = replacements.inject(template) { 
                                    content, entry -> content.replace(entry.key, entry.value) 
                                }
                                writeFile file: "${WORKSPACE}/${controllerType}.yaml", text: yamlContent
                            },
                            
                            "生成Service模板": {
                                // 读取模板内容
                                String templatesvc = readFile('svc-template.yaml')
                                
                                // 定义替换变量
                                def replacements = [
                                    '${imageName}'    : "${imageName}",
                                    '${containerPort}': "${containerPort}",
                                    '${k8s_ns}'       : "${k8s_ns}"
                                ]
                                
                                // 执行替换并写入文件
                                String svcYaml = replacements.inject(templatesvc) { 
                                    content, entry -> content.replace(entry.key, entry.value) 
                                }
                                writeFile file: "${WORKSPACE}/svc.yaml", text: svcYaml
                            },
                            
                            failFast: true  // 任一失败则立即终止
                        )
                    }
                }
            }
        }
        stage("部署到k8s"){
            when { expression { !rollback } }  // 非回滚时执行
            steps{
                script{
                    container('kubectl') {
                        tools.PrintMsg("部署到${Tenv}环境","deploy")
                        k8sdeploy.Deploy(
                        this,
                        "${Tenv}",
                        "${controllerType}",
                        "${imageName}",
                        "${env.FULL_IMAGE_NAME}",
                        "${k8s_ns}"
                        )
                        // 在发布阶段将镜像名称保存到 env.FULL_IMAGE_NAME
                        env.FULL_IMAGE_NAME = "${env.FULL_IMAGE_NAME}"
                        currentBuild.description = "${env.FULL_IMAGE_NAME}"
                    }
                }
            }
        }
        stage("是否回滚"){
            when { expression { rollback } }  // 回滚时执行
            steps{
                script{
                    container('kubectl') {
                        tools.PrintMsg("回滚到${Tenv}环境","rollback")
                        def  rollbackImage = k8sdeploy.Rollback(
                            this,
                            "${Tenv}",
                            "${controllerType}",
                            // 'k8s',
                            "${imageName}",
                            "${k8s_ns}"
                        )
                        // 保存回滚镜像信息
                        env.FULL_IMAGE_NAME = rollbackImage  // 使用回滚镜像替换 FULL_IMAGE_NAME
                        // 保存回滚镜像信息
                        currentBuild.description = "${rollbackImage}"
                    }
                }
            }
        }
    }
    post {
        always {
            script {
                // 获取构建时间和持续时间
                TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"))
                env.BUILD_TIME = new Date().format("yyyyMMdd_HHmmss")
                def buildTime = env.BUILD_TIME ?: "N/A"
                def buildDuration = currentBuild.durationString ?: "N/A"
                // 发送邮件
                toemail.Email(
                    currentBuild.currentResult,
                    "${env.emailUser}",
                    "${imageName}",
                    "${branch}",
                    "${env.BUILD_USER}",
                    buildTime,
                    buildDuration,
                    rollback,
                    "${k8s_ns}",
                    currentBuild.description,
                    "${srcURL}"
                )
            }
        }
    }
}

总结

至此,一套完整的springboot+k8s+jenkins+动态slave+pipeline的CICD流水线项目就完整的落地了,关于代码扫描部分,因服务器资源不足,便再本篇中没有实现!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值