文章目录
Jenkins 微服务持续集成十(Spring Cloud 微服务一)
Jenkins 微服务持续集成十一(Spring Cloud 微服务二)
Jenkins 微服务持续集成十二(Spring Cloud 微服务三)
Jenkins 微服务持续集成十三(Spring Cloud 微服务四)
一、Jenkins + Docker + SpringCloud 部署方案优化
上面部署方案存在的问题:
- 一次只能选择一个微服务部署
- 只有一台生产部署服务器
- 每个微服务只有一个实例,容错率低
优化方案:
- 在一个 Jenkins 工程中可以选择多个微服务同时发布
- 在一个 Jenkins 工程中可以选择多台生产服务器同时部署
- 每个微服务都是以集群高可用形式部署
二、Jenkins + Docker + SpringCloud 集群部署流程说明
服务器名称 | IP 地址 | 安装的软件 |
---|---|---|
代码托管服务器 | 192.168.10.20 | Gitlab |
持续集成服务器 | 192.168.10.30 | Jenkins,Maven,Git,JDK,Sonarqube,Docker20.10.12-ce |
Docker 仓库服务器 | 192.168.10.40 | JDK,Tomcat,Docker20.10.12-ce,Harbor1.9.2 |
生产部署服务器一 | 192.168.10.50 | Docker20.10.12-ce |
生产部署服务器二 | 192.168.10.115 | Docker20.10.12-ce |
三、新增一台 Docker 服务器
参考:
安装 Docker
指向 harbor 地址
[root@c7-5 ~]#vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://4iv7219l.mirror.aliyuncs.com"],
"insecure-registries": ["192.168.10.40:85"]
}
[root@c7-5 ~]#systemctl restart docker
四、修改所有微服务配置
修改各个模块的 application.yml 的 eureka 生产服务器地址为集群模式,我们新增了一个地址。
1. eureka
# 集群版
spring:
application:
name: EUREKA-HA
---
server:
port: 10086
spring:
# 指定profile=eureka-server1
profiles: eureka-server1
eureka:
instance:
# 指定当profile=eureka-server1时,主机名是eureka-server1
hostname: 192.168.10.50
client:
service-url:
# 将自己注册到eureka-server1、eureka-server2这个Eureka上面去
defaultZone: http://192.168.10.50:10086/eureka/,http://192.168.10.115:10086/eureka/
---
server:
port: 10086
spring:
profiles: eureka-server2
eureka:
instance:
hostname: 192.168.10.115
client:
service-url:
defaultZone: http://192.168.10.50:10086/eureka/,http://192.168.10.115:10086/eureka/
2. zuul
server:
port: 10020 # 端口
# 基本服务信息
spring:
application:
name: tensquare-zuul # 服务ID
# Eureka配置
eureka:
client:
service-url:
defaultZone: http://192.168.10.50:10086/eureka,http://192.168.10.115:10086/eureka # Eureka访问地址
instance:
prefer-ip-address: true
# 修改ribbon的超时时间
ribbon:
ConnectTimeout: 1500 # 连接超时时间,默认500ms
ReadTimeout: 3000 # 请求超时时间,默认1000ms
# 修改hystrix的熔断超时时间
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMillisecond: 2000 # 熔断超时时长,默认1000ms
# 网关路由配置
zuul:
routes:
admin:
path: /admin/**
serviceId: tensquare-admin-service
gathering:
path: /gathering/**
serviceId: tensquare-gathering
# jwt参数
jwt:
config:
key: itcast
ttl: 1800000
3. admin
server:
port: 9001
spring:
application:
name: tensquare-admin-service #指定服务名
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.10.30:3306/tensquare_user?characterEncoding=UTF8
username: root
password: 123456
jpa:
database: mysql
show-sql: true
#Eureka配置
eureka:
client:
service-url:
defaultZone: http://192.168.10.50:10086/eureka,http://192.168.10.115:10086/eureka
instance:
lease-renewal-interval-in-seconds: 5 # 每隔5秒发送一次心跳
lease-expiration-duration-in-seconds: 10 # 10秒不发送就过期
prefer-ip-address: true
# jwt参数
jwt:
config:
key: itcast
ttl: 1800000
4. gathering
server:
port: 9002
spring:
application:
name: tensquare-gathering #指定服务名
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.10.30:3306/tensquare_gathering?characterEncoding=UTF8
username: root
password: 123456
jpa:
database: mysql
show-sql: true
#Eureka客户端配置
eureka:
client:
service-url:
defaultZone: http://192.168.10.50:10086/eureka,http://192.168.10.115:10086/eureka
instance:
lease-renewal-interval-in-seconds: 5 # 每隔5秒发送一次心跳
lease-expiration-duration-in-seconds: 10 # 10秒不发送就过期
prefer-ip-address: true
5. 提交微服务代码
五、设计 Jenkins 集群项目的构建参数
1. 安装 Extended Choice Parameter 插件支持多选框
2. 创建流水线项目
3. 添加参数
添加两个参数
字符串参数:分支名称
多选框:项目名称
tensquare_eureka_server@10086,tensquare_zuul@10020,tensquare_admin_service@9001,tensquare_gathering@9002
查看构建界面效果
六、构建测试
1. 检查代码循环构建
1.1 修改 jenkinsfile
//git 的凭证
def git_auth="0b127895-eb97-4f8f-b471-1277e5549b54"
//git 的 URL
def git_url="git@192.168.10.20:test-group/tensquare_back.git"
//镜像标签
def tag="latest"
//harbor的url地址
def harbor_url="192.168.10.40:85"
//镜像仓库名
def harbor_name="tensquare"
//harbor的凭证
def harbor_auth="9269e103-c017-4555-bdf0-d9d8b56bc889"
node {
//获取当前选择项目名称
def selectedProjectNames="${project_name}".split(",")
stage('pull code') {
//切换成变量,字符串符号使用双引号
checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]])
}
stage('check code') {
for(int i=0;i<selectedProjectNames.length;i++){
//项目信息 tensquare_eureka_server@10086
def projectInfo=selectedProjectNames[i]
//当前的项目名称
def currentProjectName="${projectInfo}".split("@")[0]
//当前的项目端口
def currentProjectPort="${projectInfo}".split("@")[1]
//定义SonarQubeScanner工具
def scannerHome = tool 'sonar-scanner'
//引用SonarQube系统环境
withSonarQubeEnv('sonarqube') {
sh """
cd ${currentProjectName}
${scannerHome}/bin/sonar-scanner
"""
}
}
}
//添加公共子工程
stage('make install public sub project') {
sh "mvn -f tensquare_common clean install"
}
//打包微服务,制作镜像
stage('make package') {
sh "mvn -f ${project_name} clean package dockerfile:build"
//定义镜像名称
def imageName="${project_name}:${tag}"
//对镜像打标签
sh "docker tag ${imageName} ${harbor_url}/${harbor_name}/${imageName}"
//镜像推送到harbor
withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
//登录harbor
sh "docker login -u ${username} -p ${password} ${harbor_url}"
//镜像上传
sh "docker push ${harbor_url}/${harbor_name}/${imageName}"
sh "echo 镜像上传成功"
//部署应用
sshPublisher(publishers: [sshPublisherDesc(configName: 'master_server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deploy.sh ${harbor_url} ${harbor_name} ${project_name} ${tag} ${port}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
提交 jenkinsfile
1.2 构建测试
我们选择两个服务构建,看看是否执行了两次代码审查,最后肯定构建失败(打 jar 包还有配置需要修改)。
2. 循环打包编译制作镜像
2.1 修改 jenkinsfile
//git 的凭证
def git_auth="0b127895-eb97-4f8f-b471-1277e5549b54"
//git 的 URL
def git_url="git@192.168.10.20:test-group/tensquare_back.git"
//镜像标签
def tag="latest"
//harbor的url地址
def harbor_url="192.168.10.40:85"
//镜像仓库名
def harbor_name="tensquare"
//harbor的凭证
def harbor_auth="9269e103-c017-4555-bdf0-d9d8b56bc889"
node {
//获取当前选择项目名称
def selectedProjectNames="${project_name}".split(",")
stage('pull code') {
//切换成变量,字符串符号使用双引号
checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]])
}
stage('check code') {
for(int i=0;i<selectedProjectNames.length;i++){
//项目信息 tensquare_eureka_server@10086
def projectInfo=selectedProjectNames[i]
//当前的项目名称
def currentProjectName="${projectInfo}".split("@")[0]
//当前的项目端口
def currentProjectPort="${projectInfo}".split("@")[1]
//定义SonarQubeScanner工具
def scannerHome = tool 'sonar-scanner'
//引用SonarQube系统环境
withSonarQubeEnv('sonarqube') {
sh """
cd ${currentProjectName}
${scannerHome}/bin/sonar-scanner
"""
}
}
}
//添加公共子工程
stage('make install public sub project') {
sh "mvn -f tensquare_common clean install"
}
//打包微服务,制作镜像
stage('make package images,push images') {
for(int i=0;i<selectedProjectNames.length;i++){
//项目信息 tensquare_eureka_server@10086
def projectInfo=selectedProjectNames[i]
//当前的项目名称
def currentProjectName="${projectInfo}".split("@")[0]
//当前的项目端口
def currentProjectPort="${projectInfo}".split("@")[1]
sh "mvn -f ${currentProjectName} clean package dockerfile:build"
//定义镜像名称
def imageName="${currentProjectName}:${tag}"
//对镜像打标签
sh "docker tag ${imageName} ${harbor_url}/${harbor_name}/${imageName}"
//镜像推送到harbor
withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
//登录harbor
sh "docker login -u ${username} -p ${password} ${harbor_url}"
//镜像上传
sh "docker push ${harbor_url}/${harbor_name}/${imageName}"
sh "echo 镜像上传成功"
}
}
//部署应用
sshPublisher(publishers: [sshPublisherDesc(configName: 'master_server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deploy.sh ${harbor_url} ${harbor_name} ${project_name} ${tag} ${port}", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
提交 jenkinsfile
2.2 构建测试
最后还是会报错,发布步骤还没改。
七、完成微服务多服务器远程发布
1. 配置远程部署服务器
从 Jenkins 服务器拷贝公钥到 docker2 远程服务器
ssh-copy-id 192.168.10.115
系统配置 -》添加远程服务器
2. 修改 Docker 配置信任 Harbor 私服地址
重启 Docker
3. 添加参数
tensquare_back_cluster 项目,先前的参数不要删了
多选框:部署服务器
页面效果
4. 修改 Jenkinsfile 构建脚本
//git 的凭证
def git_auth="0b127895-eb97-4f8f-b471-1277e5549b54"
//git 的 URL
def git_url="git@192.168.10.20:test-group/tensquare_back.git"
//镜像标签
def tag="latest"
//harbor的url地址
def harbor_url="192.168.10.40:85"
//镜像仓库名
def harbor_name="tensquare"
//harbor的凭证
def harbor_auth="9269e103-c017-4555-bdf0-d9d8b56bc889"
node {
//获取当前选择项目名称
def selectedProjectNames="${project_name}".split(",")
//获取当前选择服务器
def selectedServers="${publish_server}".split(",")
stage('pull code') {
//切换成变量,字符串符号使用双引号
checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]]])
}
stage('check code') {
for(int i=0;i<selectedProjectNames.length;i++){
//项目信息 tensquare_eureka_server@10086
def projectInfo=selectedProjectNames[i]
//当前的项目名称
def currentProjectName="${projectInfo}".split("@")[0]
//当前的项目端口
def currentProjectPort="${projectInfo}".split("@")[1]
//定义SonarQubeScanner工具
def scannerHome = tool 'sonar-scanner'
//引用SonarQube系统环境
withSonarQubeEnv('sonarqube') {
sh """
cd ${currentProjectName}
${scannerHome}/bin/sonar-scanner
"""
}
}
}
//添加公共子工程
stage('make install public sub project') {
sh "mvn -f tensquare_common clean install"
}
//打包微服务,制作镜像
stage('make package images,push images') {
for(int i=0;i<selectedProjectNames.length;i++){
//项目信息 tensquare_eureka_server@10086
def projectInfo=selectedProjectNames[i]
//当前的项目名称
def currentProjectName="${projectInfo}".split("@")[0]
//当前的项目端口
def currentProjectPort="${projectInfo}".split("@")[1]
sh "mvn -f ${currentProjectName} clean package dockerfile:build"
//定义镜像名称
def imageName="${currentProjectName}:${tag}"
//对镜像打标签
sh "docker tag ${imageName} ${harbor_url}/${harbor_name}/${imageName}"
//镜像推送到harbor
withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
//登录harbor
sh "docker login -u ${username} -p ${password} ${harbor_url}"
//镜像上传
sh "docker push ${harbor_url}/${harbor_name}/${imageName}"
sh "echo 镜像上传成功"
}
//遍历所有服务器,分别部署
for (int j=0;j<selectedServers.length;j++){
//获取当前服务器名称
def currentServerName=selectedServers[j]
//调用不同服务器模块内容--spring.profiles.active=eureka-server1/eureka-server2
def activeProfile="--spring.profiles.active="
//根据不同的服务器名称调用不同的服务器配置信息
if (currentServerName=="master_server"){
activeProfile=activeProfile+"eureka-server1"
}else if (currentServerName=="slave_server"){
activeProfile=activeProfile+"eureka-server2"
}
//部署应用
sshPublisher(publishers: [sshPublisherDesc(configName: "${currentServerName}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deployCluster.sh ${harbor_url} ${harbor_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)])
}
}
}
}
上传到 gitlab
5. 编写 deployCluster.sh 部署脚本
放到两台生产服务器中路径:/opt/jenkins_shell/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 tom -p Zc120604 $harbor_url
# 下载镜像
docker pull $imageName
# 启动容器
docker run -di -p $port:$port $imageName $profile
echo "容器启动成功"
记得授权
6. 构建测试
将其他服务构建