Please enable Javascript to view the contents

Jenkins 中 Lightweight 拉取代码问题分析

 ·  ☕ 3 分钟

1. 遇到了什么问题

Jenkins 执行日志报错:

1
2
3
4
5
6
Started by user admin
Lightweight checkout support not available, falling back to full checkout.
Checking out git https://github.com/shaowenchen/pipeline-test.git into /var/jenkins_home/workspace/abc@script to read Jenkinsfile
...
...
Unable to access '.git/index.lock': File exists.

原因分析:

简单介绍一下 Jenkins 的部署情况,Jenkins 使用 Helm Chart 部署在 Kubernetes,使用 Kubernetes 动态 Pod 进行构建。Jenkins 的 /var/jenkins_home 挂载到 PV 进行持久化。

并发的流水线,抢占同一个 workspace 导致执行失败。这是一个好问题,梳理 Pipeline 的执行流程,有利于对 Jenkins 更深入地理解。解决办法: 可以直接进入 workspace 目录,删掉 index.lock ,也可以尝试解决一下 Lightweight failed 问题。

2. Jenkins 中常用的新建类型

首先,我们来看看 Jenkins 中,常见的几种新建类型。主要有三种:

  • Freestyle project

Freestyle project 类型,提供直接在 Jenkins 页面编辑流程的能力。

  • 流水线

流水线类型将,执行部分的编排交给了用户,提供更加灵活的执行方式。

  • 多分支流水线

多分支流水线类型,提供了更加丰富的多分支应用场景。

如上图,是多分支流水线扫描的日志。可以看到多分支扫描会在 /var/jenkins_home/cache 下占用大量硬盘空间,所以不能因为使用了动态 Pod 、外部对象存储归档就减少 Jenkins 的磁盘空间。

3. Jenkinsfile 语法类型

除了使用 Freestyle project 直接编辑,Jenkinsfile 也是常见的一种定义流水线的方式。Jenkinsfile 遵循的是 Groovy DSL ,主要有两种类型的语法:

  • 脚本式
  • 声明式

3.1 Scripted Pipeline - 脚本式流水线

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
node {  
    stage('Build') { 
        // 
    }
    stage('Test') { 
        // 
    }
    stage('Deploy') { 
        // 
    }
}

3.2 Declarative Pipeline - 声明式流水线

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
pipeline { 
    agent any 
    stages {
        stage('Build') { 
            steps { 
                sh 'make' 
            }
        }
        stage('Test'){
            steps {
                sh 'make check'
                junit 'reports/**/*.xml' 
            }
        }
        stage('Deploy') {
            steps {
                sh 'make publish'
            }
        }
    }
}

4. Jenkinsfile 来源类型

在使用 Jenkinsfile 定义流水线时,主要有两种方式:

  • Pipeline script

如上图,直接在页面上编辑流水线流程。

  • Pipeline script from SCM

由于在 DevOps 实践过程中,配置也属于需要管理的部分。通常会将 Jenkinsfile 也添加到仓库中,跟随版本一起维护。如上图,可以直接指定仓库和路径,选择 Jenkinsfile 文件。

这里的 Branches to buildLightweight checkout 需要特别注意,下面会进行讨论。

5. SCM 流水线的执行流程

5.1 从仓库获取 Jenkinsfile 文件

如果 Lightweight checkout 开启,并生效,Jenkins 将直接获取到 Jenkinsfile。具体有两种方式:

  • 通过 API ,可以看到如下日志
1
2
3
4
Branch indexing
05:05:03 Connecting to https://api.github.com using /******
Obtained Jenkinsfile from e216c1ce3bed549462c2bb6762b6dbce32f220e6
Running in Durability level: MAX_SURVIVABILITY
  • 通过 Git ,可以看到如下日志
1
2
3
4
5
Branch indexing
 > git rev-parse --is-inside-work-tree # timeout=10
Setting origin to https://github.com/shaowenchen/pipeline-test.git
 > git config remote.origin.url https://github.com/shaowenchen/pipeline-test.git # timeout=10
Fetching origin...

如果没有开启 Lightweight checkout 或者 Lightweight checkout 失败,则会看到如下日志。

1
2
3
Started by user admin
Lightweight checkout support not available, falling back to full checkout.
Checking out git https://github.com/shaowenchen/pipeline-test.git into /var/jenkins_home/workspace/abc@script to read Jenkinsfile

Lightweight checkout 失败的原因有很多可能:

  • Jenkins 版本旧
  • Git Server 版本旧
  • Branches to build 参数多选或错误

通过测试不同版本的组件,可以很容易检测相关问题。这里主要说下 Branches to build

Branches to build 参数指定了可以从哪些分支获取 Jenkinsfile 。如果设置为 */master ,则指定只有 master 分支的 Jenkinsfile 有效。如果设置为 */* ,则意味着全部分支的 Jenkinsfile 有效。但此时由于 Jenkins 也不清楚应该请求,哪个分支的流水线。

只能 Lightweight 失败,退化为将整个仓库 checkout 到 /var/jenkins_home/workspaces 目录下,以正则匹配的全部分支中,最后一次提交记录所在的分支的 Jenkinsfile 作为当次流水线定义。

最开始提到的问题很明显,多分支流水线场景却使用了流水线类型。如果 Branches to build 配置导致 Lightweight 退化,同时存在大量并发时,SCM 流水线会共用一个 /var/jenkins_home/workspaces/{pipeline_name} 导致并发问题。

而多分支流水线 Lightweight 退化时,会将仓库代码 checkout 到 /var/jenkins_home/workspaces/{pipeline_name_branch_name} 目录下,隔离不同的分支,可以减少并发冲突。

5.2 根据 Jenkinsfile 内容进行构建

在获得 Jenkinsfile 之后,就很简单了,根据定义动态创建 Pod 。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Obtained Jenkinsfile from ce0b611f3c443d423dcc499575eb560630625712
Running in Durability level: MAX_SURVIVABILITY
[Pipeline] Start of Pipeline
[Pipeline] node
Agent base-zrz3f is provisioned from template Kubernetes Pod Template
---
apiVersion: "v1"
kind: "Pod"
metadata:
  annotations: {}
  labels:
    jenkins: "slave"
    jenkins/base: "true"
  name: "base-zrz3f"
spec:
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - preference:
          matchExpressions:
          - key: "node-role.kubernetes.io/worker"
            operator: "In"
            values:
            - "ci"
        weight: 1

在动态 Pod 上,进行流水线的构建。

1
Running on base-zrz3f in /home/jenkins/agent/workspace/hotfix_bugfix_REQ-12956_20200618

微信公众号
作者
微信公众号