Jenkins+Docker+SpringCloud微服务持续集成
- Jenkins+Docker+SpringCloud持续集成流程说明
- SpringCloud微服务源码概述
- 本地运行微服务
- 本地部署微服务
- Docker安装和Dockerfile制作微服务镜像
- Harbor镜像仓库安装及使用
- 在Harbor创建用户和项目
- 上传镜像到Harbor
- 从Harbor下载镜像
- 微服务持续集成
- 项目代码上传到Gitlab
- 从Gitlab拉取项目源码
- 提交到SonarQube代码审查
- 微服务打包构建
- 使用Dockerfile编译生成镜像
- 上传到Harbor镜像仓库
- 拉取镜像和发布应用
- 配置远程部署服务器
- 编写远程部署脚本
- 编写Jenkins流水线脚本
- 部署和测试所有微服务
- 部署前端静态web网站
- Jenkins+Docker+SpringCloud部署方案优化
- Jenkins+Docker+SpringCloud集群部署流程说明
- 修改所有微服务配置
- 设计Jenkins集群项目的构建参数
- 多个项目提交SonarQube进行代码审查
- 多个项目打包及构建上传镜像
- 把Eureka注册中心集群部署到多台服务器
- Nginx+Zuul集群实现高可用网关
Jenkins+Docker+SpringCloud持续集成流程说明
- 开发人员每天把代码提交到Gitlab代码仓库
- Jenkins从Gitlab中拉取项目源码,编译并打成Jar包,然后构建成Docker镜像,将镜像上传到Harbor私有仓库
- Jenkins发送SSH远程命令,让生产部署服务器到Harbor私有仓库拉取镜像到本地,然后创建容器
- 最后,用户可以访问到容器
服务器列表
服务器名称 | IP地址 | 安装软件 | 硬件配置 | 系统 |
---|---|---|---|---|
代码托管服务器 | 192.168.100.240 | Gitlab-12.9.9 | 2核4G | CentOS Linux release 7.5.1804 |
持续集成服务器 | 192.168.100.241 | Jenkins 2.401.2,JDK 11,JDK 1.8,Maven 3.8.8,Git 1.8.3.1,Docker 20.10.24-ce | 2核4G | CentOS Linux release 7.5.1804 |
代码审查服务器 | 192.168.100.242 | mysql 5.7.43,sonarqube 6.7.7 | 1核2G | CentOS Linux release 7.5.1804 |
Harbor仓库服务器 | 192.168.100.251 | Docker 20.10.24-ce,Harbor 1.9.2 | 1核2G | CentOS Linux release 7.5.1804 |
生产部署服务器 | 192.168.100.252 | Docker 20.10.24-ce | 1核2G | CentOS Linux release 7.5.1804 |
SpringCloud微服务源码概述
项目架构:前后端分离
后端技术栈:SpringBoot+SpringCloud+SpringDataJpa(Spring全家桶)
微服务项目结构:
- tensquare_parent:父工程,存放基础配置
- tensquare_common:通用工程,存放工具类
- tensquare_eureka_server:SpringCloud的Eureka注册中心
- tensquare_zuul:SpringCloud的网关服务
- tensquare_admin_service:基础权限认证中心,负责用户认证(使用JWT认证)
- tensquare_gathering:一个简单的业务模块,活动微服务相关逻辑
数据库结构:
- tensquare_user:用户认证数据库,存放用户账户数据。对应tensquare_admin_service微服务
- tensquare_gathering:活动微服务数据库。对应tensquare_gathering微服务
微服务配置分析:
- tensquare_eureka
- tensquare_zuul
- tensquare_admin_service
- tensquare_gathering
本地运行微服务
本地运行微服务,按顺序逐一启动
运行eureka服务器
通过浏览器进入localhost:10086
开启zuul网关
开启权限中心
开启微服务
刷新Eureka
页面业务都成功部署
本地部署微服务
SpringBoot
微服务项目打包,必须导入该插件
在pom.xml里添加
# 在Pom.xml添加
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
项目打包
打包后在target下产生jar包
本地运行微服务的jar包
java -jar xxx.jar
查看效果
Docker安装和Dockerfile制作微服务镜像
Docker安装
# 卸载旧版本
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
# 删除docker的所有镜像和容器
rm -rf /var/lib/docker
# 安装基本的依赖包
sudo yum install yum-utils device-mapper-persistent-data lvm2 -y
# 设置镜像仓库 Docker yum源
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 更新yum软件包索引
sudo yum makecache fast
# 列出需要安装的版本列表
yum list docker-ce --showduplicates | sort -r
# 安装docker-ce-20.10
yum install docker-ce-20.10.* docker-ce-cli-20.10.* containerd -y
mkdir /etc/docker -p
cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors": ["https://k68iw3ol.mirror.aliyuncs.com"]
}
EOF
systemctl daemon-reload && systemctl enable --now docker
部分问题总结
安装docker后无法启动的问题排查
之前所有的安装设置daemon文件为daemon.json
格式,但在今天设置后发生报错,并且为之后docker添加harbor地址信任后重启docker埋下伏笔,一直找不到原因,无数次修改json
文件,问题的最终在于,参考文章:CentOS7 启动docker.service失败,但又无法连接harbor私有仓库。
cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors": [
"https://k68iw3ol.mirror.aliyuncs.com",
"https://docker.mirrors.ustc.edu.cn",
"http://hub-mirror.c.163.com"
],
"exec-opts":["native.cgroupdriver=systemd"]
}
EOF
Dockerfile制作微服务镜像
命令 | 作用 |
---|---|
FROM image_name:tag | |
MAINTAINER user_name | 声明镜像的作者 |
ENV key value | 设置环境变量 (可以写多条) |
RUN command | 编译镜像时运行的脚本(可以写多条) |
CMD | 设置容器的启动命令 |
ENTRYPOINT | 设置容器的入口程序 |
ADD source_dir/file dest_dir/file | 将宿主机的文件复制到容器内,如果是一个压缩文件,将会在复制后自动解压 |
COPY source_dir/file dest_dir/file | 和ADD相似,但是如果有压缩文件并不能解压 |
WORKDIR path_dir | 设置工作目录 |
ARG | 设置编译镜像时加入的参数 |
VOLUMN | 设置容器的挂载卷 |
利用Dockerfile制作一个Eureka注册中心的镜像
# 将eureka的jar包上传至服务器
mkdir /root/eureka
mv tensquare_eureka_server-1.0-SNAPSHOT.jar /root/eureka/
cd /root/eureka/
vim Dockerfile
FROM openjdk:8-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
EXPOSE 10086
ENTRYPOINT ["java","-jar","/app.jar"]
docker build --build-arg JAR_FILE=tensquare_eureka_server-1.0-SNAPSHOT.jar -t eureka:v1 .
# 查看镜像是否创建成功
[root@jenkins eureka]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
eureka v1 0147238f31a1 5 seconds ago 150MB
openjdk 8-jdk-alpine a3562aa0b991 4 years ago 105MB
# 创建容器验证镜像是否成功
docker run -id --name=eureka -p 10086:10086 eureka:v1
# 查看日志
docker logs -f 容器id
构建镜像
浏览器访问
Harbor镜像仓库安装及使用
Harbor简介
Harbor(港口,港湾)是一个用于存储和分发Docker镜像的企业级Registry服务器。
除了Harbor这个私有镜像仓库之外,还有Docker官方提供的Registry。相对Registry,Harbor具有很多优势:
- 提供分层传输机制,优化网络传输 Docker镜像是是分层的,而如果每次传输都使用全量文件(所以用FTP的方式并不适合),显然不经济。必须提供识别分层传输的机制,以层的UUID为标识,确定传输的对象。
- 提供WEB界面,优化用户体验 只用镜像的名字来进行上传下载显然很不方便,需要有一个用户界面可以支持登陆、搜索功能,包括区分公有、私有镜像。
- 支持水平扩展集群 当有用户对镜像的上传下载操作集中在某服务器,需要对相应的访问压力作分解。
- 良好的安全机制,企业中的开发团队有很多不同的职位,对于不同的职位人员,分配不同的权限,具有更好的安全性。
Harbor安装
Harbor需要安装在192.168.100.251
上
参考文章:Harbor prepare脚本分析
# 安装docker
# 参考之前的安装过程
# 安装docker-compose
sudo curl -L "https://kgithub.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
docker-compose version
# 安装harbor
wget https://ghproxy.com/https://github.com/goharbor/harbor/releases/download/v1.9.2/harbor-offline-installer-v1.9.2.tgz
mkdir /opt/harbor
tar xf harbor-offline-installer-v1.9.2.tgz -C /opt
# 修改Harbor的配置
vim harbor.yml
5 hostname: 192.168.100.251
10 port: 85
27 harbor_admin_password: Harbor12345 # 也可更改harbor默认密码
# 产生配置脚本
./prepare
# 安装Harbor
./install.sh
# 启动Harbor
docker-compose up -d # 启动
docker-compose stop # 停止
docker-compose restart # 重新启动
# 完成安装后,通过192.168.100.251:85进行浏览器访问
配置Harbor开机随系统启动,服务器重启后默认Harbor无法正常启动,可以使用systemd管理Harbor启停:
参考文章:harbor安装并配置https
# 先停止已经启动的harbor
docker-compose -f docker-compose.yml down
cat > /usr/lib/systemd/system/harbor.service << 'EOF'
[Unit]
Description=Harbor
After=docker.service systemd-networkd.service systemd-resolved.service
Requires=docker.service
Documentation=https://github.com/goharbor/harbor
[Service]
Type=simple
Restart=on-failure
RestartSec=5
Environment=Harbor_Path=/opt/harbor
ExecStart=/usr/local/bin/docker-compose -f ${Harbor_Path}/docker-compose.yml up
ExecStop=/usr/local/bin/docker-compose -f ${Harbor_Path}/docker-compose.yml down
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload && systemctl enable harbor --now
在Harbor创建用户和项目
创建项目
Harbor的项目分为公开和私有的:
- 公开项目:所有用户都可以访问,通常存放公共的镜像,默认有一个library公开项目。
- 私有项目:只有授权用户才可以访问,通常存放项目本身的镜像。
我们可以为微服务项目创建一个新的项目:
创建用户
给私有项目分配用户
进入tensquare项目->成员
角色 | 权限说明 |
---|---|
项目管理员 | 除了读写权限,同时拥有用户管理/镜像扫描等管理权限 |
维护人员 | 对于指定项目拥有读写权限,创建 Webhooks |
开发人员 | 对于指定项目拥有读写权限 |
访客 | 对于指定项目拥有只读权限 |
上传镜像到Harbor
# 把Harbor地址加入到Docker信任列表
vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://k68iw3ol.mirror.aliyuncs.com"],
"insecure-registries": ["192.168.100.251:85"]
}
systemctl daemon-reload && systemctl restart docker
# 登录Harbor
docker login -u haibo -p LIUhaibo123 192.168.100.251:85
# 给镜像打上标签
[root@jenkins eureka]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
eureka v1 0147238f31a1 36 minutes ago 150MB
openjdk 8-jdk-alpine a3562aa0b991 4 years ago 105MB
docker tag eureka:v1 192.168.100.251:85/tensquare/eureka:v1
[root@jenkins eureka]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
192.168.100.251:85/tensquare/eureka v1 0147238f31a1 37 minutes ago 150MB
eureka v1 0147238f31a1 37 minutes ago 150MB
openjdk 8-jdk-alpine a3562aa0b991 4 years ago 105MB
# 推送镜像
docker push 192.168.100.251:85/tensquare/eureka:v1
此时Harbor仓库有了上传的镜像
从Harbor下载镜像
需求:在192.168.100.252服务器(生产部署服务器)完成从192.168.100.251(Harbor镜像仓库)下载镜像
# 安装Docker,并启动Docker(已完成)
# 修改Docker配置添加对Harbor镜像仓库的信任
vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://k68iw3ol.mirror.aliyuncs.com"],
"insecure-registries": ["192.168.100.251:85"]
}
systemctl daemon-reload && systemctl restart docker
# 先登录,再从Harbor下载镜像
docker login -u haibo -p LIUhaibo123 192.168.100.251:85
docker pull 192.168.100.251:85/tensquare/eureka:v1
docker images
微服务持续集成
项目代码上传到Gitlab
参考之前的步骤,上传后台微服务和前端web网站代码。
上传后端代码
创建tensquare
项目组
将张三加入项目组,并赋予owner
权限
创建项目
此时tensquare
组里就有新建的两个项目
上传代码,通过IDEA操作
首先上传后端微服务代码
启用版本控制集成
新建一个仓库地址
提交代码到仓库
项目成功提交
上传前端代码
通过Tortoisegit
上传
Git小乌龟的安装及使用
在代码目录下右键创建本地仓库
提交代码到本地仓库,右键提交–>master
管理远程地址
使用用户zhangsan
执行代码推送
前往Gitlab代码仓库查看
至此,所有代码上传完毕!
从Gitlab拉取项目源码
Jenkins创建后端流水线项目
添加参数化构建
添加字符参数
配置从版本控制里抓取pipeline
脚本
使用流水线语法生成流水线脚本
// 定义变量以及引用变量,这样维护性好一点
// 引用凭证ID最好使用双引号 ""
// git凭证ID
def git_auth = "a4d0066f-6c58-4ab0-b714-6a24b3f5bc90"
// git的URL地址
def git_url = "git@192.168.100.240:tensquare_group/tensquare_back.git"
node {
stage('拉取代码') {
checkout scmGit(branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]])
}
}
上传Jenkinsfile文件至仓库,以便Jenkins项目能够获取脚本文件执行项目构建
进入代码仓库查看
开始构建项目,默认使用master
分支
提交到SonarQube代码审查
进入tensquare_back
项目,添加两个参数
在参数化构建过程添加(Choice Parameter)
添加需要选择审查的项目名称
点击构建查看效果
项目的根目录下添加sonar-project.properties
首先在eureka_server
根目录创建
# must be unique in a given SonarQube instance
sonar.projectKey=tensquare_eureka_server
# this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1.
sonar.projectName=tensquare_eureka_server
sonar.projectVersion=1.0
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
# This property is optional if sonar.modules is set.
sonar.sources=.
sonar.exclusions=**/test/**,**/target/**
sonar.java.binaries=.
sonar.java.source=1.8
sonar.java.target=1.8
#sonar.java.libraries=**/target/classes/**
# Encoding of the source code. Default is default system encoding
sonar.sourceEncoding=UTF-8
然后再相应的目录里添加不同的sonar-project.properties
,注意:修改sonar.projectKey
和sonar.projectName
修改Jenkinsfile
// git凭证ID
def git_auth = "a4d0066f-6c58-4ab0-b714-6a24b3f5bc90"
// git的URL地址
def git_url = "git@192.168.100.240:tensquare_group/tensquare_back.git"
node {
stage('拉取代码') {
checkout scmGit(branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]])
}
stage('代码审查') {
// 定义当前Jenkins的SonarQubeScanner工具的环境,位于全局工具配置的SonarQube Scanner 的name定义为'sonar-scanner'
def scannerHome = tool 'sonar-scanner'
// 引用当前JenkinsSonarQube的环境,系统配置的SonarQube servers定义的name以及代码审查服务器位置
withSonarQubeEnv('sonarqube') {
sh """
cd ${project_name}
${scannerHome}/bin/sonar-scanner
"""
}
}
}
由于修改了很多文件,统一提交至仓库
构建项目并测试代码审查能否完成
sonarqube后台查看审查结果
将其他三个项目也做代码审查
微服务打包构建
编译构建之前需要对一个公共子工程编译安装
//Jenkinsfile文件添加步骤
// git凭证ID
def git_auth = "a4d0066f-6c58-4ab0-b714-6a24b3f5bc90"
// git的URL地址
def git_url = "git@192.168.100.240:tensquare_group/tensquare_back.git"
node {
stage('拉取代码') {
checkout scmGit(branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]])
}
stage('代码审查') {
// 定义当前Jenkins的SonarQubeScanner工具的环境,位于全局工具配置的SonarQube Scanner 的name定义为'sonar-scanner'
def scannerHome = tool 'sonar-scanner'
// 引用当前JenkinsSonarQube的环境,系统配置的SonarQube servers定义的name以及代码审查服务器位置
withSonarQubeEnv('sonarqube') {
sh """
cd ${project_name}
${scannerHome}/bin/sonar-scanner
"""
}
}
stage('编译安装公共子工程') {
sh "mvn -f tensquare_common clean install"
}
}
Jenkinsfile文件上传至代码仓库,再尝试构建
构建失败
如果编译构建失败,进入IDEA
将tensquare_parent
父工程里面的spring-boot-maven
插件移到需要项目打包的项目里去
tensquare_common
子工程不需要这个插件
只需要将插件加入tensquare_eureka_server
、tensquare_admin_service
、tensquare_gathering
、tensquare_zuul
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- 在project里添加 -->
</project>
调整好之后,上传Jenkinsfile文件至Gitlab服务器
再次尝试对公共子工程进行打包,此时可以看到公共子工程安装到了本地maven仓库
继续编写Jenkinsfile,需要对其他微服务进行打包
// git凭证ID
def git_auth = "a4d0066f-6c58-4ab0-b714-6a24b3f5bc90"
// git的URL地址
def git_url = "git@192.168.100.240:tensquare_group/tensquare_back.git"
node {
stage('拉取代码') {
checkout scmGit(branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]])
}
stage('代码审查') {
// 定义当前Jenkins的SonarQubeScanner工具的环境,位于全局工具配置的SonarQube Scanner 的name定义为'sonar-scanner'
def scannerHome = tool 'sonar-scanner'
// 引用当前JenkinsSonarQube的环境,系统配置的SonarQube servers定义的name以及代码审查服务器位置
withSonarQubeEnv('sonarqube') {
sh """
cd ${project_name}
${scannerHome}/bin/sonar-scanner
"""
}
}
stage('编译安装公共子工程') {
sh "mvn -f tensquare_common clean install"
}
stage('编译打包微服务工程') {
// 项目参数传入的project_name 修改为变量
sh "mvn -f ${project_name} clean package"
}
}
首先对eureka服务器进行打包
来到Jenkins工作目录下查看一下
tensquare_eureka_server
进行了打包并且target下有jar包
将其它三个微服务以同样方式打包
但打包tensquare_zuul
报错
网关依赖父工程,但仓库里的父工程没有
因此需要按照父工程的目录结构将父工程pom.xml文件上传到Jenkins服务器的maven仓库,对应的maven仓库里去以便构建时网关服务找到父工程
那么这个动作需要手动完成
[root@jenkins ~]# cd repo/com/tensquare/
[root@jenkins tensquare]# ls
tensquare_common tensquare_parent
rz
再次进行构建
将剩下两个微服务打包
使用Dockerfile编译生成镜像
利用dockerfile-maven-plugin
插件构建Docker镜像
在每个微服务项目的pom.xml
加入dockerfile-maven-plugin
插件
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.3.6</version>
<configuration>
<repository>${project.artifactId}</repository>
<buildArgs>
<JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
在每个微服务项目根目录下建立Dockerfile文件
注意:每个项目公开的端口不一样
# 构建eureka服务器的dockerfile
FROM openjdk:8-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
EXPOSE 10086
ENTRYPOINT ["java","-jar","/app.jar"]
修改Jenkins流水线脚本,添加dockerfile:build
触发插件执行
// git凭证ID
def git_auth = "a4d0066f-6c58-4ab0-b714-6a24b3f5bc90"
// git的URL地址
def git_url = "git@192.168.100.240:tensquare_group/tensquare_back.git"
node {
stage('拉取代码') {
checkout scmGit(branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]])
}
stage('代码审查') {
// 定义当前Jenkins的SonarQubeScanner工具的环境,位于全局工具配置的SonarQube Scanner 的name定义为'sonar-scanner'
def scannerHome = tool 'sonar-scanner'
// 引用当前JenkinsSonarQube的环境,系统配置的SonarQube servers定义的name以及代码审查服务器位置
withSonarQubeEnv('sonarqube') {
sh """
cd ${project_name}
${scannerHome}/bin/sonar-scanner
"""
}
}
stage('编译安装公共子工程') {
sh "mvn -f tensquare_common clean install"
}
stage('编译打包微服务工程') {
// 项目参数传入的project_name 修改为变量
sh "mvn -f ${project_name} clean package dockerfile:build"
}
}
commit
代码,然后对eureka_server
进行构建
查看本地镜像,创建成功!
其他的服务也是如此,依然需要插件和dockerfile
# 编写dockerfile,注意端口号的区分,实际没多大区别
# 构建tensquare_zuul所需的dockerfile
FROM openjdk:8-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
EXPOSE 10020
ENTRYPOINT ["java","-jar","/app.jar"]
# 构建tensquare_admin_service所需的dockerfile
FROM openjdk:8-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
EXPOSE 9001
ENTRYPOINT ["java","-jar","/app.jar"]
# 构建tensquare_gathering所需的dockerfile
FROM openjdk:8-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
EXPOSE 9002
ENTRYPOINT ["java","-jar","/app.jar"]
上传代码后进行构建
上传到Harbor镜像仓库
修改Jenkinsfile构建脚本
// git凭证ID
def git_auth = "a4d0066f-6c58-4ab0-b714-6a24b3f5bc90"
// git的URL地址
def git_url = "git@192.168.100.240:tensquare_group/tensquare_back.git"
// 镜像的版本号
def tag = "latest"
// Harbor的url地址
def harbor_url = "192.168.100.251:85"
// 镜像库项目名称
def harbor_project = "tensquare"
node {
stage('拉取代码') {
checkout scmGit(branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]])
}
stage('代码审查') {
// 定义当前Jenkins的SonarQubeScanner工具的环境,位于全局工具配置的SonarQube Scanner 的name定义为'sonar-scanner'
def scannerHome = tool 'sonar-scanner'
// 引用当前JenkinsSonarQube的环境,系统配置的SonarQube servers定义的name以及代码审查服务器位置
withSonarQubeEnv('sonarqube') {
sh """
cd ${project_name}
${scannerHome}/bin/sonar-scanner
"""
}
}
stage('编译安装公共子工程') {
sh "mvn -f tensquare_common clean install"
}
stage('编译打包微服务工程,上传镜像') {
// 项目参数传入的project_name 修改为变量
sh "mvn -f ${project_name} clean package dockerfile:build"
// 定义镜像名称
def imageName = "${project_name}:${tag}"
// 对镜像打标签
sh "docker tag ${imageName} ${harbor_url}/${harbor_project}/${imageName}"
}
}
添加Harbor
用户凭证
创建完成后再次进入凭证查看
将生成的id复制并保存
5785fbf3-a0f0-4234-8961-c866ca1e7046
进入流水线片段生成器
编写Jenkins流水线对应脚本
// git凭证ID
def git_auth = "a4d0066f-6c58-4ab0-b714-6a24b3f5bc90"
// git的URL地址
def git_url = "git@192.168.100.240:tensquare_group/tensquare_back.git"
// 镜像的版本号
def tag = "latest"
// Harbor的url地址
def harbor_url = "192.168.100.251:85"
// 镜像库项目名称
def harbor_project = "tensquare"
// Harbor的登录凭证ID
def harbor_auth = "5785fbf3-a0f0-4234-8961-c866ca1e7046"
node {
stage('拉取代码') {
checkout scmGit(branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]])
}
stage('代码审查') {
// 定义当前Jenkins的SonarQubeScanner工具的环境,位于全局工具配置的SonarQube Scanner 的name定义为'sonar-scanner'
def scannerHome = tool 'sonar-scanner'
// 引用当前JenkinsSonarQube的环境,系统配置的SonarQube servers定义的name以及代码审查服务器位置
withSonarQubeEnv('sonarqube') {
sh """
cd ${project_name}
${scannerHome}/bin/sonar-scanner
"""
}
}
stage('编译安装公共子工程') {
sh "mvn -f tensquare_common clean install"
}
stage('编译打包微服务工程,上传镜像') {
// 项目参数传入的project_name 修改为变量
sh "mvn -f ${project_name} clean package dockerfile:build"
// 定义镜像名称
def imageName = "${project_name}:${tag}"
// 对镜像打标签
sh "docker tag ${imageName} ${harbor_url}/${harbor_project}/${imageName}"
// 把镜像推送到Harbor,项目为私有级别,登录时涉及一个问题,登录harbor需要输入账号和密码,Jenkinsfile需要配置项目目录下,会暴露给所有开发人员,为了安全使用凭证方式
withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
// 登录到Harbor,引用如上定义的username和password,就是用户haibo的信息
sh "docker login -u ${username} -p ${password} ${harbor_url}"
// 镜像上传
sh "docker push ${harbor_url}/${harbor_project}/${imageName}"
sh "echo 镜像上传成功"
}
}
}
拉取镜像和发布应用
安装Publish Over SSH插件
安装以下插件,可以实现远程发送Shell命令
配置远程部署服务器
# 将Jenkins服务器的公钥拷贝到远程pro生产部署服务器
ssh-copy-id 192.168.100.252
在Jenkins进入系统配置,找到Publish over SSH
Path to key文件指向持续集成服务器的私钥文件,写绝对路径
点击新增
新增Hostname
设置远程生产环境部署项目的主机IP地址
修改Jenkinsfile构建脚本
生成远程调用模板代码
新版Jenkins需要安装完插件后重启生效
其他置空,生成流水线脚本,需要注意的是Exec command
,这个是远程执行的命令
sshPublisher(publishers: [sshPublisherDesc(configName: 'pro_server01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
添加端口参数,作为外部可变参数进行设置
参数化构建过程–>字符参数
编写远程部署脚本
# 位于pro生产环境服务器上操作
mkdir /opt/jenkins_shell
vim /opt/jenkins_shell/deploy.sh
#!/bin/sh
# 接收外部参数
harbor_url=$1
harbor_project_name=$2
project_name=$3
tag=$4
port=$5
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 haibo -p LIUhaibo123 $harbor_url
# 下载镜像
docker pull $imageName
# 启动容器
docker run -di -p $port:$port $imageName
echo "容器启动成功"
# 添加执行权限
chmod +x /opt/jenkins_shell/deploy.sh
编写Jenkins流水线脚本
// git凭证ID
def git_auth = "a4d0066f-6c58-4ab0-b714-6a24b3f5bc90"
// git的URL地址
def git_url = "git@192.168.100.240:tensquare_group/tensquare_back.git"
// 镜像的版本号
def tag = "latest"
// Harbor的url地址
def harbor_url = "192.168.100.251:85"
// 镜像库项目名称
def harbor_project = "tensquare"
// Harbor的登录凭证ID
def harbor_auth = "5785fbf3-a0f0-4234-8961-c866ca1e7046"
node {
stage('拉取代码') {
checkout scmGit(branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]])
}
stage('代码审查') {
// 定义当前Jenkins的SonarQubeScanner工具的环境,位于全局工具配置的SonarQube Scanner 的name定义为'sonar-scanner'
def scannerHome = tool 'sonar-scanner'
// 引用当前JenkinsSonarQube的环境,系统配置的SonarQube servers定义的name以及代码审查服务器位置
withSonarQubeEnv('sonarqube') {
sh """
cd ${project_name}
${scannerHome}/bin/sonar-scanner
"""
}
}
stage('编译安装公共子工程') {
sh "mvn -f tensquare_common clean install"
}
stage('编译打包微服务工程,上传镜像') {
// 项目参数传入的project_name 修改为变量
sh "mvn -f ${project_name} clean package dockerfile:build"
// 定义镜像名称
def imageName = "${project_name}:${tag}"
// 对镜像打标签
sh "docker tag ${imageName} ${harbor_url}/${harbor_project}/${imageName}"
// 把镜像推送到Harbor,项目为私有级别,登录时涉及一个问题,登录harbor需要输入账号和密码,Jenkinsfile需要配置项目目录下,会暴露给所有开发人员,为了安全使用凭证方式
withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
// 登录到Harbor,引用如上定义的username和password,就是用户haibo的信息
sh "docker login -u ${username} -p ${password} ${harbor_url}"
// 镜像上传
sh "docker push ${harbor_url}/${harbor_project}/${imageName}"
sh "echo 镜像上传成功"
}
// 部署应用
sshPublisher(publishers: [sshPublisherDesc(configName: 'pro_server01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deploy.sh $harbor_url $harbor_project $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
文件后尝试对项目进行构建
远程登录服务器并执行部署脚本
在生产环境部署服务器上查看容器和镜像
部署和测试所有微服务
更改微服务设置
eureka配置
修改ip地址为部署的生产环境服务器地址
zuul网关配置
修改eureka的ip地址,使其部署后在eureka服务器注册服务
admin_service权限中心
需要修改数据库相关信息(选择sonarqube服务器上的mysql作为后端数据库,此数据库在sonarqube代码审查服务器上)
# mysql授权root用户远程登录
grant all privileges on *.* to 'root'@'%' identified by 'Hahaha123@#';
# 重载数据库刷新用户权限
flush privileges;
使用SQLyog
进行连接测试
gathering活动微服务
同样修改数据库和eurekaIP地址使其部署后可以注册服务
将所有更新的配置上传代码仓库
导入MySQL数据
依次导入用户表和活动表
更改了配置后重新部署微服务
需要注意的是端口的区别
查看容器运行状态
通过浏览器查看
此时所有微服务已经全部构建完毕!可以通过postman
对微服务功能进行检测
由于程序连接数据库有部分问题,通过参考文章:https://blog.csdn.net/L_it123/article/details/106845391 修改部分代码
首先检查权限中心能否登录
http://192.168.100.252:10020/admin/admin/login
部署前端静态web网站
安装Nginx服务器
rpm -ivh nginx-1.20.0-1.el7.ngx.x86_64.rpm
vim /etc/nginx/conf.d/default.conf
server {
listen 9090;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# 启动nginx
systemctl enable nginx --now
# 持续集成服务器安装nodejs环境
curl --silent --location https://rpm.nodesource.com/setup_10.x | bash -
yum install nodejs -y
npm install -g cnpm --registry=https://registry.npm.taobao.org
创建前端流水线项目
添加分支参数
编写流水线脚本
//gitlab的凭证
def git_auth = "a4d0066f-6c58-4ab0-b714-6a24b3f5bc90"
node {
stage('拉取代码') {
checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[credentialsId: "${branch}", url: 'git@192.168.100.240:tensquare_group/tensquare_front.git']])
}
stage('打包前端静态代码') {
// 使用NodeJS的npm进行打包
// sh 'npm cache clean --force'
// sh 'rm -rf node_modules && rm -rf package-lock.json'
sh 'npm install'
sh 'npm run build'
}
stage('部署网站') {
//=====以下为远程调用进行项目部署========
sshPublisher(publishers: [sshPublisherDesc(configName: 'pro_server01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '/usr/share/nginx/html', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
git_auth
凭据需要去Jenkins找
构建成功
使用浏览器访问
查询到了数据库的信息,因此证明前端页面和后端连接成功
再次查看容器日志,可以看到有修改数据库内容,证明前后端连通成功
# 查看活动微服务容器日志
docker logs -f `docker ps| grep -i 'gathering'|awk '{print $1}'`
至此Jenkins+Docker+SpringCloud微服务持续集成就搭建完成!
Jenkins+Docker+SpringCloud部署方案优化
上面部署方案存在的问题:
- 一次只能选择一个微服务部署
- 只有一台生产者部署服务器
- 每个微服务只有一个实例,容错率低
优化方案:
- 在一个Jenkins工程中可以选择多个微服务同时发布
- 在一个Jenkins工程中可以选择多台生产服务器同时部署
- 每个微服务都是以集群高可用形式部署
Jenkins+Docker+SpringCloud集群部署流程说明
服务器列表
服务器名称 | IP地址 | 安装软件 | 硬件配置 | 系统 |
---|---|---|---|---|
代码托管服务器 | 192.168.100.240 | Gitlab-12.9.9 | 2核4G | CentOS Linux release 7.5.1804 |
持续集成服务器 | 192.168.100.241 | Jenkins 2.401.2,JDK 11,JDK 1.8,Maven 3.8.8,Git 1.8.3.1,Docker 20.10.24-ce | 2核4G | CentOS Linux release 7.5.1804 |
代码审查服务器 | 192.168.100.242 | mysql 5.7.43,sonarqube 6.7.7 | 1核2G | CentOS Linux release 7.5.1804 |
Harbor仓库服务器 | 192.168.100.251 | Docker 20.10.24-ce,Harbor 1.9.2 | 1核2G | CentOS Linux release 7.5.1804 |
生产部署服务器 | 192.168.100.252 | Docker 20.10.24-ce | 1核2G | CentOS Linux release 7.5.1804 |
生产部署服务器 | 192.168.100.253 | Docker 20.10.24-ce | 1核2G | CentOS Linux release 7.5.1804 |
修改所有微服务配置
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.100.252
client:
service-url:
# 将自己注册到eureka-server1、eureka-server2这个Eureka上面去
defaultZone: http://192.168.100.252:10086/eureka,http://192.168.100.253:10086/eureka
---
server:
port: 10086
spring:
profiles: eureka-server2
eureka:
instance:
hostname: 192.168.100.253
client:
service-url:
defaultZone: http://192.168.100.253:10086/eureka,http://192.168.100.252:10086/eureka
在启动微服务的时候,加入参数:spring.profiles.active
来读取对应的配置
其他微服务配置
除了Eureka注册中心以外,其他微服务配置都需要加入所有Eureka服务
# Eureka配置
eureka:
client:
service-url:
defaultZone: http://192.168.100.252:10086/eureka,http://192.168.100.253:10086/eureka # Eureka访问地址
instance:
prefer-ip-address: true
tensquare_zuul
配置
tensquare_admin_service
配置
tensquare_gathering
配置
把代码提交到Gitlab中
设计Jenkins集群项目的构建参数
安装Extended Choice Parameter插件
支持多选框
创建流水线项目
添加参数化构建
选择字符串参数
添加多选项目参数
使用jdk1.8
构建项目
点击构建查看效果
多个项目提交SonarQube进行代码审查
// 定义变量以及引用变量,这样维护性好一点
// 引用凭证ID最好使用双引号 ""
// git凭证ID
def git_auth = "a4d0066f-6c58-4ab0-b714-6a24b3f5bc90"
// git的URL地址
def git_url = "git@192.168.100.240:tensquare_group/tensquare_back.git"
// 镜像的版本号
def tag = "latest"
// Harbor的url地址
def harbor_url = "192.168.100.251:85"
// 镜像库项目名称
def harbor_project = "tensquare"
// Harbor的登录凭证ID
def harbor_auth = "5785fbf3-a0f0-4234-8961-c866ca1e7046"
node {
// 获取当前选择的项目名称,调用split方法切割逗号,意为切开项目名称
def selectedProjectNames = "${project_name}".split(",")
stage('拉取代码') {
checkout scmGit(branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]])
}
stage('代码审查') {
// 对项目遍历进行代码审查,定义变量i=0 遍历数组selectedProjectNames 数组长度为length
for(int i=0;i<selectedProjectNames.length;i++){
// 取出每个元素里的内容 定义变量为projectInfo 就是项目信息,即包含项目名字也包含项目端口
def projectInfo = selectedProjectNames[i];
// 对 projectInfo 进行切割 引用projectInfo 变量,使用@符号切,当前遍历的项目名字 [0] 为获取的第一个元素 就是项目名字
def currentProjectName = "${projectInfo}".split("@")[0]
// 当前遍历的项目端口 [1] 就是获取的第二个元素
def currentProjectPort = "${projectInfo}".split("@")[1]
// 定义当前Jenkins的SonarQubeScanner工具的环境,位于全局工具配置的SonarQube Scanner 的name定义为'sonar-scanner'
def scannerHome = tool 'sonar-scanner'
// 引用当前JenkinsSonarQube的环境,系统配置的SonarQube servers定义的name以及代码审查服务器位置
withSonarQubeEnv('sonarqube') {
sh """
cd ${currentProjectName}
${scannerHome}/bin/sonar-scanner
"""
}
}
}
stage('编译安装公共子工程') {
sh "mvn -f tensquare_common clean install"
}
stage('编译打包微服务工程,上传镜像') {
// 项目参数传入的project_name 修改为变量
sh "mvn -f ${project_name} clean package dockerfile:build"
// 定义镜像名称
def imageName = "${project_name}:${tag}"
// 对镜像打标签
sh "docker tag ${imageName} ${harbor_url}/${harbor_project}/${imageName}"
// 把镜像推送到Harbor,项目为私有级别,登录时涉及一个问题,登录harbor需要输入账号和密码,Jenkinsfile需要配置项目目录下,会暴露给所有开发人员,为了安全使用凭证方式
withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
// 登录到Harbor,引用如上定义的username和password,就是用户haibo的信息
sh "docker login -u ${username} -p ${password} ${harbor_url}"
// 镜像上传
sh "docker push ${harbor_url}/${harbor_project}/${imageName}"
sh "echo 镜像上传成功"
}
// 部署应用
sshPublisher(publishers: [sshPublisherDesc(configName: 'pro_server01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deploy.sh $harbor_url $harbor_project $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能够对项目进行遍历审查
但构建结果是失败的,在编译打包的时候无法进行遍历与project_name
切割
多个项目打包及构建上传镜像
修改Jenkinsfile
,注释掉远程发布,查看编译构建和打包效果
// git凭证ID
def git_auth = "a4d0066f-6c58-4ab0-b714-6a24b3f5bc90"
// git的URL地址
def git_url = "git@192.168.100.240:tensquare_group/tensquare_back.git"
// 镜像的版本号
def tag = "latest"
// Harbor的url地址
def harbor_url = "192.168.100.251:85"
// 镜像库项目名称
def harbor_project = "tensquare"
// Harbor的登录凭证ID
def harbor_auth = "5785fbf3-a0f0-4234-8961-c866ca1e7046"
node {
// 获取当前选择的项目名称,调用split方法切割逗号,意为切开项目名称
def selectedProjectNames = "${project_name}".split(",")
stage('拉取代码') {
checkout scmGit(branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]])
}
stage('代码审查') {
// 对项目遍历进行代码审查,定义变量i=0 遍历数组selectedProjectNames 数组长度为length
for(int i=0;i<selectedProjectNames.length;i++){
// 取出每个元素里的内容 定义变量为projectInfo 就是项目信息,即包含项目名字也包含项目端口
def projectInfo = selectedProjectNames[i];
// 对 projectInfo 进行切割 引用projectInfo 变量,使用@符号切,当前遍历的项目名字 [0] 为获取的第一个元素 就是项目名字
def currentProjectName = "${projectInfo}".split("@")[0]
// 当前遍历的项目端口 [1] 就是获取的第二个元素
def currentProjectPort = "${projectInfo}".split("@")[1]
// 定义当前Jenkins的SonarQubeScanner工具的环境,位于全局工具配置的SonarQube Scanner 的name定义为'sonar-scanner'
def scannerHome = tool 'sonar-scanner'
// 引用当前JenkinsSonarQube的环境,系统配置的SonarQube servers定义的name以及代码审查服务器位置
withSonarQubeEnv('sonarqube') {
sh """
cd ${currentProjectName}
${scannerHome}/bin/sonar-scanner
"""
}
}
}
stage('编译安装公共子工程') {
sh "mvn -f tensquare_common clean install"
}
stage('编译打包微服务工程,上传镜像') {
for(int i=0;i<selectedProjectNames.length;i++){
// 取出每个元素里的内容 定义变量为projectInfo 就是项目信息,即包含项目名字也包含项目端口
def projectInfo = selectedProjectNames[i];
// 对 projectInfo 进行切割 引用projectInfo 变量,使用@符号切,当前遍历的项目名字 [0] 为获取的第一个元素 就是项目名字
def currentProjectName = "${projectInfo}".split("@")[0]
// 当前遍历的项目端口 [1] 就是获取的第二个元素
def currentProjectPort = "${projectInfo}".split("@")[1]
// 项目参数传入的project_name 修改为变量
sh "mvn -f ${currentProjectName} clean package dockerfile:build"
// 定义镜像名称
def imageName = "${currentProjectName}:${tag}"
// 对镜像打标签
sh "docker tag ${imageName} ${harbor_url}/${harbor_project}/${imageName}"
// 把镜像推送到Harbor,项目为私有级别,登录时涉及一个问题,登录harbor需要输入账号和密码,Jenkinsfile需要配置项目目录下,会暴露给所有开发人员,为了安全使用凭证方式
withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
// 登录到Harbor,引用如上定义的username和password,就是用户haibo的信息
sh "docker login -u ${username} -p ${password} ${harbor_url}"
// 镜像上传
sh "docker push ${harbor_url}/${harbor_project}/${imageName}"
sh "echo 镜像上传成功"
}
// 部署应用
// sshPublisher(publishers: [sshPublisherDesc(configName: 'pro_server01', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deploy.sh $harbor_url $harbor_project $project_name $tag $port", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
此时可以发现两个服务都可以进行代码扫描以及进行构建打包和上传至镜像仓库!
把Eureka注册中心集群部署到多台服务器
配置第二台生产服务器192.168.100.253
# 卸载旧版本
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
# 删除docker的所有镜像和容器
rm -rf /var/lib/docker
# 安装基本的依赖包
sudo yum install yum-utils device-mapper-persistent-data lvm2 -y
# 设置镜像仓库 Docker yum源
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 更新yum软件包索引
sudo yum makecache fast
# 列出需要安装的版本列表
yum list docker-ce --showduplicates | sort -r
# 安装docker-ce-20.10
yum install docker-ce-20.10.* docker-ce-cli-20.10.* containerd -y
# 配置Docker镜像仓库加速以及配置Harbor镜像仓库信任
mkdir /etc/docker -p
cat > /etc/docker/daemon.json <<EOF
{
"registry-mirrors": ["https://k68iw3ol.mirror.aliyuncs.com"],
"insecure-registries": ["192.168.100.251:85"]
}
EOF
systemctl daemon-reload && systemctl enable --now docker
配置远程部署服务器
Jenkins服务器拷贝公钥到远程生产服务器02
ssh-copy-id 192.168.100.253
系统配置->添加远程服务器
Publish over SSH–> 新增一台机器
项目配置添加参数
配置能够选择多个服务器
返回项目构建查看效果
更改Jenkinsfile
添加遍历服务器配置
// git凭证ID
def git_auth = "a4d0066f-6c58-4ab0-b714-6a24b3f5bc90"
// git的URL地址
def git_url = "git@192.168.100.240:tensquare_group/tensquare_back.git"
// 镜像的版本号
def tag = "latest"
// Harbor的url地址
def harbor_url = "192.168.100.251:85"
// 镜像库项目名称
def harbor_project = "tensquare"
// Harbor的登录凭证ID
def harbor_auth = "5785fbf3-a0f0-4234-8961-c866ca1e7046"
node {
// 获取当前选择的项目名称,调用split方法切割逗号,意为切开项目名称
def selectedProjectNames = "${project_name}".split(",")
// 获取当前选择的服务器名称
def selectedServers = "${publish_server}".split(",")
stage('拉取代码') {
checkout scmGit(branches: [[name: "*/${branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_url}"]])
}
stage('代码审查') {
// 对项目遍历进行代码审查,定义变量i=0 遍历数组selectedProjectNames 数组长度为length
for(int i=0;i<selectedProjectNames.length;i++){
// 取出每个元素里的内容 定义变量为projectInfo 就是项目信息,即包含项目名字也包含项目端口
def projectInfo = selectedProjectNames[i];
// 对 projectInfo 进行切割 引用projectInfo 变量,使用@符号切,当前遍历的项目名字 [0] 为获取的第一个元素 就是项目名字
def currentProjectName = "${projectInfo}".split("@")[0]
// 当前遍历的项目端口 [1] 就是获取的第二个元素
def currentProjectPort = "${projectInfo}".split("@")[1]
// 定义当前Jenkins的SonarQubeScanner工具的环境,位于全局工具配置的SonarQube Scanner 的name定义为'sonar-scanner'
def scannerHome = tool 'sonar-scanner'
// 引用当前JenkinsSonarQube的环境,系统配置的SonarQube servers定义的name以及代码审查服务器位置
withSonarQubeEnv('sonarqube') {
sh """
cd ${currentProjectName}
${scannerHome}/bin/sonar-scanner
"""
}
}
}
stage('编译安装公共子工程') {
sh "mvn -f tensquare_common clean install"
}
stage('编译打包微服务工程,上传镜像') {
for(int i=0;i<selectedProjectNames.length;i++){
// 取出每个元素里的内容 定义变量为projectInfo 就是项目信息,即包含项目名字也包含项目端口
def projectInfo = selectedProjectNames[i];
// 对 projectInfo 进行切割 引用projectInfo 变量,使用@符号切,当前遍历的项目名字 [0] 为获取的第一个元素 就是项目名字
def currentProjectName = "${projectInfo}".split("@")[0]
// 当前遍历的项目端口 [1] 就是获取的第二个元素
def currentProjectPort = "${projectInfo}".split("@")[1]
// 项目参数传入的project_name 修改为变量
sh "mvn -f ${currentProjectName} clean package dockerfile:build"
// 定义镜像名称
def imageName = "${currentProjectName}:${tag}"
// 对镜像打标签
sh "docker tag ${imageName} ${harbor_url}/${harbor_project}/${imageName}"
// 把镜像推送到Harbor,项目为私有级别,登录时涉及一个问题,登录harbor需要输入账号和密码,Jenkinsfile需要配置项目目录下,会暴露给所有开发人员,为了安全使用凭证方式
withCredentials([usernamePassword(credentialsId: "${harbor_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
// 登录到Harbor,引用如上定义的username和password,就是用户haibo的信息
sh "docker login -u ${username} -p ${password} ${harbor_url}"
// 镜像上传
sh "docker push ${harbor_url}/${harbor_project}/${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"
// 根据不同的服务器名字来读取不同的eureka配置信息
if(selectedServers=="pro_server01"){
activeProfile = activeProfile+"eureka-server1"
}else if (selectedServers=="pro_server02"){
activeProfile = activeProfile+"eureka-server2"
}
// 部署应用
sshPublisher(publishers: [sshPublisherDesc(configName: "${currentServerName}", transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deployCluster.sh $harbor_url $harbor_project $currentProjectName $tag $currentProjectPort $activeProfile", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
}
}
更改服务器端部署脚本
vim /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 haibo -p LIUhaibo123 $harbor_url
# 下载镜像
docker pull $imageName
# 启动容器
docker run -di -p $port:$port $imageName $profile
echo "容器启动成功"
# 添加执行权限
chmod +x /opt/jenkins_shell/deployCluster.sh
Nginx+Zuul集群实现高可用网关
# 位于pro02生产服务器进行配置
# 安装Nginx
# 修改Nginx配置
vim /etc/nginx/nginx.conf
# http模块添加
upstream zuulServer{
server 192.168.100.252:10020 weight=1;
server 192.168.100.253:10020 weight=1;
}
vim /etc/nginx/conf.d/default.conf
server {
listen 85;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
root /usr/share/nginx/html;
location / {
# root /usr/share/nginx/html;
# index index.html index.htm;
# 指定服务器负载均衡服务器
proxy_pass http://zuulServer/;
}
# 重启Nginx
systemctl restart nginx
修改前端Nginx的访问地址