Menu

  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay
  • Home
  • Work
    • Cloud
      • Virtualization
      • IaaS
      • PaaS
    • Java
    • Go
    • C
    • C++
    • JavaScript
    • PHP
    • Python
    • Architecture
    • Others
      • Assembly
      • Ruby
      • Perl
      • Lua
      • Rust
      • XML
      • Network
      • IoT
      • GIS
      • Algorithm
      • AI
      • Math
      • RE
      • Graphic
    • OS
      • Linux
      • Windows
      • Mac OS X
    • BigData
    • Database
      • MySQL
      • Oracle
    • Mobile
      • Android
      • IOS
    • Web
      • HTML
      • CSS
  • Life
    • Cooking
    • Travel
    • Gardening
  • Gallery
  • Video
  • Music
  • Essay

Jenkins知识集锦

11
Jul
2016

Jenkins知识集锦

By Alex
/ in Work
0 Comments
简介

Jenkin是一个自包含的CI/CD服务器,能够自动化构建、测试、交付、部署等软件工程生命周期中的任务。

基本概念
术语 说明
管线/Pipeline

Jenkins Pipeline是一套插件,支持实现/集成持续交付流水线(continuous delivery pipelines)到Jenkins服务器

所谓持续交付流水线,是从CVS签出代码,直到将最终软件呈现给用户的自动化处理过程

通过可扩展的工具集,Jenkins将持续交付流水线建模为“代码”,这些代码使用基于Groovy语言的DSL编写,命名为Jenkinsfile,并存放到项目的CVS中

阶段/Stage

表示管线中的一个阶段,例如Build、Test、Deploy

步骤/Step 表示阶段中的步骤,每个步骤下具有多个行为,例如sh执行指定的命令
节点/Node

用于脚本式管线

Node是一台机器,Stage或者Step可以放到Node上执行,如果不指定则在Master节点上执行

代理/Agent

用于声明式管线

表示整个管线,或者某个特定的Stage的执行环境

安装

Jenkins提供了平台原生安装包、Docker镜像,任何安装了JRE的机器都可以运行Jenkins。

Docker
Shell
1
2
docker run --name jenkins -h jenkins --network local --ip 172.21.0.8 --dns 172.21.0.1 \
           --restart=always  -d docker.gmem.cc/jenkins/jenkins:lts

启动容器后,执行下面的命令获取初始密码:

Shell
1
2
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
# e8ed8538336a479f9694f5c32db0a2f3

登陆到Jenkins首页,根据向导来安装。

从源码构建
Shell
1
2
3
git clone https://github.com/jenkinsci/jenkins.git
cd jenkins
mvn clean package -Dmvn.test.skip=true

构建完成后,将jenkins/war/target/jenkins.war部署到任何Servlet容器即可启动。默认情况下Jenksin的数据存储在 ~/.jenkins目录下。

基础
定义管线

有两种方式:

  1. 通过Jenkins WebUI来定义:New Item ⇨ Pipeline,在Script中编写管线脚本
  2. 编写Jenkinsfile,纳入版本控制系统,通常放在项目的根目录

推荐使用第二种方式,WebUI仅仅适合非常简单的任务。

Jenkinsfile

Jenkinsfile就是一段包含了管线定义的Groovy代码,必须以 #!groovy作为开头第一行。

触发器
Poll SCM

定时从版本库拉取代码,发现有Push动作后,则触发构建。

Push触发

很多Git服务支持Webhook。例如Gogs,点击Settings ⇨ Webhooks可以找到Webhook的指南。

DSL
全局变量

管线可以访问一系列的全局变量,到底有哪些全局变量可用,取决于你安装的插件。默认可用的变量包括:

变量 说明
env

环境变量,你可以在脚本式管线、script中读写,例如env.PATH、env.BUILD_ID

注意:此变量的属性值都是字符串,因此:

Java
1
2
3
4
env.EXIST = false
if(env.EXIST){
    // 会执行到这里
}

在字符串中,可以直接使用变量替换来访问环境变量:

Java
1
2
3
4
// 发生Groovy变量替换
sh "/usr/local/app-$APP_VERSION/bin/start"
// 使用env.***也可以
sh "/usr/local/app-${env.APP}_VERSION/bin/start"

在sh指令中你也可以使用这些环境变量:

Java
1
2
3
4
5
env.APP_VERSION = '1.33'
node {
    // 单引号,不发生Groovy变量替换,执行Bash变量替换
    sh '/usr/local/app-$APP_VERSION/bin/start'
}
params 只读的、传递给管线的参数,例如params.MY_PARAM_NAME
currentBuild 当前构建对象的相关信息,例如currentBuild.result、currentBuild.displayName
管线类型

Jenkins支持两种风格的管线:声明式、脚本式。

声明式

所有内容必须定义在 pipeline{ }块内部,只能包含Section、Directive、Step或者赋值语句。

声明式管线相对简单,而且不需要学习groovy语法,对于日常的一般任务完全够用,示例:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
                script {
                    // 可以在script这种Step中定义脚本
                    if( $VALUE1 == $VALUE2 ) {
                       currentBuild.result = 'SUCCESS'
                       return
                    }
                }
            }
        }
    }
}
脚本式

所有内容必须定义在 node{ }块内部,里面包括多个stage。脚本式管线可以通过Groovy语言的强大特性做任何你想做的事情。示例:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
node('master') {
    // 仅Multibranch Pipeline、Pipeline script from SCM支持checkout scm
    checkout scm
    stage('Build') {
        docker.image('maven:3.3.3').inside {
            sh 'mvn --version'
        }
    }
    stage('Deploy') {
        // 可以直接编写Groovy脚本
        if (env.BRANCH_NAME == 'master') {
            echo 'I only execute on the master branch'
        }
    }
}
Section
stages

包含一个或者多个stage指令,核心逻辑所在。建议对每个独立的交付部分(例如Build、Test、Deploy)至少定义一个stage指令。示例:

1
2
3
4
5
6
7
8
9
pipeline {
    agent any
    stages {
        stage( 'Build' ) {
            steps {
            }
        }
    }
}
post

这一段定义管线执行完毕后,需要执行的逻辑。你可以在此指令内部定义以下块:

  1. always:不管返回什么状态都会执行
  2. changed:如果当前管线返回值和上一次已经完成的管线返回值不同时候执行
  3. failure:当前管线返回状态值为”failed”时候执行,在Web UI界面上面是红色的标志
  4. success:当前管线返回状态值为”success”时候执行,在Web UI界面上面是绿色的标志
  5. unstable:当前管线返回状态值为”unstable”时候执行,通常因为测试失败,代码不合法引起的。在Web UI界面上面是黄色的标志

示例:

1
2
3
4
5
post {
    always {
        echo 'Hello'
    }
}
Directive 
environment

该指令用于定义环境变量:

1
2
3
environment {
    CC = 'clang'
}

你可以通过env变量访问所有环境变量: echo "Running ${env.BUILD_ID} on ${env.JENKINS_URL}"

注意:

  1. 即使不使用该指令定义,你也可以读写任意环境变量
  2. 如果使用了该指令,则流水线前一个Stage对环境变量的修改,无法被下一个Stage看到,不论这些Stage是否在同一Agent上执行。因为每个Stage都会使用environment指令所给出的默认值

所有内置环境变量的列表,参考https://ci.gmem.cc/pipeline-syntax/globals#env

agent

用于声明式管线,指定整个管线或者Stage的运行环境,支持取值:

  1. any:任意一个可用的agent
  2. none:如果放在pipeline顶层,那么每一个stage都需要定义自己的agent指令
  3. label:在Jenkins环境中指定标签的agent上面执行
  4. node:类似于label,但是可用定义更多可选项
  5. docker:指定在docker容器中运行,示例:
    Java
    1
    2
    3
    agent {
        docker { image 'docker.gmem.cc/maven:3.5.2' }
    } 
  6. dockerfile:使用源码根目录下面的Dockerfile构建容器来运行
node

用于脚本式管线。不带任何参数表示其内部的Stage可以在任何可用的节点上运行。 node('label')表示必须在具有标签label的节点上运行。

options

管线的选项,例如:

1
2
3
4
5
6
7
8
9
10
11
options {
    # 执行超时
    timeout(time: 1, unit: 'HOURS')
    # 重试次数
    retry 3
    # 在控制台输出前面加上时间戳
    timestamps()
    # 禁用声明式管线的自动签出代码的行为
    # 默认情况下,它会在任何一个Agent上签出Jenkinsfile所在的SCM仓库
    options { skipDefaultCheckout() }
}
parameters

触发管线所需要的参数,可以在step中通过params对象引用,示例:

1
2
3
parameters {
    string(name: 'user', defaultValue: 'Alex', description: 'User name')
}
triggers

触发器,定义何时执行管线:

1
2
3
4
5
6
triggers {
    # 使用Cron表达式指定何时触发
    cron('H 4/* 0 0 1-5')
    # 使用Jenkins的poll scm语法
    pollSCM('H 4/* 0 0 1-5')
}

如果使用Git服务的Webhooks则不需要该指令。

stage

定义在stages块中,stage内部至少需要保护一个steps指令,一个可选的agent。

tools

定义需要自动安装并放到PATH环境变量的工具集合,工具必须预先在Jenkins的Global Tool Configuration中配置。

when

在满足特定条件下才执行Stage:

1
2
3
4
5
6
7
8
9
10
11
12
stage('Example Deploy') {
    # 仅当分支为production时才执行此Stage
    when {
        branch 'production'
    }
    echo 'Deploying'
}
 
# 其它:
when { branch 'master' }
when { environment name: 'DEPLOY_TO', value: 'production' }
when { expression { return params.DEBUG_BUILD } }
parallel

定义一组并行执行的步骤,示例:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
node {
    parallel(
        // 下面4个步骤会并发执行
        "publish 01" : { build job: '01'  , propagate: false  },
        "publish 02" : { build job: '02'  , propagate: false  },
        "publish 03" : { build job: '03'  , propagate: false  },
        "publish 04" : { build job: '04'  , propagate: false  }
    )
    parallel(
        "publish 05" : { build job: '05'  , propagate: false  },
        "publish 06" : { build job: '06'  , propagate: false  },
        "publish 07" : { build job: '07'  , propagate: false  },
        "publish 08" : { build job: '08'  , propagate: false  }
    )
}
Steps 

包含实际的逻辑,声明式/脚本式管线都支持各种Step。

checkout

签出源代码,最简单的用法:

Java
1
2
// 根据当前流水线的配置,签出对应代码
checkout scm

你可以签出任意代码库,也可以从scm变量读取很多属性:

Java
1
2
3
4
5
checkout( [
    $class: 'GitSCM',
    branches: scm.branches,
    userRemoteConfigs: [ [ credentialsId: 'Bitbucket', url: 'git@bitbucket.org:NAVFREG/jenkinsfile-tests.git' ] ],
] )

下面的例子是签出一个标签:

Java
1
2
         // 命名参数 scm, poll
checkout scm: [$class: 'GitSCM', userRemoteConfigs: scm.userRemoteConfigs, branches: [[name: 'refs/tags/3.6.1']]], poll: false
sh

执行一个脚本,可选的,将标准输出作为返回值:

Java
1
2
cur_dir = sh script:'basename $PWD',returnStdout: true
echo "$cur_dir" 

注意:

  1. Jenkins默认使用 -xe参数来执行Shell脚本,这意味着:
    1. -x 打印每个执行的命令
    2. -e 任意一个命令失败,则Shell脚本立即退出
  2. 不论如何,脚本的最后一个命令如果返回值为非0,则整个Step总是被标记为失败
  3. 关于Shell特殊字符:
    Shell
    1
    2
    3
    4
    echo \             # 未结束的命令
    echo \s            # 输出 s
    echo \\s           # 输出 \s
    echo '\s'          # 输出 \s

    此外,步骤sh默认会将参数中的单引号脱去,例如:

    Java
    1
    2
    3
    4
    5
    6
    7
    8
    value = /\,/             // 字面值 \,
    value = /'"$value"'/
    echo value               // 输出 '"\,"'
     
    sh "echo $value"
    // 输出如下,可以看到$value外面的单引号被脱去了
    // + echo "\,"
    // "\,"

    因此,要想将反斜杠传递给Shell脚本,需要在外部包上单引号

要改变默认行为1,可以在脚本开始处添加 set +ex

build

触发指定Job的构建,参数:

参数 说明
job 下游Job的名称,可以指定Job的相对、绝对路径
parameters

传递给Job的参数。对象的数组。示例:

Java
1
[[$class: 'StringParameterValue', name: 'systemname', value: systemname]] 
propagate

如果设置为true,则下游Job没有成功的话当前Job失败

quietPeriod 执行构建之前的安静期,单位秒
wait 是否等待完成
deleteDir

递归的删除当前目录及其内容,无参数。

dir

在指定的目录下执行一系列Step:

Java
1
2
3
dir('target'){
    deleteDir()
}
echo

打印消息到控制台。示例: echo 'message'

error

触发一个错误,类似于throw new Exception(),但不会打印异常栈。示例: error 'message' 

可以使用此Step让一个流水线Fail。

fileExists

验证指定的文件是否存在于工作区,返回布尔值

git

克隆Git仓库到工作区:

Java
1
git branch: 'master', url: 'https://git.gmem.cc/alex/spring-data-ext.git', credentialsId: 'alex.git.gmem.cc' 
isUnix

验证当前运行在的节点,是否是类UNIX系统

mail

用于发送邮件

pwd

返回当前目录的路径字符串

readFile

从工作区读取文件,返回字符串形式的内容。示例: readFile file: path,  encoding: 'UTF-8'

retry

重试最多N次,如果块内出现异常,自动重试,如果最后一次重试失败,则向外抛出异常:

Java
1
2
3
try(3){
    ...
}
sleep

休眠指定的时间,示例:

Java
1
2
// 单位:NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS
sleep time:10, unit: SECONDS 
script

定义一段Groovy脚本并执行。 

stash

将当前Agent的工作区中的某些文件进行保存,供管线后续其它Stage的Agent使用:

Java
1
2
3
4
5
6
// 暂存target目录下的所有jar,保持目录结构不变
stash includes: 'target/*.jar', name: 'target'
// 暂存单个文件
stash includes: 'Dockerfile', name: 'dockerfile'
// 暂存.chart完整的目录结构和文件,允许仅仅暂存空目录
stash includes: '.chart/**/*', name: 'chart', allowEmpty: true
unstash

从之前的暂存区中恢复文件:

Java
1
2
// 目录结构保持不变
unstash 'target'
timeout

指定内部代码的超时时间,如果超时后仍然没有执行完毕,则抛出异常:

Java
1
2
3
4
// activity为true,则对日志中多长时间没有新输出进行计时,而非绝对执行时间
timeout( time: 10, unit: DAYS, activity: false ){
    // long running steps
}

不指定单位,默认分钟。

waitUntil

循环执行内部代码,直到其返回true,重试间隔会不断增加。

Java
1
2
3
4
5
6
7
8
9
10
11
timeout( 5 ) {
    waitUntil {
        if ( statusId == "FINISHED" ) {
            return true
        } else if ( statusId == "STOP_BY_ERROR" || statusId == "CANCELED" || statusId == "UNKNOWN" ) {
            return false
        } else {
            return false
        }
    }
} 
withCredentials

提取指定凭证的信息,以便在管线中引用:

Java
1
2
3
4
5
6
7
8
withCredentials( [ usernamePassword( credentialsId: 'alex.chartmuseum.gmem.cc', usernameVariable: 'USER', passwordVariable: 'PSWD' ) ] ) {
    // 可以作为环境变量使用,但是尝试打印到控制台时,会被遮罩为****
    sh 'echo $PSWD'
    // 可以作为Groovy变量使用
    echo USER
    // 可以使用Groovy的变量替换
    echo "username is $USER"
}
withEnv

为内部代码提供环境变量:

Java
1
2
3
withEnv(['MYTOOL_HOME=/usr/local/mytool']) {
    sh '$MYTOOL_HOME/bin/start'
}
writeFile

将字符串作为文件写入到当前目录下: writeFile file: 'log', text: 'OK', encoding: 'UTF-8'

Pipeline Utility Steps

提供了一系列工具,这些工具只能在脚本中使用。

readJSON

从文件、文本读取JSON,并返回一个对象。返回的对象是字符串为Key的映射、基本类型或映射的列表。

Java
1
2
3
4
5
6
7
8
9
10
11
def props = readJSON file: 'dir/input.json'
assert props['attr1'] == 'One'
assert props.attr1 == 'One'
 
def props = readJSON text: '{ "key": "value" }'
assert props['key'] == 'value'
assert props.key == 'value'
 
def props = readJSON text: '[ "a", "b"]'
assert props[0] == 'a'
assert props[1] == 'b'
readYaml

从文件、文本中读取YAML,并返回一个对象:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pipeline {
    environment {
        VERSION = '1.0.0'
    }
    stages {
        stage('test'){
            steps{
                git branch: 'refactoring', url: 'http://10.255.223.213/code-ddreader/media-job.git', credentialsId: 'wangzhen.10.255.223.213'
                script {
                    def meta = readYaml file: '.chart/Chart.yaml'
                    echo meta.version
                    echo meta.name
                    env.VERSION = meta.version
                    env.NAME = meta.name
                }
                sh 'echo $NAME:$VERSION'
            }
        }
    }
}
Copy Artifact
Java
1
2
3
4
5
6
7
// 拷贝sourceproject最后一次Stable构建的结果
copyArtifacts(projectName: 'sourceproject')
 
// 触发构建
def built = build('downstream')
// 拷贝指定编号的构建的结果
copyArtifacts(projectName: 'downstream', selector: specific("${built.number}"));
参数
参数 说明
projectName 目标项目的名称
selector 构建选择器
parameters k1=v1,k2=v2形式的过滤器,用于过滤构建
filter Ant风格表达式,用于包含、排除需要拷贝的构件
excludes
target 拷贝到的目标目录
flatten 清除拷贝来的构件的目录层次
optional 即使没有找到可用的目标构建,也不会导致当前构建失败
选择器
选择器 说明
lastSuccessful 最后一次成功的构建
specific 特定构建
permalink 使用永久连接指定目标构建
lastCompleted 最后一次完成的构建(不论构建状态)
latestSavedBuild 最后一个保存的构建(标记为永久保存的)
buildParameter 根据构建参数来匹配
upstream 触发此构建的上游构建
HTTP Request Plugin

该插件提供的Step,能够发起任意的HTTP请求:

Java
1
2
3
4
5
6
7
8
9
def resp = httpRequest url: "http://host:port/path",
                       httpMode: "POST",  // GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH
                       authentication: "$credential", // 已经定义的Jenkins凭证的ID
                       acceptType: 'APPLICATION_JSON',
                       contentType: 'APPLICATION_JSON',
                       consoleLogResponseBody: true // 打印响应内容到控制台
 
// 读取响应体
def result = readJSON text: resp.content
开发共享库

当项目规模变大后,你会有很多内容相似的Pipeline,你可以利用共享库来减少代码重复。共享库由名称、可选的默认版本、源码获取方法(例如SCM)来定义。版本可以是SCM识别的任何东西,对于Git来说,branches、tag、commit hash都可以。

原型

你可以从Jenkins提供的原型来创建共享库的代码骨架:

Shell
1
mvn -U -P jenkins archetype:generate -Dfilter="io.jenkins.archetypes:"

选择原型:io.jenkins.archetypes:global-shared-library。 

项目结构
Shell
1
2
3
4
5
6
7
8
9
10
11
12
(root)
+- src                     # Groovy源代码目录,编译结果会被放到Pipeline的classpath下
|   +- org
|       +- foo
|           +- Bar.groovy
+- vars                    # 全局变量定义目录,这些全局变量可以在Pipeline中访问
|   +- foo.groovy          # 全局变量foo
|   +- foo.txt             # 变量foo的文档,可以是HTML、Markdown,但是扩展名必须为txt
+- resources               # 外部库使用的资源文件
|   +- org
|       +- foo
|           +- bar.json
定义共享库
  1. 全局共享库,在Manage Jenkins ⇨ Configure System ⇨ Global Pipeline Libraries定义
  2. 目录共享库,在目录或者子目录上定义
  3. 某些插件可能会自动安装共享库
载入共享库
隐式载入

如果你把共享库标记为Load implicitly,则Pipeline可以直接使用。

静态载入

否则,必须通过注解@Library来启用:

Java
1
2
3
4
5
6
// 使用库的默认版本
@Library('my-library') _
// 指定版本
@Library('my-library@1.0') _
// 使用多个库
@Library(['my-library', 'otherlib@abc1234']) _

如果Pipeline仅仅需要使用全局变量,或者库仅仅包含了var目录,则可以使用 _后缀。否则,你需要使用import语句:

Java
1
2
@Library('somelib')
import com.mycorp.pipeline.somelib.UsefulClass

在编译Pipeline脚本时,共享库被加载,之后Pipeline被执行。全局变量是在运行时动态解析的 。

动态载入

2.7之后的Pipeline提供一个Step,用于动态加载共享库:

Java
1
2
// 仅仅需要使用全局变量/函数
library 'my-library'
使用共享库
动态载入的库
Java
1
2
3
4
5
// 调用方法
library('my-shared-library').com.mycorp.pipeline.Utils.someStaticMethod()
// 引入包
def lib = library('my-shared-library').com.mycorp.pipeline
lib.Helper.new(...)
编写共享库

任何Groovy代码都是允许的,例如数据结构、工具函数、类。

访问Step

共享库不能在全局作用域直接调用sh或者git这样的Step ,你需要声明方法,在其中调用:

src/cc/gmem/ci/Utils.groovy
Java
1
2
3
4
5
6
7
package cc.gmem.ci;
 
def checkOutFrom(repo) {
  git url: "git@github.com:jenkinsci/${repo}"
}
 
return this

对于上述共享库,你可以在脚本式Pipeline中这样使用:

Java
1
2
def u = new cc.gmem.ci.Utils()
u.checkOutFrom(repo)
传递Steps

当前可用的Step集合,可以通过this关键字传递给共享库。例如共享库代码:

Java
1
2
3
4
5
6
7
8
9
10
11
package cc.gmem.ci
class Utils implements Serializable {
  // 实例变量
  def steps
  // 构造函数
  Utils(steps) {this.steps = steps}
  // 方法
  def mvn(args) {
    steps.sh "${steps.tool 'Maven'}/bin/mvn -o ${args}"
  }
}

使用上述共享库的代码:

Java
1
2
3
4
5
6
@Library('utils') import cc.gmem.ci.Utils
def utils = new Utils(this)  // 传递this
node {
  // 调用共享库
  utils.mvn 'clean package'
}
定义全局变量

共享库的var目录中的脚本,会被按需的创建为单例:

vars/log.groovy
Java
1
2
3
4
5
6
7
def info(message) {
    echo "INFO: ${message}"
}
 
def warning(message) {
    echo "WARNING: ${message}"
}

你可以在Pipeline中这样调用:

Java
1
2
3
4
5
6
// utils为库名
@Library('utils') _
 
// log为脚本文件名,info为其中定义的方法
log.info 'Starting'
log.warning 'Nothing to do!'

当使用声明式Pipeline时,你必须在script指令内部来使用共享库全局变量。 

自定义Step

如果要定义一个名为sayHello的Step,你需要:

  1. 创建脚本vars/sayHello.groovy
  2. 在脚本中定义call方法 

这个call方法允许你像调用Step那样调用全局变量:

vars/sayHello.groovy
Java
1
2
3
4
def call(String name = 'human') {
    // 这个方法中可以调用任何合法的Step
    echo "Hello, ${name}."
}

你可以在自己的Pipeline中调用上述Step:

Java
1
2
3
4
5
6
// 传递参数
sayHello 'Alex'
// 使用默认参数
sayHello()
 
// 传递闭包

自定义Step可以接受闭包:

vars/windows.groovy
Java
1
2
3
4
5
6
// 允许传递闭包
def call(Closure body) {
    node('windows') {
        body()
    }
}

调用上述Step:

Java
1
2
3
windows {
    bat "cmd /?"
}

注意:要使用命名参数传参,必须使用下一节的def  call(Map config)方式,否则会报错:is applicable for argument types: (java.util.LinkedHashMap) value

大规模DSL 

如果你有很多大规模的、非常相似的Pipeline,可以将其直接封装到自定义Step中:

vars/buildPlugin.groovy
Java
1
2
3
4
5
6
7
def call(Map config) {
    node {
        git url: "https://github.com/jenkinsci/${config.name}-plugin.git"
        sh 'mvn install'
        mail to: '...', subject: "${config.name} plugin build", body: '...'
    }
}

这样,只需要一行代码的脚本式Pipeline即可,代码大大简化:

Java
1
buildPlugin name: 'git'

甚至,完整的声明式Pipeline也可以封装到自定义Step中:

vars/evenOrOdd.groovy
Java
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
def call(int buildNumber) {
  if (buildNumber % 2 == 0) {
    pipeline {
      agent any
      stages {
        stage('Even Stage') {
          steps {
            echo "The build number is even"
          }
        }
      }
    }
  } else {
    pipeline {
      agent any
      stages {
        stage('Odd Stage') {
          steps {
            echo "The build number is odd"
          }
        }
      }
    }
  }
}
使用第三方库

使用@Grab可以从Maven仓库动态的载入共享库:

Java
1
2
3
4
5
6
7
@Grab('org.apache.commons:commons-math3:3.4.1')
import org.apache.commons.math3.primes.Primes
void parallelize(int count) {
  // 调用第三方库
  if (!Primes.isPrime(count)) {
  }
}
分布式构建

尽管你可以在单个Jenkins服务器中完成所有构建操作,但是在云环境下分布式构建更加常见。

Jenkins从根本上说是Master + Agent的架构:

  1. Master负责任务协调、提供GUI和API端点
  2. Agent负责执行任务,运行Agent的服务器称为Node

Agent可以被打标签,在流水线中可以指定哪些标签可以运行流水线的步骤。

Master-Agent连接

Agent上不需要安装Jenkins服务,Agent和Master需要创建双向通信连接(TCP)以进行交互。

Master主动

最流行的配置Agent的途径是经由Master创建的连接,这要求Master能够访问到Agent(通常是基于SSH)。

Agent主动

出于安全的原因,某些环境下可能无法访问SSH,这种情况下就需要使用JNLP,从Agent连接到Master。

工具自动安装

在云环境下,按Job所需安装工具很重要,因为Agent可能仅仅是一个最小化的操作系统。

默认情况下,任何Agent支持:执行任何Shell命令、下载和解压缩归档文件、下载并安装Oracle官方JDK,下载并安装Ant、Maven。

集成K8S

Jenkins可以作为一个Service或者Pod,在K8S中运行。这样可以避免Master节点的单点故障。

Jenkins提供了Kubernetes插件,利用此插件,可以在K8S中动态创建Pod,作为Slave节点使用。可以获得的好处包括:

  1. 构建环境固化:针对不同的开发语言、项目,分别打包Docker镜像作为构建环境
  2. 节约硬件资源:Slave节点是按需创建,用后即毁的,不会长时间占用资源
安装Jenkins插件

以下插件可能有用:

插件 说明
Kubernetes 动态的在Kubernetes集群中创建Agent
Kubernetes Continuous Deploy

支持将K8S资源部署到Kubernetes集群中:

  1. 从Master节点抓取K8S集群凭证信息,也支持手工配置凭证
  2. 支持对资源配置文件模板进行变量替换
  3. 管理Docker私服的登陆凭证

Slave节点不需要安装kubectl

Jenkins配置

本文的例子中,Jenkins是部署在Kubernetes集群外部的。如果Jenkins作为集群服务运行则配置方式有所不同,需要注意。

要配置用于动态创建Agent的K8S宿主机,定位到Manage Jenkins ⇨ Configure System ⇨ Cloud ⇨ Add a new Cloud ⇨ Kubernetes,参考下图进行设置:

jenkins-k8s

 

需要注意的地方:

  1. Kubernetes URL即API Server的URL
  2. 禁用HTTP证书检查,除非你的API Server证书是经过知名CA签名的
  3. Credentials使用数字证书
  4. Jenkins URL需要使用HTTP方式,除非你的服务器证书经过知名CA签名
  5. 如果在K8S集群内部可以直接访问Jenkins URL,则Jenkins tunnel不需要填写
  6. Container Cap貌似是目标命名空间中整个Pod数量的限制。因为我看到Jenkins日志中提示超过Cap,但是一个Agent都没有
数字证书配置

配置界面如下图示意:

jenkins-k8s-cert

证书可以从Kubectl配置文件中获取,一般位于~/.kube/config,内容如下:

YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: v1
clusters:
  - cluster:
      certificate-authority-data: ***
      server: 'https://10.0.0.100:6443'
    name: kubernetes
contexts:
  - context:
      cluster: kubernetes
      namespace: jx
      user: kubernetes-admin
    name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
  - name: kubernetes-admin
    user:
      client-certificate-data: Base64编码后的证书
      client-key-data: Base64编码后的私钥

 

可以使用OpenSSL来把上面的证书、私钥合并为Jenkins要求的PKCS#12格式:

Shell
1
2
3
4
5
# admin.pfx上传给Jenkins,其密码为passwd
# ca.crt为根证书,可以在APIServer的/etc/kubernetes/pki目录下找到
echo "上面client-key-data的值"           | base64 -d > admin.key
echo "上面client-certificate-data的值"   | base64 -d > admin.crt
openssl pkcs12 -export -out admin.pfx -inkey admin.key -in admin.crt -certfile ca.crt -passout pass:passwd
PodTemplate配置

上文的步骤,仅仅完成了Jenkins到Kubernetes的连接的配置。要进行代码构建,必须配置K8S Pod。点击ADD POD TEMPLATE按钮,参考下图配置:

jenkins-k8s-pod-template

注意点:

  1. Name会成为K8S Pod实际名称的前缀
  2. Namespace指定在哪个名字空间创建Pod
  3. Labels + Usage用于指明,哪些构建任务在此Pod中运行
  4. Containers配置:
    1. 你可以配置1-N个容器,在Jenkins Pipeline中,你可以通过Name引用任意一个容器,并在此容器内执行具体构建Step
    2. 必然有一个名为jnlp的容器,此容器负责连接到Jenkins服务器。如果不显式定义此容器,则Jenkins自动创建,并使用jenkinsci/jnlp-slave作为其镜像
    3. 你可以FROM jnlp-slave镜像,把其它所需的构建工具整合到单个容器中,但是并不推荐这样做。对于基于Java的构建工具,建议FROM openjdk:8-jdk
    4. Command to run仅仅指定一个让容器不会退出的命令即可。如果容器入口点已经有此功能,则不必配置
    5. EnvVars覆盖针对单个容器的环境变量
  5. 下面的EnvVars,定义覆盖所有容器的环境变量
  6. Node Selector可以指定,哪些节点可以运行此Pod

 PodTemplate也可以直接在Pipeline脚本中定义:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
podTemplate( label: 'maven-k8s', cloud: 'k8s', containers: [
        containerTemplate(
                name: 'maven',
                image: 'docker.gmem.cc/dang/maven:3.5.2',
                ttyEnabled: true,
                command: 'cat'
        ),
        containerTemplate(
                name: 'jnlp',
                image: 'docker.gmem.cc/jenkinsci/jnlp-slave',
                alwaysPullImage: false,
                args: '${computer.jnlpmac} ${computer.name}'
        ),
] ) {
 
    node( 'k8s-maven' ) {
 
    }
}
测试Agent

Jenkins任务脚本如下:

Java
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
26
27
28
29
30
pipeline {
    agent none
    options { skipDefaultCheckout() }
    stages {
        stage( 'maven build' ) {
            agent {
                label 'maven-k8s'
            }
            steps {
                checkout scm
                container( 'maven' ) {
                    sh 'mvn clean package'
                }
                stash includes: 'target/*.jar', name: 'target'
                stash includes: 'Dockerfile', name: 'dockerfile'
            }
        }
        stage( 'docker build' ) {
            agent {
                label 'docker'
            }
            steps {
                unstash 'target'
                unstash 'dockerfile'
                sh 'docker build --force-rm -t docker.gmem.cc/ci-maven .'
                sh 'docker push docker.gmem.cc/ci-maven'
            }
        }
    }
}
集成Docker
安装Jenkins插件

以下插件可能有用:

插件 说明
docker-build-step

支持将多种Docker命令作为Step使用:

  1. 提交特定容器中的变更(为新镜像)
  2. 从镜像、Dockerfile创建容器
  3. 执行docker exec
  4. 杀死容器
  5. 移除容器、移除全部容器
  6. 拉取、推送镜像
  7. 启动、重启、停止(全部)容器
  8. 启动、停止所有从指定镜像创建的容器
Docker 利用一个Docker主机,动态创建容器作为Agent(Slave),然后运行一次构建,最后销毁Agent
Jenkins配置

要配置用于动态创建Agent的Docker宿主机,定位到Manage Jenkins ⇨ Configure System ⇨ Cloud ⇨ Add a new Cloud ⇨ Docker,参考下图进行设置:

jenkins-docker-settings

 

如果Docker主机不是Jenkins Master主机,需要TCP方式连接,否则可以使用Unix Domain Socket,具体取决于Dockerd的配置。点击Test Connection可以检查Docker宿主机是否能够连通。

点击Add Docker Template按钮,可以创建一个基于Docker的Agent的模板。示例:

jenkins-docker-settings-3

注意点:

  1. 如果镜像仓库私服要求身份验证,请正确填写Registry Authentication
  2. Labels用于标识此Agent的用途,可以设置Usage,仅仅允许标签匹配的的任务在此Agent上运行
  3. Idle Time设置多少分钟后删除Agent
  4. Remove volumes,删除Agent的容器后,同时删除其卷
  5. 注意Container Settings,要进行适当的配置,确保容器能够访问到Jenkin Server。主要是DNS、网络的设置
准备镜像
maven

Java工程常常基于Maven构建,因此需要定义好包含了Maven运行时的Docker镜像,并且配置好到公司Maven仓库私服的连接。

推荐以jenkins/jnlp-slave 为基础制作镜像。此镜像可以和上面使用的Attach Docker container连接方式配合工作,Dockerfile示例如下:

Shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FROM jenkins/jnlp-slave
 
# Maven配置文件需要进行修改,包括:
# 1、正确的私服信息
# 2、本地仓库位置设置为maven_repository
COPY /mvn /maven
COPY /repo /maven_repository
COPY /jenkins-slave /usr/local/bin/jenkins-slave
COPY logging.properties /usr/share/jenkins/logging.properties
 
ENV PATH=/maven/bin:/usr/local/bin:$PATH
ENV JENKINS_URL=http://ci.gmem.cc:80
ENV JENKINS_AGENT_WORKDIR=/home/jenkins/agent
 
USER root
RUN chown -R jenkins:jenkins /maven_repository && chmod +x /usr/local/bin/jenkins-slave
USER jenkins
 
ENTRYPOINT ["jenkins-slave"]

此镜像项目的完整源码:

Shell
1
git clone https://git.gmem.cc/alex/dockerfile-maven-docker.git
测试Agent

可以使用如下的脚本式管线测试:

Java
1
2
3
4
5
6
// 匹配上面配置的Agent模板
node('maven-docker'){
    stage('test'){
        sh 'mvn --version'
    }
} 
Jenkins X
简介

Jenkins的子项目,基于Kubernetes的CI/CD解决方案。

CLI

Jenkinsx提供命令行接口jx,你可以在任何K8S客户端上运行此CLI,实现:

  1. 安装Jenkins X到K8S集群
  2. 创建新K8S集群并安装Jenkins X到其中
  3. 导入项目、及其CD流水线到Jenkins X
  4. 创建Spring Boot项目并集成到Jenkins X
流水线

你不需要了解Jenkins流水线机制的细节,Jenkins X提供了很多优秀的默认流水线,完整的实现了CI/CD。

环境

这里的环境是指应用程序被部署到的运行环境。环境的命名例如Dev、Testing、Staging/UAT、Production。

使用Jenkins X时,每个团队都拥有自己的一套(Dev/Testing...)环境。你可以随时创建新的环境。

Jenkins X使用GitOps(一款基于Kubernetes的高速CI/CD框架)来管理部署到各环境中的K8S资源的配置/版本信息。每个环境都有自己的Git仓库,其中包含了所有Helm Charts及其配置/版本信息。

每个环境映射为K8S的独立名字空间。当Pull Request被合并到环境的Git仓库时,环境的流水线会运行,Git仓库中最新的Helm Charts会部署到K8S名字空间。

强调一下:

  1. 每个环境都有自己的Git仓库,开发和运维都使用此仓库
  2. 该Git仓库管理环境中部署的所有应用程序、资源的所有配置/版本信息
  3. 所有对环境的变更都被捕获、记录,便于追踪、回滚
升级

所谓升级(Promotion),是指变更被应用到环境的过程。默认情况下Staging环境被设置为自动(Auto)升级,Production环境被设置为手工(Manual)升级。

变更是通过GitOps实现的,首先是向环境的Git仓库提交一个PR,保证所有变更可基于Git进行审核、批准或回滚。

当一个新的变更被Merge到master分支后,环境的流水线被触发,通过Helm部署新版本的应用。

升级的过程如下图:

jx-overview

生态

很多和CI/CD相关的软件,被打包为Helm Chart,并且和Jenkins X进行了预集成。这些工具包括Nexus、Chart Museum、Monocular、Prometheus、Grafana。

其中的一部分软件被打包自动安装,其它则作为加载项: jx create addon grafana

定制的K8S资源

K8S提供了定制资源(Custom Resources)的机制,Jenkins X添加了多种自定义资源。

Environments

表示Jenkins X的环境:

Shell
1
2
3
4
5
6
jx get environments
jx edit environment
 
# 基于K8S客户端访问
kubectl get environments
kubectl edit env staging
Release

Jenkins X 流水线生成了一种Release资源,用来跟踪:

  1. 某个Chart Release(对应一组K8S资源)对应的版本、Git Tag
  2. 执行某次Chart Release使用的Jenkins X 流水线URL、流水线日志
  3. 每个Release关联的Commit、Issue、PR
PipelineActivity

以Pipeline Stage + Promotion Activity的形式存储流水线状态。

组件

典型安装的Jenkins X 包含以下组件:

  1. 每个团队一个的开发环境,对应K8S名字空间
  2. 0-N个永久环境( Permanent Environments):
    1. 每个团队开箱即用的Staging、Production环境
    2. 只要需要,每个团队可以创建任意多的任意名字的环境
  3. 可选的预览环境(Preview Environments)

通常每个环境都关联自己的K8S命名空间,确保隔离性。

开发环境

开发环境中安装了基于K8S进行CI/CD所必需的核心应用,你可以通过加载项扩充此应用集。默认安装的包括:

  1. Jenkins:提供CI/CD自动化
  2. Nexus:作为Java、NodeJS应用的依赖仓库(缓存)
  3. Docker Registry:集群内部私服,流水线推送应用程序镜像到其上
  4. Chartmuseum:Helm Chart仓库
  5. Monocular:发现、运行(Release)Helm Chart的UI工具
永久环境

类似于Staging、Production的环境,使用GitOps进行自我管理。它们对应各自的Git仓库,包含了配置应用程序所需的所有代码(DevOps —— Config as Code),这些代码主要是一些Helm Chart相关的配置文件。

Build Packs

Jenkins X使用Draft风格的Build Packs,并基于BP实现:

  1. 对不同语言、运行时、构建工具的支持
  2. 在导入/创建项目时,添加必要的配置文件。如果导入/创建时以下文件不存在,则自动创建:
    1. Dockerfile 把代码转换为不可变镜像
    2. Jenkinsfile,定义声明式的CI/CD流水线
    3. chart目录,包含Helm Chart定义。用于生成K8S资源
    4. charts/preview目录,包含Preview Chart的定义,包含基于PR部署Preview Enviroment所需的依赖

官方提供的默认BP位于https://github.com/jenkins-x/draft-packs,不同语言/构建工具对应了子目录。jx命令会将此BP克隆到~/.jx/draft/packs/目录,并在你创建/导入项目时pull以更新。

安装到K8S

要安装Jenkins到现有的K8S集群,需要满足条件:启用了RBAC的1.8+版本的K8S,并且有一个非HTTPS的Docker Registory,此外Helm也需要被安装。

镜像拉取

如果集群在网络访问方面有困难,可以提前拉取以下镜像:

bitnami/mongodb:3.4.9-r1
bitnami/monocular-api:v0.6.1
bitnami/monocular-ui:v0.6.1
chartmuseum/chartmuseum:v0.2.8
jenkinsxio/exposecontroller:2.3.58
jenkinsxio/jenkinsx:0.0.15
jenkinsxio/nexus:0.0.14
k8s.gcr.io/addon-resizer:1.7
k8s.gcr.io/heapster:v1.3.0
migmartri/prerender:latest
rawlingsj/pipeline-controller:dev
registry:2.6.2

安装jx

这是Jenkins X的命令行工具,执行下面的命令安装:

Shell
1
2
curl -L https://github.com/jenkins-x/jx/releases/download/v1.2.8/jx-linux-amd64.tar.gz | tar xzv
sudo mv jx /usr/local/bin
安装Jenkins X

首先在~/.jx/extraValues.yaml中添加一些覆盖值。例如默认配置下给的磁盘空间太大,可以减小一点:

YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
expose:
  config:
    exposer: Ingress
    http: "true"
    tlsacme: "false"
  Annotations:
    helm.sh/hook: post-install,post-upgrade
    helm.sh/hook-delete-policy: hook-succeeded
 
docker-registry:
  persistence:
    size: 16Gi
 
jenkins:
  Persistence:
    Size: 32Gi

执行下面的命令即可安装:

Shell
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
26
27
# 确保以下域名的DNS正确配置
10.0.0.102      chartmuseum.jx.k8s.gmem.cc
10.0.0.103      docker-registry.jx.k8s.gmem.cc
10.0.0.104      jenkins.jx.k8s.gmem.cc
10.0.0.105      monocular.jx.k8s.gmem.cc
10.0.0.100      nexus.jx.k8s.gmem.cc
 
# Jenkins X 默认使用GitHub,在企业中工作时往往使用其它种类的Git服务器
# 列出现有的Git服务器
jx get git  
# 添加一个GitLab服务器
jx create git server gitlab https://vcs.gmem.cc --name GitLab
# 也可以使用Addon,支持gitea服务器
jx create addon gitea
 
# 根据需要,创建K8S资源
kubectl -n jx delete secret gmemk8scert-jx
kubectl -n jx create secret generic gmemk8scert-jx --from-file=/home/alex/Documents/puTTY/jx.k8s.gmem.cc
 
# 初始化
jx init
# 根据提示选择匹配的运行环境
jx install
# 或者
jx install --provider=kubernetes
# 减少不必要的步骤
jx install --provider=kubernetes --default-admin-password=admin --domain='k8s.gmem.cc' --http='false' --default-environments=false --skip-ingress=true

注意:

  1. 如果kube-system没有可用的Ingress控制器,则jx可以帮你自动安装
  2. 你需要拥有GitHub账号,该账号用于拉取代码
  3. Jenkins X会注册一个名为jenkins-x的Chart仓库
  4. 如果使用自己的Git服务器,则服务器必须兼容GitHub API。可以考虑GitBucket
完成安装

你将获得以下服务:

服务 说明
Jenkins 2 老牌的CI服务
Helm Kubernetes包管理器,如果K8S集群中已经存在则不会重复安装
Monocular Helm的GUI
Chartmuseum 支持云后端(Amazon S3、Google Cloud Storage、 Microsoft Azure Blob Storage、Alibaba Cloud OSS Storage)存储的Helm Chart仓库
Nexus Maven仓库管理器
Docker Registory Docker镜像私服
卸载
Shell
1
2
3
4
5
6
7
8
9
10
11
12
# 基于Helm的卸载程序
helm del --purge jenkins-x
# 删除文件
rm -rf ~/.jx
 
# 删除Env资源
kubectl -n jx delete env dev
kubectl -n jx delete env staging
kubectl -n jx delete env production
 
# 确保名字空间中没有多余的资源
kubectl -n jx get all
jx命令
子命令 说明

crud git

资源管理类命令:

create 创建
delete 删除
edit      编辑
get       查看

管理Git服务器

示例:

Shell
1
2
3
4
5
6
# 添加一个新的Git服务器URL
jx create git server gitlab https://vcs.gmem.cc --name GitLab
 
jx create git server gitea https://gitea.jx.k8s.gmem.cc --name Gitea
git config --global user.email "gmem@me.com"
git config --global user.name "alex"
cdx 打开CDX仪表盘,此仪表盘用于CI/CD、环境的可视化
console 打开Jenkins X控制台
context 查看或者修改当前使用的Kubernetes Context
environment 查看或修改当前K8S集群中的当前环境
import 导入本地项目或Git仓库到Jenkins X
install

安装Jenkins X到当前K8S集群

选项:

--http='true' 设置为false则创建基于HTTPS的Ingress规则
--default-environments=true 是否创建默认Staging/Production环境
--git-username='alex'
--git-provider-url='http://gitbucket.jx.k8s.gmem.cc/' 在哪创建新Git仓库
--git-api-token='XRvFrHxxrxrsMgq6D_C4' 访问Git仓库所需的凭证
--skip-ingress=true 不要安装Ingress控制器
--verbose=true 详细日志

init 初始化Jenkins X环境,例如创建K8S角色、角色绑定等对象
preview 创建或更新应用程序当前版本的Preview环境
promote 升级某个环境的某个应用到新版本
rsh 在某个Pod中打开Terminal或者执行命令
start 启动一个进程,例如Pipeline
status 显示K8S集群或者命名节点的状态
step 和Pipeline Step有关
uninstall 从集群移除Jenkins X
upgrade

升级某个组件或者整个Jenkins X

示例:

Shell
1
2
3
4
# 升级整个Jenkins X
jx upgrade platform
# 升级命令行工具
jx upgrade cli
常见问题 
ssh方式无法访问git仓库

在Jenkins的流水线配置界面,填写好Repository URL 、Credentials后,报错:/tmp/ssh8260207358463523765.sh: ssh: not found

原因:可能是Master Agent的环境变量PATH设置有问题,没有包含ssh所在目录。

如何删除僵尸任务

如果任务一直再转圈,停止不了,打开Manage Jenkins中的Script Console:

Java
1
2
3
4
5
6
Jenkins.instance.getItemByFullName("任务名称")  // 例如 dev-1/media-hapi
                .getBuildByNumber(任务编号)
                .finish(
                        hudson.model.Result.ABORTED,
                        new java.io.IOException("Aborting build")
                );
Jenkins正在启动,请稍后
解决方案

修改hudson.model.UpdateCenter.xml(Docker环境下此文件默认位于/var/jenkins_home),将其中的URL改为修改后的版本。

或者修改updates/default.json将connectionCheckUrl改为国内可访问的URL。

无法使用HTTPS

启动报错:java.io.IOException: DerValue.getBigInteger, not an int 48

解决办法:

Shell
1
2
# 转换为RSA私钥
sudo openssl rsa -in  /etc/letsencrypt/live/ci.gmem.cc/privkey.pem -out /etc/letsencrypt/live/ci.gmem.cc/priv.key 
无法启动Docker Agent
问题现象

Jenkins UI长时间没有出现Docker Agent对应的节点。

问题原因

很可能是镜像存在问题,无法启动容器。可以查看Jenkins日志发现端倪。示例:

com.github.dockerjava.api.exception.NotFoundException: {"message":"invalid header field value \"oci runtime error: container_linux.go:247: starting container process caused \\\"exec: \\\\\\\"jenkins-slave\\\\\\\": executable file not found in $PATH\\\"\\n\""}

后面会跟着找不到容器的错误。

Apr 19, 2018 8:00:53 AM com.github.dockerjava.core.async.ResultCallbackTemplate onError
SEVERE: Error during callback
com.github.dockerjava.api.exception.NotFoundException: {"message":"No such container: 829878a88c745360b6141b86b7d825590a7de697c869545e63e1cc6b97ca2414"}

解决方案

首先尝试直接运行容器,解决镜像本身的问题。executable file not found in $PATH可能是因为可执行文件不存在,或者没有可执行权限。

如何使用动态参数

考虑使用插件。下面是通过System Groovy Choice Parameter的动态脚本获取Git分支列表的例子:

Java
1
2
3
4
5
6
7
8
9
10
11
def cmd = "/var/jenkins_home/extendsible-choice/scripts/fetch-git-branches.sh git.dangdang.com/prodapi/api-k8s.git"
// Groovy中,字符串的execute方法,可以执行操作系统命令
def proc = cmd.execute()
// 等待命令完成
proc.waitFor()              
// 脚本退出码
if ( proc.exitValue() != 0 ) {
    return [""]
}
//      读取标准输出,注意split返回的是数组,该插件要求返回值是列表
return  proc.in.text.split() as List

/var/jenkins_home/extendsible-choice/scripts/fetch-git-branches.sh
Shell
1
2
#!/bin/sh
git ls-remote --heads --tags http://user:pswd@$1 | awk '{print $2}' | cut -d'/' -f3 | egrep '^[0-9\\.]+$'
jx相关问题
安装gitea

主要问题如下:

  1. gitea-expose权限不足,没有集群级别的权限。授权即可:
    YAML
    1
    2
    3
    4
    5
    6
    7
    subjects:
    - apiGroup: rbac.authorization.k8s.io
      kind: Group
      name: system:masters
    - kind: ServiceAccount
      name: gitea-expose
      namespace: jx
  2. 某些资源要删除,否则后续进行的jx install会出错:
    Shell
    1
    kubectl -n jx delete configmap exposecontroller 
  3. 创建Ingress时出错:no known automatic ways to get an external ip to use with nip,解决办法:
    Shell
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    cat <<EOF | kubectl create -n jx -f -
    apiVersion: "v1"
    data:
      config.yml: |-
        exposer: "Ingress"
        domain: "k8s.gmem.cc"
    kind: "ConfigMap"
    metadata:
      name: "exposecontroller"
    EOF
pvc无法绑定

由于jx install默认创建的PVC没有设置StorageClass,因此需要手工创建PV。或者可以修改资源定义,添加正确的storageClassName字段。 

https无法访问

原因是Ingress没有指定有效的证书,解决办法:

Shell
1
2
# 把密钥tls.key和证书tls.crt放在当前目录
kubectl -n jx create secret generic gmemk8scert-jx --from-file=.

修改各Ingress,例如:

YAML
1
2
3
4
5
6
# kubectl -n jx edit ingress chartmuseum
  tls:
  - hosts:
    - chartmuseum.jx.k8s.gmem.cc
    # 添加下面这行
    secretName: gmemk8scert-jx
← Previous Post
Graphite学习笔记 →

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Related Posts

  • YAML快速参考
  • SVN知识集锦
  • Git学习笔记
  • OpenID Connect
  • 安装和配置GitLab社区版

Recent Posts

  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
  • A Comprehensive Study of Kotlin for Java Developers
  • 背诵营笔记
  • 利用LangChain和语言模型交互
  • 享学营笔记
ABOUT ME

汪震 | Alex Wong

江苏淮安人,现居北京。目前供职于腾讯云,专注容器方向。

GitHub:gmemcc

Git:git.gmem.cc

Email:gmemjunk@gmem.cc@me.com

ABOUT GMEM

绿色记忆是我的个人网站,域名gmem.cc中G是Green的简写,MEM是Memory的简写,CC则是我的小天使彩彩名字的简写。

我在这里记录自己的工作与生活,同时和大家分享一些编程方面的知识。

GMEM HISTORY
v2.00:微风
v1.03:单车旅行
v1.02:夏日版
v1.01:未完成
v0.10:彩虹天堂
v0.01:阳光海岸
MIRROR INFO
Meta
  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org
Recent Posts
  • Investigating and Solving the Issue of Failed Certificate Request with ZeroSSL and Cert-Manager
    In this blog post, I will walk ...
  • A Comprehensive Study of Kotlin for Java Developers
    Introduction Purpose of the Study Understanding the Mo ...
  • 背诵营笔记
    Day 1 Find Your Greatness 原文 Greatness. It’s just ...
  • 利用LangChain和语言模型交互
    LangChain是什么 从名字上可以看出来,LangChain可以用来构建自然语言处理能力的链条。它是一个库 ...
  • 享学营笔记
    Unit 1 At home Lesson 1 In the ...
  • K8S集群跨云迁移
    要将K8S集群从一个云服务商迁移到另外一个,需要解决以下问题: 各种K8S资源的迁移 工作负载所挂载的数 ...
  • Terraform快速参考
    简介 Terraform用于实现基础设施即代码(infrastructure as code)—— 通过代码( ...
  • 草缸2021
    经过四个多月的努力,我的小小荷兰景到达极致了状态。

  • 编写Kubernetes风格的APIServer
    背景 前段时间接到一个需求做一个工具,工具将在K8S中运行。需求很适合用控制器模式实现,很自然的就基于kube ...
  • 记录一次KeyDB缓慢的定位过程
    环境说明 运行环境 这个问题出现在一套搭建在虚拟机上的Kubernetes 1.18集群上。集群有三个节点: ...
  • eBPF学习笔记
    简介 BPF,即Berkeley Packet Filter,是一个古老的网络封包过滤机制。它允许从用户空间注 ...
  • IPVS模式下ClusterIP泄露宿主机端口的问题
    问题 在一个启用了IPVS模式kube-proxy的K8S集群中,运行着一个Docker Registry服务 ...
  • 念爷爷
      今天是爷爷的头七,十二月七日、阴历十月廿三中午,老人家与世长辞。   九月初,回家看望刚动完手术的爸爸,发

  • 6 杨梅坑

  • liuhuashan
    深圳人才公园的网红景点 —— 流花山

  • 1 2020年10月拈花湾

  • 内核缺陷触发的NodePort服务63秒延迟问题
    现象 我们有一个新创建的TKE 1.3.0集群,使用基于Galaxy + Flannel(VXLAN模式)的容 ...
  • Galaxy学习笔记
    简介 Galaxy是TKEStack的一个网络组件,支持为TKE集群提供Overlay/Underlay容器网 ...
TOPLINKS
  • Zitahli's blue 91 people like this
  • 梦中的婚礼 64 people like this
  • 汪静好 61 people like this
  • 那年我一岁 36 people like this
  • 为了爱 28 people like this
  • 小绿彩 26 people like this
  • 彩虹姐姐的笑脸 24 people like this
  • 杨梅坑 6 people like this
  • 亚龙湾之旅 1 people like this
  • 汪昌博 people like this
  • 2013年11月香山 10 people like this
  • 2013年7月秦皇岛 6 people like this
  • 2013年6月蓟县盘山 5 people like this
  • 2013年2月梅花山 2 people like this
  • 2013年淮阴自贡迎春灯会 3 people like this
  • 2012年镇江金山游 1 people like this
  • 2012年徽杭古道 9 people like this
  • 2011年清明节后扬州行 1 people like this
  • 2008年十一云龙公园 5 people like this
  • 2008年之秋忆 7 people like this
  • 老照片 13 people like this
  • 火一样的六月 16 people like this
  • 发黄的相片 3 people like this
  • Cesium学习笔记 90 people like this
  • IntelliJ IDEA知识集锦 59 people like this
  • 基于Kurento搭建WebRTC服务器 38 people like this
  • Bazel学习笔记 37 people like this
  • PhoneGap学习笔记 32 people like this
  • NaCl学习笔记 32 people like this
  • 使用Oracle Java Mission Control监控JVM运行状态 29 people like this
  • Ceph学习笔记 27 people like this
  • 基于Calico的CNI 27 people like this
Tag Cloud
ActiveMQ AspectJ CDT Ceph Chrome CNI Command Cordova Coroutine CXF Cygwin DNS Docker eBPF Eclipse ExtJS F7 FAQ Groovy Hibernate HTTP IntelliJ IO编程 IPVS JacksonJSON JMS JSON JVM K8S kernel LB libvirt Linux知识 Linux编程 LOG Maven MinGW Mock Monitoring Multimedia MVC MySQL netfs Netty Nginx NIO Node.js NoSQL Oracle PDT PHP Redis RPC Scheduler ServiceMesh SNMP Spring SSL svn Tomcat TSDB Ubuntu WebGL WebRTC WebService WebSocket wxWidgets XDebug XML XPath XRM ZooKeeper 亚龙湾 单元测试 学习笔记 实时处理 并发编程 彩姐 性能剖析 性能调优 文本处理 新特性 架构模式 系统编程 网络编程 视频监控 设计模式 远程调试 配置文件 齐塔莉
Recent Comments
  • qg on Istio中的透明代理问题
  • heao on 基于本地gRPC的Go插件系统
  • 黄豆豆 on Ginkgo学习笔记
  • cloud on OpenStack学习笔记
  • 5dragoncon on Cilium学习笔记
  • Archeb on 重温iptables
  • C/C++编程:WebSocketpp(Linux + Clion + boostAsio) – 源码巴士 on 基于C/C++的WebSocket库
  • jerbin on eBPF学习笔记
  • point on Istio中的透明代理问题
  • G on Istio中的透明代理问题
  • 绿色记忆:Go语言单元测试和仿冒 on Ginkgo学习笔记
  • point on Istio中的透明代理问题
  • 【Maven】maven插件开发实战 – IT汇 on Maven插件开发
  • chenlx on eBPF学习笔记
  • Alex on eBPF学习笔记
  • CFC4N on eBPF学习笔记
  • 李运田 on 念爷爷
  • yongman on 记录一次KeyDB缓慢的定位过程
  • Alex on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • will on Istio中的透明代理问题
  • haolipeng on 基于本地gRPC的Go插件系统
  • 吴杰 on 基于C/C++的WebSocket库
©2005-2025 Gmem.cc | Powered by WordPress | 京ICP备18007345号-2