jenkins-04-2-持续集成

持续集成流程

在这里插入图片描述

大致流程说明

  1. 开发人员每天把代码提交到Gitlab代码仓库。
  2. Jenkins从Gitlab中拉取项目源码,编译并打成jar包,然后构建成Docker镜像,将镜像上传到Harbor私有仓库。
  3. Jenkins发送SSH远程命令,让生产部署服务器到Harbor私有仓库拉取镜像到本地,然后创建容器。
  4. 最后,用户可以访问到容器

编译

   stage('1_pull_and_compile') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: '*/${selected_branch}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${git_token_id}", url: 'ssh://git@192.168.129.159:2222/D6573/web-demo.git']]])
                sh "mvn clean compile"
            }
        }

代码检查

sonar静态代码检测

  stage('2_sonar') {
            steps{
                script {
                    scannerHome = tool 'sonarqube-scanner'
                }
                withSonarQubeEnv('sonar-8.6') {
                    sh "${scannerHome}/bin/sonar-scanner"
                }
            }
        }

sonar 质量门

stage("3_sonar_quality_gate") {
            steps {
                script{
                    timeout(time: 120, unit: 'SECONDS') {
                        def qg = waitForQualityGate('sonar-8.6')
                        if (qg.status != 'OK') {
                            withCredentials([string(credentialsId: sonar_auth_id, variable: 'SONAR_AUTH_KEY')]) {							
                                // 打印项目状态信息
                                final String url = "http://192.168.129.44:9000/api/qualitygates/project_status?projectKey=${project_name}"
                                final String requestScript = "curl -u $SONAR_AUTH_KEY: -s ${url} "
                                final String response = sh(script: requestScript, returnStdout: true).trim()
                                echo response
                               //  解析错误项并打印。
                                def parser = new groovy.json.JsonSlurper()
                                def applicationResp = parser.parseText(response)
                                println("状态<span style=\"color:red\">$applicationResp.projectStatus.status</span>")
                                for(item in applicationResp.projectStatus.conditions){
                                    if(item.status == 'ERROR'){
                                        println("错误项:"+item)
                                    }
                                }
                            }
                            error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status}"
                        }
                    }
                }
            }
        }

构建项目

安装Pipeline Utility Steps 插件

findFiles等方法需要使用该插件

dockerfile

FROM openjdk:8-jdk-alpine
ARG JAR_FILE="Default_Value"

## 从构建命令获取参数
COPY ${JAR_FILE} /app.jar

## 每个项目公开的端口不一样
ARG SERVER_PORT=8080
ENV env_srver_port=$SERVER_PORT

EXPOSE $env_srver_port
ENTRYPOINT ["java","-jar","/app.jar","--server.port=$env_srver_port"]

构建脚本

stage('4_package_and_build') {
            steps{
                script{
                    //编译,打包
                    sh "mvn package -Dmaven.test.skip=true "

                    //Pipeline Utility Steps 插件 --- findFiles
                    def files = findFiles(glob: 'target/*.jar')
                    if(files.length < 0){
                        error "未找到输出物*.jar."
                    }

                    def jarFile = files[0]
                    echo "---------------${files[0]}"


                    //定义镜像名称
                    def imageName = "${project_name}:${tag}"
                    //构建本地镜像
                    sh "docker build --build-arg JAR_FILE=${jarFile} --build-arg SERVER_PORT=${serverPort} -t ${imageName} ."

                    //定义远程标签名称
                    def remoteImageName = "${harbor_url}/${harbor_project_name}/${imageName}";
                    //给镜像打标签
                    sh "docker tag ${imageName} ${remoteImageName}"

                    //登录Harbor,并上传镜像
                    withCredentials([usernamePassword(credentialsId: "${harbor_auth}",
                            passwordVariable: 'password', usernameVariable: 'username')]) {
                       //登录
                        sh "docker login -u ${username} -p ${password} ${harbor_url}"
                        //上传镜像
                        sh "docker push ${remoteImageName}"
                    }

                    //删除本地镜像
                    sh "docker rmi -f ${imageName}"
                    sh "docker rmi -f ${remoteImageName}"
                }
            }
        }

远程部署

部署脚本

在远程服务器192.168.129.42服务器,创建/home/docker/jenkins_shell,脚本内容如下

#! /bin/bash
harbor_url=$1
harbor_project_name=$2
project_name=$3
tag=$4
port=$5

echo "请求参数: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=$imageName"


# 查询容器是否存在
containerId=`docker ps -a | grep -w ${project_name}:${tag} | awk '{print $1}'`
if [ "$containerId" != "" ]; then
  # 停掉容器
   docker stop $containerId

  # 删掉容器
  docker rm $containerId

   echo "成功删除容器"
fi


# 删除存在的镜像
docker images| grep -w $project_name | awk '{print $3}' | xargs docker rmi -f

# 登录Harbor私服
docker login -u jhs -p Jhs123456 $harbor_url

# 下载镜像
docker pull $imageName

# 启动容器
docker run -di -p $port:$port $imageName

echo "容器启动成功:${containId}"

测试脚本内容
在jenkins容器内执行如下命令

[docker@s42 jenkins_shell]$ chmod +x deploy.sh
[docker@s42 jenkins_shell]$ sh deploy.sh  192.168.129.44:85 demo1 web-demo 1223 10086
....
容器启动成功:2a90fcb9e541a7f8d694e493e4cc39614674b7b8564abc34b948db3bad687c19

,测试执行成功。

远程登录

免密登录原理
免密登录,需要先在本机生成公钥,然后将公钥拷贝到远程主机,拷贝的过程,既可以手动(在远程主机根目录下创建.ssh目录,然后将公钥存入该目录下authorized_keys文件中即可),也可以直接命令操作ssh-copy-id,这个操作做完了,即可免密登录远程主机。

  1. 首先在 serverA 上生成一对秘钥(ssh-keygen)
  2. 将公钥拷贝到 serverB,重命名 authorized_keys
  3. serverA 向 serverB 发送一个连接请求,信息包括用户名、ip
  4. serverB 接到请求,会从 authorized_keys 中查找,是否有相同的用户名、ip,如果有 serverB 会随机生成一个字符串
  5. 然后使用使用公钥进行加密,再发送个 serverA
  6. serverA 接到 serverB 发来的信息后,会使用私钥进行解密,然后将解密后的字符串发送给 serverB
  7. serverB 接到 serverA 发来的信息后,会给先前生成的字符串进行比对,如果一直,则允许免密登录

jenkins容器内执行


# 1.生成秘钥 
[root@4e9029ecaeca .ssh]# ssh-keygen

# 2. 将本机公钥复制到远程机器的 ~/.ssh/authorized_key.文件中
[root@4e9029ecaeca /]# ssh-copy-id -i ~/.ssh/id_rsa.pub docker@192.168.129.42
# 输入密码
docker@192.168.129.42s password: *******

## 3. 远程登录目标机器 --- 无需使用密码
[root@vm1 web_demo_cloud_pipeline]# ssh 'docker@192.168.129.42'
## 4. 测试、执行脚本成功
[docker@s42 ~]$ sh /ssh_deploy/deploy.sh 192.168.129.44:85 demo1 web-demo 1223 10086

jenkin配置

安装Publish over SSH插件
略.

jenkins配置
在这里插入图片描述

pipeline脚本配置

流水线语法片段
在这里插入图片描述

生成的脚本

sshPublisher(
    publishers: [
        sshPublisherDesc(
                configName: '192.168.129.42-ssh', 
                transfers: [
                        sshTransfer(
                                cleanRemote: false, 
                                excludes: '', 
                                 execCommand: '/home/docker/jenkins_shell/deploy.sh 192.168.129.44:85 demo1 web-demo 1223 10086',
                                execTimeout: 120000, 
                                flatten: false, 
                                makeEmptyDirs: false, 
                                noDefaultExcludes: false, 
                                patternSeparator: '[, ]+', 
                                remoteDirectory: '', 
                                remoteDirectorySDF: false, 
                                removePrefix: '', 
                                sourceFiles: ''
                        )
                ],
                usePromotionTimestamp: false, 
                useWorkspaceInPromotion: false, 
                verbose: false
        )
    ]
)

完整脚本(parameter)

DockerFile

FROM openjdk:8-jdk-alpine
ARG JAR_FILE="Default_Value"

## 从构建命令获取参数
COPY ${JAR_FILE} /app.jar

## 每个项目公开的端口不一样
ARG SERVER_PORT=8080
ENV env_srver_port=$SERVER_PORT

EXPOSE $env_srver_port
#ENTRYPOINT ["java","-jar","/app.jar","--server.port=$env_srver_port"]
ENTRYPOINT java -jar /app.jar --server.port=$env_srver_port

JenkinsFile

def git_token_id = "6cac69be-bb16-49d4-a315-d71bc2883106"

def tag = "1225"

def git_project_ssh_url_base = "ssh://git@192.168.129.159:2222/D6573"
def git_project_url="${git_project_ssh_url_base}/${project_name}.git"

def harbor_url = "192.168.129.44:85"
def harbor_project_name = "demo1"
def harbor_auth = "ee84a7cd-098b-4890-97cb-609850851ee6"

def sonar_auth_id = "sonar-authentication-token-test"
def sonar_project_status_url = "http://192.168.129.44:9000/api/qualitygates/project_status?projectKey=${project_name}"

pipeline {
    agent any
    stages {
        stage('0_print_envs') {
            steps {
                echo "selected_branch=${selected_branch} ,serverPort=${serverPort} "
            }
        }

        stage('1_pull_and_compile') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: '*/${selected_branch}']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${git_token_id}", url: "${git_project_url}"]]])
                sh "mvn clean compile"
            }
        }

        stage('2_sonar') {
            steps{
                script {
                    scannerHome = tool 'sonarqube-scanner'
                }
                withSonarQubeEnv('sonar-8.6') {
                    sh "${scannerHome}/bin/sonar-scanner"
                }
            }
        }

        stage("3_sonar_quality_gate") {
            steps {
                script{
                    timeout(time: 120, unit: 'SECONDS') {
                        def qg = waitForQualityGate('sonar-8.6')
                        if (qg.status != 'OK') {
                            withCredentials([string(credentialsId: sonar_auth_id, variable: 'SONAR_AUTH_KEY')]) {
                                final String requestScript = "curl -u $SONAR_AUTH_KEY: -s ${sonar_project_status_url} "
                                final String response = sh(script: requestScript, returnStdout: true).trim()
                                echo response
                                def parser = new groovy.json.JsonSlurper()
                                def applicationResp = parser.parseText(response)
                                println("状态<span style=\"color:red\">$applicationResp.projectStatus.status</span>")
                                for(item in applicationResp.projectStatus.conditions){
                                    if(item.status == 'ERROR'){
                                        println("错误项:"+item)
                                    }
                                }
                            }
                            error "未通过Sonarqube的代码质量阈检查,请及时修改!failure: ${qg.status}"
                        }
                    }
                }
            }
        }

        stage('4_package_and_build') {
            steps{
                script{
                    //编译,打包
                    sh "mvn package -Dmaven.test.skip=true "

                    //Pipeline Utility Steps 插件 --- findFiles
                    def files = findFiles(glob: 'target/*.jar')
                    if(files.length < 0){
                        error "未找到输出物*.jar."
                    }

                    def jarFile = files[0]
                    echo "---------------${files[0]}"


                    //定义镜像名称
                    def imageName = "${project_name}:${tag}"
                    //构建本地镜像
                    sh "docker build --build-arg JAR_FILE=${jarFile} --build-arg SERVER_PORT=${serverPort} -t ${imageName} ."

                    //定义远程标签名称
                    def remoteImageName = "${harbor_url}/${harbor_project_name}/${imageName}";
                    //给镜像打标签
                    sh "docker tag ${imageName} ${remoteImageName}"

                    //登录Harbor,并上传镜像
                    withCredentials([usernamePassword(credentialsId: "${harbor_auth}",
                            passwordVariable: 'password', usernameVariable: 'username')]) {
                        //登录
                        sh "docker login -u ${username} -p ${password} ${harbor_url}"
                        //上传镜像
                        sh "docker push ${remoteImageName}"
                    }

                    //删除本地镜像
                    sh "docker rmi -f ${imageName}"
                    sh "docker rmi -f ${remoteImageName}"
                }
            }
        }


        stage("5_ssh_deploy") {
            steps {
                sshPublisher(
                        publishers: [
                                sshPublisherDesc(
                                        configName: '192.168.129.42-ssh',
                                        transfers: [
                                                sshTransfer(
                                                        cleanRemote: false,
                                                        excludes: '',
                                                        execCommand: "/home/docker/jenkins_shell/deploy.sh $harbor_url $harbor_project_name $project_name $tag ${serverPort}",
                                                        execTimeout: 120000,
                                                        flatten: false,
                                                        makeEmptyDirs: false,
                                                        noDefaultExcludes: false,
                                                        patternSeparator: '[, ]+',
                                                        remoteDirectory: '',
                                                        remoteDirectorySDF: false,
                                                        removePrefix: '',
                                                        sourceFiles: ''
                                                )
                                        ],
                                        usePromotionTimestamp: false,
                                        useWorkspaceInPromotion: false,
                                        verbose: false
                                )
                        ]
                )
            }
        }
    }

}

deploy.sh

#! /bin/bash
harbor_url=$1
harbor_project_name=$2
project_name=$3
tag=$4
port=$5

echo "请求参数: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=$imageName"


# 查询容器是否存在
containerId=`docker ps -a | grep -w ${project_name}:${tag} | awk '{print $1}'`
if [ "$containerId" != "" ]; then
  # 停掉容器
   docker stop $containerId

  # 删掉容器
  docker rm $containerId

   echo "成功删除容器"
fi

# 删除存在的镜像
docker images| grep -w $project_name | awk '{print $3}' | xargs docker rmi -f

# 登录Harbor私服
docker login -u jhs -p Jhs123456 $harbor_url

# 下载镜像
docker pull $imageName

# 启动容器
docker run -di -p $port:$port $imageName

echo "容器启动成功:${containId}"
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值