1 持续集成流程说明

Jenkins+Docker+SpringCloud(单机)_docker

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全家桶)

微服务项目结构:

Jenkins+Docker+SpringCloud(单机)_docker_02

  • 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 前端代码报错处理

Jenkins+Docker+SpringCloud(单机)_git_03

2.3 后端代码报错处理

Jenkins+Docker+SpringCloud(单机)_git_04

SpringCloud的Finchley版本中,很多在maven中央仓库是不存在的(可能,多次测试不通过)。Finchley.RC1,Finchley.M9在中央仓库都是残缺的,无法实现dependencies功能,无法管理依赖版本,最终是更换spring-cloud-dependencies版本。

更改版本为Finchley.SR1

Jenkins+Docker+SpringCloud(单机)_微服务_05

2.4 jenkins新增插件

Publish Over SSH

2.5 harbor新增仓库

Harbor新增私有仓库tensquare

3 微服务持续集成(后端)

3.1 项目代码上传到Gitlab

Jenkins+Docker+SpringCloud(单机)_docker_06

3.2 从Gitlab拉取项目源码

3.2.1 创建项目,并设置参数

创建tensquare_back项目,添加两个参数

Jenkins+Docker+SpringCloud(单机)_docker_07

Jenkins+Docker+SpringCloud(单机)_微服务_08

3.2.2 创建Jenkinsfile文件

Jenkins+Docker+SpringCloud(单机)_docker_09

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构建脚本

Jenkins+Docker+SpringCloud(单机)_docker_10

使用 sed对sonar-project.properties中的project_name做了替换

3.4 整体构建

Jenkins+Docker+SpringCloud(单机)_微服务_11

3.5 生成镜像,推送harbor

3.5.1 添加barbor账号密码凭据

Jenkins+Docker+SpringCloud(单机)_微服务_12

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构建脚本

Jenkins+Docker+SpringCloud(单机)_git_13

3.6 拉取镜像和发布应用

Jenkins+Docker+SpringCloud(单机)_微服务_14

192.168.137.129已经提前安装好docker,并且配置了insecure-registries

3.6.1 配置远程部署服务器

1、拷贝公钥到远程服务器

ssh-copy-id 192.168.137.129

2、系统配置->添加远程服务器

Jenkins+Docker+SpringCloud(单机)_git_15

Jenkins+Docker+SpringCloud(单机)_微服务_16

3.6.2 免密设置

还需要在jenkins机器向业务机器做好免密

3.6.3 添加选项参数

Jenkins+Docker+SpringCloud(单机)_git_17

3.6.4 修改Jenkinsfile构建脚本

Jenkins+Docker+SpringCloud(单机)_git_18

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/

Jenkins+Docker+SpringCloud(单机)_git_19

4 网站web部署前端静态

4.1 新增字符参数

Jenkins+Docker+SpringCloud(单机)_docker_20

4.2 创建jenkinsfile文件

Jenkins+Docker+SpringCloud(单机)_微服务_21

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)])
    }
}