# K8S 基于 Jenkins 实现 SpringCloud 微服务 CI 与 CD 实践(三)

CI 阶段:开发人员 -> 提交代码 ->gitlab 仓库 ->Jenkins/CI 抓取代码 -> 漏洞扫描 -> 编译 -> 构建镜像 -> 推送 Harbor-> 部署应用至 K8S 测试环境;
CD 阶段:Jenkins/CD-> 拉取 Harbor 仓库对应项目镜像 -> 部署应用至 K8S 测试环境;

1.jpg

# 一、环境准备

准备好 Java 代码、Dockerfile、deploy.yaml 资源清单文件 提交到 Gitlab 服务器;

$ git config --global user.name "oldxu"
$ git config --global user.email 
$ git init
$ git add .
$ git commit -m "first commit"
$ git remote remove origin
$ git remote add origin http://gitlab.hmallleasing.com/root/ruoyi-cloud-cicd.git
$ git remote -v
origin  http://gitlab.hmallleasing.com/root/ruoyi-cloud-cicd.gi (fetch)
origin  http://gitlab.hmallleasing.com/root/ruoyi-cloud-cicd.gi (push)
$ git push -u origin "master"

# 二、CI 阶段

CI 阶段分为以下 6 步:
1、获取代码
2、漏洞扫描
3、检测漏洞扫描结果,如果正常则继续、否则就终止
4、使用 maven 进行编译,打包
5、制作 Docker 镜像、推送到 Harbor 仓库 (Dockerfile 文件)
6、交付应用到 K8S

# 2.1 编写 Pipeline 框架
pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          volumes:
          - name: data
            nfs:
              server: 192.168.1.75
              path: /data/nfs/maven
          - name: dockersocket
            hostPath:
              path: /run/docker.sock
          containers:
          - name: maven
            image: s.hmallleasing.com/base/maven:3.8.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: data
              mountPath: /root/.m2
          - name: nodejs
            image: s.hmallleasing.com/base/nodejs:14.20
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: sonar
            image: s.hmallleasing.com/base/sonar-scanner:2.3.0
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: docker
            image: s.hmallleasing.com/base/docker:20.10
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: dockersocket
              mountPath: /run/docker.sock
          - name: kubectl
            image: s.hmallleasing.com/base/kubectl:1.32.3
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    }
  }
  stages {
    stage('获取代码') {
      steps {
        container('maven') {
		  sh 'echo "获取代码"'
        }
      }
    }
    stage('代码扫描') {
      steps {
        container('sonar') {
		  sh 'echo "代码扫描"'
        }
      }
    }		
    stage('检查扫描结果') {
      steps {
        container('sonar') {
		  sh 'echo "Code Result Check"'
        }
      }
    }		    
    stage('代码编译') {
      steps {
        container('maven') {
		  sh 'echo "代码编译"'
        }
      }
    }	    
    stage('制作Docker镜像') {
      steps {
        container('docker') {
		  sh 'echo "制作Docker镜像"'
        }
      }
    }	
    stage('交付应用至K8S') {
      steps {
        container('kubectl') {
		  sh 'echo "交付应用至K8S"'
        }
      }
    }		
  }
}

1.png

# 2.2 获取代码

1、Jenkins 上配置 Gitlab 认证信息

系统管理 ->manage credentials-> 全局 ->Add Credential

2.png

2、SlavePod 访问 Gitlab

通过 Coredns 配置自定义域名解析,如果 sgitlab 解析至公网,无需配置此步骤;

[root@k8s-master01 ~]# kubectl edit cm coredns -n kube-system
...
        ready
        hosts {
           192.168.1.74 gitlab.hmallleasing.com
           fallthrough
        }
        kubernetes cluster.local in-addr.arpa ip6.arpa {
...

测试解析是否生效;

[root@k8s-master01 ~]# kubectl exec -it gitlab-0 -n ops -- /bin/bash
root@gitlab-0:/# ping gitlab.hmallleasing.com
PING gitlab.hmallleasing.com (192.168.1.74): 56 data bytes
64 bytes from 192.168.1.74: seq=0 ttl=63 time=0.381 ms
64 bytes from 192.168.1.74: seq=1 ttl=63 time=0.203 ms

3、生成流水线脚本获取代码

通过流水线语法 -> 片段生成器 -> 生成流水线脚本,生成 Pipeline 流水线

1.png

2.png

4、测试获取代码 Pipeline

pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          volumes:
          - name: data
            nfs:
              server: 192.168.1.75
              path: /data/nfs/maven
          - name: dockersocket
            hostPath:
              path: /run/docker.sock
          containers:
          - name: maven
            image: s.hmallleasing.com/base/maven:3.8.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: data
              mountPath: /root/.m2
          - name: nodejs
            image: s.hmallleasing.com/base/nodejs:14.20
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: sonar
            image: s.hmallleasing.com/base/sonar-scanner:2.3.0
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: docker
            image: s.hmallleasing.com/base/docker:20.10
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: dockersocket
              mountPath: /run/docker.sock
          - name: kubectl
            image: s.hmallleasing.com/base/kubectl:1.32.3
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    }
  }
  
  environment{
    //定义git变量
    Git_Id = "gitlab-root-token"
    Git_Url = "http://gitlab.hmallleasing.com/root/ruoyi-cloud-cicd.git"
  }
  
  stages {
    stage('获取代码') {
      steps {
        container('maven') {
		  checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "${Git_Id}", url: "${Git_Url}"]])
		  sh 'pwd && ls' 		  
        }
      }
    }
    stage('代码扫描') {
      steps {
        container('sonar') {
		  sh 'echo "代码扫描"'
        }
      }
    }		
    stage('检查扫描结果') {
      steps {
        container('sonar') {
		  sh 'echo "Code Result Check"'
        }
      }
    }		    
    stage('代码编译') {
      steps {
        container('maven') {
		  sh 'echo "代码编译"'
        }
      }
    }	    
    stage('制作Docker镜像') {
      steps {
        container('docker') {
		  sh 'echo "制作Docker镜像"'
        }
      }
    }	
    stage('交付应用至K8S') {
      steps {
        container('kubectl') {
		  sh 'echo "交付应用至K8S"'
        }
      }
    }		
  }
}

1.png

# 2.3 Sonarqube 漏洞扫描

1、配置 dns 解析(如果 sonarqube 解析至公网,无需配置此步骤)
slavePod 需要访问 sonarqube 服务端,sonarqube 服务端需要回调 jenkins,获取扫描结果 。

[root@k8s-master01 ~]# kubectl edit cm -n kube-system coredns
...
        ready
        hosts {
           192.168.1.74 gitlab.hmallleasing.com        #gitalb
           192.168.1.74 sonar.hmallleasing.com         #sonar
           192.168.1.74 jenkins.hmallleasing.com       #jenkins
           fallthrough
        }
        kubernetes cluster.local in-addr.arpa ip6.arpa {
...

测试解析是否生效;

[root@k8s-master01 ~]# kubectl exec -it gitlab-0 -n ops -- /bin/bash
root@gitlab-0:/# ping sonar.hmallleasing.com
PING sonar.hmallleasing.com (192.168.1.74): 56 data bytes
64 bytes from 192.168.1.74: seq=0 ttl=63 time=0.187 ms
64 bytes from 192.168.1.74: seq=1 ttl=63 time=0.202 ms
root@gitlab-0:/# ping jenkins.hmallleasing.com
PING jenkins.hmallleasing.com (192.168.1.74): 56 data bytes
64 bytes from 192.168.1.74: seq=0 ttl=63 time=0.189 ms
64 bytes from 192.168.1.74: seq=1 ttl=63 time=0.202 ms

2、Jenkins 集成 Sonarqube

生成 sonarqube 令牌:用户 -> 我的账号 -> 安全

保存令牌信息:squ_6e57e496a22ebdf0723c3683bd489df57f967f49

1.png

配置 sonarqube 认证信息:系统管理 ->manage credentials-> 全局 ->Add Credential

1.png

Jenkins 集成 Sonarqube:系统管理 -> 系统配置 ->SonarQube servers->Add SonarQube

2.png

3、获取 sonar-scanner 扫描命令(此步骤不用执行)

项目 -> 手工 -> 显示名

3.png

4.png

5.png

6.png

#获取 sonar-scanner 扫描命令,需要在项目目录下进行扫描

sonar-scanner \
  -Dsonar.projectKey=springboot \
  -Dsonar.sources=. \
  -Dsonar.host.url=http://sonar.hmallleasing.com \
  -Dsonar.login=squ_6e57e496a22ebdf0723c3683bd489df57f967f49

4、测试代码扫描 Pipeline

pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          volumes:
          - name: data
            nfs:
              server: 192.168.1.75
              path: /data/nfs/maven
          - name: dockersocket
            hostPath:
              path: /run/docker.sock
          containers:
          - name: maven
            image: s.hmallleasing.com/base/maven:3.8.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: data
              mountPath: /root/.m2
          - name: nodejs
            image: s.hmallleasing.com/base/nodejs:14.20
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: sonar
            image: s.hmallleasing.com/base/sonar-scanner:2.3.0
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: docker
            image: s.hmallleasing.com/base/docker:20.10
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: dockersocket
              mountPath: /run/docker.sock
          - name: kubectl
            image: s.hmallleasing.com/base/kubectl:1.32.3
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    }
  }
  
  environment{
    //定义git变量
    Git_Id = "gitlab-root-token"
    Git_Url = "http://gitlab.hmallleasing.com/root/ruoyi-cloud-cicd.git"
  }
  
  stages {
    stage('获取代码') {
      steps {
        container('maven') {
		  checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "${Git_Id}", url: "${Git_Url}"]])
		  sh 'pwd && ls' 		  
        }
      }
    }
    stage('代码扫描'){
      parallel {
        stage('检测Gateway'){
          environment {
            AppName="ruoyi-gateway"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
              sh 'cd $(find ./ -type d -name "${AppName}") && \
                    sonar-scanner \
                      -Dsonar.projectKey=${AppName} \
                      -Dsonar.java.binaries=src \
                      -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Auth'){
          environment {
            AppName="ruoyi-auth"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测System'){
          environment {
            AppName="ruoyi-system"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && pwd && ls && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Monitor'){
          environment {
            AppName="ruoyi-monitor"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && pwd && ls &&  \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测UI'){
          environment {
            AppName="ruoyi-ui"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar') {
                sh 'cd $(find ./ -type d -name "${AppName}") && pwd && ls &&  \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
      }       //parallel end
    }        //stage end		
	
    stage('检查扫描结果') {
      steps {
        container('sonar') {
		  sh 'echo "Code Result Check"'
        }
      }
    }		    
    stage('代码编译') {
      steps {
        container('maven') {
		  sh 'echo "代码编译"'
        }
      }
    }	    
    stage('制作Docker镜像') {
      steps {
        container('docker') {
		  sh 'echo "制作Docker镜像"'
        }
      }
    }	
    stage('交付应用至K8S') {
      steps {
        container('kubectl') {
		  sh 'echo "交付应用至K8S"'
        }
      }
    }		
  }
}

1.png

2.png

5、此处遇到问题总结

由于上传数据太大,在 ingress 中 client_max_body_size 默认值为 1m , 该参数的作用是设置最大允许客户端请求体的大小,如果超过了此值,客户端会收到 413 状态码,意思是请求的实体太大。

3.png

处理方法:

#1. 针对单个 ingress
$ cat 03-sonarqube-ingress.yaml 
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: sonarqube-ingress
  namespace: ops
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "200m" 
spec:
  ingressClassName: "nginx"
  rules:
  - host: "sonar.hmallleasing.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: sonarqube-svc
            port:
              name: web
              
#2. 针对所有 ingress
$ kubectl edit configmap -n ingress-nginx ingress-nginx-controller
apiVersion: v1
data:
  proxy-body-size: "200m"
...
# 2.4 检查扫描结果

1、配置 sonarquber, 让其将检查结果通知 jiekins,如果正常则继续、否则就终止执行

4.png

5.png

URL:http://admin:talent@jenkins.hmallleasing.com/sonarqube-webhook
Jenkins 访问地址:jenkins.hamlllleasing.com
Jenkins 用户名:admin
Jenkins 密码:talent

2、检查扫描结果 pipeline

pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          volumes:
          - name: data
            nfs:
              server: 192.168.1.75
              path: /data/nfs/maven
          - name: dockersocket
            hostPath:
              path: /run/docker.sock
          containers:
          - name: maven
            image: s.hmallleasing.com/base/maven:3.8.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: data
              mountPath: /root/.m2
          - name: nodejs
            image: s.hmallleasing.com/base/nodejs:14.20
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: sonar
            image: s.hmallleasing.com/base/sonar-scanner:2.3.0
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: docker
            image: s.hmallleasing.com/base/docker:20.10
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: dockersocket
              mountPath: /run/docker.sock
          - name: kubectl
            image: s.hmallleasing.com/base/kubectl:1.32.3
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    }
  }
  
  environment{
    //定义git变量
    Git_Id = "gitlab-root-token"
    Git_Url = "http://gitlab.hmallleasing.com/root/ruoyi-cloud-cicd.git"
  }
  
  stages {
    stage('获取代码') {
      steps {
        container('maven') {
		  checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "${Git_Id}", url: "${Git_Url}"]])
		  sh 'pwd && ls' 		  
        }
      }
    }
    stage('代码扫描'){
      parallel {
        stage('检测Gateway'){
          environment {
            AppName="ruoyi-gateway"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
              sh 'cd $(find ./ -type d -name "${AppName}") && \
                    sonar-scanner \
                      -Dsonar.projectKey=${AppName} \
                      -Dsonar.java.binaries=src \
                      -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Auth'){
          environment {
            AppName="ruoyi-auth"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测System'){
          environment {
            AppName="ruoyi-system"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && pwd && ls && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Monitor'){
          environment {
            AppName="ruoyi-monitor"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && pwd && ls &&  \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测UI'){
          environment {
            AppName="ruoyi-ui"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar') {
                sh 'cd $(find ./ -type d -name "${AppName}") && pwd && ls &&  \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
      }       //parallel end
    }        //stage end		
	
    stage('检查漏洞扫描结果'){
      steps {
        container('sonar'){
          script {
            timeout(5) {
              def qg = waitForQualityGate()
              if (qg.status != 'OK') {
                error "Sonarqube 代码检查失败, error的原因 ${qg.status}"
              }
            }
          }
        }
      }
    }
	
    stage('代码编译') {
      steps {
        container('maven') {
		  sh 'echo "代码编译"'
        }
      }
    }	    
    stage('制作Docker镜像') {
      steps {
        container('docker') {
		  sh 'echo "制作Docker镜像"'
        }
      }
    }	
    stage('交付应用至K8S') {
      steps {
        container('kubectl') {
		  sh 'echo "交付应用至K8S"'
        }
      }
    }		
  }
}

4.png

# 2.5 代码编译
pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          volumes:
          - name: data
            nfs:
              server: 192.168.1.75
              path: /data/nfs/maven
          - name: dockersocket
            hostPath:
              path: /run/docker.sock
          containers:
          - name: maven
            image: s.hmallleasing.com/base/maven:3.8.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: data
              mountPath: /root/.m2
          - name: nodejs
            image: s.hmallleasing.com/base/nodejs:14.20
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: sonar
            image: s.hmallleasing.com/base/sonar-scanner:2.3.0
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: docker
            image: s.hmallleasing.com/base/docker:20.10
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: dockersocket
              mountPath: /run/docker.sock
          - name: kubectl
            image: s.hmallleasing.com/base/kubectl:1.32.3
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    }
  }
  
  environment{
    //定义git变量
    Git_Id = "gitlab-root-token"
    Git_Url = "http://gitlab.hmallleasing.com/root/ruoyi-cloud-cicd.git"
  }
  
  stages {
    stage('获取代码') {
      steps {
        container('maven') {
		  checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "${Git_Id}", url: "${Git_Url}"]])
		  sh 'pwd && ls' 		  
        }
      }
    }
    stage('代码扫描'){
      parallel {
        stage('检测Gateway'){
          environment {
            AppName="ruoyi-gateway"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
              sh 'cd $(find ./ -type d -name "${AppName}") && \
                    sonar-scanner \
                      -Dsonar.projectKey=${AppName} \
                      -Dsonar.java.binaries=src \
                      -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Auth'){
          environment {
            AppName="ruoyi-auth"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测System'){
          environment {
            AppName="ruoyi-system"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && pwd && ls && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Monitor'){
          environment {
            AppName="ruoyi-monitor"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && pwd && ls &&  \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测UI'){
          environment {
            AppName="ruoyi-ui"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar') {
                sh 'cd $(find ./ -type d -name "${AppName}") && pwd && ls &&  \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
      }       //parallel end
    }        //stage end		
	
    stage('检查漏洞扫描结果'){
      steps {
        container('sonar'){
          script {
            timeout(5) {
              def qg = waitForQualityGate()
              if (qg.status != 'OK') {
                error "Sonarqube 代码检查失败, error的原因 ${qg.status}"
              }
            }
          }
        }
      }
    }	
    stage('代码编译'){
      parallel {
        stage('编译Gateway'){
          environment{
            AppName = "ruoyi-gateway"
          }
          steps {
            container('maven'){
              sh '''
				pwd && ls -l && \
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am && \
				pwd && ls -l ${Build_Path}/target/*
              '''
            }
          }
        }
        stage('编译Auth'){
          environment{
            AppName = "ruoyi-auth"
          }
          steps {
            container('maven'){
              sh '''
				pwd && ls -l && \
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am && \
				pwd && ls -l ${Build_Path}/target/*
              '''
            }
          }
        }
        stage('编译System'){
          environment{
            AppName = "ruoyi-system"
          }
          steps {
            container('maven'){
              sh '''
				pwd && ls -l && \
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am && \
				pwd && ls -l ${Build_Path}/target/*
              '''
            }
          }
        }
        stage('编译Monitor'){
          environment{
            AppName = "ruoyi-monitor"
          }
          steps {
            container('maven'){
              sh '''
				pwd && ls -l && \
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am && \
				pwd && ls -l ${Build_Path}/target/*
              '''
            }
          }
        }
        stage('编译UI'){
          environment{
            AppName = "ruoyi-ui"
          }
          steps {
            container('nodejs'){
              sh '''
                cd $(find ./ -type d -name "${AppName}") && \
				pwd && ls -l && \
                npm install --registry=https://registry.npmmirror.com && \
                npm run build:prod && \
				pwd && ls -l ./dist/*
              '''
            }
          }
        }
      }   //parallel end
    }     //代码编译stage end		
	
    stage('制作Docker镜像') {
      steps {
        container('docker') {
		  sh 'echo "制作Docker镜像"'
        }
      }
    }	
    stage('交付应用至K8S') {
      steps {
        container('kubectl') {
		  sh 'echo "交付应用至K8S"'
        }
      }
    }		
  }
}

5.png

# 2.6 制作 Docker 镜像

制作 Docker 镜像、推送到 Harbor 仓库,根据 Dockerfile 文件生成 Docker 镜像

1、配置 dns 解析(如果 harbor 解析至公网,无需配置此步骤)

[root@k8s-master01 ~]# kubectl edit cm -n kube-system coredns
...
        ready
        hosts {
           192.168.1.74 gitlab.hmallleasing.com        #gitalb
           192.168.1.74 sonar.hmallleasing.com         #sonar
           192.168.1.74 jenkins.hmallleasing.com       #jenkins
           192.168.1.68 s.hmallleasing.com             #harbor
           fallthrough
        }
        kubernetes cluster.local in-addr.arpa ip6.arpa {
...

测试解析是否生效;

[root@k8s-master01 ~]# kubectl exec -it gitlab-0 -n ops -- /bin/bash
root@gitlab-0:/# ping s.hmallleasing.com
PING s.hmallleasing.com (192.168.1.68): 56 data bytes
64 bytes from 192.168.1.68: seq=0 ttl=63 time=0.257 ms
64 bytes from 192.168.1.68: seq=1 ttl=63 time=0.198 ms

2、配置 harbor 认证信息:系统管理 ->manage credentials-> 全局 ->Add Credential

2.png

3、制作 docker 镜像 Pipeline

pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          volumes:
          - name: data
            nfs:
              server: 192.168.1.75
              path: /data/nfs/maven
          - name: dockersocket
            hostPath:
              path: /run/docker.sock
          containers:
          - name: maven
            image: s.hmallleasing.com/base/maven:3.8.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: data
              mountPath: /root/.m2
          - name: nodejs
            image: s.hmallleasing.com/base/nodejs:14.20
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: sonar
            image: s.hmallleasing.com/base/sonar-scanner:2.3.0
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: docker
            image: s.hmallleasing.com/base/docker:20.10
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: dockersocket
              mountPath: /run/docker.sock
          - name: kubectl
            image: s.hmallleasing.com/base/kubectl:1.32.3
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    }
  }
  
  environment{
    //定义git变量
    Git_Id = "gitlab-root-token"
    Git_Url = "http://gitlab.hmallleasing.com/root/nf-flms.git"
	
    //Harbor相关的全局变量
    Url = "s.hmallleasing.com"
    Pro = "nf-flms"
    HARBOR_ID = "harbor-auth"
  }
  
  stages {
    stage('获取代码') {
      steps {
        container('maven') {
		  checkout scmGit(branches: [[name: '*/release']], extensions: [], userRemoteConfigs: [[credentialsId: "${Git_Id}", url: "${Git_Url}"]])
		  sh 'pwd && ls' 		  
        }
      }
    }	
    stage('代码扫描'){
      parallel {
        stage('检测Gateway'){
          environment {
            AppName="nf-flms-gateway"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
              sh 'cd $(find ./ -type d -name "${AppName}") && \
                    sonar-scanner \
                      -Dsonar.projectKey=${AppName} \
                      -Dsonar.java.binaries=src \
                      -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Order'){
          environment {
            AppName="nf-flms-order"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测System'){
          environment {
            AppName="nf-flms-system"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && pwd && ls && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Monitor'){
          environment {
            AppName="nf-flms-admin"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && pwd && ls &&  \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Statistics'){
          environment {
            AppName="nf-flms-statistics"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar') {
                sh 'cd $(find ./ -type d -name "${AppName}") && pwd && ls &&  \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Openapi'){
          environment {
            AppName="nf-flms-openapi"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar') {
                sh 'cd $(find ./ -type d -name "${AppName}") && pwd && ls &&  \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
      }       //parallel end
    }        //stage end		
	
    stage('检查漏洞扫描结果'){
      steps {
        container('sonar'){
          script {
            timeout(5) {
              def qg = waitForQualityGate()
              if (qg.status != 'OK') {
                error "Sonarqube 代码检查失败, error的原因 ${qg.status}"
              }
            }
          }
        }
      }
    }
	
    stage('代码编译') {
      steps {
        container('maven') {
          sh '''
			pwd && ls -l && \
			cd nf-flms && \
			echo "*********************开始编译代码*********************" && \
            mvn  clean package && \
			pwd && ls -l nf-flms-order/target/*.jar && \
			pwd && ls -l nf-flms-gateway/target/*.jar && \
			pwd && ls -l nf-flms-statistics/target/*.jar && \
			pwd && ls -l nf-flms-system/target/*.jar && \
			pwd && ls -l nf-flms-openapi/target/*.jar && \
			pwd && ls -l nf-flms-admin/target/*.jar && 
            echo "*********************代码编译成功*********************"			
          '''
        }
      }
    }	    
    stage('生成镜像Tag'){
      steps {
        container('maven') {
          script {
            //本次git提交的commid     (git log -n1 --pretty=format:'%h')
            env.COMMITID = sh(returnStdout: true, script: "git log -n1 --pretty=format:'%h'").trim()
            //构建的时间   (date +%Y%m%d_%H%M%S)
            env.BuildTime = sh(returnStdout: true, script: "date +%Y%m%d_%H%M%S").trim()
            
            //完整的镜像Tag   (c106654_20221115_133911)
            env.ImageTag = COMMITID + "_" +  BuildTime
          }
          
          sh 'echo "镜像的Tag: ${ImageTag}"'
        }
      }
    }
	
    stage('制作Docker镜像'){
      parallel{
        stage('构建Gateway镜像') {
          environment {
            AppName = "nf-flms-gateway"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建System镜像') {
          environment {
            AppName = "nf-flms-system"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建Order镜像') {
          environment {
            AppName = "nf-flms-order"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建Monitor镜像') {
          environment {
            AppName = "nf-flms-admin"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建Openapi镜像') {
          environment {
            AppName = "nf-flms-openapi"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
		
        stage('构建Statistics镜像') {
          environment {
            AppName = "nf-flms-statistics"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
      }   //parallel end
    }   // stage end
	
    stage('交付应用至K8S') {
      steps {
        container('kubectl') {
		  sh 'echo "交付应用至K8S"'
        }
      }
    }		
  }
}

1.png

5、编译报错总结

编译过程出现报错,由于多个服务同时进行编译,CPU 负载过高,解决方案让部分服务 sleep 60;

stage('编译Gateway'){
          environment{
            AppName = "ruoyi-gateway"
          }
          steps {
            container('maven'){
              sh '''
	            sleep 60  && \
				pwd && ls -l && \
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am && \
				pwd && ls -l ${Build_Path}/target/*
              '''
            }
          }
        }
# 2.7 交付应用到 K8S

1、将 K8S 的 kubeconfig 文件下载至本地,配置 Jenkins,将 config 文件制作为一个 Scretfile;

[root@k8s-master01 ~]# sz /root/.kube/config

配置 k8s 认证信息:系统管理 ->manage credentials-> 全局 ->Add Credential

1.png

2、编写 pipeline 流水线

在 Stage 中引用这个 config 文件,就可以操作对应的集群,部署前需做以下操作;

$ kubectl create ns test
$ kubectl create secret docker-registry harbor-admin --docker-server=s.hmallleasing.com --docker-username=admin --docker-password=Talent*19871988 -n test
  • 部署的名称空间需要提前创建
  • 需要创建对应 namespace 的 Harbor 认证 Secrets ->harbor-admin
pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          volumes:
          - name: data
            nfs:
              server: 192.168.1.125
              path: /data/maven
          - name: dockersocket
            hostPath:
              path: /run/docker.sock
          containers:
          - name: maven
            image: s.hmallleasing.com/ops/maven:3.8.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: data
              mountPath: /root/.m2
          - name: nodejs
            image: s.hmallleasing.com/ops/nodejs:14.20
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: sonar
            image: s.hmallleasing.com/ops/sonar-scanner:2.3.0
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: docker
            image: s.hmallleasing.com/ops/docker:20.10
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: dockersocket
              mountPath: /run/docker.sock
          - name: kubectl
            image: s.hmallleasing.com/ops/kubectl:1.22.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    }
  }
 
  environment{
    //定义git变量
    Git_Id = "gitlab-root-token"
    Git_Url = "https://gitee.com/chinagei/ruoyi-cloud-cicd.git"
	
    //Harbor相关的全局变量
    Url = "s.hmallleasing.com"
    Pro = "ruoyi"
    HARBOR_ID = "harbor-auth"
  }
    
  stages {
    stage('获取代码') {
      steps {
        container('maven') {
          checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "${Git_Id}", url: "${Git_Url}"]]])
		  sh 'pwd && ls -l'  
        }
      }
    }
	
    stage('代码扫描'){
      parallel {
        stage('检测Gateway'){
          environment {
            AppName="ruoyi-gateway"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
              sh 'cd $(find ./ -type d -name "${AppName}") && \
                    sonar-scanner \
                      -Dsonar.projectKey=${AppName} \
                      -Dsonar.java.binaries=src \
                      -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Auth'){
          environment {
            AppName="ruoyi-auth"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测System'){
          environment {
            AppName="ruoyi-system"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Monitor'){
          environment {
            AppName="ruoyi-monitor"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测UI'){
          environment {
            AppName="ruoyi-ui"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar') {
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
      }       //parallel end
    }        //stage end	
	
    stage('检查漏洞扫描结果'){
      steps {
        container('sonar'){
          script {
            timeout(5) {
              def qg = waitForQualityGate()
              if (qg.status != 'OK') {
                error "Sonarqube 代码检查失败, error的原因 ${qg.status}"
              }
            }
          }
        }
      }
    }
	
    stage('代码编译'){
      parallel {
        stage('编译Gateway'){
          environment{
            AppName = "ruoyi-gateway"
          }
          steps {
            container('maven'){
              sh '''
				pwd && ls -l && \
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am && \
				pwd && ls -l ${Build_Path}/target/*
              '''
            }
          }
        }
        stage('编译Auth'){
          environment{
            AppName = "ruoyi-auth"
          }
          steps {
            container('maven'){
              sh '''
				pwd && ls -l && \
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am && \
				pwd && ls -l ${Build_Path}/target/*
              '''
            }
          }
        }
        stage('编译System'){
          environment{
            AppName = "ruoyi-system"
          }
          steps {
            container('maven'){
              sh '''
				pwd && ls -l && \
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am && \
				pwd && ls -l ${Build_Path}/target/*
              '''
            }
          }
        }
        stage('编译Monitor'){
          environment{
            AppName = "ruoyi-monitor"
          }
          steps {
            container('maven'){
              sh '''
				pwd && ls -l && \
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am && \
				pwd && ls -l ${Build_Path}/target/*
              '''
            }
          }
        }
        stage('编译UI'){
          environment{
            AppName = "ruoyi-ui"
          }
          steps {
            container('nodejs'){
              sh '''
                cd $(find ./ -type d -name "${AppName}") && \
				pwd && ls -l && \
                npm install --registry=https://registry.npmmirror.com && \
                npm run build:prod && \
				pwd && ls -l ./dist/*
              '''
            }
          }
        }
      }   //parallel end
    }     //代码编译stage end	
 
    stage('生成镜像Tag'){
      steps {
        container('maven') {
          script {
            //本次git提交的commid     (git log -n1 --pretty=format:'%h')
            env.COMMITID = sh(returnStdout: true, script: "git log -n1 --pretty=format:'%h'").trim()
            //构建的时间   (date +%Y%m%d_%H%M%S)
            env.BuildTime = sh(returnStdout: true, script: "date +%Y%m%d_%H%M%S").trim()
            
            //完整的镜像Tag   (c106654_20221115_133911)
            env.ImageTag = COMMITID + "_" +  BuildTime
          }
          
          sh 'echo "镜像的Tag: ${ImageTag}"'
        }
      }
    }
    stage('制作Docker镜像'){
      parallel{
        stage('构建Gateway镜像') {
          environment {
            AppName = "ruoyi-gateway"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建System镜像') {
          environment {
            AppName = "ruoyi-system"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建Auth镜像') {
          environment {
            AppName = "ruoyi-auth"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建Monitor镜像') {
          environment {
            AppName = "ruoyi-monitor"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建UI镜像') {
          environment {
            AppName = "ruoyi-ui"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
      }   //parallel end
    }   // stage end	
    stage('部署微服务至K8S') {
      parallel {
        stage('交付Gateway') {
          environment {
            AppName = "ruoyi-gateway"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && echo ${KUBECONFIG} >/root/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
					cat deploy.yaml && \
                    sed -i "s#{namespace}#test#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
					cat deploy.yaml && \
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
        stage('交付Auth') {
          environment {
            AppName = "ruoyi-auth"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && echo ${KUBECONFIG} >/root/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
					cat deploy.yaml && \
                    sed -i "s#{namespace}#test#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
					cat deploy.yaml && \
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
        stage('交付System') {
          environment {
            AppName = "ruoyi-system"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && echo ${KUBECONFIG} >/root/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
					cat deploy.yaml && \
                    sed -i "s#{namespace}#test#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
					cat deploy.yaml && \
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
        stage('交付Monitor') {
          environment {
            AppName = "ruoyi-monitor"
            ImageName = "${Url}/${Pro}/${AppName}"
            IngressHost = "monitor.hmallleasing.com"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && echo ${KUBECONFIG} >/root/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
					cat deploy.yaml && \
                    sed -i "s#{namespace}#test#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
                    sed -i "s#{host}#${IngressHost}#g" deploy.yaml
					cat deploy.yaml && \
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
        stage('交付UI') {
          environment {
            AppName = "ruoyi-ui"
            ImageName = "${Url}/${Pro}/${AppName}"
            IngressHost = "ruoyi.hmallleasing.com"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && echo ${KUBECONFIG} >/root/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
					cat deploy.yaml && \
                    sed -i "s#{namespace}#test#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
                    sed -i "s#{host}#${IngressHost}#g" deploy.yaml
					cat deploy.yaml && \
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
      }     //parallel end
    }      //stage	
	
  }   //stages end
}   //pipeline end

1.png

2.png

3.png

# 2.8 选择制定微服组件部署

4.png

5.png

7.png

6.png

选择指定微服务进行部署(Deploy_UI == "true" 或者 Deploy_All == "true")

pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          volumes:
          - name: data
            nfs:
              server: 192.168.1.125
              path: /data/maven
          - name: dockersocket
            hostPath:
              path: /run/docker.sock
          containers:
          - name: maven
            image: s.hmallleasing.com/ops/maven:3.8.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: data
              mountPath: /root/.m2
          - name: nodejs
            image: s.hmallleasing.com/ops/nodejs:14.20
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: sonar
            image: s.hmallleasing.com/ops/sonar-scanner:2.3.0
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: docker
            image: s.hmallleasing.com/ops/docker:20.10
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: dockersocket
              mountPath: /run/docker.sock
          - name: kubectl
            image: s.hmallleasing.com/ops/kubectl:1.22.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    }
  }
 
  environment{
    //定义git变量
    Git_Id = "gitlab-root-token"
    Git_Url = "https://gitee.com/chinagei/ruoyi-cloud-cicd.git"
	
    //Harbor相关的全局变量
    Url = "s.hmallleasing.com"
    Pro = "ruoyi"
    HARBOR_ID = "harbor-auth"
  }
    
  stages {
    stage('获取代码') {
      steps {
        container('maven') {
          checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "${Git_Id}", url: "${Git_Url}"]]])
		  sh 'pwd && ls -l'  
        }
      }
    }
	
    stage('代码扫描'){
      parallel {
        stage('检测Gateway'){
		  when { expression {Deploy_GATEWAY == "true" || Deploy_All == "true" } }
          environment {
            AppName="ruoyi-gateway"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
              sh 'cd $(find ./ -type d -name "${AppName}") && \
                    sonar-scanner \
                      -Dsonar.projectKey=${AppName} \
                      -Dsonar.java.binaries=src \
                      -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Auth'){
		  when { expression {Deploy_AUTH == "true" || Deploy_All == "true" } }
          environment {
            AppName="ruoyi-auth"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测System'){
		  when { expression {Deploy_SYSTEM == "true" || Deploy_All == "true" } }
          environment {
            AppName="ruoyi-system"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Monitor'){
		  when { expression {Deploy_MONITOR == "true" || Deploy_All == "true" } }
          environment {
            AppName="ruoyi-monitor"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测UI'){
		  when { expression {Deploy_UI == "true" || Deploy_All == "true" } }
          environment {
            AppName="ruoyi-ui"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar') {
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
      }       //parallel end
    }        //stage end	
	
    stage('检查漏洞扫描结果'){
      steps {
        container('sonar'){
          script {
            timeout(5) {
              def qg = waitForQualityGate()
              if (qg.status != 'OK') {
                error "Sonarqube 代码检查失败, error的原因 ${qg.status}"
              }
            }
          }
        }
      }
    }
	
    stage('代码编译'){
      parallel {
        stage('编译Gateway'){
		  when { expression {Deploy_GATEWAY == "true" || Deploy_All == "true" } }
          environment{
            AppName = "ruoyi-gateway"
          }
          steps {
            container('maven'){
              sh '''
				pwd && ls -l && \
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am && \
				pwd && ls -l ${Build_Path}/target/*
              '''
            }
          }
        }
        stage('编译Auth'){
		  when { expression {Deploy_AUTH == "true" || Deploy_All == "true" } }
          environment{
            AppName = "ruoyi-auth"
          }
          steps {
            container('maven'){
              sh '''
				pwd && ls -l && \
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am && \
				pwd && ls -l ${Build_Path}/target/*
              '''
            }
          }
        }
        stage('编译System'){
		  when { expression {Deploy_SYSTEM == "true" || Deploy_All == "true" } }
          environment{
            AppName = "ruoyi-system"
          }
          steps {
            container('maven'){
              sh '''
				pwd && ls -l && \
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am && \
				pwd && ls -l ${Build_Path}/target/*
              '''
            }
          }
        }
        stage('编译Monitor'){
		  when { expression {Deploy_MONITOR == "true" || Deploy_All == "true" } 
          environment{
            AppName = "ruoyi-monitor"
          }
          steps {
            container('maven'){
              sh '''
				pwd && ls -l && \
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am && \
				pwd && ls -l ${Build_Path}/target/*
              '''
            }
          }
        }
        stage('编译UI'){
		  when { expression {Deploy_UI == "true" || Deploy_All == "true" } }
          environment{
            AppName = "ruoyi-ui"
          }
          steps {
            container('nodejs'){
              sh '''
                cd $(find ./ -type d -name "${AppName}") && \
				pwd && ls -l && \
                npm install --registry=https://registry.npmmirror.com && \
                npm run build:prod && \
				pwd && ls -l ./dist/*
              '''
            }
          }
        }
      }   //parallel end
    }     //代码编译stage end	
 
    stage('生成镜像Tag'){
      steps {
        container('maven') {
          script {
            //本次git提交的commid     (git log -n1 --pretty=format:'%h')
            env.COMMITID = sh(returnStdout: true, script: "git log -n1 --pretty=format:'%h'").trim()
            //构建的时间   (date +%Y%m%d_%H%M%S)
            env.BuildTime = sh(returnStdout: true, script: "date +%Y%m%d_%H%M%S").trim()
            
            //完整的镜像Tag   (c106654_20221115_133911)
            env.ImageTag = COMMITID + "_" +  BuildTime
          }
          
          sh 'echo "镜像的Tag: ${ImageTag}"'
        }
      }
    }
    stage('制作Docker镜像'){
      parallel{
        stage('构建Gateway镜像') {
		  when { expression {Deploy_GATEWAY == "true" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-gateway"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建System镜像') {
		  when { expression {Deploy_SYSTEM == "true" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-system"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建Auth镜像') {
		  when { expression {Deploy_AUTH == "true" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-auth"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建Monitor镜像') {
		  when { expression {Deploy_MONITOR == "true" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-monitor"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建UI镜像') {
		  when { expression {Deploy_UI == "true" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-ui"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
      }   //parallel end
    }   // stage end	
    stage('部署微服务至K8S') {
      parallel {
        stage('交付Gateway') {
		  when { expression {Deploy_GATEWAY == "true" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-gateway"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && echo ${KUBECONFIG} >/root/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
					cat deploy.yaml && \
                    sed -i "s#{namespace}#test#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
					cat deploy.yaml && \
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
        stage('交付Auth') {
		  when { expression {Deploy_AUTH == "true" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-auth"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && echo ${KUBECONFIG} >/root/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
					cat deploy.yaml && \
                    sed -i "s#{namespace}#test#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
					cat deploy.yaml && \
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
        stage('交付System') {
		  when { expression {Deploy_SYSTEM == "true" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-system"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && echo ${KUBECONFIG} >/root/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
					cat deploy.yaml && \
                    sed -i "s#{namespace}#test#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
					cat deploy.yaml && \
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
        stage('交付Monitor') {
          when { expression {Deploy_MONITOR == "true" || Deploy_All == "true" } }	
          environment {
            AppName = "ruoyi-monitor"
            ImageName = "${Url}/${Pro}/${AppName}"
            IngressHost = "monitor.hmallleasing.com"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && echo ${KUBECONFIG} >/root/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
					cat deploy.yaml && \
                    sed -i "s#{namespace}#test#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
                    sed -i "s#{host}#${IngressHost}#g" deploy.yaml
					cat deploy.yaml && \
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
        stage('交付UI') {
          when { expression {Deploy_UI == "true" || Deploy_All == "true" } }		
          environment {
            AppName = "ruoyi-ui"
            ImageName = "${Url}/${Pro}/${AppName}"
            IngressHost = "ruoyi.hmallleasing.com"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && echo ${KUBECONFIG} >/root/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
					cat deploy.yaml && \
                    sed -i "s#{namespace}#test#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
                    sed -i "s#{host}#${IngressHost}#g" deploy.yaml
					cat deploy.yaml && \
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
      }     //parallel end
    }      //stage		
	
  }   //stages end
}   //pipeline end
# 2.9 配置自动触发构建

Jenkins 配置:

#Gitee 配置参考连接 https://blog.csdn.net/hali90s/article/details/126991514

#Gitlab 配置参考连接 https://blog.csdn.net/LlZzSss/article/details/123595996

1、安装插件:Gitee

2、配置 -> 构建触发器 ->Gitee webhook 触发构建

1.png

Gitee 配置:

2.png

//交付微服务的CI流水线
pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          volumes:
          - name: data
            nfs:
              server: 10.0.0.206
              path: /data/maven
          - name: dockersocket
            hostPath:
              path: /run/docker.sock
          containers:
          - name: maven
            image: harbor.oldxu.net/ops/maven:3.8.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: data
              mountPath: /root/.m2
          - name: nodejs
            image: harbor.oldxu.net/ops/nodejs:14.20
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: sonar
            image: harbor.oldxu.net/ops/sonar-scanner:2.3.0
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
          - name: docker
            image: harbor.oldxu.net/ops/docker:20.10
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
            volumeMounts:
            - name: dockersocket
              mountPath: /run/docker.sock
          - name: kubectl
            image: harbor.oldxu.net/ops/kubectl:1.22.3
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    }
  }
  //参数化构建  Deploy_All
  parameters {
    choice choices: ['false', 'true'], description: '是否部署所有的微服务', name: 'Deploy_All'
  }
  environment{
    Gitlab_Id = "gitlab-root-token"
    Gitlab_Pro = "http://gitlab.oldxu.net/root/ruo-yi-cloud.git"
    //Harbor相关的全局变量
    Url = "harbor.oldxu.net"
    Pro = "base"
    HARBOR_ID = "harbor-auth"
    //对外暴露的域名
    Ingress_Host = "spring-dev.oldxu.net"
  }
  stages {
    stage('获取代码') {
      steps {
        container('maven') {
          checkout([$class: 'GitSCM', branches: [[name: '*/k8s']], extensions: [], userRemoteConfigs: [[credentialsId: "${Gitlab_Id}", url: "${Gitlab_Pro}"]]])
          sh 'pwd && ls -l'
        }
      }
    }
    stage('检查变动的模块名称') {
      steps {
        sh '''#!/bin/bash
                # 定义微服务模块名称
                Init_Module=(ruoyi-gateway ruoyi-auth ruoyi-system ruoyi-monitor ruoyi-ui)
                # 变动的模块
                Change_Module=$(git diff --name-only $(git log -n2 --pretty=format:"%h") |cut -d / -f1,2)
                # 外循环 {提取变动的模块名称}
                for Change in ${Change_Module}
                do
                  # echo "改变的微服务模块是: $Change"
                  # 内循环
                  for init in ${Init_Module[@]}
                  do
                    if [[ "${Change}" =~ "${init}" ]];then
                      echo "${init}=ok" >> var.txt
                    fi
                  done
                done
        '''
        //制作自定义变量
        script {
          env.GATEWAY = sh(returnStdout: true, script: "grep 'ruoyi-gateway' var.txt | awk -F '=' '{print \$2}'").trim()
          env.AUTH = sh(returnStdout: true, script: "grep 'ruoyi-auth' var.txt | awk -F '=' '{print \$2}'").trim()
          env.SYSTEM = sh(returnStdout: true, script: "grep 'ruoyi-system' var.txt | awk -F '=' '{print \$2}'").trim()
          env.MONITOR = sh(returnStdout: true, script: "grep 'ruoyi-monitor' var.txt | awk -F '=' '{print \$2}'").trim()
          env.UI = sh(returnStdout: true, script: "grep 'ruoyi-ui' var.txt | awk -F '=' '{print \$2}'").trim()
        }
      }
    }
    stage('输出更新的模块状态'){
      steps {
        sh 'echo "gateway: ${GATEWAY}"'
        sh 'echo "auth: ${AUTH}"'
        sh 'echo "system: ${SYSTEM}"'
        sh 'echo "monitor: ${MONITOR}"'
        sh 'echo "ui: ${UI}"'
      }
    }
    stage('代码扫描'){
      parallel {
        stage('检测Gateway'){
          when { expression {GATEWAY == "ok" || Deploy_All == "true" } }
          environment {
            AppName="ruoyi-gateway"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
              sh 'cd $(find ./ -type d -name "${AppName}") && \
                    sonar-scanner \
                      -Dsonar.projectKey=${AppName} \
                      -Dsonar.java.binaries=src \
                      -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Auth'){
          when { expression {AUTH == "ok" || Deploy_All == "true" } }
          environment {
            AppName="ruoyi-auth"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测System'){
          when { expression {SYSTEM == "ok" || Deploy_All == "true" } }
          environment {
            AppName="ruoyi-system"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测Monitor'){
          when { expression {MONITOR == "ok" || Deploy_All == "true" } }
          environment {
            AppName="ruoyi-monitor"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar'){
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
        stage('检测UI'){
          when { expression {UI == "ok" || Deploy_All == "true" } }
          environment {
            AppName="ruoyi-ui"
          }
          steps {
            withSonarQubeEnv('sonar-k8s'){
              container('sonar') {
                sh 'cd $(find ./ -type d -name "${AppName}") && \
                      sonar-scanner \
                        -Dsonar.projectKey=${AppName} \
                        -Dsonar.java.binaries=src \
                        -Dsonar.sources=.'
              }
            }
          }
        }
      }       //parallel end
    }        //stage end
    stage('检查漏洞扫描结果'){
      steps {
        container('sonar'){
          script {
            timeout(5) {
              def qg = waitForQualityGate()
              if (qg.status != 'OK') {
                error "Sonarqube 代码检查失败, error的原因 ${qg.status}"
              }
            }
          }
        }
      }
    }
    stage('代码编译'){
      parallel {
        stage('编译Gateway'){
          when { expression {GATEWAY == "ok" || Deploy_All == "true" } }
          environment{
            AppName = "ruoyi-gateway"
          }
          steps {
            container('maven'){
              sh '''
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am
              '''
            }
          }
        }
        stage('编译Auth'){
          when { expression {AUTH == "ok" || Deploy_All == "true" } }
          environment{
            AppName = "ruoyi-auth"
          }
          steps {
            container('maven'){
              sh '''
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am
              '''
            }
          }
        }
        stage('编译System'){
          when { expression {SYSTEM == "ok" || Deploy_All == "true" } }
          environment{
            AppName = "ruoyi-system"
          }
          steps {
            container('maven'){
              sh '''
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am
              '''
            }
          }
        }
        stage('编译Monitor'){
          when { expression {MONITOR == "ok" || Deploy_All == "true" } }
          environment{
            AppName = "ruoyi-monitor"
          }
          steps {
            container('maven'){
              sh '''
                Build_Path=$(find ./ -type d -name "${AppName}")
                mvn package -Dmaven.test.skip=true -pl ${Build_Path} -am
              '''
            }
          }
        }
        stage('编译UI'){
          when { expression {UI == "ok" || Deploy_All == "true" } }
          environment{
            AppName = "ruoyi-ui"
          }
          steps {
            container('nodejs'){
              sh '''
                cd $(find ./ -type d -name "${AppName}") && \
                npm install --registry=https://registry.npmmirror.com && \
                npm run build:prod
              '''
            }
          }
        }
      }   //parallel end
    }     //代码编译stage end
    stage('生成镜像Tag'){
      steps {
        container('maven') {
          script {
            //本次git提交的commid     (git log -n1 --pretty=format:'%h')
            env.COMMITID = sh(returnStdout: true, script: "git log -n1 --pretty=format:'%h'").trim()
            //构建的时间   (date +%Y%m%d_%H%M%S)
            env.BuildTime = sh(returnStdout: true, script: "date +%Y%m%d_%H%M%S").trim()
            
            //完整的镜像Tag   (c106654_20221115_133911)
            env.ImageTag = COMMITID + "_" +  BuildTime
          }
          
          sh 'echo "镜像的Tag: ${ImageTag}"'
        }
      }
    }
    stage('制作Docker镜像'){
      parallel{
        stage('构建Gateway镜像') {
          when { expression {GATEWAY == "ok" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-gateway"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建System镜像') {
          when { expression {SYSTEM == "ok" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-system"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建Auth镜像') {
          when { expression {AUTH == "ok" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-auth"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建Monitor镜像') {
          when { expression {MONITOR == "ok" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-monitor"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
        stage('构建UI镜像') {
          when { expression {UI == "ok" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-ui"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('docker'){
              withCredentials([usernamePassword(credentialsId: "${HARBOR_ID}", passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USER')]) {
                  //登陆harbor
                  sh 'echo "${HARBOR_PASSWORD}" | docker login ${Url} -u "${HARBOR_USER}" --password-stdin'
                  //构建镜像
                  sh 'cd $(find ./ -type d -name "${AppName}") && docker build -t ${ImageName}:${ImageTag} .'
                  //推送镜像
                  sh 'docker push ${ImageName}:${ImageTag}'
                  //删除镜像
                  sh 'docker rmi ${ImageName}:${ImageTag}' 
              }
            }
          }
        }
      }   //parallel end
    }   // stage end
    stage('交付微服务至K8S') {
      parallel {
        stage('交付Gateway') {
          when { expression {GATEWAY == "ok" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-gateway"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
                    sed -i "s#{namespace}#dev#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
        stage('交付Auth') {
          when { expression {AUTH == "ok" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-auth"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
                    sed -i "s#{namespace}#dev#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
        stage('交付System') {
          when { expression {SYSTEM == "ok" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-system"
            ImageName = "${Url}/${Pro}/${AppName}"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
                    sed -i "s#{namespace}#dev#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
        stage('交付Monitor') {
          when { expression {MONITOR == "ok" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-monitor"
            ImageName = "${Url}/${Pro}/${AppName}"
            IngressHost = "monitor-dev.oldxu.net"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
                    sed -i "s#{namespace}#dev#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
                    sed -i "s#{host}#${IngressHost}#g" deploy.yaml
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
        stage('交付UI') {
          when { expression {UI == "ok" || Deploy_All == "true" } }
          environment {
            AppName = "ruoyi-ui"
            ImageName = "${Url}/${Pro}/${AppName}"
            IngressHost = "ui-dev.oldxu.net"
          }
          steps {
            container('kubectl') {
              withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                  sh 'mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config'
                  sh '''
                    cd $(find ./ -type d -name "${AppName}") && \
                    sed -i "s#{namespace}#dev#g" deploy.yaml
                    sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
                    sed -i "s#{host}#${IngressHost}#g" deploy.yaml
                    kubectl apply -f deploy.yaml
                  '''
              }
            }
          }
        }
      }     //parallel end
    }
    }       //stages end
}       //pipeline end

# 三、CD 阶段

CD 阶段:Jenkins/CD-> 拉取 Harbor 仓库对应项目镜像 -> 部署应用至 K8S 生产环境;

1.jpg

# 3.1 获取完整的镜像名称

1、获取 Harbor 镜像 Tag

[root@k8s-master01 ~]# curl -s -uadmin:passwd -H'Content-Type: application/json' -X GET https://s.hmallleasing.com/v2/base/springboot/tags/list | sed -r 's#(\{.*\[)(.*)(\]\})#\2#g' | xargs -d "," -n1 | xargs -n1 | sort -t "_" -k2 -k3 -nr | head -5
3e24684_20230326_153018
5041fbd_20230326_144343
5da8d15_20230326_143533
5da8d15_20230326_005207
5da8d15_20230326_004940

2、安装插件

系统管理 -> 插件管理 ->Active Choices

3、级联变量的方式来提取对应的 tag
变量:Harbor_Url
项目 -> 配置 -> 参数化构建 ->Active Choices Parameter
语法:return ["s.hmallleasing.com","harbor.hmallleasing.com"]

3.png

变量:Harbor_Pro
项目 -> 配置 -> 参数化构建 ->Active Choices Parameter
语法:return ["base","nf-flms","ops","xx"]

4.png

变量:Image_Name
项目 -> 配置 -> 参数化构建 ->Active Choices Parameter
语法:return ["springboot","nf-flms-statistics","nf-flms-order","xx"]

5.png

级联变量:Image_Tag

项目 -> 配置 -> 参数化构建 ->Active Choices Reactive Parameter
语法:

def get_tag = [ "bash", "-c", "curl -s -uadmin:passwd -H 'Content-Type: application/json' -X GET https://${Harbor_Url}/v2/${Harbor_Pro}/${Image_Name}/tags/list | sed -r 's#(\\{.*\\[)(.*)(\\]\\})#\\2#g' | xargs -d ',' -n1 | xargs -n1 | sort -t '_' -k2 -k3 -nr | head -5"]
return get_tag.execute().text.tokenize("\n")

6.png

7.png

#级联变量:Harbor_Url+Harbor_Pro+Image_Name

4、编写 pipeline 输出完整的镜像名称

pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          containers:
          - name: kubectl
            image: s.hmallleasing.com/ops/kubectl:1.22.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    }
  }
  environment {
      Full_Image = "${Harbor_Url}/${Harbor_Pro}/${Image_Name}:${Image_Tag}"
    }
  stages {
    stage('输出完整的镜像名称') {
      steps {
        sh 'echo 镜像名称-tag: ${Full_Image}'
      }
    }
  }
}

8.png

9.png

# 3.2 生产环境中部署对应至 K8S

1、编写 yaml

[root@k8s-master01 java]# cat deploy-prod.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: springboot
  namespace: prod			#修改为 {} 特殊字符,后期好替换
spec:
  replicas: 2
  selector:
    matchLabels:
      app: spring
  template:
    metadata:
      labels:
        app: spring
    spec:
      imagePullSecrets:
      - name: harbor-admin
      containers:
      - name: springboot
        image: s.hmallleasing.com/base/springboot:5da8d15_20230326_004659			# 修改为 {} 特殊字符,后期好替换
        ports:
        - name: http
          containerPort: 8080
        env:            # 传递初始堆内存和最大堆内存占用
        - name: XMS_OPTS
          valueFrom:
            resourceFieldRef:
              resource: requests.memory
        - name: XMX_OPTS
          valueFrom:
            resourceFieldRef:
              resource: limits.memory
        resources:
          requests:
            memory: 150Mi
          limits:
            memory: 300Mi
        readinessProbe:         # 就绪探针;如果端口不存活,则从负载均衡中移除
          tcpSocket:
            port: http          # http 是一个名字;它会获取这个名字对应的端口;
          initialDelaySeconds: 10
          failureThreshold: 3
        livenessProbe:          # 存活探针;获取 url,状态码不对那么则触发重启操作
          httpGet:
            path: /
            port: http
          initialDelaySeconds: 10
          failureThreshold: 3
---
apiVersion: v1
kind: Service
metadata:
  name: spring-svc
  namespace: prod
spec:
  selector:
    app: spring
  ports:
  - port: 8080
    targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: springboot-ingress
  namespace: prod
spec:
  ingressClassName: "nginx"
  rules:
  - host: "spring-prod.hmallleasing.com"
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: spring-svc
            port:
              number: 8080

2、部署应用

[root@k8s-master01 java]# kubectl create ns prod
[root@k8s-master01 java]# kubectl create secret docker-registry harbor-admin --docker-server=s.hmallleasing.com --docker-username=admin --docker-password=Talent*19871988 -n prod
[root@k8s-master01 java]# kubectl apply -f deploy-prod.yaml
# 3.3 CD 流水线部署指定 Tag 版本到 K8S 生产环境
pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          containers:
          - name: kubectl
            image: s.hmallleasing.com/ops/kubectl:1.22.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    }
  }
  environment {
      Full_Image = "${Harbor_Url}/${Harbor_Pro}/${Image_Name}:${Image_Tag}"
    }
  stages {
    stage('输出完整的镜像名称') {
      steps {
        sh 'echo 镜像名称-tag: ${Full_Image}'
      }
    }
    stage('部署应用至K8S') {
      steps {
          withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
            container('kubectl'){
              sh 'mkdir -p ~/.kube && echo ${KUBECONFIG} >> ~/.kube/config'
              sh 'kubectl set image deployment/${Image_Name} ${Image_Name}=${Full_Image} -n prod'
            }
          }
      }
    }
  }
}

11.png

12.png

# 3.4 询问是否需要回退
pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          containers:
          - name: kubectl
            image: s.hmallleasing.com/ops/kubectl:1.22.6
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    }
  }
  environment {
      Full_Image = "${Harbor_Url}/${Harbor_Pro}/${Image_Name}:${Image_Tag}"
    }
  stages {
    stage('输出完整的镜像名称') {
      steps {
        sh 'echo 镜像名称-tag: ${Full_Image}'
      }
    }
    stage('部署应用至K8S') {
      steps {
          withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
            container('kubectl'){
              sh 'mkdir -p ~/.kube && echo ${KUBECONFIG} >> ~/.kube/config'
              sh 'kubectl set image deployment/${Image_Name} ${Image_Name}=${Full_Image} -n prod'
            }
          }
      }
    }
	
    stage('快速回滚') {
      steps {
          withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
            container('kubectl'){
              script {
                timeout(time:1 , unit: 'HOURS'){
                  def UserInput = input message: '是否回退至上一个版本', parameters: [choice(choices: ['No', 'Yes'], name: 'rollback')]
                  if (UserInput == "Yes"){
                    sh 'mkdir -p ~/.kube && echo ${KUBECONFIG} >> ~/.kube/config'
                    sh 'kubectl rollout undo deployment ${Image_Name} -n prod'
                  }else {
                    echo "没有选择回退"
                  }
                }
              }
            }
          }
      }
    }
	
  }
}

13.png

#如果选择 “Yes” 继续,则回退至上一个版本,等待时间为 1 小时

#如果选择 “No” 继续,则不回退至上一个版本

#如果没有选择,则不回退至上一个版本

此文章已被阅读次数:正在加载...更新于

请我喝[茶]~( ̄▽ ̄)~*

Xu Yong 微信支付

微信支付

Xu Yong 支付宝

支付宝