jenkinspipeline部署实践及重点问题分析(代码片段)

涂宗勋 涂宗勋     2022-12-30     637

关键词:

前言

根据网上的说法,以及暂时使用过程中的感受,使用自由风格或者maven风格来创建jenkins item,虽然也能实现自动化部署,但是面对相对复杂的构建需求时可能就不太好实现。
一般正式的项目,除了基本的拉取代码、编译代码、运行junit、打包、启动或者重启外,可能还会涉及到sonar代码检查、集成测试、关联例如jira或者conflunce等系统。
因此,我目前所知道的很多正式项目在使用jenkins时可能都会使用pipeline流水线,如果要使用pipeline,需要先安装pipeline插件。
pipeline看起来好像也不是太难,但是真正自己操作的时候可能会发现很多地方会有小问题。例如可能涉及到ssh问题,可能涉及到容器操作问题,也可能涉及到shell脚本问题。
以下是我实际操作过程中的一些记录,主要分为两个部分:一部分是我只使用了一台linux虚拟机,jenkins、git、maven、springboot服务都在上边跑;另一部分是我把spirngboot服务单独放到了另一个虚拟机上。
同一台机的时候,不涉及ssh,但是却会涉及到jenkins杀死java进程的问题,不同机器就涉及到ssh远程调用shell脚本的问题。

构建步骤

jenkins pipeline进行构建的时候,根据需求,可以有各种不同的步骤,我这里模拟的只有这样几个基础的:

拉取git代码;
maven执行junit并打包;
推送jar包到服务目录;
启动或者重启springboot服务。

那么这里,前两步其实是一样的,后两步略有区别。推送jar包,在jenkins本机实际就是用的cp操作,而其他机器则是使用的scp操作。
启动和重启spingboot服务,本机直接启动脚本,远程就需要额外的配置。

部署到jenkins本机

jenkins的pipeline配置,分为脚本式和声明式两种,语法上不同,但是主要内容是差不多的。
这里需要注意的是部署到jenkins本机,最后启动服务的操作。
我一开始写的是sh "/opt/sh/test.sh restart base-springboot.jar",然后如果是把这些内容直接贴到jenkins 的管理界面配置pipeline那里,build的时候都是成功的,服务也都是正常启动。
但是当我把这些内容写到jenkinsfile文件中,然后和springboot项目代码放在一起,上传到github上,然后jenkins build的时候也都是成功的,可是却发现每次构建结束后springboot服务都没有启动起来,也查不到相关的java进程
这个地方其实花了好长时间,后来查了各种资料才大概知道了jenkins会杀掉这里启动的java进程,至于为什么,到现在都还没能弄清楚。
所以需要把上边的内容改成sh "JENKINS_NODE_COOKIE=dontKillMe /opt/sh/test.sh restart base-springboot.jar"
当然了,在使用其他远程机器部署的时候,就不会有这个问题。

jenkins本机部署服务-脚本式

node 
   stage('Print Message') 
      echo "【start build】 workspace: $WORKSPACE"
   

   stage('git pull code') 
      echo "【git pull】"
      git branch: 'jenkin-test', credentialsId: '3bdb20f7-1c4f-4a34-98c0-ef1b9202cbe2', url: 'git@github.com:tuzongxun/base-springboot.git'
   

   //mvn打包
   stage('mvn build project') 
      echo "【mvn build】"
      sh 'mvn clean package'
   

   //上传jar
   stage('Push jar') 
      echo "【push jar】"
      sh 'cp /root/.jenkins/workspace/pip-test/target/base-springboot-0.0.1-SNAPSHOT.jar /opt/code/base-springboot.jar'
      sh '''rm -rf  `ls | egrep -v '(Jenkinsfile|start.sh)'` '''
   

   stage('start/restart service') 
        echo "【start/restart service】"
        sh "JENKINS_NODE_COOKIE=dontKillMe /opt/sh/test.sh restart base-springboot.jar"
    


jenkins本机部署服务-声明式

pipeline 
    agent any

    stages 
        //打印信息
       stage('Print Message') 
          steps 
             echo "【start build】 workspace: $WORKSPACE"
          
       

       stage('git pull code') 
          steps 
             echo "【git pull】"
             git branch: 'jenkin-test', credentialsId: '3bdb20f7-1c4f-4a34-98c0-ef1b9202cbe2', url: 'git@github.com:tuzongxun/base-springboot.git'
          
       

       //mvn打包
       stage('mvn build project') 
          steps 
             echo "【mvn build】"
             script 
                try 
                //刚开始使用mvn clean package 提示找不到mvn,所以就这样指定地址,指定配置文件
                    sh 'mvn clean package'
                 catch (err) 
                   echo 'mvn打包失败'
                
             
          
       

       //上传jar
       stage('Push jar') 
          steps 
             echo "【push jar】"
             dir('/root/.jenkins/workspace/pip-test')  //指定工作目录
                script 
                   try 
                      sh 'cp ./target/base-springboot-0.0.1-SNAPSHOT.jar /opt/code/base-springboot.jar'
                    catch (err) 
                      echo "上传jar构建失败"
                   
                
             
             //推送镜像后,删除工作空叫初Jenkinsfile & start.sh 以外所有文件
             sh '''rm -rf  `ls | egrep -v '(Jenkinsfile|start.sh)'` '''
          
       

       stage('Deploy to the Target server') 
          steps 
                sh "JENKINS_NODE_COOKIE=dontKillMe /opt/sh/test.sh restart base-springboot.jar"
          
       

    

shell脚本

在上边的pipeline中调用了shell脚本来重启springboot服务,或者说java进程,对应的shell脚本如下:

#!/bin/bash
APP_NAME=$2
source /etc/profile
BUILD_ID=dontKillMe
usage() 
    echo "Usage: sh test.sh [start|stop|restart|status]"
    exit 1


is_exist()
    pid=0
    javaps=`jps -l |grep base-springboot.jar`
    if [ -n "$javaps" ]; then
      echo $javaps
      pid=`echo $javaps | awk 'print $1'`
      echo "bb":$pid
      return 0
    else
      echo "cc":$pid
      return 1
    fi


start()
    is_exist
    if [ $? -eq "0" ]; then
        echo "$APP_NAME is already running. pid=$pid ."
    else
        nohup java -jar /opt/code/$APP_NAME >> /opt/code/test.log 2>&1 &
        echo "$APP_NAME start success"
    fi


stop()
    is_exist
    if [ $? -eq "0" ]; then
        echo "aa":$pid
        kill -9 $pid
    else
        echo "$APP_NAME is not running"
    fi


status()
    is_exist
    if [ $? -eq "0" ]; then
        echo "$APP_NAME is running. Pid is $pid"
    else
        echo "$APP_NAME is NOT running."
    fi


restart()
    stop
    sleep 5
    start


case "$1" in
    "start")
        start
        ;;
    "stop")
        stop
        ;;
    "status")
        status
        ;;
    "restart")
        restart
        ;;
    *)
        usage
        ;;
esac

exit 0

这里其实也有一个问题,由于对linux和shell脚本不太熟,所以很多东西都是网上找的,一开始根据网上找到的资料,查看相关java进程,使用的是这段代码:

is_exist()
    pid=`ps -ef|grep $APP_NAME|grep -v grep|grep -v $SCRIPT|awk 'print $2'`
    echo $pid
    if [ -z "$pid" ]; then
        return 1
    else
        return 0
    fi

但是后来发现总在报错,于是又改成了这样:

is_exist()
    pid=`ps -ef|grep $APP_NAME|grep -v grep|awk 'print $2'`
    echo $pid
    if [ -z "$pid" ]; then
        return 1
    else
        return 0
    fi

但是这样之后又发现每次拿到的pid都不止1个,于是在后边stop的时候一样有问题。所以最终改成了上边脚本示例的这样:

is_exist()
    pid=0
    javaps=`jps -l |grep base-springboot.jar`
    if [ -n "$javaps" ]; then
      echo $javaps
      pid=`echo $javaps | awk 'print $1'`
      echo "bb":$pid
      return 0
    else
      echo "cc":$pid
      return 1
    fi

部署到非jenkins所在机器的pipeline配置

虽然jenkins本机部署可用了,但是一般真实项目,基本都不会是业务服务和jenkins在同一台机上,所以我又克隆了一台虚拟机作为业务服务器。
不过,这里暂时只跑通了脚本式的pipeline配置,声明式的没有成功。以下是脚本式配置的代码:

node 
    def remote = [:]
    remote.name = 'test'
    remote.host = '192.168.19.200'
    remote.user = 'root'
    remote.allowAnyHosts = true

   stage('Print Message') 
      echo "【start build】 workspace: $WORKSPACE"
   

   stage('git pull code') 
      echo "【git pull】"
      git branch: 'jenkin-test', credentialsId: '3bdb20f7-1c4f-4a34-98c0-ef1b9202cbe2', url: 'git@github.com:tuzongxun/base-springboot.git'
   

   //mvn打包
   stage('mvn build project') 
      echo "【mvn build】"
      sh 'mvn clean package'
   

   //上传jar
   stage('Push jar') 
      echo "【push jar】"
      sh 'scp /root/.jenkins/workspace/pip-test/target/base-springboot-0.0.1-SNAPSHOT.jar root@192.168.19.200:/opt/code/base-springboot.jar'
      sh '''rm -rf  `ls | egrep -v '(Jenkinsfile|start.sh)'` '''
   

   stage('start/restart service') 
        withCredentials([sshUserPrivateKey(credentialsId: '3bdb20f7-1c4f-4a34-98c0-ef1b9202cbe2', keyFileVariable: 'identity', passphraseVariable: '', usernameVariable: 'root')]) 
            echo "【start/restart service】"
            remote.user = 'root'
            remote.identityFile = identity
            sshCommand remote: remote, command: "/opt/sh/test.sh restart base-springboot.jar"
        
    


这里主要就是上传jar时使用了scp,这里没有出现密码,是因为在另一台机上配置了jenkins所在机的sshkey的公钥。
然后就是远程调用时配置的credentialsId,我两台机192.168.19.199192.168.19.200,我是需要用jenkins所在的199去调用200机器上的shell脚本。
一开始以为和scp一样,已经在200配置了199的公钥,那么应该可以直接调用了。但是结果发现并不是想的那样,还需要配置jenkins中配置的credentials的Id。

总结

pipeline本身或许不难,但是每一步涉及到的可能都是一个面的知识,例如shell、ssh、jenkins作用域或者工作空间之类的,需要有比较好的综合知识来支持。
pipeline可以支持的需求和功能很多,基于它相关的插件也有很多,待后续再逐步了解。

jenkinspipeline部署补充记录(代码片段)

...xff1a;centos7中jenkins安装和验证jenkins初步理解及参数化构建jenkinspipeline部署实践及重点问题分析pipeline中gi 查看详情

jenkispipeline构建项目实践(代码片段)

...ver在k8s环境部署之后,本文我们来测试在k8s集群环境中的jenkinspipeline构建项目和更新,具体环境要求如下:1、jenkinspipeline插件安装成功2、要更新的应用已提前部署3、Jenkinsslave中需要有kubectl、svn、mvn客户端且环境变量设置准确4... 查看详情

jenkinspipeline脚本编写

参考文章Jenkinspipeline脚本编写实践分享(一)上篇Jenkinspipeline脚本编写实践分享(一)下篇官方pipelinedoc编写Java代码 查看详情

jenkinspipeline脚本编写

参考文章Jenkinspipeline脚本编写实践分享(一)上篇Jenkinspipeline脚本编写实践分享(一)下篇官方pipelinedoc编写Java代码 查看详情

jenkinspipeline脚本编写

参考文章Jenkinspipeline脚本编写实践分享(一)上篇Jenkinspipeline脚本编写实践分享(一)下篇官方pipelinedoc编写Java代码 查看详情

[转]jenkinspipeline插件十大最佳实践(代码片段)

[From] http://blog.didispace.com/jenkins-pipeline-top-10-action/ JenkinsPipeline插件对于Jenkins用户来说可以让用户能够改变游戏规则。基于Groovy中的领域特定语言(DSL),Pipeline插件使Pipelines可以有脚本来定义,并且提供了非常强大的方法... 查看详情

jenkispipeline构建菠菜源码搭建出售项目实践(代码片段)

...ver在k8s环境部署之后,本文我们来测试在k8s集群环境中的jenkinspipeline构建项目和更新,具体环境要求如下:1、jenkinspipeline插件安装成功2、要更新的应用已提前部署3、Jenkinsslave中需要有kubectl、svn、mvn客户端且环境变量设置准确4... 查看详情

#201892082018-2019-2《移动平台开发实践》第12周学习总结

教材学习内容总结47章重点48章重点49章重点50章重点教材学习中的问题和解决过程问题1:问题1解决方案:问题2:问题2解决方案:...代码调试中的问题和解决过程问题1:问题1解决方案:问题2:问题2解决方案:...代码托管(stati... 查看详情

安装部署jasigcasserver及相关实践

jasigcasserver是一个开源的单点登录认证服务器。部署很简单,下载后将war包放入tomcat后启动。需要理解并解决的问题有:一、使tomcat支持httpshttps协议通过ssl和数字证书来保证c/s间的数据传输安全。一般说来非对称加密算法性能要... 查看详情

react打包部署多级目录实践

参考技术A使用react-create-app创建项目,想实现打包后部署服务器的3级目录,经过多次调试跑通,并记录一下,方便自己后期使用,也方便其他伙伴有相同需求可以参考。服务端部署后的域名及路由预期:http://www.demo.com/webapp本地... 查看详情

深度学习实战——不同方式的模型部署(cnnyolo)(代码片段)

...2.实验数据3.实验目标4.实验步骤二、ML/DL任务综述与模型部署知识补充1.ML/DL任务综述2.模型部署知识补充二、预训练模型知识补充与本地部署实践1.任务与模型简介1.1任务简介1.2模型简介2.本地部署实践2.1DL模型的框架选择2.2模型... 查看详情

基于jupyterlab的dash应用开发环境部署实践及问题解决(代码片段)

Dash是用于搭建响应式Web应用的Python开源库,Dash目标是建立数据分析性应用的Python框架,不需要直接使用JavaScript,并可以灵活嵌入HTML标签方式定制页面,框架是基于Plotly.js、React和Flask构建,可以直接结合数据... 查看详情

基于jupyterlab的dash应用开发环境部署实践及问题解决(代码片段)

Dash是用于搭建响应式Web应用的Python开源库,Dash目标是建立数据分析性应用的Python框架,不需要直接使用JavaScript,并可以灵活嵌入HTML标签方式定制页面,框架是基于Plotly.js、React和Flask构建,可以直接结合数据... 查看详情

serverless架构落地实践及案例解析

...演进我们先简单回顾下互联网软件架构的演进之路。单机部署在单机部署中,将所有的业务和数据库都部署在一台主机中。此架构的优点是:开发、部署以及运维都非常简单。缺点是:一旦遇到流量过大或者机器故障... 查看详情

elasticstack5.1.2集群日志系统部署及实践

 Elasticstack5.1.2集群日志系统部署及实践一、ELKStack简介ELKStack是Elasticsearch、Logstash、Kibana三个开源软件的组合,在实时数据检索和分析场合,三者通常是配合共用的。可参考:https://www.elastic.co/products二、Elasticstack重要组件Elastic... 查看详情

jenkinspipeline的语法实例介绍及踩坑记录(代码片段)

本文介绍一下Jenkinspipeline的语法实例介绍及踩坑记录废话不多说,直接上案例pipelineagentanystagesstage(‘download‘)stepsecho‘Thisisabuildstep‘gitcredentialsId:‘0c3d0852-8a03-42e2-a893-a445308a257b‘,url:‘http://192.168.0.6/softwa 查看详情

istio实践-路由控制及多应用部署(netcore&springboot)

前言:接上一篇istio应用部署及服务间调用,本文介绍通过构建.netcore与springboot简单服务应用,实现服务间调用及相关路由控制等1、.netcore代码介绍及应用部署新建.netcorewebapi服务项目,添加简单服务调用方法(getStrByServiceName与g... 查看详情

openstack实践系列①openstack简介及基础环境部署

OpenStack实践系列①openstack简介及基础环境部署一、OpenStack初探1.1OpenStack简介 OpenStack是一整套开源软件项目的综合,它允许企业或服务提供者建立、运行自己的云计算和存储设施。Rackspace与NASA是最初重要的两个贡献者,前者提... 查看详情