美文网首页AI-大数据CI/CDGo
【DevOps实践】3. Jenkins流水线搭建golang项

【DevOps实践】3. Jenkins流水线搭建golang项

作者: 笔名辉哥 | 来源:发表于2021-03-17 17:33 被阅读0次

    1. 摘要

    Jenkins是一个自动化服务器,目前发展超过15年,比较成熟的CI工具(也可以CD)能够实现自动化集成发布。
    Jenkins构件任务一般有2种,一种是“构建一个自由风格的软件项目”和“流水线”项目。本文讲解的是使用pipeline流水线搭建一个GO工程的持续集成任务的完整方法。

    2. 实践内容

    2.1 pipeline流水线简介

    本质上,jenkins是一个自动化引擎,它支持许多自动模式。流水线向Jenkins添加了一组强大的工具,支持用例、简单的持续集成到全面的持续交付流水线。 通过对一系列的发布任务建立标准的模板,用户可以利用更多流水线的特性,比如:

    代码化: 流水线是在代码中实现的,通常会存放到源代码控制,使团队具有编辑、审查和更新他们项目的交付流水线的能力。

    耐用性:流水线可以从Jenkins的master节点重启后继续运行。

    可暂停的:流水线可以由人功输入或批准继续执行流水线。

    解决复杂发布: 支持复杂的交付流程。例如循环、并行执行。

    可扩展性: 支持扩展DSL和其他插件集成。

    构建一个可扩展是Jenkins的核心价值,流水线可以通过ShareLibrary的方式来扩展。

    2.2 Jenkins环境准备

    2.2.1 配置Jenkins的SSH key

    由于jenkins需要从gitlab上拉取代码,通过ssh方式。所以需要在jenkins机器上安装git,并且将jenkins机器上生成的ssh密钥的公钥(id_rsa.pub中的内容)添加到gitlab的ssh keys中。

    配置username和Email,生成ssh密钥

    git config --global user.name "duncan.wang"
    git config --global user.email "duncan.wang@artarva.com"
    ssh-keygen -t rsa  -C "duncan.wang@artarva.com"
    

    拷贝公钥内容填到gitlab服务器。

    2.2.2 配置GitLab connections

    (1)配置GitLab connections连接到gitlab拉取代码使用,配置证书,使用gitlab api token。
    token从gitlab中获取,在个人设置中有Access Token一栏,创建一个token。

    (2)在jenkins的系统管理/凭据管理(manageCredentials)/全局凭证/添加凭证,如下:
    把gitlab创建的Feed token填写到API token的位置,描述增加点说明,例如"GITLAB的授权信息",ID不用设置,会被Jenkins系统自动更新出来。

    (3)在Jenkins的系统管理处配置gitlab授权配置
    授权凭证选择刚才配置的GitLab API token即可。可以点击“Test Connection”,返回显示为"Success"表示连接成功。

    Test Connection,显示success则表示配置成功。

    (4)配置Jenkins所在服务器拉取代码的服务器私钥访问凭证
    该凭证用于在下面章节的流水线配置拉取代码时以私钥访问凭证形式访问目标环境。此处的私钥“Private Key”为“2.2.1 配置Jenkins的SSH key”产生的rsa_privatekey。

    2.2.3 配置管道任务梳理

    丢弃旧的构建配置为只保留7天,10个构建版本。


    2.3 创建流水线任务

    (1)输入命名“preproduct-training-ip-demo”,选择流水线风格。


    (2)配置Gitlab连接
    选择 2.2.2 配置GitLab connections 配置的“artarva”连接。


    (3)设置流水线脚本

    #!/usr/bin/env groovy
    
    def git_address = "git@101.132.137.193:trainingipmanagement/training-ip-demo.git"
    def git_auth = "10a4d4f3-1977-486c-945f-31cfcd8c04db"
    def git_branch = "dev"
    
    
    pipeline {
        agent { node { label "master"}}
    
        stages {
            stage('1.拉取代码'){
                steps {
                 //checkout([$class: 'GitSCM', branches: [[name: '${Branch}']], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
                     git  branch: "${git_branch}", credentialsId: "${git_auth}", url: "${git_address}"
                         // }
                }
            }
    
    
            stage('3.编译程序'){
                steps {
                    sh """  
                        export GOPATH=/opt/GOPATH
                        export PATH=$PATH:\$GOPATH/bin
                        go build -o training-ip-demo main.go
                cp training-ip-demo /home/training-ip-management/training-ip-demo       
                    """   
                }      
            }  
            
            stage('4.部署程序'){
                steps {
                    script{
                        PROCESS_ID = sh(script: "ps aux|grep training-ip|grep -v grep|grep -v jenkins|awk \'{print \$2}\'", returnStdout: true).trim()
                        echo "${PROCESS_ID}"
    
                        if (PROCESS_ID != "") {
                            sh """
                                 echo "Kill process: ${PROCESS_ID}"
                                 sudo kill -9 ${PROCESS_ID}
                                """
                        }
                    }
    
                    sh """
                    JENKINS_NODE_COOKIE=dontKillMe nohup /home/training-ip-management/training-ip-demo/training-ip-demo & 
                    """
                }  
            }
        }
    
        post {
            always{
                script{
                    println("流水线结束后,经常做的事情")
                }
            }
                
            success{
                script{
                    println("流水线成功后,要做的事情")
                }
                
            }
            failure{
                script{
                    println("流水线失败后,要做的事情")
                }
            }
                
            aborted{
                script{
                    println("流水线取消后,要做的事情")
                }
                
            }
        }
    }
    
    

    (4) 立即构建
    点击“立即构建”,根据输出结果排查问题后即可完成流水线工作,包含
    拉取Gitlab版本,编辑代码,部署程序这3个主要步骤。
    本样例暂时不包含2.代码静态检查的步骤,后面其他文章再涉及。

    截图.png

    2.4 典型问题解答

    2.4.1 流水线配置的程序后台部署运行正常后即被关闭

    问题现象:
    在普通的shell环境中,nohup,并且& 某个程序后,会抛到后台执行,在退出当前shell环境后,程序依然可以执行。但是在Jenkins的pipeline中,通过nohup,且使用&之后,step结束后,执行的程序还是会退出,导致程序起不来。

    在pipeline中需要使用修改 JENKINS_NODE_COOKIE 的值来解决问题,这样后续结束的时候,后面的sh程序就不会被kill掉了。
    例如代码:

            stage('4.部署程序'){
                steps {
                    script{
                        PROCESS_ID = sh(script: "ps aux|grep training-ip|grep -v grep|grep -v jenkins|awk \'{print \$2}\'", returnStdout: true).trim()
                        echo "${PROCESS_ID}"
    
                        if (PROCESS_ID != "") {
                            sh """
                                 echo "Kill process: ${PROCESS_ID}"
                                 sudo kill -9 ${PROCESS_ID}
                                """
                        }
                    }
    
                    sh """
                    JENKINS_NODE_COOKIE=dontKillMe nohup /home/training-ip-management/training-ip-demo/training-ip-demo & 
                    """
                }  
            }
    

    2.4.2 GO编译命令不认识

    问题现象:
    Jenkins有如下输出,表示go 命令找不到。

    [Pipeline] { (3.编译程序)[](https://jenkins.artarva.com/job/preproduct-training-ip-demo/5/console#) [Pipeline] sh[](https://jenkins.artarva.com/job/preproduct-training-ip-demo/5/console#) + export GOPATH=/opt/GOPATH
    + GOPATH=/opt/GOPATH
    + export PATH=/usr/local/bin:/usr/local/java/jdk1.8.0_212//bin:/usr/local/java/jdk1.8.0_212//jre/bin:/usr/local/mycat/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/opt/GOPATH/bin
    + PATH=/usr/local/bin:/usr/local/java/jdk1.8.0_212//bin:/usr/local/java/jdk1.8.0_212//jre/bin:/usr/local/mycat/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/opt/GOPATH/bin
    + go build -o training-ip-demo main.go
    /var/lib/jenkins/workspace/preproduct-training-ip-demo@tmp/durable-86459043/script.sh: line 4: go: command not found</pre>
    

    运行的对应流水线脚本为:

            stage('3.编译程序'){
                steps {
                    sh """  
                        export GOPATH=/opt/GOPATH
                        export PATH=$PATH:\$GOPATH/bin
                        go build -o training-ip-demo main.go
                sudo cp training-ip-demo /home/training-ip-management/training-ip-demo       
                    """   
                }      
            } 
    

    问题分析:
    JENKINS下,默认采用Jenkins账号运行。该账号下的PATH并没有包含GO的环境,上面脚本的路径也写错了。改为如下即可成功运行。
    运行成功的对应流水线脚本为:

            stage('3.编译程序'){
                steps {
                    sh """  
                        export GOROOT=/opt/module/go
                        export PATH=$PATH:\$GOROOT/bin
                        go build -o training-ip-demo main.go
                sudo cp training-ip-demo /home/training-ip-management/training-ip-demo       
                    """   
                }      
            } 
    

    关于Jenkins pipeline单引号、双引号和转义字符的说明参考 https://blog.csdn.net/nklinsirui/article/details/100539935 这篇文章。

    3.参考

    (1)Jenkins持续集成Go项目
    https://www.pianshen.com/article/8603599995/

    (2)Jenkins流水线(pipeline)实战之:golang项目
    https://blog.csdn.net/qq_36270681/article/details/105449608
    【说明】
    [1]安装插件,按照次更新了镜像
    jenkins Publish over SSH 的配置与使用
    https://www.cnblogs.com/yechen2019/p/11529755.html
    [2] 流水线失败,不用。
    [3] GIT相关命令
    https://www.jenkins.io/doc/pipeline/steps/git/
    [4] SHELL相关命令
    https://www.jenkins.io/doc/pipeline/steps/workflow-durable-task-step/#sh-shell-script
    [5] jenkins全局变量
    https://wiki.jenkins.io/display/JENKINS/Building+a+software+project#Buildingasoftwareproject-JenkinsSetEnvironmentVariables
    https://jenkins.artarva.com/env-vars.html/
    [6]官方groovy语法
    http://docs.groovy-lang.org/latest/html/documentation/core-operators.html#_conditional_operators

    (3)使用Docker+Jenkins实现Go语言项目的持续集成
    https://juejin.cn/post/6844904152829542413
    【说明】自由风格构件成功GO项目,不是流水线

    (4)使用Docker+Jenkins实现Go语言项目的持续集成
    https://juejin.cn/post/6844904152829542413
    【说明】自由风格构件成功GO项目,不是流水线

    (5)搭建jenkins+gitlab持续集成环境
    https://jingyan.baidu.com/article/5552ef47fbb9b3518ffbc983.html
    【说明】构建项目部分未使用。

    (6)Jenkins集成Gitlab配置提交流水线
    https://blog.csdn.net/valada/article/details/104272152

    (7)基于GitLab CI搭建Golang自动构建环境
    https://segmentfault.com/a/1190000019525332
    【说明】将直接在GitLab上部署自动化构件环境的。

    (8)Jenkins中使用GitLab的配置
    https://www.cnblogs.com/gongxr/p/9257434.html

    (9)JENKINS插件查询的地方
    https://plugins.jenkins.io/
    https://blog.csdn.net/valada/article/details/104272154

    (10)Jenkins中pipeline后台进程起不来的问题
    https://shanhy.blog.csdn.net/article/details/79637311
    【说明】有效,采用JENKINS_NODE_COOKIE=dontKillMe解决。

    (11)Jenkins pipeline中优雅的执行shell/python/groovy脚本
    https://www.jianshu.com/p/2cdc8efedf2f

    (12)Jenkins pipeline单引号、双引号和转义字符
    https://blog.csdn.net/nklinsirui/article/details/100539935

    (13)Jenkins实践
    http://www.idevops.site/jenkins/

    (14)版本控制系统集成
    http://www.idevops.site/jenkins/pipelineintegrated/chapter03/

    (15)Groovy 语法教程
    https://www.w3cschool.cn/groovy/
    【说明】
    <1> DEF 是在 Groovy 用来定义标识符的关键字。

    相关文章

      网友评论

        本文标题:【DevOps实践】3. Jenkins流水线搭建golang项

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