Administrator
发布于 2019-12-31 / 231 阅读
5

Jenkins 搭建第一个 CI 流水线

交接过来的 Jenkins 里躺着两个 Freestyle 任务

12 月中旬,运维同事离职,他把公司那台 Jenkins 丢给了我。机器上跑的是 Jenkins 2.190.3,装在一台 4 核 8G 的 CentOS 7 虚拟机上,加起来只有两个 Freestyle 任务:一个构建后端 jar,一个构建前端 dist。

Freestyle 的毛病是配置全在页面上,改一次要点七八下鼠标,而且没人知道上次谁改了什么。我接手第一周想给构建加一个"跳过测试"的开关,结果在页面里翻了半小时。于是下定决心换成 Pipeline,把构建过程写成代码放进仓库。

第一个 Jenkinsfile

Pipeline 有两种写法:声明式(Declarative)和脚本式(Scripted)。我选了声明式,语法更死板但不容易写出花来,团队里其他人也看得懂。第一个版本长这样,放在项目根目录的 Jenkinsfile

pipeline {
    agent any

    tools {
        maven 'maven-3.6.3'
        jdk 'jdk8'
    }

    stages {
        stage('拉取代码') {
            steps {
                checkout([$class: 'GitSCM',
                    branches: [[name: '*/develop']],
                    userRemoteConfigs: [[url: 'git@git.corp.com:trade/order-service.git']]])
            }
        }

        stage('编译打包') {
            steps {
                sh 'mvn -B -DskipTests clean package'
            }
        }

        stage('单元测试') {
            steps {
                sh 'mvn -B test'
            }
            post {
                always {
                    junit 'target/surefire-reports/*.xml'
                }
            }
        }

        stage('部署到测试环境') {
            steps {
                sh '/opt/deploy/restart.sh order-service target/order-service.jar'
            }
        }
    }

    post {
        failure {
            dingtalk accessToken: 'xxxx', message: "构建失败:${env.JOB_NAME} #${env.BUILD_NUMBER}"
        }
    }
}

这里有个细节,mvn 命令我都加了 -B(batch mode)。不加的话 Maven 会输出一堆下载进度条,控制台日志里全是 ANSI 控制字符,看着很乱。

踩的第一个坑:找不到 mvn

第一次跑就红了,报错是:

+ mvn -B -DskipTests clean package
/var/jenkins_home/workspace/order-service@tmp/durable-1a2b/script.sh: line 1: mvn: command not found

原因是 tools 块里的 maven 'maven-3.6.3' 引用的是 Jenkins 全局工具配置里的名字,得先在「系统管理 → 全局工具配置」里配好 Maven 安装路径,名字一字不差才能对上。我之前那台机器上配的名字叫 M3,对不上就当没配,于是直接裸调 mvn

排这种问题有个快办法,在 stage 里加一行 sh 'echo $PATH',看看 PATH 到底有没有被改。Jenkins 的 tools 原理就是往当前 stage 的 PATH 前面塞一段。

第二个坑:Maven 本地仓库被重复下载

第二次构建跑了 11 分钟,其中 9 分钟在下载依赖。因为 Jenkins 默认每个任务用工作区下自己的仓库路径。解决方式是指定一个共享仓库,在 /var/jenkins_home/.m2/settings.xml 里:

<localRepository>/var/jenkins_home/.m2/repository</localRepository>
<mirrors>
  <mirror>
    <id>aliyun</id>
    <url>https://maven.aliyun.com/repository/public</url>
    <mirrorOf>central</mirrorOf>
  </mirror>
</mirrors>

换成阿里云镜像加共享仓库之后,第二次构建 2 分 40 秒,第三次 1 分 50 秒。

加上人工确认和参数化

测试环境可以随便发,生产环境不行。我在末尾加了一个带确认的 stage:

stage('部署生产') {
    when {
        branch 'master'
    }
    steps {
        timeout(time: 30, unit: 'MINUTES') {
            input message: '确认发布到生产?', ok: '发布',
                  submitter: 'zixin,tech-lead'
        }
        sh '/opt/deploy/rolling.sh order-service target/order-service.jar'
    }
}

timeout 包在外面是必要的,不然没人点确认的话这个任务会一直挂着,占着 executor 不释放。我有一次挂了一个通宵,第二天早上来了发现其他任务全在排队。

submitter 填的是 Jenkins 用户 ID,只有列出来的人能点这个按钮,防止有人误触。

常见的失败,以及怎么看日志

用了一个多月,整理几个高频失败:

报错实际原因处理
mvn: command not foundtools 里的名字和全局配置对不上核对「全局工具配置」里的 Name
Permission denied (publickey)Jenkins 用户的 ssh key 没配~jenkins/.ssh/id_rsa.pub 加到 GitLab
java.lang.OutOfMemoryError: GC overhead limit exceeded构建时给的堆不够MAVEN_OPTS=-Xmx1024m
WorkflowScript: 3: unexpected tokenGroovy 语法写错,常见于漏括号用 Jenkins 自带的 Pipeline Syntax 片段生成器

前三个看控制台输出就能定位。第四个最烦,因为报错行号经常指向一个莫名其妙的位置。我的办法是把可疑的 stage 单独贴到「Pipeline Syntax → 片段生成器」里验证,它会在浏览器里直接跑一遍语法检查。

另外有个不常提但很有用的东西:env 变量。在 Pipeline 里写 sh 'echo ${BUILD_NUMBER}' 是错的,单引号是 Groovy 的字符串,不会被 Jenkins 替换。要用双引号:

sh "echo 构建号 ${env.BUILD_NUMBER}, 分支 ${env.GIT_BRANCH}"

这个坑我踩过两次。

就写到这。如果哪天你也被《Jenkins 搭建第一个 CI 流水线》里同一个坑绊住,回来翻这篇,能省半小时。

参考