美文网首页持续集成CICD微服务架构和实践
Jenkins多分支流水线+Docker自动化部署SpringB

Jenkins多分支流水线+Docker自动化部署SpringB

作者: 阿B咬佢只鸡 | 来源:发表于2020-01-03 12:22 被阅读0次

    Jenkins

    官方安装地址
    安装最新版,支持jenkins多分支流水线(Multibranch Pipeline Job )

    目录

    • 安装docker-ce
    • docker容器安装、运行jenkins
    • 登录,配置jenkins
    • 编写Jenkinsfile,Dockerfile,start.sh
    • 构建jenkins多分支流水线工程
    • 安装docker私有镜像仓库Harbor ; 安装方法
    • Jenkins Pipeline 发送邮件配置
    • 往后补充: 服务发布到Kubernetes集群()

    安装docker-ce

    官方安装文档

    1. 卸载旧版本
      如果是新机器可以忽略这一步,因为centos还没自带docker服务。
    yum remove docker \
                      docker-client \
                      docker-client-latest \
                      docker-common \
                      docker-latest \
                      docker-latest-logrotate \
                      docker-logrotate \
                      docker-engine
    
    1. 安装依赖包
     yum install -y yum-utils device-mapper-persistent-data lvm2
    
    1. 添加Docker软件包源
    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    

    但是鉴于国内网络问题,建议使用国内阿里的源

    yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
    1. 执行安装操作
    安装:
    yum install -y docker-ce
    设置docker服务开机自启动:
    systemctl enable docker
    

    安装、运行jenkins

    docker run \
    -u root \
    -itd \
    --name=jenkins \
    --restart=always \
    -e TZ='Asia/Shanghai' \
    -p 8080:8080 ‐p 5000:5000 \
    -v /data/jenkins/jenkins_home:/var/jenkins_home \
    -v /data/jenkins/home:/home \
    -v /var/run/docker.sock:/var/run/docker.sock \
    jenkinsci/blueocean
    

    参数解析:

    • -u 指定用户运行容器
    • -itd -i, –interactive 交互式 -t, –tty 分配一个伪终端 -d, –detach 运行容器到后台
    • --name=jenkins 指定容器名
    • --restart=always 设置容器自启动
    • -e 指定容器运行所属时区
    • -p 8080:8080 容器的端口映射到宿主机上(“:”前数字为宿主机端口,“:”后数字为容器端口)
    • -v /data/jenkins/home:/home 将容器的/home目录映射到宿主机上目录中的/data/jenkins/home子目录
    • -v /var/run/docker.sock:/var/run/docker.sock Docker守护进程(Docker daemon)默认监听的Unix域套接字(Unix domain socket),容器中的进程可以通过它与Docker守护进程进行通信。简单来说容器使用宿主机docker命令
    • jenkinsci/blueocean 镜像名字

    登录jenkins

    1. 访问http://宿主机IP:8080访问Jenkins。如果无法访问请检查系统防火墙、云的安全组设置。
    2. 查看并填写初始密码
    cat /data/jenkins/jenkins_home/secrets/initialAdminPassword
    
    01.png
    02.png
    03.png
    04.png

    配置jenkins

    容器内安装:jdk1.8maven-3
    下载jdk,maven包(.tar),直接解压到/usr/local, 然后配置环境变量,再执行(docker exec jenkins soure /etc/profile)

    maven & jdk.png
    配置:
    系统管理 ==> 全局工具配置
    jdk,maven

    编写Jenkinsfile,Dockerfile,start.sh

    1. 什么是Jenkinsfile
    流水线语法
    Jenkinsfile 是 Jenkins 2.x 核心特性 Pipeline 的脚本,由Groovy语言实现。Jenkinsfile一般是放在项目根目录,随项目一起受源代码管理软件控制,无需像创建“自由风格"(Jenkins FreeStyle)项目一样,每次可能需要拷贝很多设置到新项目,提供了一些直接的好处:

    • Pipeline上的代码审查/迭代
    • Pipeline的审计跟踪
    • Pipeline的唯一真实来源,可以由项目的多个成员查看和编辑。

    Pipeline支持:Declarative(声明式)和Scripted Pipeline(脚本式)两种格式。两者都支持建立Pipeline,两者都可以用于在Web UI中定义一个流水线Jenkinsfile,将Jenkinsfile文件创建并检查到源代码控制库中通常被认为是最佳做法。

    Declarative Pipeline (声明式格式一)
    当然,还有别的格式样例

    pipeline {
        agent any 
        stages {
            stage('Build') { 
                steps {
                    // 
                }
            }
            stage('Test') { 
                steps {
                    // 
                }
            }
            stage('Prod') { 
                steps {
                    // 
                }
            }
        }
    }
    

    Scripted Pipeline(脚本式基本格式)

    node {  
        stage('Build') { 
            // 
        }
        stage('Test') { 
            // 
        }
        stage('Prod') { 
            // 
        }
    }
    

    2. 编写声明式Jenkinsfile ; 使用文本编辑器(最好支持 Groovy 语法高亮显示)
    工作原理:获取源代码 ==> maven打包 ==> 构建并推送镜像到Harbor ==>根据环境部署服务到服务器

    pipeline {
        agent any
        environment {
            registryUrl= "192.168.1.110:5000"       #搭建docker私有仓库(Harbor)或者 用DockerHub
            registry_user= "xxx"
            registry_pass= "xxx"
            env=test       //"test" or "prod"        #选择打包及发布环境
        }
        options {
            timestamps()                    //设置在项目打印日志时带上对应时间
            disableConcurrentBuilds()       //不允许同时执行流水线,被用来防止同时访问共享资源等
            buildDiscarder(logRotator(numToKeepStr: '3'))   // 表示保留n次构建历史
        }
    
        //gitlab  webhook触发器
        //聚合项目,代码发生以下动作后,所有子项目将被触发构建,可选择使用
        //triggers{
        //   gitlab( triggerOnPush: true,                       //代码有push动作就会触发job
        //       triggerOnMergeRequest: true,                   //代码有merge动作就会触发job
        //        branchFilterType: "NameBasedFilter",          //只有符合条件的分支才会触发构建 “ALL/NameBasedFilter/RegexBasedFilter”
        //        includeBranchesSpec: "${JOB_BASE_NAME}")      //基于branchFilterType值,输入期望包括的分支的规则
        //}
    
        stages{
            stage('Print Message') {      //打印信息
                steps {
                    echo "workspace: ${WORKSPACE}  build_id: ${BUILD_ID}  branch(gitlab分支名): ${env.BRANCH_NAME}"        
                    echo "registryUrl: ${registryUrl}"
               }
            }
            //stage('Test') {         //测试
            //    steps {
            //        echo 'Testing..'
            //    }
            //}
            stage('Build project') {        //mvn打包
                steps {
                    echo 'mvn打包'
                    script {
                        try {
                            sh 'mvn clean package -pl ${JOB_NAME%%/*} -am -amd -P${env} -Dmaven.test.skip=true'     //springboot maven 多模块 jenkins 单独打包子项目
                        } catch (err) {
                            echo '打包失败 & End of Pipeline!!!'
                        }
                    }
                }
            }
            stage('Build & Push Image') {      //构建,推送镜像
                steps {
                    echo '构建,推送镜像'
                    dir ('./') {       //指定工作目录(默认为${WORKSPACE})
                        script {
                            try {
                                sh 'mv ${JOB_NAME%%/*}/Dockerfile  ./'
                                sh 'docker login  --username=${registry_user} --password=${registry_pass}   ${registryUrl}'
                                def app = docker.build('${registryUrl}/${JOB_NAME%%/*}:${build_id}')
                                app.push('${build_id}')
                            } catch (err) {
                                echo '打包失败 & End of Pipeline!!!'
                            }
                        }
                    }
                    echo "delete local image"
                    sh 'docker rmi ${registryUrl}/${JOB_NAME%%/*}:${build_id}'     //删除jenkins本地新建的镜像
                }
            }
            stage('Deploy for test') {
                when {
                    beforeAgent true
                    branch 'xxx'            //填写test环境gitlab分支名(我的分支名:release_eat_away)
                }
                steps {
                    timeout(time: 40, unit: 'SECONDS') {    // 设置远程部署超过40秒,将终止该步骤
                    echo '部署到开发环境'
                    sh 'bash ./start.sh ${JOB_NAME%%/*} ${registryUrl}/${JOB_NAME%%/*}:${build_id} ${env}'    //位置变量1:工程名字  ; 位置变量2:镜像名字  ; 位置变量3:指定环境
                }            
            }
            stage('Deploy for prod') {          //部署到生产环境
                when {
                    beforeAgent true
                    branch 'xxx'       //填写prod环境gitlab分支名(我的分支名:release-3.0-prod)
                }
                steps {
                    timeout(time: 40, unit: 'SECONDS') {    // 设置远程部署超过40秒,将终止该步骤
                    echo '部署到生产环境'
                    sh 'bash ./start.sh ${JOB_NAME%%/*} ${registryUrl}/${JOB_NAME%%/*}:${build_id} ${env}'     //位置变量1:工程名字  ; 位置变量2:镜像名字 ; 位置变量3:指定环境
                }
            }
            stage('Delete Workspace') {         //清理工作目录
                steps {
                    echo "清理工作目录: ${WORKSPACE}"
                    deleteDir()     //表示删除当前目录(${WORKSPACE})下内容,通常用在构建完毕之后清空工作空间
                }
            }
        }
    }
    

    3. 编写Dockerfile

    FROM java:latest
    MAINTAINER qiujt  qiujt123@163.com
    WORKDIR /data/logs
    ADD canfu-admin/target/canfu-admin-0.0.1-SNAPSHOT.jar     canfu-admin.jar
    EXPOSE  8091
    ENTRYPOINT    ["java","-jar","-Xms512m","-Xmx1024m","-XX:PermSize=512M","-XX:MaxPermSize=1024M","/canfu-admin.jar"]
    
    • FROM 指定基础镜像,并且必须是第一条指令。(可以选择更小的镜像openjdk:8-jdk-alpine,不过一些后台项目验证码图片会出不来,慎用)
    • MAINTAINER 指定维护者信息 语法:MAINTAINER user_name user_email
    • WORKDIR:指定在创建容器后,终端默认登陆进来的工作目录,一个落脚点
    • ADD 将宿主机目录下的文件拷贝进镜像且 ADD 命令会自动处理 URL 和解压 tar 压缩包
    • EXPOSE 当前容器对外暴露出的端口
    • ENTRYPOINT:指定一个容器启动时要运行的命令,ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数

    4. 编写部署脚本(start.sh)

    #/bin/bash
    #ENV
     #docker私有仓库(Harbor)
      registryUrl=192.168.1.110:5000
      registry_user="xxx"
      registry_pass="xxx"
      project_name=$1
      image_name=$2
      env=$3
      node_user=root
    if [ "${env}"  == test ];then
       #测试环境
      node1=192.168.1.105
    elif [ "${env}" == prod ];then
      #生产环境
      node1=192.168.1.106
    else
        echo '没有${env}环境!!!'
    fi
      
    #Prepare
    echo "project_name: $1 , image_name: $2 , env:$3"
    
    #Deploying
    if [ $1 == canfu-eureka ];then
        ssh $node_user@node1 "docker login --username=${registry_user} --password=${registry_pass} ${registryUrl} && docker pull $image_name && docker stop $project_name | xargs docker rm && docker  run -itd  --name=$project_name  --restart=always -e TZ="Asia/Shanghai" -p 7002:7002 $image_name && docker image prune -a  -f --filter 'until=1h' "
    else
        #可以像上面写成一个ssh"",为了解析方便,切分开(接下来为第一行,以此类推)
        ssh $node_user@$node1 "docker login --username=${registry_user} --password=${registry_pass} ${registryUrl} “
        ssh $node_user@$node1 “docker pull $image_name && docker stop $project_name | xargs docker rm ”
        ssh $node_user@$node1 ”docker run -itd --name=$project_name --restart=always --net=host -e TZ="Asia/Shanghai" -P -v /data/logs/$project_name:/data/logs/$project_name $image_name“
        ssh $node_user@$node1 “docker image prune -a -f --filter 'until=1h' ”
        ssh $node_user@$node1 ”tailf /data/logs/$project_name/${project_name}.log"
    fi
    
    • 登录docker私有仓库(Harbor)

    • 服务器拉取对应镜像,并且停止、删除对应原有服务容器

    • 基于镜像创建容器。--net=bridge缺省设置(除注册中心外,其他为 --net=host )

    • 删除1小时前拉取的,并且未被使用的镜像

    • 打印服务输出日志

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

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

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


    创建多分支流水线项目

    SpringBoot聚合项目与Jenkinsfile ,Dockerfile , start.sh位置关系;及子项目下创建各个环境文件(application-test.yml 和 application-prod.yml)

    Springboot聚合项目
    指定环境文件
    创建jenkins流水线项目
    配置流水线项目
    点击 “立刻扫描 多分支流水线”,接下来Jenkins会在分支源中扫描每个分支下的Jenkinsfile,如果该分支下有Jenkinsfile,那么就会创建一个
    分支Job
    (下图扫描到release_eat_away 和 release-3.0-prod两个分支下有Jenkinsfile,所以jenkins创建了两个Job)
    (这里需要注意的是,只有需要部署的分支,才加上Jenkinsfile,不然Jenkins会将其余分支也创建一个分支job。例如:下图的release-3.0-beta 和 release_pressuretest)
    扫描多分支流水线
    到此,多分支流水线项目配置完毕,可以点击触发构建项目了
    多分枝流水线.png

    相关文章

      网友评论

        本文标题:Jenkins多分支流水线+Docker自动化部署SpringB

        本文链接:https://www.haomeiwen.com/subject/fpdzoctx.html