【k8s】Jenkins实现springcloud应用CI、CD实践 【三】

一、运行Jenkins流水线流程思路:

场景:java微服务应用, 单体仓库,多个微服务模块,(并行构建、自动化构建、根据模块变更仅更新特定模块)    java、nodejs

CI阶段          并行方式; 根据模块变更仅更新特定模块
1、准备项日;
	目录结构 (源代码 、dockerfile 、deploy.yaml资源清单文件)
2、下载代码
3、漏洞检测;
4、项目编译;
5、镜像制作;
6、部署测试环境;

CD阶段:
1、拉取harbor中的镜像;
2、交付到生产环境;
3、添加回退阶段;

二、部署springcloud 若依项目

本文不做详细的部署细节,可参考以前的写的: https://blog.csdn.net/Nightwish5/article/details/130308650
在这里插入图片描述

1、准备项目;
			目录结构   (源代码、Dockerfile、deploy.yaml资源清单文件)
			deploy.yaml中要替换的变量:
			{namespace}
			{image}
		
			ui  {host}
			monitor    {host}    ingress
	
		前置条件:
			1、harbor.oldxu.net/springcloud/skywalking-java-agent:8.8
			2、依赖的MySQL、Redis、Skywalking、Nacos(配置)得有;
		
		ruoyi-gateway-dev.yml
		ruoyi-auth-dev.yml
		
		ruoyi-monitor-dev.yml
		ruoyi-system-dev.yml
		
		1、提交代码到gitlab
		2、下载代码

大致说明:

部署基础组件: ruoyi的springcloud部署顺序
1、mysql (若依项目的DB库)
2、redis
3、nacos 、 mysql(nacos依赖的库)
配置nacos中的对应的yml文件

4、sentinel  
5、skywalking (oap和ui)

#part 6可以在CICD过程中部署
6、service-all
  1、system-dp
  2、auth-dp
  3、gateway-dp
  4、monitor-dp-ingress
  5、ui-dp-ingress

三、将代码提交到gitlab

gitlab创建项目:RuoYiCloud

git init 

git config --global user.email "123456@qq.com"
git config --global user.name  "old133"

git  remote add origin http://gitlab.oldxu.net:30080/root/ruoyicloud.git
git add .
git commit -m "初始化"

git checkout -b k8s
git push --set-upstream origin k8s


#方式2:
代码上传到gitee , gitlab导入项目 -> 从URL导入仓库

四、创建流水线 springcloud-ruoyi-CI

2、下载代码
		3、漏洞检测;
		
			并行;
			进入到对应的微服务文件夹中;
			不规则的,需要再cd进个子目录:
			cd ruoyi-monitor
			cd ruoyi-system

在这里插入图片描述

4.1 获取代码和代码扫描和漏洞扫描结果

pipeline{
    agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          volumes:
          - name: data
            nfs:
              server: 192.168.79.33
              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.18.0
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    } //kubernetes ned
  }	//agent end

		environment{
			Gitlab_Id = "gitlab-root-token"
			Gitlab_Pro = "http://gitlab.oldxu.net:30080/root/ruoyi-cloud.git"
			
		} //environment end
		
		stages{
		    
			stage('获取代码'){
				steps{
					container('maven'){
					   //注意这里的分支是k8s
					   checkout([$class: 'GitSCM', branches: [[name: '*/k8s']], extensions: [], userRemoteConfigs: [[credentialsId: "${Gitlab_Id}", url: "${Gitlab_Pro}"]]])
                       sh 'pwd && ls -l'
					
					}
				}
			
			} //获取代码 end
			
			stage('代码扫描'){
				//并行处理
				parallel{
				    // 1 检测Gateway
					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=.'
								}
							}
						}
					}
					
					// 2 Auth
					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=.'
								}
							}
						}
					}
					
					// 3 system
					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=.'
								}
							}
						}
					}
					
					// 4 monitor
					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=.'
								}
							}
						}
					}
					
					// 5 UI
					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 end
			
			
		}//stages end

}//pipeline end

在这里插入图片描述

五、项目编译(maven和nodejs)

#思路:
也是要并行构建
				Java:
					找到这个微服务模块的路径 (使用刚才的find方法)
					mvn package -Dmaven.test.skip=true -pl ${模块路径} -am
				NodeJS:
				npm install --registry=https://registry.npmmirror.com
				npm run build:prod
				

maven编译举例:
在这里插入图片描述

5.1 对应的pipeline代码:

		stages{
		    			
			stage('代码编译'){
				parallel{
					// 1、编译Gateway
					stage('编译Gateway'){
						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
								'''
							}
							
						}
					}
					
					// 2、编译Auth
					stage('编译Auth'){
						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
								'''
							}							
						}
					}
					
					// 3、编译system
					stage('编译system'){
						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
								'''
							}							
						}
					}
					
					// 4、编译monitor
					stage('编译monitor'){
						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
								'''
							}							
						}
					}
					
					// 5、编译UI
					stage('编译UI'){
						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
			
		}//总stages end

运行结果:
在这里插入图片描述

六、制作Docker镜像

6.1 对应的pipeline代码

   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}' 
              }
            }
          }
        }



执行结果:
在这里插入图片描述

七、部署至K8s测试环境

之前部署成功的效果:
在这里插入图片描述

//交付至K8s
			stage('交付微服务至k8s'){
				parallel{
					// 1 gateway
					stage('交付Gateway'){
						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#" deploy.yaml
										sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
										
										cat deploy.yaml
										kubectl apply -f deploy.yaml
									'''
								}

							}
						}
					}
					
					// 2 交付auth
					stage('交付auth'){
						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#" deploy.yaml
										sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
										
										cat deploy.yaml
										kubectl apply -f deploy.yaml
									'''
								}

							}
						}
					}
					
					// 3 交付system
					stage('交付system'){
						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#" deploy.yaml
										sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
										
										cat deploy.yaml
										kubectl apply -f deploy.yaml
									'''
								}

							}
						}
					}
					
					// 4 交付monitor
					stage('交付monitor'){
						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#" 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
									'''
								}

							}
						}
					}
					
					// 5 交付UI
					stage('交付UI'){
						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#" 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
			
			}//交付至K8s ,stage end

执行结果:
在这里插入图片描述

八、全自动化CI (问题一)

8.1 自动化构件操作

在这里插入图片描述
构建触发器 -> build when a change …
高级 -> 生成Secret token -> f5b73ada8be70c2259e587eb787e5a4b

gitlab界面:
地址:http://gitlab.oldxu.net:30080/root/ruoyi-cloud/-/hooks
Administrator -> Ruoyi Cloud -> Webhook设置
在这里插入图片描述
在这里插入图片描述
更新代码内容,提交。测试是否自动触发构建CI
ruoyi-auth/src/main/java/com/ruoyi/auth/service/SysLoginService.java
在这里插入图片描述
web页面效果
在这里插入图片描述

全自动化CI的问题:
		如果开发只更新了某个模块,就检测这个某块,编译这个某块、以及发布这个模块;
			
		解决方案:
			1、获取所有更新的模块名称,与我们的微服务名称进行比对,将更新的模块名称写入到文件中 [模块名=ok]
			2、过滤文件中模块的名称,提取OK,将这个OK赋值给新的变量,将新的变量提供给Jenkins的Stage进行判断;

主要逻辑判断脚本:

#!/usr/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
		    # 正则匹配的逻辑 ->  ruoyi-module/ruoyi-system =~ ruoyi-system
		    echo "更新的模块是: $init"
			echo "${init}=ok" >> var.txt
		fi
	done
done

解释:
在这里插入图片描述

8.2 上述脚本的细节:

1、git diff对比更新的模块
在这里插入图片描述

2、对更新模块做ok判断的逻辑
在这里插入图片描述

8.3 本小节对pipeline的改动有:

新增:
    1、stage('检查变动的模块名称') {
    2、stage('输出更新的模块状态'){
    3、在parallel的各个stage中,加上when判断,如 when{ expression {GATEWAY == "ok"} }

运行效果:
在这里插入图片描述

九、全自动化CI (问题二)

	全自动化CI的问题2:
		自动化触发的时候,就更新那特定的模块;
		**手动点击**的时候是希望**所有的微服务组件都进行构建**;

9.1 新增参数化构建

Deploy_All 自动化构建的时候默认是false,手动构建的时候,选择true.
和
when { expression {UI == "ok" || Deploy_All == "true" } }


parameters {
  choice choices: ['false', 'true'], description: '是否部署所有微服务', name: 'Deploy_All'
}

在这里插入图片描述

完整的springcloud-ruoyi-CI代码:

pipeline{
    agent {
    kubernetes {
      cloud 'kubernetes'
      yaml '''
        apiVersion: v1
        kind: Pod
        spec:
          imagePullSecrets:
          - name: harbor-admin
          volumes:
          - name: data
            nfs:
              server: 192.168.79.33
              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.18.0
            imagePullPolicy: IfNotPresent
            command: ["cat"]
            tty: true
      '''
    } //kubernetes ned
  }	//agent end
  
		//参数话构建 Deploy_All
		parameters{
			choice choices: ['false', 'true'], description: '是否部署所有的微服务', name: 'Deploy_All'

		}

		environment{
			Gitlab_Id = "gitlab-root-token"
			Gitlab_Pro = "http://gitlab.oldxu.net:30080/root/ruoyi-cloud.git"
			
			//Harbor相关的全局变量
			Url = "harbor.oldxu.net"
			Pro = "base"
			HARBOR_ID = "harbor-auth"
			
			//对外暴露的域名
			Ingress_Host = "spring-dev.oldxu.net"
			
		} //environment end
		
		stages{
		    
			stage('获取代码'){
				steps{
					container('maven'){
					   //注意这里的分支是k8s
					   checkout([$class: 'GitSCM', branches: [[name: '*/k8s']], extensions: [], userRemoteConfigs: [[credentialsId: "${Gitlab_Id}", url: "${Gitlab_Pro}"]]])
                       sh 'pwd && ls -l'
					
					}
				}
			
			} //获取代码 end
			
			//检查变动的模块名称
			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 end
			
			stage('输出更新的模块状态'){
				steps{
					sh 'echo "gateway: ${GATEWAY}"'
					sh 'echo "auth: ${AUTH}"'
					sh 'echo "system: ${SYSTEM}"'
					sh 'echo "monitor: ${MONITOR}"'
					sh 'echo "ui: ${UI}"'
				}
			}//输出更新的模块状态 stage end
			
			stage('代码扫描'){
				//并行处理
				parallel{
				    // 1 检测Gateway
					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=.'
								}
							}
						}
					}
					
					// 2 Auth
					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=.'
								}
							}
						}
					}
					
					// 3 system
					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=.'
								}
							}
						}
					}
					
					// 4 monitor
					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=.'
								}
							}
						}
					}
					
					// 5 UI
					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 end
			
			stage('代码编译'){
				parallel{
					// 1、编译Gateway
					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
								'''
							}
							
						}
					}
					
					// 2、编译Auth
					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
								'''
							}							
						}
					}
					
					// 3、编译system
					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
								'''
							}							
						}
					}
					
					// 4、编译monitor
					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
								'''
							}							
						}
					}
					
					// 5、编译UI
					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
			
			//生产镜像tag
			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()
							//构建时间
							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}"'
					}
				}
			}//生产镜像tag , stage end
			
			//制作Docker镜像
			stage('制作Docker镜像'){
				parallel{
					//1 gateway
					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}'
								}

							}
						}
					}
					
					//2 system
					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}'
								}

							}
						}
					}
					
					//3 auth
					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}'
								}

							}
						}
					}
					
					//4 monitor
					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}'
								}

							}
						}
					}
					
					//5 ui
					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
			
			}//制作Docker镜像 stage end
			
			
			//交付至K8s
			stage('交付微服务至k8s'){
				parallel{
					// 1 gateway
					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#" deploy.yaml
										sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
										
										cat deploy.yaml
										kubectl apply -f deploy.yaml
									'''
								}

							}
						}
					}
					
					// 2 交付auth
					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#" deploy.yaml
										sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
										
										cat deploy.yaml
										kubectl apply -f deploy.yaml
									'''
								}

							}
						}
					}
					
					// 3 交付system
					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#" deploy.yaml
										sed -i "s#{image}#${ImageName}:${ImageTag}#g" deploy.yaml
										
										cat deploy.yaml
										kubectl apply -f deploy.yaml
									'''
								}

							}
						}
					}
					
					// 4 交付monitor
					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#" 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
									'''
								}

							}
						}
					}
					
					// 5 交付UI
					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#" 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
			
			}//交付至K8s ,stage end
			
			
			
		}//总stages end

}//pipeline end

springcloud-CI阶段 END

十、Jenkins交付微服务-CD流水线设计与实现

		1、拉取harbor中的镜像;
		2、交付到生产环境;
		3、添加回退阶段;
		
可复用上次写的springboot-CD 。 由于环境内存有限,这里就没有部署prod环境下的springcloud ruoyi环境, 仍然使用dev环境。
复用后,需要修改的地方:

在这里插入图片描述
构建界面:
在这里插入图片描述
部署与回滚:
在这里插入图片描述
在这里插入图片描述
回滚:
在这里插入图片描述

10.1 对应的springcloud-ruoyi-CD代码:

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

            }
            
        }
        
        stage('部署应用至生产 K8S'){
            steps{
                withCredentials([file(credentialsId: 'kubeconfig', variable: 'KUBECONFIG')]) {
                    container('kubectl'){
                        sh 'mkdir -p ~/.kube && cp ${KUBECONFIG} ~/.kube/config'
                        sh 'kubectl set image deployment/${Image_Name} ${Image_Name}=${Full_Image} -n dev'
                    }
                }
    
            
            }
        }
        
        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 && cp ${KUBECONFIG} ~/.kube/config'
                               sh 'kubectl rollout undo deployment $Image_Name -n dev'
 
                            }
                            
                        }
                    }
                }
              }

            }
        }
        
    }//stages end
    
    
    
}//pipeline end

本文多数是贴代码,沿用了上篇Jenkins文章的基础/操作 https://blog.csdn.net/Nightwish5/article/details/130761785

END

2023年5月24日。 队友崔了,我什么时候提崔呢?88133

微服务是什么?微服务是用于构建应用程序的架构风格,一个大的系统可由一个或者多个微服务组成,微服务架构可将应用拆分成多个核心功能,每个功能都被称为一项服务,可以单独构建和部署,这意味着各项服务在工作和出现故障的时候不会相互影响。为什么要用微服务?单体架构下的所有代码模块都耦合在一起,代码量大,维护困难,想要更新一个模块的代码,也可能会影响其他模块,不能很好的定制化代码。微服务中可以有java编写、有Python编写的,他们都是靠restful架构风格统一成一个系统的,所以微服务本身与具体技术无关、扩展性强。大型电商平台微服务功能图为什么要将SpringCloud项目部署k8s平台?SpringCloud只能用在SpringBoot的java环境中,而kubernetes可以适用于任何开发语言,只要能被放进docker的应用,都可以在kubernetes上运行,而且更轻量,更简单。SpringCloud很多功能都跟kubernetes重合,比如服务发现,负载均衡,配置管理,所以如果把SpringCloud部署k8s,那么很多功能可以直接使用k8s原生的,减少复杂度。Kubernetes作为成熟的容器编排工具,在国内外很多公司、世界500强等企业已经落地使用,很多中小型公司也开始把业务迁移到kubernetes中。kubernetes已经成为互联网行业急需的人才,很多企业都开始引进kubernetes技术人员,实现其内部的自动化容器云平台的建设。对于开发、测试、运维、架构师等技术人员来说k8s已经成为的一项重要的技能,下面列举了国内外在生产环境使用kubernetes的公司: 国内在用k8s的公司:阿里巴巴、百度、腾讯、京东、360、新浪、头条、知乎、华为、小米、富士康、移动、银行、电网、阿里云、青云、时速云、腾讯、优酷、抖音、快手、美团等国外在用k8s的公司:谷歌、IBM、丰田、iphone、微软、redhat等整个K8S体系涉及到的技术众多,包括存储、网络、安全、监控、日志、DevOps、微服务等,很多刚接触K8S的初学者,都会感到无从下手,为了能让大家系统地学习,克服这些技术难点,推出了这套K8S架构师课程。Kubernetes的发展前景 kubernetes作为炙手可热的技术,已经成为云计算领域获取高薪要掌握的重要技能,在招聘网站搜索k8s,薪资水平也非常可观,为了让大家能够了解k8s目前的薪资分布情况,下面列举一些K8S的招聘截图: 讲师介绍:  先超容器云架构师、IT技术架构师、DevOps工程师,曾就职于世界500强上市公司,拥有多年一线运维经验,主导过上亿流量的pv项目的架构设计和运维工作;具有丰富的在线教育经验,对课程一直在改进和提高、不断的更新和完善、开发更多的企业实战项目。所教学员遍布京东、阿里、百度、电网等大型企业和上市公司。课程学习计划 学习方式:视频录播+视频回放+全套源码笔记 教学服务:模拟面试、就业指导、岗位内推、一对一答疑、远程指导 VIP终身服务:一次购买,终身学习课程亮点:1. 学习方式灵活,不占用工作时间:可在电脑、手机观看,随时可以学习,不占用上班时间2.老师答疑及时:老师24小时在线答疑3. 知识点覆盖全、课程质量高4. 精益求精、不断改进根据学员要求、随时更新课程内容5. 适合范围广,不管你是0基础,还是拥有工作经验均可学习:0基础1-3年工作经验3-5年工作经验5年以上工作经验运维、开发、测试、产品、前端、架构师其他行业转行做技术人员均可学习课程部分项目截图   课程大纲 k8s+SpringCloud全栈技术:基于世界500强的企业实战课程-大纲第一章 开班仪式老师自我介绍、课程大纲介绍、行业背景、发展趋势、市场行情、课程优势、薪资水平、给大家的职业规划、课程学习计划、岗位内推第二章 kubernetes介绍Kubernetes简介kubernetes起源和发展kubernetes优点kubernetes功能kubernetes应用领域:在大数据、5G、区块链、DevOps、AI等领域的应用章  kubernetes中的资源对象最小调度单元Pod标签Label和标签选择器控制器Replicaset、Deployment、Statefulset、Daemonset等四层负载均衡器Service第四章 kubernetes架构和组件熟悉谷歌的Borg架构kubernetes单master节点架构kubernetes多master节点高可用架构kubernetes多层架构设计原理kubernetes API介绍master(控制)节点组件:apiserver、scheduler、controller-manager、etcdnode(工作)节点组件:kube-proxy、coredns、calico附加组件:prometheus、dashboard、metrics-server、efk、HPA、VPA、Descheduler、Flannel、cAdvisor、Ingress     Controller。第五章 部署多master节点的K8S高可用集群(kubeadm)第六章 带你体验kubernetes可视化界面dashboard在kubernetes部署dashboard通过token令牌登陆dashboard通过kubeconfig登陆dashboard限制dashboard的用户权限在dashboard界面部署Web服务在dashboard界面部署redis服务第七章 资源清单YAML文件编写技巧编写YAML文件常用字段,YAML文件编写技巧,kubectl explain查看帮助命令,手把手教你创建一个Pod的YAML文件第八章 通过资源清单YAML文件部署tomcat站点编写tomcat的资源清单YAML文件、创建service发布应用、通过HTTP、HTTPS访问tomcat第九章  kubernetes Ingress发布服务Ingress和Ingress Controller概述Ingress和Servcie关系安装Nginx Ingress Controller安装Traefik Ingress Controller使用Ingress发布k8s服务Ingress代理HTTP/HTTPS服务Ingress实现应用的灰度发布-可按百分比、按流量分发第十章 私有镜像仓库Harbor安装和配置Harbor简介安装HarborHarbor UI界面使用上传镜像到Harbor仓库从Harbor仓库下载镜像第十一章 微服务概述什么是微服务?为什么要用微服务?微服务的特性什么样的项目适合微服务?使用微服务需要考虑的问题常见的微服务框架常见的微服务框架对比分析第十二章 SpringCloud概述SpringCloud是什么?SpringCloudSpringBoot什么关系?SpringCloud微服务框架的优缺点SpringCloud项目部署k8s的流程第十SpringCloud组件介绍服务注册与发现组件Eureka客户端负载均衡组件Ribbon服务网关Zuul熔断器HystrixAPI网关SpringCloud Gateway配置中心SpringCloud Config第十四章 将SpringCloud项目部署k8s平台的注意事项如何进行服务发现?如何进行配置管理?如何进行负载均衡?如何对外发布服务?k8s部署SpringCloud项目的整体流程第十五章 部署MySQL数据库MySQL简介MySQL特点安装部署MySQL在MySQL数据库导入数据对MySQL数据库授权第十六章 将SpringCLoud项目部署k8s平台SpringCloud的微服务电商框架安装openjdk和maven修改源代码、更改数据库连接地址通过Maven编译、构建、打包源代码在k8s部署Eureka组件在k8s部署Gateway组件在k8s部署前端服务在k8s部署订单服务在k8s部署产品服务在k8s部署库存服务第十七章 微服务的扩容和缩容第十八章 微服务的全链路监控什么是全链路监控?为什么要进行全链路监控?全链路监控能解决哪些问题?常见的全链路监控工具:zipkin、skywalking、pinpoint全链路监控工具对比分析第十九章 部署pinpoint服务部署pinpoint部署pinpoint agent在k8s中重新部署带pinpoint agent的产品服务在k8s中重新部署带pinpoint agent的订单服务在k8s中重新部署带pinpoint agent的库存服务在k8s中重新部署带pinpoint agent的前端服务在k8s中重新部署带pinpoint agent的网关和eureka服务Pinpoint UI界面使用第二十章 基于Jenkins+k8s+harbor等构建企业级DevOps平台第二十一章 基于Promethues+Alert+Grafana搭建企业级监控系统第二十二章 部署智能化日志收集系统EFK 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值