1 持续集成流程说明
1)开发人员每天把代码提交到Gitlab代码仓库
2)Jenkins从Gitlab中拉取项目源码,编译并打成jar包,然后构建成Docker镜像,将镜像上传到Harbor私有仓库。
3)Jenkins发送SSH远程命令,让生产部署服务器到Harbor私有仓库拉取镜像到本地,然后创建容器。
4)最后,用户可以访问到容器
1.1 服务列表
服务器名称 | IP地址 | 安装软件 |
代码托管服务器 | 192.168.137.129 | Gitlab |
持续集成服务器 | 192.168.137.130 | Jenkins、Maven、Nodejs、Mysql、docker、harbor、sonar |
业务服务器主 | 192.168.137.129 | Docker |
业务服务器备 | 192.168.137.128 | Docker |
1.2 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
2 环境准备
2.1 nodejs安装
https://nodejs.org/download/release/v12.8.0/
tar -xf node-v12.8.0-linux-x64.tar.gz
ln -s node-v12.8.0-linux-x64 nodejs
vim /etc/profile
export NODEJS_HOME=/app/module/nodejs
export PATH=$PATH:$NODEJS_HOME/bin
source /etc/profile
npm config get registry
npm config set registry https://registry.npmmirror.com
npm config set unsafe-perm true
2.2 前端代码报错处理
2.3 后端代码报错处理
SpringCloud的Finchley版本中,很多在maven中央仓库是不存在的(可能,多次测试不通过)。Finchley.RC1,Finchley.M9在中央仓库都是残缺的,无法实现dependencies功能,无法管理依赖版本,最终是更换spring-cloud-dependencies版本。
更改版本为Finchley.SR1
2.4 jenkins新增插件
Publish Over SSH
2.5 harbor新增仓库
Harbor新增私有仓库tensquare
3 微服务持续集成(后端)
3.1 项目代码上传到Gitlab
3.2 从Gitlab拉取项目源码
3.2.1 创建项目,并设置参数
创建tensquare_back项目,添加两个参数
3.2.2 创建Jenkinsfile文件
3.3 提交SonarQube代码审查
3.3.1 添加sonar-project.properties
每个项目的根目录下添加sonar-project.properties
# must be unique in a given SonarQube instance
sonar.projectKey=project_name
# this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1.
sonar.projectName=project_name
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
3.3.2 修改Jenkinsfile构建脚本
使用 sed对sonar-project.properties中的project_name做了替换
3.4 整体构建
3.5 生成镜像,推送harbor
3.5.1 添加barbor账号密码凭据
3.5.2 建立Dockerfile文件
在每个微服务项目根目录下建立Dockerfile文件
#FROM java:8
FROM openjdk:8-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
EXPOSE 10086
ENTRYPOINT ["java","-jar","/app.jar"]
注意:每个项目公开的端口不一样
3.5.3 修改Jenkinsfile构建脚本
3.6 拉取镜像和发布应用
192.168.137.129已经提前安装好docker,并且配置了insecure-registries
3.6.1 配置远程部署服务器
1、拷贝公钥到远程服务器
ssh-copy-id 192.168.137.129
2、系统配置->添加远程服务器
3.6.2 免密设置
还需要在jenkins机器向业务机器做好免密
3.6.3 添加选项参数
3.6.4 修改Jenkinsfile构建脚本
3.6.5 deploy.sh部署脚本
1、上传deploy.sh文件到/opt/jenkins_shell目录下,且文件至少有执行权限!
2、vim 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 admin -p Harbor12345 $harbor_url
# 下载镜像
docker pull $imageName
# 启动容器
docker run -di -p $port:$port $imageName
echo "容器启动成功"
3、授予执行权限
chmod a+x deploy.sh
3.7 启动服务
登录注册中心
http://192.168.137.129:10086/
4 网站web部署前端静态
4.1 新增字符参数
4.2 创建jenkinsfile文件
4.3 启动应用容器
docker run -itd -p 9090:80 -v /data/nginx:/usr/share/nginx/html --name tensquare_web_nginx hub.atomgit.com/amd64/nginx:1.25.2-perl
4.4 构建完成测试
完成后,访问:http://192.168.137.129:9090 进行测试。 admin/123456
5 补充
5.1 注册中心配置
server:
port: 10086 # 端口
# 基本服务信息
spring:
application:
name: eureka-server # 服务ID
# eureka服务器配置
eureka:
client:
fetch-registry: false
register-with-eureka: false
service-url:
defaultZone: http://192.168.137.129:${server.port}/eureka # 暴露Eureka服务访问地址
server:
enable-self-preservation: false # 关闭自我保护
5.2 前端Jenkinsfile
//gitlab凭据
def git_auth='8c8a0774-7d4c-48a9-8f52-dd1a04f71320'
node {
stage('拉取代码') {
checkout scmGit(branches: [[name: "*/${branch}"]],
extensions: [],
userRemoteConfigs: [[credentialsId: "${git_auth}",
url: 'http://192.168.137.129/root/tensquare_front.git']])
}
stage('打包,部署网站') {
//使用nodejs的npm打包
nodejs('NPM') {
sh '''
npm install
npm run build
'''
}
//远程调用进行项目部署
sshPublisher(publishers: [sshPublisherDesc(configName: 'master_server',
transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false,
makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+',
remoteDirectory: '/data/nginx', remoteDirectorySDF: false, removePrefix: 'dist', sourceFiles: 'dist/**')],
usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
}
}
5.3 后端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 {
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}") {
sh """
cd ${project_name}
sed -i "s#project_name#${project_name}#g" sonar-project.properties
${scannerHome}/bin/sonar-scanner
"""
}
}
stage('整体构建') {
def mvnHome = tool 'M2'
//编译、整体构建,每个stage都会重新回到${WORKSPACE}
sh " ${mvnHome}/bin/mvn clean package "
}
stage('构建镜像、推送harbor') {
def imageName = "${harbor_url}/${harbor_project_name}/${project_name}:${tag}"
//生成镜像
sh " cd ${project_name} && docker build -t ${imageName} --build-arg JAR_FILE=target/${project_name}-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} "
//远程调用进行项目部署
sshPublisher(publishers: [sshPublisherDesc(configName: 'master_server',
transfers: [sshTransfer(cleanRemote: false, excludes: '',
execCommand: "/opt/jenkins_shell/deploy.sh ${harbor_url} ${harbor_project_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)])
}
}