Jenkins实践(二)------ Jenkinsfile

Jenkins 能做的事情太多了,如果要一一讲解肯定讲不完,因此此系列文章只以多分支流水线任务(Pipeline)为例。

我们要实现的是,能够通过 Jenkins 的 pipeline,拉取代码,编译,发布 docker 镜像,部署 docker 服务器。

我们使用 git 作为代码版本管理工具,使用 nexus3 私服作为jar包、docker 镜像的仓库。Pipeline 中,主要是使用 Jenkinsfile 来实现主要的逻辑。

代码将分为 master、dev、test、prod 四个分支,其中 master 分支主要开发使用,dev 为部署到开发环境服务器的代码分支,test 为部署到测试环境服务器的代码分支,prod 为部署到正式环境的代码分支。

Jenkinsfile 是 Jenkins 2.x 核心特性 Pipeline 的脚本,由Groovy语言实现。Jenkinsfile一般是放在项目根目录,随项目一起受源代码管理软件控制。

代码中存在 Jenkinsfile-dev、Jenkinsfile-test、Jenkinsfile-pord 三个文件,依次在开发、测试、正式三个环境中会使用到。

关于 Jenkinsfile 的具体语法规则,不在本文的讲解范围之内,需要读者自行去查看资料,官方文档可参考:https://jenkins.io/zh/doc/book/pipeline/jenkinsfile/

在开始之前,我们尚有一些准备工作要做。

1. 创建一个全局的 git 访问凭证。

然后在打开的页面中填写访问 git 的账号密码,并且填写一个 ID,记住这个ID,后面在 Jenkisnfile-prod 中要用。本文中定义ID为 config-user。

 

首先讲解下各个环境的Jenkinsfile脚本的主要思路:

Jenkins 脚本中,统一设置 dev 环境拉取 master 分支最新的代码,test 环境拉取 dev 分支最新的代码,prod 环境依旧拉取 prod 分支最新的代码。

Jenkinsfile-dev 脚本逻辑:因为 dev 环境获取的是master分支最新的代码, 因此会先执行 git check out dev , 然后 git merge master,在本地切出 dev 分支,并且将 master 代码合并入 dev 分支,然后执行编译操作,编译通过之后,执行 git push origin dev 将合并后的本地 dev 分支代码推送到远端 dev 分支,最后进行 docker 打包发布的操作。

pipeline {
    agent {
        docker {
            image 'maven:3.6.0-jdk-8'
            args '-v /root/.m2:/root/.m2'
        }
    }
 
    environment { 
        DOCKER_REPOSITORY_HOST = 'wx.ankoninc.com.cn'
        ACTIVED_PROFILE = 'dev'
        NETWORK = 'servicenet'
	GIT_REPOSITORY = 'wx.ankoninc.com.cn/gitbucket/git/cloud/cloud-app-service.git'
    }
    stages {

        stage('merge code') {
	    steps {
                sh '''git checkout dev
                      git merge master'''
            }
	}
 
        stage('Maven install') {
            steps {
                sh 'mvn install'
            }
        } 
 
        stage('Sonar') {
            steps {
                sh 'mvn sonar:sonar'
            }
        } 

	stage('push code to branch dev') {
	    steps {
		withCredentials([usernamePassword(credentialsId: 'config-user', usernameVariable: 'username', passwordVariable: 'password')]){
                    sh "git push https://$username:$password@${GIT_REPOSITORY}"
                }
            }
	}
 
        stage('Deploy cloud-app-service-server') {
            steps {
                script {
                    env.WORKSPACE_PATH = "./cloud-app-service-server"
                    def pom = readMavenPom file: "${WORKSPACE_PATH}/pom.xml"
                    env.PROJECT_NAME = pom.artifactId
                    env.JAR_FILE_PATH = "./target/${PROJECT_NAME}-${pom.version}.jar"
                    env.PORT = pom.properties.ankonAppPort
                    env.IMAGE = "${DOCKER_REPOSITORY_HOST}/${PROJECT_NAME}:${pom.version}-${ACTIVED_PROFILE}-${BUILD_TIMESTAMP}"
                }
                sh "docker build --build-arg JAR_FILE_PATH=${JAR_FILE_PATH} --build-arg PORT=${PORT} -t ${IMAGE} ${WORKSPACE_PATH}"
                sh "docker service rm ${PROJECT_NAME} || true" 
                sh "docker push ${IMAGE}"
                sh "docker service create   --name ${PROJECT_NAME} --limit-memory 500M  --with-registry-auth --replicas 1   --publish published=${PORT},target=${PORT}   --network ${NETWORK}    ${IMAGE} --spring.profiles.active=${ACTIVED_PROFILE}"
            }
        } 
    }
}

Jenkinsfile-test 脚本逻辑:因为 test 环境获取的是 dev 分支最新的代码, 因此会先执行 git check out test , 然后 git merge dev,在本地切出 test 分支,并且将 dev 代码合并入 test 分支,然后执行编译操作,编译通过之后,执行 git push origin test 将合并后的本地 test 分支代码推送到远端 test 分支,最后进行 docker 打包发布的操作。

pipeline {
    agent {
        docker {
            image 'maven:3.6.0-jdk-8'
            args '-v /root/.m2:/root/.m2'
        }
    }
 
    environment { 
        DOCKER_REPOSITORY_HOST = 'wx.ankoninc.com.cn'
        ACTIVED_PROFILE = 'test'
        NETWORK = 'servicenet'
	GIT_REPOSITORY = 'wx.ankoninc.com.cn/gitbucket/git/cloud/cloud-app-service.git'
    }
    stages {
        stage('merge code') {
	    steps {
                sh '''git checkout test
                      git merge dev'''
            }
	}
 
        stage('Maven install') {
            steps {
                sh 'mvn install'
                
            }
        } 
 
        stage('Sonar') {
            steps {
                sh 'mvn sonar:sonar'
                
            }
        }

	stage('push code to branch test') {
	    steps {
		withCredentials([usernamePassword(credentialsId: 'config-user', usernameVariable: 'username', passwordVariable: 'password')]){
                    sh "git push https://$username:$password@${GIT_REPOSITORY}"
                }
            }
	}
 
        stage('Deploy cloud-app-service-server') {
            steps {
                script {
                    env.WORKSPACE_PATH = "./cloud-app-service-server"
                    def pom = readMavenPom file: "${WORKSPACE_PATH}/pom.xml"
                    env.PROJECT_NAME = pom.artifactId
                    env.JAR_FILE_PATH = "./target/${PROJECT_NAME}-${pom.version}.jar"
                    env.PORT = pom.properties.ankonAppPort
                    env.IMAGE = "${DOCKER_REPOSITORY_HOST}/${PROJECT_NAME}:${pom.version}-${ACTIVED_PROFILE}-${BUILD_TIMESTAMP}"
                }
                sh "docker build --build-arg JAR_FILE_PATH=${JAR_FILE_PATH} --build-arg PORT=${PORT} -t ${IMAGE} ${WORKSPACE_PATH}"
                sh "docker service rm ${PROJECT_NAME} || true" 
                sh "docker push ${IMAGE}"
                sh "docker service create   --name ${PROJECT_NAME} --limit-memory 500M  --with-registry-auth --replicas 1   --publish published=${PORT},target=${PORT}   --network ${NETWORK}    ${IMAGE} --spring.profiles.active=${ACTIVED_PROFILE}"
            }
        } 
    }
}

Jenkinsfile-prod 脚本逻辑:prod 环境获取的依旧是 prod 分支最新的代码。Jenkinsfile-prod 脚本获取 prod 分支最新代码之后,读取 pom 文件中的项目配置,主要获取项目名、版本号,然后从 nexus 的 docker 仓库中获取当前项目、当前版本对应的 test 环境打包的最后那个镜像,将其重新切出一个 XXXX-release 的镜像之后,推送这个 release 镜像到 nexus 仓库中。最后使用这个 release 的镜像进行 docker 打包发布的操作。

#!groovy

import groovy.json.JsonSlurperClassic;

pipeline {
    agent any
 
    environment { 
        DOCKER_REPOSITORY_HOST = 'wx.ankoninc.com.cn'
        ACTIVED_PROFILE = 'prod'
        NETWORK = 'servicenet'
    }
    stages {
 
        stage('Deploy cloud-app-service-server') {
            when {
                branch "${ACTIVED_PROFILE}"
            }
 
            steps {
                script {
                    env.WORKSPACE_PATH = "./cloud-app-service-server"
                    def pom = readMavenPom file: "${WORKSPACE_PATH}/pom.xml"
                    env.PROJECT_NAME = pom.artifactId
                    env.PORT = pom.properties.ankonAppPort
                    env.IMAGE = "${DOCKER_REPOSITORY_HOST}/${PROJECT_NAME}:${pom.version}-release"
		    env.POM_VERSION = "${pom.version}"

		    env.LAST_TEST_TAG = getTagFromDockerHub("${PROJECT_NAME}", "${POM_VERSION}")
		    echo "get ${PROJECT_NAME} last test image tag ${LAST_TEST_TAG}"
		    env.LAST_TEST_IMAGE_WITH_TAG = "${DOCKER_REPOSITORY_HOST}/${PROJECT_NAME}:${LAST_TEST_TAG}"
                }

                echo "pull image ${LAST_TEST_IMAGE_WITH_TAG}"
		sh "docker pull ${LAST_TEST_IMAGE_WITH_TAG}"

		echo "tag image ${LAST_TEST_IMAGE_WITH_TAG} to ${IMAGE}"
		sh "docker tag ${LAST_TEST_IMAGE_WITH_TAG} ${IMAGE}"

		echo "push image tag ${IMAGE}"
		sh "docker push ${IMAGE}"

                echo "depoy docker service for ${LAST_TEST_IMAGE_WITH_TAG}"
                sh "docker service rm ${PROJ<!-- ECT_NAME} || true" 
                sh "docker service create   --name ${PROJECT_NAME}  --limit-memory 500M  --with-registry-auth --replicas 1   --publish published=${PORT},target=${PORT}   --network ${NETWORK}    ${IMAGE} --spring.profiles.active=${ACTIVED_PROFILE}"
            }
        } 
    }
}

def getTagFromDockerHub(imgName, version) {
    echo "find image ${imgName} all tags"
    def url = new URL("https://wx.ankoninc.com.cn/nexus/repository/local-docker/v2/${imgName}/tags/list")
    def jsonResponse = url.getText()
    def parsedJSON = parseJSON(jsonResponse)
    def testTags = []
    def tagPattern = "${version}-test-"
    for (result in parsedJSON.tags) {
	if (result.indexOf(tagPattern) != -1) {
            testTags.add(result)   
	}
    }

    if (testTags.size() > 0) {
        return testTags[testTags.size() - 1]
    } else {
        return "";
    }
}

def parseJSON(json) {
    return new groovy.json.JsonSlurperClassic().parseText(json)
}

在下一篇文章中,将会讲解使用 jenkins 创建多分支流水线任务(pipeline),那时将会用到本文中的三个 jenkinsfile,来执行编辑构建发布工作。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值