美文网首页
基于Jenkins的持续集成部署(CI/CD)

基于Jenkins的持续集成部署(CI/CD)

作者: 一二先生 | 来源:发表于2019-11-24 12:42 被阅读0次

    先简要说明一下大致流程:
    1. 提交代码至gitlab代码仓库
    2. 配置jenkins集成部署任务

    集成部署任务包含以下步骤:
    1.拉取gitlab代码
    2.编译代码,同时生成docker镜像
    3.将docker镜像推送至docker镜像仓库
    4.使用docker远程命令模式,远程docker主机拉取docker镜像仓库镜像,并运行
    如果不使用docker镜像仓库,可合并3、4步骤

    • 环境

      • Gitlab:项目代码仓库
      • Jenkins:持续集成部署任务管理工具
      • Docker:部署容器
    • 编译准备(Java)
      由于工作需要,平时都是使用gradle进行java代码编译,因此我们需要对build.gradle进行配置(如果使用的是Maven,请修改想要的pom.xml),配置如下:

      buildscript {
          ...
          dependencies {
              ...
              classpath("se.transmode.gradle:gradle-docker:1.2")
          }
      }
      
      ...
      apply plugin: 'docker'
      
      docker {
          baseImage 'adoptopenjdk/openjdk8-openj9'
          maintainer 'lv@eairlv'
      }
      
      sourceCompatibility = '1.8'
      group = 'com.eairlv.cli'
      archivesBaseName = 'projectName'
      version = '1.0.3'
      
      task buildDocker(type: Docker, dependsOn: build) {
          applicationName = archivesBaseName
          tagVersion = version.toString()
          addFile("${applicationName}-${tagVersion}.jar","app.jar")
          entryPoint(["sh","-c",'java $JAVA_OPTIONS -jar app.jar $APP_OPTIONS'])
          doFirst {
              copy {
                  from jar
                  into stageDir
              }
          }
      }
      

      JAVA_OPTIONS:配置jvm参数
      APP_OPTIONS:配置程序启动参数

    • 任务准备
      之前在另外一篇文章中介绍了jenkins的安装与简述了一下任务创建的步骤:

      • 输入gitlab地址与分支

      • 选择之前创建的全局凭据

      • 配置gradle脚本(选择版本、配置task启动任务):-x test clean buildDocker

        image.png
      • 配置自定义shell脚本(用于执行docker部署指令的脚本),这里分享一个示例吧:

        #!/bin/bash
        set +v
        
        # -------------------主机配置-------------------
        
        # 步骤配置:1 编译构建,并推送至远程仓库;2 从远程仓库拉取镜像运行;3 编译构建,推送至远程仓库,拉取镜像运行
        STEP=3
        
        # 主机IP,多个IP以空格隔开,如:(192.168.1.1 192.168.1.2)
        HOST_IP=(192.168.1.1)
        
        # 主机端口,多个端口以空格隔开,如:(8090 8090),注意数量需要和<主机IP>保持一致
        HOST_PORT=(8761)
        
        # 主机日志目录
        HOST_LOG_PATH=/home/eairlv
        
        # -------------------项目配置-------------------
        
        # 项目名称
        PROJECT_NAME=projectName
        
        # 项目包路径
        PROJECT_PACKAGE=com.eairlv.cli
        
        # 项目版本
        PROJECT_VERSION=1.0.3
        
        # 项目分支
        PROJECT_BRANCH=develop
        
        # 项目端口,如果设置为-1,则默认项目端口为映射的主机端口
        PROJECT_PORT=-1
        
        # 项目参数
        PROJECT_APP_OPTIONS="--spring.cloud.config.label=${PROJECT_BRANCH} --eureka.client.service-url.defaultZone=http://192.168.1.1:8761/eureka/"
        
        # 项目日志目录
        PROJECT_LOG_PATH=/eairlv
        
        # -------------------编译配置-------------------
        
        # 远端仓库地址
        MIRROR_WAREHOUSE={MIRROR_WAREHOUSE}
        
        # 远端仓库组织
        MIRROR_WAREHOUSE_ORG={MIRROR_WAREHOUSE_ORG}
        
        # 远端仓库用户
        MIRROR_WAREHOUSE_USER={MIRROR_WAREHOUSE_USER}
        
        # 远端仓库密钥
        MIRROR_WAREHOUSE_PWD={MIRROR_WAREHOUSE_PWD}
        
        # 编译后镜像名,项目包路径/项目名称:项目版本(多环境下同一项目版本号生成的镜像名一致,极端情况下可能会出现镜像交叉问题)
        JENKINS_IMAGE_NAME=${PROJECT_PACKAGE}/${PROJECT_NAME}:${PROJECT_VERSION}
        
        # 远端仓库镜像名,远端仓库地址/远端仓库组织/项目名称:项目版本-项目分支
        WAREHOUSE_IMAGE_NAME=${MIRROR_WAREHOUSE}/${MIRROR_WAREHOUSE_ORG}/${PROJECT_NAME}:${PROJECT_VERSION}-${PROJECT_BRANCH}
        
        # -------------------环境配置-------------------
        
        # 语言环境配置,快速启动;编码设置;(可自定义增加其他参数,如限制内存使用:-Xms100m -Xmx 500m)
        JAVA_OPTIONS="-Duser.timezone=GMT+8 -Djava.security.egd=file:/dev/./urandom -Dfile.encoding=UTF-8"
        
        # 容器环境配置,日志限制;时区同步;日志挂载
        DOCKER_ENVIRONMENT="--log-opt max-size=1m --log-opt max-file=1 -v /etc/localtime:/etc/localtime:ro -v ${HOST_LOG_PATH}:${PROJECT_LOG_PATH} -d"
        
        # DOCKER远程端口
        DOCKER_REMOTE_PORT=2375
        
        # -------------------运行配置-------------------
        
        # 1 推送至远程仓库
        function step1(){
            
            # 将镜像标记为远程仓库名
            docker tag ${JENKINS_IMAGE_NAME} ${WAREHOUSE_IMAGE_NAME}
            
            # 删除历史编译镜像,释放空间(不可删除未使用镜像,因为编译的镜像均为未使用,且系统可同时进行代码编译与镜像构建)
            if [ -n "$(docker images | grep none | awk '{print $3}')" ]; then
                docker images | grep none | awk '{print $3}' | sort -u | xargs docker rmi -f
            fi
            
            # 登录远程仓库
            docker login -u ${MIRROR_WAREHOUSE_USER} -p ${MIRROR_WAREHOUSE_PWD} ${MIRROR_WAREHOUSE}
            
            # 推送镜像至远程仓库
            docker push ${WAREHOUSE_IMAGE_NAME}
            
            # 删除本次编译镜像,释放空间
            if [ -n "$(docker images | grep ${PROJECT_NAME} | awk '{print $3}')" ]; then
                docker images | grep ${PROJECT_NAME} | awk '{print $3}' | sort -u | xargs docker rmi -f
            fi
        }
        
        # 2 从远程仓库拉取镜像运行
        function step2(){
            
            # 主机与端口配置校验
            if [ ${#HOST_IP[@]} -eq ${#HOST_PORT[@]} ];then
                
                # 遍历主机列表
                for ip_index in "${!HOST_IP[@]}";
                do
                    remote_ip=${HOST_IP[$ip_index]}
                    remote_port=${HOST_PORT[$ip_index]}
                    
                    echo "------------远程主机:$remote_ip:$remote_port 拉取远程仓库镜像------------"
                    docker login -u ${MIRROR_WAREHOUSE_USER} -p ${MIRROR_WAREHOUSE_PWD} ${MIRROR_WAREHOUSE}
                    docker -H $remote_ip:${DOCKER_REMOTE_PORT} pull ${WAREHOUSE_IMAGE_NAME}
                    
                    if [ -n "$(docker -H $remote_ip:${DOCKER_REMOTE_PORT} ps -a | grep ${PROJECT_NAME}.$remote_port | awk '{print $1}' | sed 's/%//g')" ]; then
                        echo "------------远程主机:$remote_ip:$remote_port 停止并删除容器------------"
                        docker -H $remote_ip:${DOCKER_REMOTE_PORT} stop $(docker -H $remote_ip:${DOCKER_REMOTE_PORT} ps -a | grep ${PROJECT_NAME}.$remote_port | awk '{print $1}' | sed 's/%//g')
                        docker -H $remote_ip:${DOCKER_REMOTE_PORT} rm -f $(docker -H $remote_ip:${DOCKER_REMOTE_PORT} ps -a | grep ${PROJECT_NAME}.$remote_port | awk '{print $1}' | sed 's/%//g')
                    fi
        
                    echo "------------远程主机:$remote_ip:$remote_port 启动容器------------"
                    if [ ${PROJECT_PORT} -eq -1 ];then
                        docker -H $remote_ip:${DOCKER_REMOTE_PORT} run -e JAVA_OPTIONS="${JAVA_OPTIONS}" -e APP_OPTIONS="${PROJECT_APP_OPTIONS} --eureka.instance.ip-address=$remote_ip --server.port=$remote_port" ${DOCKER_ENVIRONMENT} --name ${PROJECT_NAME}.$remote_port -p $remote_port:$remote_port ${WAREHOUSE_IMAGE_NAME}
                    else
                        docker -H $remote_ip:${DOCKER_REMOTE_PORT} run -e JAVA_OPTIONS="${JAVA_OPTIONS}" -e APP_OPTIONS="${PROJECT_APP_OPTIONS} --eureka.instance.ip-address=$remote_ip" ${DOCKER_ENVIRONMENT} --name ${PROJECT_NAME}.$remote_port -p $remote_port:${PROJECT_PORT} ${WAREHOUSE_IMAGE_NAME}
                    fi
                    
                    # 删除未使用的镜像,释放空间
                    docker -H $remote_ip:${DOCKER_REMOTE_PORT} image prune -a -f
                done
            else
                echo "HOST_IP HOST_PORT 数量不匹配,请重新配置"
            fi
        }
        
        # 3 推送至远程仓库,并拉取镜像运行
        function step3(){
            step1
            step2
        }
        
        # 步骤控制
        if [ ${STEP} -eq 1 ];then
            step1
        elif [ ${STEP} -eq 2 ];then
            step2
        else
            step3
        fi
        

        可根据实际情况对脚本进行调整。这里友情提示一下,在配置任务的时候建议将任务的删除工作空间功能给选上,不然jenkins编译的机器上的磁盘空间很容易就会被占满,因为jenkins会将编译的依赖包等放入工作空间

        • 编译前清空


          image.png
        • 编译后清空


          image.png

    相关文章

      网友评论

          本文标题:基于Jenkins的持续集成部署(CI/CD)

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