基于-Jenkins-Pipeline-自动化部署

4次阅读

共计 4337 个字符,预计需要花费 11 分钟才能阅读完成。

最近在公司推行 Docker Swarm 集群的过程中,需要用到 Jenkins 来做自动化部署,Jenkins 实现自动化部署有很多种方案,可以直接在 jenkins 页面写 Job,把一些操作和脚本都通过页面设置,也可以在每个项目中直接写 Pipeline 脚本,但像我那么优秀,那么追求极致的程序员来说,这些方案都打动不了我那颗骚动的心,下面我会跟你们讲讲我是如何通过 Pipeline 脚本实现自动化部署方案的,并且实现多分支构建,还实现了所有项目共享一个 Pipeline 脚本。

使用 Jenkins 前的一些设置

为了快速搭建 Jenkins,我这里使用 Docker 安装运行 Jenkins:

$ sudo docker run -it -d \
  --rm \
  -u root \
  -p 8080:8080 \
  -v jenkins-data:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v "$HOME":/home \
  --name jenkins jenkinsci/blueocean

初次使用 jenkins,进入 Jenkins 页面前,需要密码验证,我们需要进入 docker 容器查看密码:

$ sudo docker exec -it jenkins /bin/bash
$ vi /var/jenkins_home/secrets/initialAdminPassword

Docker 安装的 Jenkins 稍微有那么一点缺陷,shell 版本跟 CenOS 宿主机的版本不兼容,这时我们需要进入 Jenkins 容器手动设置 shell:

$ sudo docker exec -it jenkins /bin/bash
$ ln -sf /bin/bash /bin/sh

由于我们的 Pipeline 还需要在远程服务器执行任务,需要通过 ssh 连接,那么我们就需要在 Jenkins 里面生成 ssh 的公钥密钥:

$ sudo docker exec -it jenkins /bin/bash
$ ssh-keygen -C "root@jenkins"

在远程节点的~/.ssh/authorized_keys 中添加 jenkins 的公钥(id_rsa.pub)

还需要安装一些必要的插件:

  1. Pipeline Maven Integration
  2. SSH Pipeline Steps

安装完插件后,还需要去全局工具那里添加 maven:

这里后面 Jenkinsfile 有用到。

mutiBranch 多分支构建

由于我们的开发是基于多分支开发,每个开发环境都对应有一条分支,所以普通的 Pipeline 自动化构建并不能满足现有的开发部署需求,所以我们需要使用 Jenkins 的 mutiBranch Pipeline。

首先当然是新建一个 mutiBranch 多分支构建 job:

接着设置分支源,分支源就是你项目的 git 地址,选择 Jenkinsfile 在项目的路径

接下来 Jenkins 会在分支源中扫描每个分支下的 Jenkinsfile,如果该分支下有 Jenkinsfile,那么就会创建一个分支 Job

该 job 下的分支 job 如下:

这里需要注意的是,只有需要部署的分支,才加上 Jenkinsfile,不然 Jenkins 会将其余分支也创建一个分支 job。

通用化 Pipeline 脚本

到这里之前,基本就可以基于 Pipeline 脚本自动化部署了,但如果你是一个追求极致,不甘于平庸的程序员,你一定会想,随着项目的增多,Pipeline 脚本不断增多,这会造成越来越大的维护成本,随着业务的增长,难免会在脚本中修改东西,这就会牵扯太多 Pipeline 脚本了,而且这些脚本基本都相同,那么对于我这么优秀的程序员,怎么会想不到这个问题呢,我第一时间就想到通用化 pipeline 脚本。所幸,Jenkins 已经看出了我不断骚动的心了,Jenkins 甩手就给我一个 Shared Libraries。

Shared Libraries 是什么呢?顾名思义,它就是一个共享库,它的主要作用是用于将通用的 Pipeline 脚本放在一个地方,其它项目可以从它那里获取到一个全局通用化的 Pipeline 脚本,项目之间通过不通的变量参数传递,达到通用化的目的。

接下来我们先创建一个用于存储通用 Pipeline 脚本的 git 仓库:

仓库目录不是随便乱添加了,Jenkins 有一个严格的规范,下面是官方说明:

官方已经讲得很清楚了,大概意思就是 vars 目录用于存储通用 Pipeline 脚本,resources 用于存储非 Groovy 文件。所以我这里就把 Pipeline 需要的构建脚本以及编排文件都集中放在这里,完全对业务工程师隐蔽,这样做的目的就是为了避免业务工程师不懂瞎几把乱改,导致出 bug。

创建完 git 仓库后,我们还需要在 jenkins 的 Manage Jenkins » Configure System » Global Pipeline Libraries 中定义全局库:

这里的 name,可以在 jenkinsfile 中通过以下命令引用:

@Library 'objcoding-pipeline-library'

下面我们来看通用 Pipeline 脚本的编写规则:

#!groovy

def getServer() {def remote = [:]
    remote.name = 'manager node'
    remote.user = 'dev'
    remote.host = "${REMOTE_HOST}"
    remote.port = 22
    remote.identityFile = '/root/.ssh/id_rsa'
    remote.allowAnyHosts = true
    return remote
}

def call(Map map) {

    pipeline {
        agent any

        environment {REMOTE_HOST = "${map.REMOTE_HOST}"
            REPO_URL = "${map.REPO_URL}"
            BRANCH_NAME = "${map.BRANCH_NAME}"
            STACK_NAME = "${map.STACK_NAME}"
            COMPOSE_FILE_NAME = "docker-compose-" + "${map.STACK_NAME}" + "-" + "${map.BRANCH_NAME}" + ".yml"
        }

        stages {stage('获取代码') {
                steps {git([url: "${REPO_URL}", branch: "${BRANCH_NAME}"])
                }
            }

            stage('编译代码') {
                steps {withMaven(maven: 'maven 3.6') {sh "mvn -U -am clean package -DskipTests"}
                }
            }

            stage('构建镜像') {
                steps {
                    sh "wget -O build.sh https://git.x-vipay.com/docker/jenkins-pipeline-library/raw/master/resources/shell/build.sh"
                    sh "sh build.sh ${BRANCH_NAME}"
                }
            }

            stage('init-server') {
                steps {
                    script {server = getServer()
                    }
                }
            }

            stage('执行发版') {
                steps {writeFile file: 'deploy.sh', text: "wget -O ${COMPOSE_FILE_NAME}" +
                        "https://git.x-vipay.com/docker/jenkins-pipeline-library/raw/master/resources/docker-compose/${COMPOSE_FILE_NAME} \n" +
                        "sudo docker stack deploy -c ${COMPOSE_FILE_NAME} ${STACK_NAME}"
                    sshScript remote: server, script: "deploy.sh"
                }
            }
        }
    }
}
  1. 由于我们需要在远程服务器执行任务,所以定义一个远程服务器的信息其中 remote.identityFile 就是我们上面在容器生成的密钥的地址;
  2. 定义一个 call()方法,这个方法用于在各个项目的 Jenkinsfile 中调用,注意一定得叫 call;
  3. 在 call()方法中定义一个 pipeline;
  4. environment 参数即是可变通用参数,通过传递参数 Map 来给定值,该 Map 是从各个项目中定义的传参;
  5. 接下来就是一顿步骤操作啦,“编译代码”这步骤需要填写上面我们在全局工具类设置的 maven,“构建镜像”的构建脚本巧妙地利用 wget 从本远程仓库中拉取下来,”执行发版“的编排文件也是这么做,“init-server”步骤主要是初始化一个 server 对象,供“执行发版使用”。

从脚本看出来 Jenkins 将来要推崇的一种思维:配置即代码。

写完通用 Pipeline 脚本后,接下来我们就需要在各个项目的需要自动化部署的分支的根目录下新建一个 Jenkinsfile 脚本了:

接下来我来解释一下 Jenkinsfile 内容:

#!groovy

// 在多分支构建下,严格规定 Jenkinsfile 只存在可以发版的分支上

// 引用在 jenkins 已经全局定义好的 library
library 'objcoding-pipeline-library'
def map = [:]

// 远程管理节点地址(用于执行发版)map.put('REMOTE_HOST','xxx.xx.xx.xxx')
// 项目 gitlab 代码地址
map.put('REPO_URL','https://github.com/objcoding/docker-jenkins-pipeline-sample.git')
// 分支名称
map.put('BRANCH_NAME','master')
// 服务栈名称
map.put('STACK_NAME','vipay')

// 调用 library 中 var 目录下的 build.groovy 脚本
build(map)
  1. 通过 library 'objcoding-pipeline-library' 引用我们在 Jenkins 定义的全局库,定义一个 map 参数;
  2. 接下来就是将项目具体的参数保存到 map 中,调用 build()方法传递给通用 Pipeline 脚本。

Shared Libraries 共享库极大地提升了 Pipeline 脚本的通用性,避免了脚本过多带来的问题,也符合了一个优秀程序员的审美观,如果你是一个有追求的程序员,你一定会爱上它。

更多精彩文章请关注作者维护的公众号「后端进阶」,这是一个专注后端相关技术的公众号。
关注公众号并回复「后端」免费领取后端相关电子书籍。
欢迎分享,转载请保留出处。

正文完
 0