正文
5.5 完成微服务构建镜像,上传私服
//gitlab的凭证 def git_auth = "14ae86e8-c3b4-4d7d-afe1-8c23d9fed317" //gitlab的地址 def git_url = "git@192.168.5.4:root/tensquare_bak.git" // 构建版本的名称 def tag = "latest" //Harbor私服地址 def harbor_url = "192.168.5.5:8080" //Harbor的项目名称 def harbor_project_name = "tensquare" //Harbor的凭证 def harbor_auth = "cd0b948d-e82b-4c0c-8a7c-8c6b8fb5454b" node { // 获取当前选择的项目名称 def selectedProjects = "${project_name}".split(',') stage('拉取代码') { checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]]) } stage('代码审查') { for(int i=0;i<selectedProjects.size();i++){ //取出每个项目的名称和端口 def currentProject = selectedProjects[i]; //项目名称 def currentProjectName = currentProject.split('@')[0] //项目启动端口 def currentProjectPort = currentProject.split('@')[1] //定义当前Jenkins的SonarQubeScanner工具 def scannerHome = tool 'sonarqube-scanner' //引用当前Jenkins SonarQube环境 withSonarQubeEnv('sonarqube') { sh """ cd ${currentProjectName} ${scannerHome}/bin/sonar-scanner """ } } } stage('编译,安装公共工程') { //编译,安装公共工程 sh "mvn -f tensquare_common clean install" } stage('编译,打包微服务工程,上传镜像') { for(int i=0;i<selectedProjects.size();i++){ //取出每个项目的名称和端口 def currentProject = selectedProjects[i]; //项目名称 def currentProjectName = currentProject.split('@')[0] //项目启动端口 def currentProjectPort = currentProject.split('@')[1] //编译,构建本地镜像 sh "mvn -f ${currentProjectName} clean package dockerfile:build" //定义镜像名称 def imageName = "${currentProjectName}:${tag}" //给镜像打标签 sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}" //登录Harbor,并上传镜像 withCredentials([usernamePassword(credentialsId: "${harbor_auth}",passwordVariable: 'password', usernameVariable: 'username')]) { //登录 sh "docker login -u ${username} -p ${password} ${harbor_url}" //上传镜像 sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}" } //删除本地镜像 sh "docker rmi -f ${imageName}" sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}" } } }
2)编译部署 for循环遍历分割详解
node { // 获取当前选择的项目名称 def selectedProjects = "${project_name}".split(',') stage('代码审查') { for(int i=0;i<selectedProjects.size();i++){ //取出每个项目的名称和端口 def currentProject = selectedProjects[i]; //项目名称 def currentProjectName = currentProject.split('@')[0] //项目启动端口 def currentProjectPort = currentProject.split('@')[1] //定义当前Jenkins的SonarQubeScanner工具 def scannerHome = tool 'sonarqube-scanner' //引用当前Jenkins SonarQube环境 withSonarQubeEnv('sonarqube') { sh """ cd ${currentProjectName} ${scannerHome}/bin/sonar-scanner """ } } }
首先在node中定义获取当前项目的变量 def selectedProjects = "${project_name}".split(',')
- selectedProjects:这个是变量名称可以随意定义,
- ${project_name}:这里指的Extended Choice Parameter参数化构建中定义的项目名称
- .split(','):这是一个分割语法,意思为以","来进行分割,因为之前在Extended Choice Parameter参数化变量中是这样定义的,同理,如果这里更换为其他的分割服务符号,那么在.split(',')更换为对应的分割符号
tensquare_eureka_server@10086,tensquare_zuul@10020,tensquare_admin_service@9001,tensquare_gathering@9002
这时就可以获取到对应的微服务,比如这个样子
selectedProjects=tensquare_eureka_server@10086 selectedProjects=tensquare_zuul@10020 selectedProjects=tensquare_admin_service@9001 selectedProjects=tensquare_gathering@9002
目前已经成功获取到对应的微服务,但是还是无法直接使用,因为服务名称和端口号为同一个字符串并且使用"@"符号进行连接,那么这里可以使用之前的方法,对"@"符号进行分割再次分割,得到最后的服务名称和端口
for(int i=0;i<selectedProjects.size();i++){ 基础的for循环取出语句
//取出每个项目的名称和端口 def currentProject = selectedProjects[i]; 这里可以理解为,逐步取出selectedProjects变量中每个项目的名称及端口号
//项目名称 def currentProjectName = currentProject.split('@')[0] 对currentProject变量以@符号进行分割,并获取第0个字符,可以理解为获取@符号左边的字符,可以得出服务名称
//项目启动端口 def currentProjectPort = currentProject.split('@')[1] 对currentProject变量以@符号进行分割,并获取第1个字符,可以理解为获取@符号右边的字符,可以得出服务名称
经过两次分割(第一个以","分割从Extended Choice Parameter参数化构建中获取服务,第二次以"@"分割分割获取到的服务名称和端口)获得到
项目名称变量(currentProjectName)
项目端口号变量(currentProjectPort)
在进行代码质量扫描时更换相对于的变量,并且在编译,打包微服务工程,上传镜像时使用的for循环分割与此处逻辑一致
3)编译镜像测试
5.6 完成微服务多服务器远程发布
1)环境配置
- 安装docker
- 拷贝公钥到远程服务器
- 系统配置->添加远程服务器
2)添加参数
多选框:部署服务器
master_server,slave_server_01
最终效果:
3)修改Jenkinsfile构建脚本
//gitlab的凭证 def git_auth = "14ae86e8-c3b4-4d7d-afe1-8c23d9fed317" //gitlab的地址 def git_url = "git@192.168.5.4:root/tensquare_bak.git" // 构建版本的名称 def tag = "latest" //Harbor私服地址 def harbor_url = "192.168.5.5:8080" //Harbor的项目名称 def harbor_project_name = "tensquare" //Harbor的凭证 def harbor_auth = "cd0b948d-e82b-4c0c-8a7c-8c6b8fb5454b" node { // 获取当前选择的项目名称 def selectedProjects = "${project_name}".split(',') // 获取当前选择的服务器名称 def selectedServers = "${publish_server}".split(',') stage('拉取代码') { checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]]) } stage('代码审查') { for(int i=0;i<selectedProjects.size();i++){ //取出每个项目的名称和端口 def currentProject = selectedProjects[i]; //项目名称 def currentProjectName = currentProject.split('@')[0] //项目启动端口 def currentProjectPort = currentProject.split('@')[1] //定义当前Jenkins的SonarQubeScanner工具 def scannerHome = tool 'sonarqube-scanner' //引用当前Jenkins SonarQube环境 withSonarQubeEnv('sonarqube') { sh """ cd ${currentProjectName} ${scannerHome}/bin/sonar-scanner """ } } } stage('编译,安装公共工程') { //编译,安装公共工程 sh "mvn -f tensquare_common clean install" } stage('编译,打包微服务工程,上传镜像') { for(int i=0;i<selectedProjects.size();i++){ //取出每个项目的名称和端口 def currentProject = selectedProjects[i]; //项目名称 def currentProjectName = currentProject.split('@')[0] //项目启动端口 def currentProjectPort = currentProject.split('@')[1] //编译,构建本地镜像 sh "mvn -f ${currentProjectName} clean package dockerfile:build" //定义镜像名称 def imageName = "${currentProjectName}:${tag}" //给镜像打标签 sh "docker tag ${imageName} ${harbor_url}/${harbor_project_name}/${imageName}" //登录Harbor,并上传镜像 withCredentials([usernamePassword(credentialsId: "${harbor_auth}",passwordVariable: 'password', usernameVariable: 'username')]) { //登录 sh "docker login -u ${username} -p ${password} ${harbor_url}" //上传镜像 sh "docker push ${harbor_url}/${harbor_project_name}/${imageName}" } //删除本地镜像 sh "docker rmi -f ${imageName}" sh "docker rmi -f ${harbor_url}/${harbor_project_name}/${imageName}" //远程部署服务器应用 for(int j=0;j<selectedServers.size();j++){ //每个服务名称 def currentServer = selectedServers[j] //添加微服务运行时的参数:spring.profiles.active def activeProfile = "--spring.profiles.active=" if(currentServer=="master_server"){ activeProfile = activeProfile+"eureka-server1" }else if(currentServer=="slave_server_01"){ activeProfile = activeProfile+"eureka-server2" } // 远程触发部署脚本 sshPublisher(publishers: [sshPublisherDesc(configName: "${currentServer}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deployCluster.sh $harbor_url $harbor_project_name $currentProjectName $tag $currentProjectPort $activeProfile", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) } echo "${currentProjectName}完成编译,构建镜像!" } } }
4)远程部署 for循环遍历分割详解
for(int j=0;j<selectedServers.size();j++){ //每个服务名称 def currentServer = selectedServers[j] //添加微服务运行时的参数:spring.profiles.active def activeProfile = "--spring.profiles.active=" if(currentServer=="master_server"){ activeProfile = activeProfile+"eureka-server1" }else if(currentServer=="slave_server_01"){ activeProfile = activeProfile+"eureka-server2" } }
这里远程部署到对应的服务器和之前编译,打包微服务工程,上传镜像时使用的for循环分割逻辑基本一致,
首先在node中定义获取当前项目的变量 def selectedServers = "${publish_server}".split(',')
- selectedServers:这个是变量名称可以随意定义,
- ${publish_server}:这里指的Extended Choice Parameter参数化构建中定义的服务器名称
- .split(','):这是一个分割语法,意思为以","来进行分割,因为之前在Extended Choice Parameter参数化变量中是这样定义的,同理,如果这里更换为其他的分割服务符号,那么在.split(',')更换为对应的分割符号
master_server,slave_server_01
这时就可以获取到对应的微服务,比如这个样子
selectedServers = master_server
selectedServers = slave_server_01
目前已经成功获取到对应服务器名称,但是还是无法直接使用,因为要对应之前在eureka网关中定义的服务器地址来进行远程部署
for(int j=0;j<selectedServers.size();j++){ 基础的for循环取出语句,这里不能再次使用i,因为在之前已经使用过
//每个服务名称 def currentServer = selectedServers[j] 这里可以理解为,逐步取出selectedServers变量中服务器名称
//添加微服务运行时的参数:spring.profiles.active def activeProfile = "--spring.profiles.active=" 因为要读取服务中的配置文件,所以这里使用变量(activeProfile)定义了参数(--spring.profiles.active)
//if 判断语句 if(currentServer == "master_server"){ activeProfile = activeProfile+"eureka-server1" }else if(currentServer == "slave_server_01"){ activeProfile = activeProfile+"eureka-server2" }
//if 判断语句 if(currentServer == "master_server"){ activeProfile = activeProfile+"eureka-server1" }else if(currentServer == "slave_server_01"){ activeProfile = activeProfile+"eureka-server2" }
这里可以理解为 当currentServer等于master_server时,就读取配置文件中的eureka-server1 当currentServer等于slave_server_01时,就读取配置文件中的eureka-server2
5)编写deployCluster.sh部署脚本
#! /bin/sh #接收外部参数 harbor_url=$1 harbor_project_name=$2 project_name=$3 tag=$4 port=$5 profile=$6 imageName=$harbor_url/$harbor_project_name/$project_name:$tag echo "$imageName" #查询容器是否存在,存在则删除 containerId=`docker ps -a | grep -w ${project_name}:${tag} | awk '{print $1}'` if [ "$containerId" != "" ] ; then #停掉容器 docker stop $containerId #删除容器 docker rm $containerId echo "成功删除容器" fi #查询镜像是否存在,存在则删除 imageId=`docker images | grep -w $project_name | awk '{print $3}'` if [ "$imageId" != "" ] ; then #删除镜像 docker rmi -f $imageId echo "成功删除镜像" fi # 登录Harbor私服 docker login -u admin -p Harbor12345 $harbor_url # 下载镜像 docker pull $imageName # 启动容器 docker run -di -p $port:$port $imageName $profile echo "容器启动成功"
登录Harbor私服
docker login -u admin -p Harbor12345 $harbor_url
下载镜像
docker pull $imageName
启动容器
docker run -di -p p o r t : port:port:port $imageName $profile
echo “容器启动成功”
2)修改Dockerfile
FROM nginx COPY ./dist /usr/share/nginx/html COPY ./nginx.conf /etc/nginx/nginx.conf EXPOSE 80
3)修改代码连接后端微服务服务
4)项目测试
由于是测试阶段,如果前端容器没有开通85端口,那么一定会造成Network Error网关错误,正常环境中建议部署一台专门的转发服务器,来达成轮询访问的效果
造成这种原因是因为转发请求并没有到达容器内的Nginx 所有并不能完成转发,本次测试也是在启动容器前,修改了容器启动脚本,开通了85端口,所以可以正常访问
5.8 Jenkins的Master-Slave分布式构建
5.8.1 什么是Master-Slave分布式构建
Jenkins的Master-Slave分布式构建,就是通过将构建过程分配到从属Slave节点上,从而减轻Master节点的压力,而且可以同时构建多个,有点类似负载均衡的概念。
5.8.2 如何实现Master-Slave分布式构建
1)开启代理程序的TCP端口
Manage Jenkins -> Configure Global Security
2)新建节点
Manage Jenkins—Manage Nodes—新建节点
3)节点部署
下载jar包到slave节点服务器(slave节点服务器必须有java环境)
java -jar agent.jar -jnlpUrl http://192.168.5.3:808/computer/slave_01/jenkins-agent.jnlp -secret 2d2371ae0ed0e91ad74c67aabecb5af24d001ff7f5fb440cb5bc663b57a52898 -workDir "/root/jenkins"
4)完成部署
5)拉取测试