1 集群版持续集成流程
1.1 单机版问题
上面部署方案存在的问题:
1)一次只能选择一个微服务部署
2)只有一台生产者部署服务器
3)每个微服务只有一个实例,容错率低
1.2 优化方案
1)在一个Jenkins工程中可以选择多个微服务同时发布
2)在一个Jenkins工程中可以选择多台生产服务器同时部署
3)每个微服务都是以集群高可用形式部署
2 部署方案优化
2.1 注册中心配置
在启动微服务的时候,加入参数: spring.profiles.active 来读取对应的配置
# 集群版
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.137.129
client:
service-url:
# 将自己注册到eureka-server1、eureka-server2这个Eureka上面去
defaultZone: http://192.168.137.129:10086/eureka/,http://192.168.137.128:10086/eureka/
---
server:
port: 10086
spring:
profiles: eureka-server2
eureka:
instance:
hostname: 192.168.137.128
client:
service-url:
defaultZone: http://192.168.137.129:10086/eureka/,http://192.168.137.128:10086/eureka/
2.2 其他微服务配置
除了Eureka注册中心以外,其他微服务配置都需要加入所有Eureka服务
#Eureka配置
eureka:
client:
service-url:
defaultZone: http://192.168.137.128:10086/eureka,http://192.168.137.129:10086/eureka
instance:
lease-renewal-interval-in-seconds: 5 # 每隔5秒发送一次心跳
lease-expiration-duration-in-seconds: 10 # 10秒不发送就过期
prefer-ip-address: true
2.3 安装Extended Choice Parameter插件
支持多选框
2.4 创建流水线项目
tensquare_backend_cluster
2.5 添加参数
字符串参数:分支名称
多选框:项目名称
最后效果
2.6 微服务构建镜像,上传私服
此时镜像启动是报错的,因为注册中心配置做了修改
2.7 微服务多服务器远程发布
2.7.1 配置远程部署服务器
拷贝公钥到远程服务器
ssh-copy-id 192.168.137.128
系统配置->添加远程服务器
2.7.2 配置信任Harbor私服地址
"insecure-registries": ["192.168.137.130"]
2.7.3 添加参数
多选框:部署服务器
最终效果
2.7.4 修改Jenkinsfile
2.7.5 deployCluster.sh部署脚本
1、上传deploy.sh文件到/opt/jenkins_shell目录下,且文件至少有执行权限!
2、vim 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 "容器启动成功"
3、授予执行权限
chmod a+x deployCluster.sh
2.7.6 构建效果
2.8 注册中心
2.9 Nginx+Zuul集群实现高可用网关
2.9.1 nginx新增配置文件
cat /data/nginxConf/nginxZuul.conf
log_format zuul '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"'
'"$upstream_addr" "$upstream_bytes_sent" ';
access_log /var/log/nginx/access.log zuul;
upstream zuulServer{
hash $remote_addr consistent;
server 192.168.137.128:10020 weight=1;
server 192.168.137.129:10020 weight=1;
}
server {
listen 9091 default_server;
listen [::]:9091 default_server;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
location / {
### 指定服务器负载均衡服务器
proxy_pass http://zuulServer/;
}
}
2.9.2 重启nginx容器
更新nginx容器
docker rm -f tensquare_web_nginx
docker run -itd -p 9090:80 -p 9091:9091 -v /data/nginxConf/nginxZuul.conf:/etc/nginx/conf.d/nginxZuul.conf -v /data/nginx:/usr/share/nginx/html --name tensquare_web_nginx hub.atomgit.com/amd64/nginx:1.25.2-perl
2.9.3 修改前端Nginx的访问地址
2.9.4 重新构建前端
docker logs -f tensquare_web_nginx
3 附录补充
3.1 后端Jenkinsfile
//gitlab凭据
def git_auth='8c8a0774-7d4c-48a9-8f52-dd1a04f71320'
//sonar token凭据
def sonar_token='869f9770-0467-496a-84f9-d7b2c729fd8f'
//构建版本的名称
def tag = "latest"
//Harbor私服地址
def harbor_url = '192.168.137.130'
//Harbor项目名称
def harbor_project_name = 'tensquare'
//Harbor凭据
def harbor_auth = 'e70d6e6b-5c46-4dd4-b96a-fb59f6d2443c'
node {
//把选择的项目信息转换为数组
def selectedProjects = "${project_name}".split(',')
//把选择的服务信息转换为数组
def selectedServers = "${publish_server}".split(',')
stage('拉取代码') {
checkout scmGit(branches: [[name: "*/${branch}"]],
extensions: [cleanBeforeCheckout()],
userRemoteConfigs: [[credentialsId: "${git_auth}",
url: 'http://192.168.137.129/root/tensquare_backend.git']])
}
stage('代码审查') {
def scannerHome = tool 'sonar-scanner'
withSonarQubeEnv(credentialsId: "${sonar_token}") {
for (int i=0;i<selectedProjects.size();i++){
//取出每个项目的名称和端口
def currentProject = selectedProjects[i]
//项目名称
def currentProjectName = currentProject.split('@')[0]
//项目端口
def currentProjectPort = currentProject.split('@')[1]
sh """
cd ${currentProjectName}
sed -i "s#project_name#${currentProjectName}#g" sonar-project.properties
${scannerHome}/bin/sonar-scanner
"""
echo "${currentProjectName} 完成代码审查"
}
}
}
stage('整体构建') {
def mvnHome = tool 'M2'
//编译、整体构建,每个stage都会重新回到${WORKSPACE}
sh " ${mvnHome}/bin/mvn clean package "
}
stage('构建镜像、推送harbor') {
for (int i=0;i<selectedProjects.size();i++){
//取出每个项目的名称和端口
def currentProject = selectedProjects[i]
//项目名称
def currentProjectName = currentProject.split('@')[0]
//项目端口
def currentProjectPort = currentProject.split('@')[1]
def imageName = "${harbor_url}/${harbor_project_name}/${currentProjectName}:${tag}"
//生成镜像
sh " cd ${currentProjectName} && docker build -t ${imageName} --build-arg JAR_FILE=target/${currentProjectName}-1.0-SNAPSHOT.jar . "
//登录harbor并上传镜像
withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
//登录
sh " docker login -u ${username} -p ${password} ${harbor_url} "
//上传镜像
sh " docker push ${imageName} "
}
//删除本地镜像
sh " docker rmi ${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'){
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} 完成编译,构建镜像"
}
}
}