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

先简要说明一下大致流程:
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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341

推荐阅读更多精彩内容