jenkins声明式pipline和shell从环境变量配置到打包构建再到发布到k8s

shell

shell 复制代码
mvn clean package -U -DskipTests=true
JAR_NAME=hotel-directsales-data-web.jar
JAR_DIR=hotel-directsales-data-web
SHORT_COMMIT_ID=${GIT_COMMIT:0:7}
Image_Name=hotel-directsales-data-sync
Image_Repository=aliyun-regirest-vpc.cn-shanghai.personal.cr.aliyuncs.com/tengrong-pro/xinlimei
short_branch=$(echo "$GIT_BRANCH" | awk -F'/' '{print $2}')
Image=$Image_Repository:$Image_Name-$short_branch-$SHORT_COMMIT_ID
Deployment=hotel-directsales-data-sync
NameSpace=pro
PODNUM=5
PodPort=8080
SKYWALKING_URL="aliyun-skywalking:8000"
SKYWALKING_AUTH="aliyun-skywalking-secret"
APOLLO_URL="http://apollo.com.cn  "
EurekaUrl="k8s-eureka.com.cn:8761"
cpuLimit=4
memoryLimit=10241Mi
cpuRequest=1
memoryRequest=6144Mi
HEAP="-Xms4g -Xmx4g \
-Dserver.port=8080 \
-Deureka.client.serviceUrl.defaultZone=http://k8s-eureka.com.cn:8761/eureka/   \
-Dxxl.job.admin.addresses=http://k8s-xxljob.com.cn/   \
-Drocketmq.name-server=rocketmq-namesrv.com.cn:19876 \
-Dspring.activemq.broker-url=failover:(tcp://activemq.com.cn:61616) "
ENVIROMENT="pro"
#cd到项目目录下
cd $JAR_DIR
#拷贝Dockerfile文件
cp -rf /data/Dockerfile-pro/Dockerfile-xlm ./Dockerfile
#构建镜像
docker build \
--build-arg APOLLO_URL="$APOLLO_URL" \
--build-arg SKYWALKING_URL="$SKYWALKING_URL" \
--build-arg SKYWALKING_AUTH="$SKYWALKING_AUTH" \
--build-arg JAVA_NAME_ARG="$JAR_NAME" \
--build-arg SERVICE_NAME="$Deployment" \
--build-arg HEAP="$HEAP" \
--build-arg ENVIROMENT="$ENVIROMENT"  \
-t $Image .
#替换资源模版变量
cp /data/Dockerfile-pro/xinlimei-template-k8s-memory.yml ./application.yml
sed -i "s/ProjectName/$Deployment/g" application.yml
sed -i "s#imageName#$Image#g" application.yml
sed -i "s/NameSpace/$NameSpace/g" application.yml
sed -i "s/PODNUM/$PODNUM/g" application.yml
sed -i "s/PodPort/$PodPort/g" application.yml
sed -i "s/EurekaUrl/$EurekaUrl/g" application.yml
sed -i "s/cpuLimit/$cpuLimit/g" application.yml
sed -i "s/memoryLimit/$memoryLimit/g" application.yml
sed -i "s/cpuRequest/$cpuRequest/g" application.yml
sed -i "s/memoryRequest/$memoryRequest/g" application.yml
cat application.yml
#推送镜像
docker push $Image
#滚动更新
kubectl --kubeconfig=/data/Dockerfile-pro/pro-kube/config -n $NameSpace apply -f application.yml
#健康检查
kubectl --kubeconfig=/data/Dockerfile-pro/pro-kube/config -n $NameSpace rollout status --timeout=480s deployment/$Deployment

pipline

pipline 复制代码
pipeline {
    // 使用任意可用的agent执行
    agent any
    
    // 定义环境变量,这些变量将在整个Pipeline中使用
    environment {
        // ========== 项目配置 ==========
        // JAR包名称
        JAR_NAME = 'hotel-directsales-data-web.jar'
        // 项目目录名
        JAR_DIR = 'hotel-directsales-data-web'
        // 镜像基础名称
        Image_Name = 'hotel-directsales-data-sync'
        // 阿里云镜像仓库地址
        Image_Repository = 'aliyun-regirest-vpc.cn-shanghai.personal.cr.aliyuncs.com/tengrong-pro/xinlimei'
        // Kubernetes部署名称
        Deployment = 'hotel-directsales-data-sync'
        // Kubernetes命名空间
        NameSpace = 'pro'
        // Pod副本数量
        PODNUM = '5'
        // 容器端口
        PodPort = '8080'
        
        // ========== SkyWalking APM配置 ==========
        // SkyWalking服务地址,用于链路追踪
        SKYWALKING_URL = 'aliyun-skywalking:8000'
        // SkyWalking认证信息(secret名称)
        SKYWALKING_AUTH = 'aliyun-skywalking-secret'
        
        // ========== 中间件配置 ==========
        // Apollo配置中心地址
        APOLLO_URL = 'http://apollo.com.cn'
        // Eureka注册中心地址
        EurekaUrl = 'k8s-eureka.com.cn:8761'
        
        // ========== K8s资源限制配置 ==========
        // CPU限制(最大可用)
        cpuLimit = '4'
        // 内存限制(最大可用)
        memoryLimit = '10241Mi'
        // CPU请求(最小保证)
        cpuRequest = '1'
        // 内存请求(最小保证)
        memoryRequest = '6144Mi'
        
        // ========== JVM及Java应用参数 ==========
        // Java堆内存设置及服务配置
        HEAP = '-Xms4g -Xmx4g -Dserver.port=8080 -Deureka.client.serviceUrl.defaultZone=http://k8s-eureka.com.cn:8761/eureka/ -Dxxl.job.admin.addresses=http://k8s-xxljob.com.cn/ -Drocketmq.name-server=rocketmq-namesrv.com.cn:19876 -Dspring.activemq.broker-url=failover:(tcp://activemq.com.cn:61616)'
        
        // 运行环境标识
        ENVIROMENT = 'pro'
        
        // ========== 动态生成的变量(将在Pipeline中赋值) ==========
        // 短提交ID(Git commit前7位)
        SHORT_COMMIT_ID = ''
        // 短分支名(去掉origin/前缀)
        short_branch = ''
        // 完整镜像标签
        Image = ''
    }
    
    // 选项配置
    options {
        // 构建历史保留策略:保留最近10个构建
        buildDiscarder(logRotator(numToKeepStr: '10'))
        // 禁止并发构建,防止资源冲突
        disableConcurrentBuilds()
        // 控制台输出带时间戳
        timestamps()
        // Pipeline超时设置为30分钟
        timeout(time: 30, unit: 'MINUTES')
    }
    
    // 定义构建触发器(可选,根据需要启用)
    triggers {
        // 每15分钟检查一次代码变更(轮询SCM)
        // pollSCM('H/15 * * * *')
    }
    
    // 构建阶段定义
    stages {
        // ========== 阶段1:初始化与变量准备 ==========
        stage('初始化环境') {
            steps {
                script {
                    // 获取Git提交ID前7位作为版本标识
                    env.SHORT_COMMIT_ID = env.GIT_COMMIT ? env.GIT_COMMIT.take(7) : "unknown"
                    
                    // 提取短分支名:去掉origin/前缀,只保留分支名
                    // 例如:origin/release/1.0 -> release/1.0
                    if (env.GIT_BRANCH) {
                        def branchParts = env.GIT_BRANCH.split('/')
                        if (branchParts.length > 1) {
                            // 去掉第一个元素(origin),用剩下的部分重新组合
                            env.short_branch = branchParts[1..-1].join('/')
                        } else {
                            env.short_branch = env.GIT_BRANCH
                        }
                    } else {
                        env.short_branch = "unknown"
                    }
                    
                    // 组合完整的镜像地址:仓库/命名空间:镜像名-分支-提交ID
                    env.Image = "${env.Image_Repository}:${env.Image_Name}-${env.short_branch}-${env.SHORT_COMMIT_ID}"
                    
                    // 打印关键变量用于调试
                    echo "=========================================="
                    echo "Git分支: ${env.GIT_BRANCH}"
                    echo "短分支名: ${env.short_branch}"
                    echo "提交ID: ${env.SHORT_COMMIT_ID}"
                    echo "构建镜像: ${env.Image}"
                    echo "=========================================="
                }
            }
        }
        
        // ========== 阶段2:Maven编译打包 ==========
        stage('Maven构建') {
            steps {
                // 进入项目子目录执行构建
                dir("${env.JAR_DIR}") {
                    // 使用Maven构建
                    // clean: 清理目标目录
                    // package: 打包
                    // -U: 强制更新依赖快照
                    // -DskipTests=true: 跳过测试加速构建(生产环境建议开启测试)
                    sh '''
                        mvn clean package -U -DskipTests=true
                    '''
                }
            }
        }
        
        // ========== 阶段3:Docker镜像构建 ==========
        stage('构建Docker镜像') {
            steps {
                dir("${env.JAR_DIR}") {
                    script {
                        // 拷贝生产环境Dockerfile到项目目录
                        sh 'cp -rf /data/Dockerfile-pro/Dockerfile-xlm ./Dockerfile'
                        
                        // 构建Docker镜像,通过build-arg传递配置参数
                        // 这些参数将在Dockerfile中用于配置Java应用
                        sh """
                            docker build \\
                                --build-arg APOLLO_URL="${env.APOLLO_URL}" \\
                                --build-arg SKYWALKING_URL="${env.SKYWALKING_URL}" \\
                                --build-arg SKYWALKING_AUTH="${env.SKYWALKING_AUTH}" \\
                                --build-arg JAVA_NAME_ARG="${env.JAR_NAME}" \\
                                --build-arg SERVICE_NAME="${env.Deployment}" \\
                                --build-arg HEAP="${env.HEAP}" \\
                                --build-arg ENVIROMENT="${env.ENVIROMENT}" \\
                                -t ${env.Image} .
                        """
                    }
                }
            }
        }
        
        // ========== 阶段4:K8s部署模板处理 ==========
        stage('生成K8s部署配置') {
            steps {
                dir("${env.JAR_DIR}") {
                    script {
                        // 拷贝K8s模板文件到项目目录
                        sh 'cp /data/Dockerfile-pro/xinlimei-template-k8s-memory.yml ./application.yml'
                        
                        // 使用sed替换模板中的占位符变量
                        // 注意:使用#作为分隔符避免URL中的/冲突
                        sh """
                            sed -i "s/ProjectName/${env.Deployment}/g" application.yml
                            sed -i "s#imageName#${env.Image}#g" application.yml
                            sed -i "s/NameSpace/${env.NameSpace}/g" application.yml
                            sed -i "s/PODNUM/${env.PODNUM}/g" application.yml
                            sed -i "s/PodPort/${env.PodPort}/g" application.yml
                            sed -i "s/EurekaUrl/${env.EurekaUrl}/g" application.yml
                            sed -i "s/cpuLimit/${env.cpuLimit}/g" application.yml
                            sed -i "s/memoryLimit/${env.memoryLimit}/g" application.yml
                            sed -i "s/cpuRequest/${env.cpuRequest}/g" application.yml
                            sed -i "s/memoryRequest/${env.memoryRequest}/g" application.yml
                        """
                        
                        // 打印生成的K8s配置用于审查
                        echo "========== 生成的K8s部署配置 =========="
                        sh 'cat application.yml'
                        echo "======================================="
                    }
                }
            }
        }
        
        // ========== 阶段5:镜像推送 ==========
        stage('推送镜像到仓库') {
            steps {
                script {
                    // 将构建好的镜像推送到阿里云私有镜像仓库
                    // 需要确保Jenkins节点已配置镜像仓库登录凭证(docker login)
                    sh "docker push ${env.Image}"
                    
                    // 可选:清理本地镜像释放空间
                    // sh "docker rmi ${env.Image} || true"
                }
            }
        }
        
        // ========== 阶段6:Kubernetes部署 ==========
        stage('部署到K8s集群') {
            steps {
                dir("${env.JAR_DIR}") {
                    script {
                        // 使用kubectl apply部署到Kubernetes集群
                        // 指定kubeconfig文件路径和命名空间
                        sh """
                            kubectl --kubeconfig=/data/Dockerfile-pro/pro-kube/config \\
                                -n ${env.NameSpace} \\
                                apply -f application.yml
                        """
                    }
                }
            }
        }
        
        // ========== 阶段7:部署验证与健康检查 ==========
        stage('健康检查') {
            steps {
                script {
                    // 使用kubectl rollout status检查部署状态
                    // 等待Deployment滚动更新完成,超时时间为480秒(8分钟)
                    // 会检查Pod是否成功创建并达到就绪状态
                    sh """
                        kubectl --kubeconfig=/data/Dockerfile-pro/pro-kube/config \\
                            -n ${env.NameSpace} \\
                            rollout status \\
                            --timeout=480s \\
                            deployment/${env.Deployment}
                    """
                    
                    echo "✅ 部署成功!应用已在Kubernetes集群中正常运行。"
                }
            }
        }
    }
    
    // ========== 后置处理 ==========
    post {
        // 无论构建成功与否都执行
        always {
            script {
                // 清理工作区(可选,根据磁盘空间需求决定)
                // cleanWs()
                
                echo "Pipeline执行完成,构建结果: ${currentBuild.currentResult}"
            }
        }
        
        // 构建成功时执行
        success {
            script {
                echo "🎉 构建部署成功!"
                echo "部署镜像: ${env.Image}"
                echo "K8s部署: ${env.Deployment}"
                echo "命名空间: ${env.NameSpace}"
                
                // 可以在这里添加通知,如钉钉/企业微信/邮件等
                // dingtalk(
                //     robot: 'jenkins-robot',
                //     type: 'MARKDOWN',
                //     title: '部署成功通知',
                //     text: "${env.Deployment} 部署成功\n镜像: ${env.Image}"
                // )
            }
        }
        
        // 构建失败时执行
        failure {
            script {
                echo "❌ 构建失败,请检查日志定位问题"
                
                // 可以在这里添加失败通知
                // 钉钉/企业微信/Slack通知等
            }
        }
        
        // 构建不稳定时执行(如测试失败但构建成功)
        unstable {
            script {
                echo "⚠️ 构建状态不稳定"
            }
        }
        
        // 构建被中止时执行
        aborted {
            script {
                echo "🛑 构建被手动中止"
            }
        }
    }
}

deployment优雅上下线(nacos)

yaml 复制代码
[root@jenkins Dockerfile-pro]# cat template-k8s.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: ProjectName
  name: ProjectName
  namespace: NameSpace
spec:
  progressDeadlineSeconds: 600
  replicas: PODNUM
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: ProjectName
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: ProjectName
      annotations:
        alibabacloud.com/burst-resource: eci
    spec:
      containers:
        - env:
            - name: POD_IP
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: status.podIP
            - name: PODNAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_PORT
              value: 'PodPort'
            - name: POD_NAME
              value: Nacos_AppName
            - name: NAMESPACE_ID
              value: NAMESPACEID
            - name: NACOS_URL
              value: 'NacosUrl'
          name: ProjectName
          ports:
            - containerPort: PodPort
              name: app-port-PodPort
              protocol: TCP
          image: imageName
          imagePullPolicy: IfNotPresent
          lifecycle:
            preStop:
              exec:
                command:
                  - /bin/sh
                  - '-c'
                  - >-
                    curl -X PUT
                    "${NACOS_URL}/nacos/v2/ns/instance?serviceName=${POD_NAME}&ip=${POD_IP}&port=${POD_PORT}&ephemeral=true&weight=0&namespaceId=${NAMESPACE_ID}"
                    & sleep 5
                    curl -X PUT
                    "${NACOS_URL}/nacos/v2/ns/instance?serviceName=${POD_NAME}&ip=${POD_IP}&port=${POD_PORT}&ephemeral=true&weight=0&namespaceId=${NAMESPACE_ID}"
                    & sleep 5
                    curl -X PUT
                    "${NACOS_URL}/nacos/v2/ns/instance?serviceName=${POD_NAME}&ip=${POD_IP}&port=${POD_PORT}&ephemeral=true&weight=0&namespaceId=${NAMESPACE_ID}"
                    & sleep 35
          livenessProbe:
            failureThreshold: 10
            initialDelaySeconds: 50
            periodSeconds: 10
            successThreshold: 1
            tcpSocket:
              port: PodPort
            timeoutSeconds: 1
          readinessProbe:
            failureThreshold: 10
            initialDelaySeconds: 50
            periodSeconds: 10
            successThreshold: 1
            tcpSocket:
              port: PodPort
            timeoutSeconds: 1
          resources:
            limits:
              cpu: 'cpuLimit'
              memory: memoryLimit
            requests:
              cpu: 'cpuRequest'
              memory: memoryRequest
          volumeMounts:
            - name: localtime
              mountPath: /etc/localtime
      dnsPolicy: ClusterFirst
      imagePullSecrets:
        - name: aliyun-pro-register
      restartPolicy: Always
      terminationGracePeriodSeconds: 120
      volumes:
        - hostPath:
            path: /etc/localtime
            type: ''
          name: localtime

deployment优雅上下线(eureka)

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: ProjectName
  name: ProjectName
  namespace: NameSpace
spec:
  progressDeadlineSeconds: 600
  replicas: PODNUM
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: ProjectName
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: ProjectName
      annotations:
        alibabacloud.com/burst-resource: eci
    spec:
      containers:
        - env:
            - name: POD_IP
              valueFrom:
                fieldRef:
                  apiVersion: v1
                  fieldPath: status.podIP
            - name: PODNAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_PORT
              value: 'PodPort'
            - name: POD_NAME
              value: ProjectName
            - name: EUREKA_URL
              value: 'EurekaUrl'
          name: ProjectName
          ports:
            - containerPort: PodPort
              name: app-port-PodPort
              protocol: TCP
          image: imageName
          imagePullPolicy: IfNotPresent
          lifecycle:
            preStop:
              exec:
                command:
                  - /bin/sh
                  - '-c'
                  - |
                    curl -X PUT "${EUREKA_URL}/eureka/apps/${POD_NAME}/${POD_IP}:${POD_PORT}/status?value=OUT_OF_SERVICE"
                    sleep 45
                    sleep 15
          livenessProbe:
            failureThreshold: 10
            initialDelaySeconds: 50
            periodSeconds: 10
            successThreshold: 1
            tcpSocket:
              port: PodPort
            timeoutSeconds: 1
          readinessProbe:
            failureThreshold: 10
            initialDelaySeconds: 50
            periodSeconds: 10
            successThreshold: 1
            tcpSocket:
              port: PodPort
            timeoutSeconds: 1
          resources:
            limits:
              cpu: 'cpuLimit'
              memory: memoryLimit
            requests:
              cpu: 'cpuRequest'
              memory: memoryRequest
          volumeMounts:
            - mountPath: /etc/localtime
              name: volume-localtime
      dnsPolicy: ClusterFirst
      imagePullSecrets:
        - name: aliyun-pro-register
      restartPolicy: Always
      terminationGracePeriodSeconds: 120
      volumes:
        - hostPath:
            path: /etc/localtime
            type: ''
          name: volume-localtime
相关推荐
林shir2 小时前
3-19-项目部署(Linux)
linux·运维·服务器
顶点多余2 小时前
Linux第一个系统程序-进度条
linux·运维·服务器
代码AI弗森2 小时前
NAS 服务器 vs 普通服务器:一场关于「存储中心」与「计算中心」的系统分工之争
运维·服务器
来鸟 鸣间2 小时前
linux下ffmpeg源码编译
linux·运维·ffmpeg
赛博云推-Twitter热门霸屏工具2 小时前
Twitter 自动化与热门霸屏实战:以赛博云推为例的技术解析
运维·自动化·twitter
菜鸟程序猿小章2 小时前
人大金仓服务器定时备份
运维·服务器
zt1985q2 小时前
本地部署静态网站生成工具 Vuepress 并实现外部访问
运维·服务器·网络·数据库·网络协议
楼田莉子2 小时前
Linux学习:进程信号
linux·运维·服务器·c++·学习
kabcko2 小时前
Windows10安装Docker
运维·docker·容器