我在B站学运维之Jenkins进阶学习Kubernetes集群自动化部署单war包应用实践(11)
全栈工程师修炼指南
2021年11月08日 20:12
收录于文集
共63篇

本章目录:

  • 0x01 Jenkins之K8s集群自动化部署单war包应用

    • 前期准备

    • (0) HelloWorld - Tomcat 应用服务器

    • (1) Nginx - Web服务器

    • (2) Jenkins Pipeline 脚本

    • (3) Shell 部署脚本

    • (4) 效果预览


0x01 Jenkins之K8s集群自动化部署单war包应用

实验目的 描述: 采用Jenkins从Gitlab中拉取测试项目以及代码质量检测,然后进行编译构建并利用K8s集群进行WAR包应用部署实践,最后将生成的制品进行归档到Gitlab的Release之中;

动态配置文件或者脚本:

  • setting.xml (Maven编译构建配置文件)

  • HelloWorld.yaml (应用部署资源清单)

  • options.sh (部署脚本)

前期准备

描述: 假定你已经按照我前面几篇文章中安装好基础环境。

(0) HelloWorld - Tomcat 应用服务器

  • Step 1.基础镜像拉取于上传到私有仓库中

代码块
JavaScript
自动换行
复制代码
weiyigeek@weiyigeek-107:~/k8s/sonarqube$ docker tag tomcat:8.5.61-jdk8-corretto harbor.weiyigeek.top/devops/tomcat:8.5.61-jdk8-corretto
weiyigeek@weiyigeek-107:~/k8s/sonarqube$ docker push harbor.weiyigeek.top/devops/tomcat:8.5.61-jdk8-corretto
The push refers to repository [harbor.weiyigeek.top/devops/tomcat]
f13244e4918b: Pushed
536f15f78828: Pushed
1c98b6d16fad: Pushed
fdef502bf282: Pushed
cee8d35c645b: Pushed
8.5.61-jdk8-corretto: digest: sha256:110d7391739e868daf8c1bdd03fcb7ffe9eaf2b134768b9162e2cd47d58f7255 size: 1368
复制成功

  • Step 2.资源清单文件

代码块
JavaScript
自动换行
复制代码
cat > HelloWorld.yaml <<'END'
apiVersion: v1
kind: Service
metadata:
  name: deploy-maven-svc
spec:
  type: NodePort         # Service 类型
  selector:
    app: java-maven       # 【注意】与deployment资源控制器创建的Pod标签进行绑定;
    release: stabel       # Service 服务发现不能缺少Pod标签,有了Pod标签才能与之SVC对应
  ports:                  # 映射端口
  - name: http
    port: 8080              # cluster 访问端口
    targetPort: 8080        # Pod 容器内的服务端口
    nodePort: 30089
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: deploy-java-maven
spec:
  serviceName: "deploy-maven-svc"
  replicas: 3                # 副本数
  selector:                  # 选择器
    matchLabels:
      app: java-maven   # 匹配的Pod标签非常重要
      release: stabel
  template:
    metadata:
      labels:
        app: java-maven      # 模板标签
        release: stabel
    spec:
      volumes:                # 关键点
      - name: webapps         # 卷名称
        hostPath:             # 采用hostPath卷
          type: DirectoryOrCreate   # 卷类型DirectoryOrCreate: 如果子节点上没有该目录便会进行创建
          path: /nfsdisk-31/test/  # 各主机节点上已存在的目录此处是NFS共享
      - name: timezone     # 容器时区设置
        hostPath:
          path: /usr/share/zoneinfo/Asia/Shanghai
      containers:
      - name: java-maven
        image: harbor.weiyigeek.top/devops/tomcat:8.5.61-jdk8-corretto  # 拉取的镜像
        imagePullPolicy: IfNotPresent
        command: ["bash","-c","cp /tmp/${APPNAME} /usr/local/tomcat/webapps/ROOT.war && catalina.sh run"]
        env:
          - name: APPNAME
            value: HelloWorld-v1.40.war
        ports:
        - name: http         # 此端口在服务中的名称
          containerPort: 8080  # 容器暴露的端口
        volumeMounts:        # 挂载指定卷目录
        - name: webapps      # Tomcat 应用目录
          mountPath: /tmp/
        - name: logs         # Tomcat 日志目录
          mountPath: /usr/local/tomcat/logs
        - name: timezone     # 镜像时区设置
          mountPath: /usr/share/zoneinfo/Asia/Shanghai
  volumeClaimTemplates:      # 卷的体积要求模板此处采用StorageClass存储类主要针对于应用日志的存储;
  - metadata:                # 根据模板自动创建PV与PVC并且进行一一对应绑定;
      name: logs
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: managed-nfs-storage # StorageClass存储类
      resources:
        requests:
          storage: 1Gi
END
复制成功

(1) Nginx - Web服务器

  • Step 1.Nginx 配置文件

代码块
JavaScript
自动换行
复制代码
root@468dd7df372e:/# grep -v "#" /etc/nginx/conf.d/default.conf
server {
    listen       80;
    listen  [::]:80;
    server_name  rel.weiyigeek.top;
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
复制成功

  • Step 2.将Jenkins构建的Release进行上传在Nginx 中进行归档所以需要资源清单进行部署;

代码块
JavaScript
自动换行
复制代码
cat > jenkins-release-nginx.yaml <<'END'
# apiVersion: extensions/v1beta1
# kind: Ingress
# metadata:
#   annotations:
#     kubernetes.io/ingress.class: nginx
#   labels:
#     app: release
#   name: jenkins-release
#   namespace: devops
# spec:
#   rules:
#   - host: rel.weiyigeek.top
#     http:
#       paths:
#       - backend:
#           serviceName: release-svc
#           servicePort: 80
#         path: /
# ---
apiVersion: v1 
kind: Service 
metadata:
  name: release-svc
  namespace: devops
spec:
  type: NodePort
  selector:
    app: release
  ports:
  - name: http
    port: 80
    targetPort: 80
    nodePort: 30003
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginxconf
  namespace: devops
data:
  default.conf: |
    server {
      listen       80;
      listen  [::]:80;
      server_name  rel.weiyigeek.top;
      location / {
          root   /usr/share/nginx/html;
          index  index.html index.htm;
      }
      error_page   500 502 503 504  /50x.html;
      location = /50x.html {
          root   /usr/share/nginx/html;
      }
    }

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: release-deployment
  namespace: devops
  labels:
    app: release
spec:
  replicas: 1
  selector:
    matchLabels:
      app: release
  template:
    metadata:
      labels:
        app: release
    spec:
      containers:
      - name: release-nginx
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort:  80
        volumeMounts:
          - name: conf
            mountPath: /etc/nginx/conf.d/
            readOnly: false
          - name: data
            mountPath: /usr/share/nginx/html/
            readOnly: false
      volumes:
      - name: conf
        configMap:
          name: nginxconf
          defaultMode: 0755
      - name: data
        hostPath: 
          type: DirectoryOrCreate
          path: /nfsdisk-31/release
END
复制成功

  • Step 3.访问验证

代码块
JavaScript
自动换行
复制代码
# 1.测试文件
~$ cd /nfsdisk-31/release && touch index.html
  # echo "Hello World, Boy or Girl;" > index.html

# 2.deployments状态查询与SVC
~$ kubectl get deployments.apps -n devops  release-deployment -o wide
  # NAME                 READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS      IMAGES         SELECTOR
  # release-deployment   1/1     1            1           4d11h   release-nginx   nginx:latest   app=release
~$ kubectl get svc -n devops  release-svc
  # NAME          TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
  # release-svc   NodePort   10.106.107.201   <none>        80:30003/TCP   4d11h

# 3.访问验证
~/k8s/nginx$ curl rel.weiyigeek.top/index.html
~/k8s/nginx$ curl http://192.168.12.108:30003/index.html
  # Hello World, Boy or Girl;
复制成功

(2) Jenkins Pipeline 脚本

Jenkins 流水线脚本如下:

代码块
JavaScript
自动换行
复制代码
// @ ssh 登陆链接函数
def getSSH() {
  def remote = [:]
  remote.name = "K8S#${DEPLOY_HOST}"
  remote.user = "${DEPLOY_USER}"
  remote.host = "${DEPLOY_HOST}"
  remote.port = 20211
  remote.allowAnyHosts = true   
  withCredentials([string(credentialsId: "${DEPLOY_CREDENTIALSID}", variable: 'sshpassword')]) {
    remote.password = sshpassword
  }
  return remote
}

// @ Secure 密钥获取
def getSecure(Ticket_Token) {
  def token = ""
  withCredentials([string(credentialsId: Ticket_Token, variable: 'secure_token')]) {
    token = secure_token
  }
  println token
  return token
}

pipeline {
  /* Job 工作节点的绑定 */
  agent {
    // 1) 采用K8s动态生成Jnlp的Jenkins 节点
    kubernetes {
      cloud 'kubernetes'
      namespace 'devops'
      inheritFrom 'jenkins-slave'
      workingDir '/home/jenkins/agent'
      // yamlFile 'KubernetesPod.yaml' // 指定 yaml 资源清单其内容包含在yaml对象中
      yaml """\
apiVersion:
kind: Pod
metadata:
  labels:
    jenkins: "slave"
    jenkins/label: 'k8s-slave'
spec:
  containers:
  - name: 'jnlp'
    image: 'harbor.weiyigeek.top/devops/jenkins-jnlp:3.13.6-alpine'
    imagePullPolicy: 'IfNotPresent'                                    
    command: ["/bin/sh","-c","/usr/local/bin/jenkins-agent.sh && cat"] 
    tty: true
    volumeMounts:
    - mountPath: "/home/jenkins/.m2"
      name: "volume-0"
    - mountPath: "/var/run/docker.sock"
      name: "volume-1"
    """.stripIndent()
    }
  }

  /* 全局选项 */
  options {
    // Gitlab 相关设置(连接凭据与Pipeline步骤)
    gitLabConnection('Gitlab-weiyigeek')
    gitlabBuilds(builds: ['代码拉取', '代码检测', '项目构建','项目部署','成品归档','消息通知'])
    
    // 全局超时设置 30分钟
    timeout(time: 30, unit: 'MINUTES')   
  }

  /* Job 自动触发构建参数 */
  triggers {
    // gitlab 项目进行 git Push或者是Comment 触发构建
    gitlab(
      triggerOnPush: false, 
      triggerOnMergeRequest: true, 
      triggerOpenMergeRequestOnPush: "never", 
      noteRegex: "jenkins build",
      branchFilterType: 'All'
    )
  } 
 
  /* 
  全局参数, 在shell可通过变量名访问,而在script pipeline脚本中通过params.参数名称访问.  ${params.RELEASE_VERSION}
  */
  parameters {
    string(name: 'RELEASE_VERSION', defaultValue: "master", description: 'Message: 请选择部署的Tags版本?',trim: 'True')
	  choice(name: 'PREJECT_OPERATION', choices: ['deploy', 'rollback', 'redeploy'], description: 'Message: 选择项目操作方式?')
    choice(name: 'SONARQUBE', choices: ['False','True'],description: 'Message: 是否进行代码质量检测?')
    choice(name: 'GITLAB_RELEASE', choices: ['False','True'],description: 'Message: 是否进行编译成品发布到Gitlab开发项目Release中?')
  }
  

  /* 
  环境变量设置, 在shell可通过变量名访问,而在script pipeline脚本中通过env.参数名称访问.  ${env.GITLAB_URL} 
  */
  environment {
    // # GitLab 仓库相关信息(修改点1)
    GITLAB_URL = 'ssh://git@gitlab.weiyigeek.top:2222/weiyigeek/helloworld.git'  
    GITLAB_PROJECTID = '45'
    GITLAB_PUB = '5e2f46d9-6725-4399-847f-dafb3f29d0ce'
    GITLAB_TOKEN = 'ce228573-ad3d-40e8-a3bf-b9de510080db'
    GITLAB_REL = "http://gitlab.weiyigeek.top/api/v4/projects/${GITLAB_PROJECTID}/releases"
    GITLAB_DOWNLOAD = "http://192.168.12.107:30003/${JOB_NAME}/${APP_NAME}"
    
    // # 应用名称&后缀以及集成结果(修改点2)
    APP_NAME = "${JOB_NAME}-${RELEASE_VERSION}${APP_SUFFIX}"
    APP_SUFFIX = ".war"
    APP_FILE = 'target/*.war'
    APP_DIR = '/nfsdisk-31/test/'
    // # Kubernetes 部署应用相关名称空间以及标签和SVC地址
    K8S_NAMESPACE = "default"
    K8S_SELECTOR = "app=java-maven"
    KS8_ADDRESS = "http://192.168.12.107:30089"

    // # 应用部署主机(修改点3)
    def SERVER = ''
    DEPLOY_CREDENTIALSID = "b846141f-55c2-4b46-98a7-29b9e76af3cc" //12.107
    DEPLOY_HOST = "192.168.12.107"
    DEPLOY_USER = "root"
    DEPLOY_PORT = 20211

    // # 代码质量检测项目名称和检测反馈超时时间
    SONARQUBE_CREDENTIALSID = "18d24c8f-9772-4b3e-934c-f80ed0e96cde"
    SONARQUBE_PROJECTKEY = "${JOB_NAME}"
    SONARQUBE_TIMEOUT = '10';

    // # 企业微信WebHookURL 1150-曾老师
    QYWX_WEBHOOK = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=7d7f46fa-28c6-40b3-9c45-de243ec51150'
  }


  /* Job 步骤状态 */
  stages {
    stage ('代码拉取') {
      steps {
        // (1) 代码超时时间设置 5 分钟
        timeout(time: 5, unit: 'MINUTES') {
          script {
            try {
              // (1) 动态SSH公与私钥导入到我们动态运行的工作节点之中。
              withCredentials([
                sshUserPrivateKey(credentialsId: '1d4e61cb-5d59-4da8-813b-a5fb345770c9', keyFileVariable: 'keyFile', passphraseVariable: 'pass', usernameVariable: 'user'),
                string(credentialsId: '79a78423-e539-4b1f-a872-f0cf1dd3f9fa', variable: 'keyPub')
              ]) {
              // SSH Private Key
              writeFile file: 'private.key', text: keyFile
              sh "cp \$(cat private.key) ~/.ssh/id_ed25519"

              // SSH Public
              writeFile file: 'private.pub', text: keyPub
              sh "echo \$(cat private.pub) | base64 -d > ~/.ssh/id_ed25519.pub"
              }  

              // (2) Gitlab 项目拉取
              checkout([$class: 'GitSCM', branches: [[name: "${params.RELEASE_VERSION}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${env.GITLAB_PUB}", url: "${env.GITLAB_URL}"]]])
              // (3) 流水线状态同步
              updateGitlabCommitStatus name: '代码拉取', state: 'success'
            } catch(Exception err) {
              updateGitlabCommitStatus name: '代码拉取', state: 'failed'
              // (4) 显示错误信息但还是会进行下一阶段操作(err.toString()  == unstable '拉取代码失败')
              echo err.toString()  
              // 将会停止构建
              error "[-Error] : 代码拉取失败\n [-Msg] : ${err.getMessage()} "
            } 
            
            // (5) 显示当前版本Commit信息以及可用Tag版本
            // def INFO = sh label: 'release_info',returnStdout: true, returnStatus: true, script: """\
            // echo "Git Commit:" && \
            // git branch && \
            // git log --oneline  | grep "${params.RELEASE_VERSION}" && \
            // echo "Available Tag Version:" && \
            // git tag -l --column;
            // """
            // """
            // git tag -l --column | tr -d ' '  > tag ;
            // export a="\$(sed "s#v#, v#g" tag)'";
            // echo [\${a#,*}]
            // """
            
            // (6) 输出预定变量值
            echo "[-Info] : RELEASE_VERSION: ${params.RELEASE_VERSION} , PREJECT_OPERATION: ${params.PREJECT_OPERATION}, SONARQUBE: ${params.SONARQUBE}, GITLAB_RELEASE: ${params.GITLAB_RELEASE}"
            
            // (7) 切换数据库版本(Jenkinsfile中使用)
            //sh label: 'checkout_version', script: '[ -n "${RELEASE_VERSION}" ] &&  git checkout ${RELEASE_VERSION} || { echo -e "切换至指定的tag的版本 tag:${RELEASE_VERSION} 不存在或为空,请检查输入的tag!" && exit 1; }'
          }
        }
    }
  }
   
stage ('代码检测') {  
  // (8) 判断是否进行代码质量检测(满足则继续否则跳过)
  when {
    // 如果项目是回滚操作则不进行代码质量检测,然后当SONARQUBE参数为True时进行质量检测,否则不进行。
    expression { return params.PREJECT_OPERATION != "rollback" }
    anyOf {
      environment name: 'SONARQUBE', value: 'True'
    }
  }
  steps {
    script {
    // (9) Tool 后的名称一定是对应全局工具配置中sonar扫描器的名字进行代码质量检测
      def sonarqubeScanner = tool 'Sonar-Scanner';
      withSonarQubeEnv(credentialsId: "${SONARQUBE_CREDENTIALSID}") {
        // 注意: 可以将sonarQube的属性定义在这里,也可以定义在项目文件中然后在这里引用配置文件
        sh "${sonarqubeScanner}/bin/sonar-scanner " + 
              "-Dsonar.projectName=${JOB_NAME} " + 
              "-Dsonar.projectKey=${SONARQUBE_PROJECTKEY} " + 
              "-Dsonar.projectVersion=${RELEASE_VERSION} " + 
              "-Dsonar.sourceEncoding=utf8  " + 
              "-Dsonar.sources=.  " + 
              "-Dsonar.language=java  " + 
              "-Dsonar.java.binaries=."

        // (10) 暂停xxs等待sonarqube扫描反馈,根据实际时间进行调整; 
        // NANOSECONDS SECONDS
        sleep time: "${SONARQUBE_TIMEOUT}", unit: 'SECONDS'
        // sh "sleep ${SONARQUBE_TIMEOUT}"
      }
  
      try {
        // 方式1
        // timeout(1) {
        //     def qg = waitForQualityGate() 
        //     if (qg.status != 'OK') {
        //         error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status}"
        //     } else {
        //         echo "successful"
        //     }
        // } 

        // 方式2 (根据实际时间进行调整)
          timeout(time: 1, unit: 'MINUTES') {
            waitForQualityGate abortPipeline: true
          }
          updateGitlabCommitStatus name: '代码检测', state: 'success'
        } catch(Exception err) {
          updateGitlabCommitStatus name: '代码检测', state: 'failed'
          unstable '代码检测失败'
        }
      }
    }
  }
    
  // (11) 项目构建打包处理
  stage ("项目构建") {
    // 当操作为 PREJECT_OPERATION 为回滚(rollback) 时不进行构建打包
    when {
      expression { return params.PREJECT_OPERATION != "rollback" }
    }
    steps {
      script {
        try {
          // Maven 配置依赖(动态配置)
          sh script: "curl -s -o settings.xml http://gitlab.weiyigeek.top/weiyigeek/helloworld/-/raw/master/settings.xml"
          sh script: '/usr/local/maven/bin/mvn -s "settings.xml" clean install -Dmaven.test.skip=true'
          // 补充还可以采用此种方式进行更新流水线状态
          // gitlabCommitStatus(connection: gitLabConnection('Gitlab 用户认证凭据'),name: "项目构建") { ... }   
          updateGitlabCommitStatus name: '项目构建', state: 'success'
        } catch(Exception err) {
          updateGitlabCommitStatus name: '项目构建', state: 'failed'
          unstable '项目构建失败' 
        }
      }
    }
  }

  // (12) 进行开发测试部署
  stage ("项目部署") {
    steps {
      script {
        // ssh 连接函数调用
        SERVER = getSSH() 
          try {
            // 项目部署所用得资源清单以及
            sh script: """\
            curl -s http://gitlab.weiyigeek.top/weiyigeek/helloworld/-/raw/master/${JOB_NAME}.yaml | sed s#__APPNAME__#${APP_NAME}#g > ./${JOB_NAME}.yaml && \
            wget http://gitlab.weiyigeek.top/weiyigeek/helloworld/-/raw/master/options.sh -O options.sh
            """
            // ssh 插件调用
            sshPut remote: SERVER, from: "./${JOB_NAME}.yaml" , into: "${APP_DIR}${JOB_NAME}.yaml"
            // 部署脚本
            sh script: "chmod +x options.sh && sh -x ./options.sh"
            updateGitlabCommitStatus name: '测试部署', state: 'success'
          }catch(Exception err) {
            updateGitlabCommitStatus name: '测试部署', state: 'failed'
            unstable '部署失败' // echo err.toString() 
            error "[-Error] : 项目部署失败 \n[-Msg] : ${err.getMessage()} "
          }
      }
    }
  }

  // (13) 进行成品归档 archiveArtifacts 
  stage('成品归档') {
    when {
      // 当操作为 PREJECT_OPERATION 为rollback(回滚)时 且当 params.GITLAB_RELEASE 为False 时不进行成品归档
      expression { return params.PREJECT_OPERATION != "rollback" && params.GITLAB_RELEASE }
    }
    steps {
      script {
        if ( ! params.RELEASE_VERSION.contains("master") ) {
          try {
            // Jenkins 归档
            archiveArtifacts artifacts: "${APP_FILE}", onlyIfSuccessful: true

            // Gitlab Token 用于 Gitlab - Release 版本分发
            def gitlab_token = getSecure("${GITLAB_TOKEN}")

            // 判断 Gitlab - Release 版本是否存在
            def gitlab_release = sh(returnStatus: true, script: "sudo apk add jq && curl -s --header 'PRIVATE-TOKEN: ${gitlab_token}' ${GITLAB_REL} | jq .[].tag_name | grep -c '${params.RELEASE_VERSION}'")
            
            // 上传得 Release Nginx web 根路径
            def gitlab_release_dir = "/nfsdisk-31/release/${JOB_NAME}/"
          
            echo gitlab_token + " --- " + gitlab_release + "====="  + gitlab_release_dir + "---" 

            // 根据 gitlab_release 变量判断是否进行release-cli 发布指定版本得 `Release`
            if ( gitlab_release != 0 ) {
              sshCommand remote: SERVER, command: "mkdir -p ${gitlab_release_dir}"
              sshPut remote: SERVER, from: "./${APP_NAME}" ,into: "${gitlab_release_dir}${APP_NAME}"
              sh script: """
              /usr/local/bin/release-cli --server-url "http://gitlab.weiyigeek.top" --private-token "${gitlab_token}" \
              --project-id ${GITLAB_PROJECTID}  create \
              --name "${JOB_NAME}-${params.RELEASE_VERSION}" \
              --tag-name "${params.RELEASE_VERSION}" \
              --description "此 ${JOB_NAME} 项目 ${params.RELEASE_VERSION}版本 来自 Jenkins Build 编译构建上传。" \
              --assets-link '{"name": "${APP_NAME}","url":"${GITLAB_DOWNLOAD}","link_type":"other"}'
              """
            } else {
              echo "[-Tips] :  Gitlab Release 中已存在 ${params.GITLAB_RELEASE} 版本, 将不会提交该Release到Gitlab中"
            }
            updateGitlabCommitStatus name: '成品归档', state: 'success'
          } catch (Exception err) {
            updateGitlabCommitStatus name: '成品归档', state: 'failed'
            echo err.toString()  
          }
        } else {
            echo "# 版本不能为 ${param.RELEASE_VERSION} , 将不会进行归档!"
        }
      }
    }
  }
}
  
  // (12) POST阶段当所有任务执行后触发进行工作空间的清理以及消息通知;
  post {
    // 无论如何,我已经完成了 无论成功与否都将执行
    always {
      script {
        // 企业微信消息通知
        def GIT_COMMIT=sh returnStdout: true, script: "git show --oneline --ignore-all-space --text | head -n 1"
        qyWechatNotification aboutSend: true, failNotify: true, failSend: true, mentionedMobile: '', startBuild: true, successSend: true, unstableSend: true, webhookUrl: "${env.QYWX_WEBHOOK}"
        sh """\
        curl -s ${env.QYWX_WEBHOOK} \
        -H 'Content-Type: application/json' \
        -d '
        {
          "msgtype": "text",
          "text": {
              "content": "项目: ${env.SONARQUBE_PROJECTKEY}, 版本: ${params.RELEASE_VERSION}, 操作: ${params.PREJECT_OPERATION} , 信息: ${GIT_COMMIT}, Gitlab-Release 发布: ${params.GITLAB_RELEASE}, 部署地址: ${env.KS8_ADDRESS}"
          }
        }'
      """
      }
      deleteDir()  /* clean up our workspace */
    }
    success {
      echo '[-Info] : I succeeeded! - 任务成功'
      updateGitlabCommitStatus name: '消息通知', state: 'success'
    }
    unstable {
      echo '[-Info] : I am unstable - 不稳定 :/'
    }
    failure {
      echo '[-Info] : I failed - 任务失败 :( '
      updateGitlabCommitStatus name: '消息通知', state: 'failed'
    }
    changed {
      echo '[-Info] : Things were different before - 与以前的情况不一样...'
    }
  }
}
复制成功

(3) Shell 部署脚本

代码块
JavaScript
自动换行
复制代码
#!/bin/sh
# // 基础变量
export K8sMaster="${DEPLOY_USER}@${DEPLOY_HOST}"
export AppName="${APP_FILE##*/}"
export FindAppName=$(find ${WORKSPACE} -maxdepth 2 -type f -name ${AppName})

# // 若操作值不为rollback则执行该判断代码块中的内容。
if [${PREJECT_OPERATION} != "rollback" ];then
  if [ "${FindAppName}" != "" ]; then
    echo "[-Msg]: ${FindAppName} ./${APP_NAME} Copying...."
    cp -rf ${FindAppName} ./${APP_NAME}
  else
    echo "[-Error]: FindAppName param error!"
    exit 1
  fi
fi

# // 判断应用文件是否存在与共享目录中
function judge () {
  export ExistResult=$(ssh -p ${DEPLOY_PORT} ${K8sMaster} "ls ${APP_DIR} | grep ${RELEASE_VERSION} | wc -l")
}

# // 部署操作函数
function deploy () {
  if [[ ${ExistResult} -eq 0 || "${RELEASE_VERSION}" == "master" ]];then
    scp -P ${DEPLOY_PORT} "${APP_NAME}" ${K8sMaster}:${APP_DIR}${APP_NAME}
  else
    echo "[-Msg]: /${APP_NAME} current Deploy....."
  fi

  # 部署脚本
  if [[ "${RELEASE_VERSION}" == "master" ]];then
    ssh -p ${DEPLOY_PORT} ${K8sMaster} "kubectl -n ${K8S_NAMESPACE} delete pod -l ${K8S_SELECTOR}"
  else
    ssh -p ${DEPLOY_PORT} ${K8sMaster} "kubectl apply -f ${APP_DIR}${JOB_NAME}.yaml"
  fi
}

# // 回退操作函数
function rollback () {
  if [[ ${ExistResult} -ne 0 ]];then
    ssh -p ${DEPLOY_PORT} ${K8sMaster} "kubectl apply -f ${APP_DIR}${JOB_NAME}.yaml"
  else
    echo "[-Eroor]: /${APP_NAME} version not exsit....."
    exit 1
  fi
}

# // 重部署操作函数
function redeploy () {
  # 如果是以前部署过则删除以前部署的项目目录,否则重新部署;
  if [[ "v${GIT_COMMIT}" = "v${GIT_PREVIOUS_SUCCESSFUL_COMMIT}" || ${ExistResult} -gt 0 ]];then
    echo -e "[-Message]:  ${RELEASE_VERSION} Version Exsit !"
    ssh -p ${DEPLOY_PORT} ${K8sMaster} "find ${APP_DIR} -d -maxdepth 1 -type f -name ${APP_NAME}"
    scp -P ${DEPLOY_PORT} "${APP_NAME}" ${K8sMaster}:${APP_DIR}${APP_NAME}
  fi

  # 触发重新部署脚本
  deploy
}

# // 判断是否存在历史版本
judge

# 主操作入口判断
case ${PREJECT_OPERATION} in
"deploy")
  echo "1.deploy"
  deploy
;;
"rollback")
  echo "2.rollback"
  rollback
;;
"redeploy")
  echo "3.redeploy"
  redeploy
;;
*)
  echo "[-Error] : ${PREJECT_OPERATION} Param Error not in deploy/rollback/redeploy !"
  exit 1
;;
esac
复制成功

(4) 效果预览

  • Step 1.在BlueOcean进行构建部署参数选择。 描述: 我们可以进行初次部署回滚以及重新构建部署,还包括代码质量检测以及成品自动归档的选择控制。

  • Step 2.在K8s集群中进行查看Pod迭代变化。

代码块
JavaScript
自动换行
复制代码
/nfsdisk-31/test# kubectl get pod --watch
  # NAME                                      READY   STATUS        RESTARTS   AGE
  # deploy-java-maven-0                       1/1     Running       0          83s
  # deploy-java-maven-1                       1/1     Running       0          80s
  # deploy-java-maven-2                       1/1     Terminating   0          76s
  # deploy-java-maven-2                       0/1     Terminating   0          106s
  # deploy-java-maven-2                       0/1     Pending       0          0s
  # deploy-java-maven-2                       0/1     ContainerCreating   0          0s
  # deploy-java-maven-2                       1/1     Running             0          2s
  # deploy-java-maven-1                       1/1     Terminating         0          112s
  # deploy-java-maven-1                       0/1     Terminating         0          2m26s
  # deploy-java-maven-1                       0/1     Pending             0          0s
  # deploy-java-maven-1                       0/1     ContainerCreating   0          0s
  # deploy-java-maven-1                       1/1     Running             0          3s
  # deploy-java-maven-0                       1/1     Terminating         0          2m32s
复制成功

可以看到我们的项目自动从gitlab仓库中拉取helloworld项目,并根据Job前的输入进行项目MAVEN 构建,生成的war包成品归档,然后将成品推送到K8S集群中进行更新,更新完成后将该war包上传到gitlab release 页面中,以供其它环境拉取使用和备份。

至此本章实践完毕!


欢迎各位志同道合的朋友一起学习交流,如文章有误请在下方留下您宝贵的经验知识,个人邮箱地址【master#weiyigeek.top】


更多文章来源: https://weiyigeek.top 【WeiyiGeek Blog - 为了能到远方,脚下的每一步都不能少】

如果你觉得这个专栏还不错的,请给这篇专栏点个赞、投个币、收个藏、关个注,这将对我有很大帮助!