505 lines
19 KiB
Markdown
505 lines
19 KiB
Markdown
|
||
|
||
因收到Google相关通知,网站将会择期关闭。相关通知内容
|
||
|
||
|
||
15.Jenkins Kubernetes Plugin介绍与语法详解
|
||
在上一节中介绍了jenkins使用不同的安装方式配置连接kubernetes集群的方法以及简单的示例,根据kubernetes插件用途的不同,对于插件语法的介绍也会区分开来,本节主要介绍一下使用kubernetes集群动态生成Jenkins slave pod的插件Kubernetes plugin。
|
||
|
||
kubernetes plugin
|
||
|
||
编写pipeline脚本集成kubernetes插件生成动态slave时,对于启动agent代理部分的代码编写根据pipeline语法类型的不同会有所差异。但无论有什么差异,有一个核心方法不变,那就是PodTemplate,顾名思义,就是pod的模板。
|
||
|
||
PodTemplate是使用kubernetes插件生成动态slave节点的核心方法。用于配置启动的agent代理(Pod)的资源对象定义。熟悉kubernetes的应该都知道,kubernetes资源对象pod的定义严格来说主要包含两部分:Pod各项配置定义和容器各项配置定义。而在Jenkins中则通过kubernetes插件的PodTemplate()方法,对这两部分进行定义,并启动代理。
|
||
|
||
下面会根据不同的语法类型分别介绍PodTemplate的配置方法。
|
||
|
||
基本语法
|
||
|
||
脚本式语法
|
||
|
||
首先看一个在脚本式pipeline中使用kubernetes插件的基本语法。
|
||
|
||
podTemplate(label: 'xxx',cloud:'',<pod_option1>,<pod_option2>,...,containers: [
|
||
<containerTemplate(name: 'c1',<container_option1>,<container_option2>...)>,
|
||
<containerTemplate(<name: 'c2',<container_option1>,<container_option2>...)>,
|
||
....
|
||
]){
|
||
node('xxx'){
|
||
stage('test'){
|
||
....
|
||
}
|
||
}
|
||
container('c1') {
|
||
stage('c1'){
|
||
....
|
||
}
|
||
}
|
||
container('c2') {
|
||
stage('c2'){
|
||
....
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
说明:
|
||
|
||
上面PodTemplate包含两部分:
|
||
|
||
第一部分为对kubernetes资源对象Pod自身的一些定义,包括pod的标签,pod的名称,使用的云(cloud)名称,挂载的volume等。
|
||
|
||
第二部分(container)为对该pod下container的定义。container是一个列表,可以包含一个或多个containerTemplate,用于详细描述container的配置参数,比如镜像地址,容器名称,镜像拉取策略等。
|
||
|
||
podTemplate()定义了一个label名称,该lable用于在node()中引用,用于生成一个唯一的pod名称。
|
||
|
||
containerTemplate()定义了该container的名称,用于作为流水线执行的环境,通过container('container_name')引用给容器。
|
||
|
||
声明式语法
|
||
|
||
相对于脚本式语法,声明式的语法就显得相对比较混乱。是因为声明式最基础的配置是将kubernetes中资源对象pod的定义的内容通过yaml方式直接放到流水线脚本中,如下所示(只展示了部分定义)。
|
||
|
||
pipeline {
|
||
agent {
|
||
kubernetes {
|
||
label <label_name>
|
||
yaml """
|
||
kind: Pod
|
||
metadata:
|
||
name: <pod_name>
|
||
spec:
|
||
containers:
|
||
- name: <container_name>
|
||
image: <image_name>
|
||
imagePullPolicy: IfNotPresent
|
||
command:
|
||
- xxxx
|
||
tty: true
|
||
volumeMounts:
|
||
....
|
||
restartPolicy: Never
|
||
volumes:
|
||
......
|
||
"""
|
||
}
|
||
}
|
||
stages {
|
||
stage('one') {
|
||
steps {
|
||
container('container_name') {
|
||
<command1>
|
||
}
|
||
container('container_name') {
|
||
<command2>
|
||
}
|
||
}
|
||
steps {
|
||
sh 'hostname'
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
说明
|
||
|
||
agent{}代理配置使用kubernetes关键字通过yaml指令将yaml文件内容直接放到PodTemplate中。
|
||
|
||
在不同的stage中引用容器的方式也是通过container('container_name')语法格式。
|
||
|
||
以上便是使用脚本式与声明式流水线集成kubernetes插件动态生成jenkins slave节点的基础语法。需要说明的是,对于PodTemplage方法中Pod Template和Container Tempalte的配置,既可以在常规的Jenkins UI中配置,也可以在pipeline script中通过脚本定义配置信息。本节主要介绍在pipeline script中使用脚本的方式定义PodTemplate;在Jenkins UI中配置PodTemplate的方法将在以后介绍,并且如果会使用脚本定义PodTemplate后,在Jenkins UI中配置相对会简单一些。
|
||
|
||
参数说明
|
||
|
||
PodTemplate()既然是方法,那么肯定会涉及到参数配置,所以接下来先了解一下PodTemplate方法的参数配置,为了区分pod配置和container配置,下面会将PodTemplate()总体配置拆分为Pod Template配置和container Template配置分别进行说明。
|
||
|
||
Pod Template 配置部分参数说明-
|
||
cloud :用来指定在jenkins的系统配置中设置的云名称,默认为kubernetes。在上一节中对于该设置已经做个说明,这里不在重复介绍。
|
||
|
||
name :pod名称。
|
||
|
||
namespace :pod运行的namespace(命名空间)。
|
||
|
||
label :pod的标签. 可以自己定义,也可以使用插件内置的 POD_LABEL 变量。
|
||
|
||
yaml :Pod定义的yaml表现形式,可参考kubernetes官网。
|
||
|
||
yamlMergeStrategy :包含merge() 和 override()属性。控制yaml定义是覆盖还是与从声明(或者继承)的pod模板的yaml定义合并。默认为override()。
|
||
|
||
containers :Pod内容器模板的定义。
|
||
|
||
serviceAccount :Pod使用的服务账户。
|
||
|
||
nodeSelector :生成的Pod绑定到的node节点,以key:value的形式表现。
|
||
|
||
nodeUsageMode :包括NORMAL和EXCLUSIVE两个值,它控制Jenkins在选择到这个代理的方式是:只有指定该节点标签时使用这个节点,还是尽可能多地使用该节点。
|
||
|
||
volumes :定义的数据卷,用于pod和所有容器。
|
||
|
||
envVars:应用于所有容器的环境变量。
|
||
|
||
envVar :可以理解为在pipeline内定义的环境变量。
|
||
|
||
secretEnvVar :secret变量,其值是从Kubernetes 的secret获取的。
|
||
|
||
imagePullSecrets :一个存放私有仓库认证信息的secret,用于从私有仓库拉取镜像时对私有仓库认证。
|
||
|
||
annotations:Pod的注解。
|
||
|
||
InheritFrom:继承的一个或多个Pod模板的列表。
|
||
|
||
slaveConnectTimeout :代理pod连接jenkins的超时时间(以秒为单位_)_。
|
||
|
||
podRetention :控制保留Pod的行为,用户设置在agent执行完毕后是否保留该pod。有多个选项,可以是never(),onFailure(),always()或default()。如果为空,则默认为在经过activeDeadlineSeconds指定的时间之后删除pod 。
|
||
|
||
activeDeadlineSeconds :如果 podRetention 参数设置为never() 或者 onFailure(),pod经过该参数设置的时间后会自动删除。
|
||
|
||
idleMinutes :允许Pod保持活动状态以供重用,直到时间达到从开始执行到经过该值设置时间为止,默认为分钟。
|
||
|
||
showRawYaml :启用或禁用原始yaml文件的输出。默认为true。
|
||
|
||
runAsUser :用于设置Pod中所有容器运行的用户ID。
|
||
|
||
runAsGroup :用于设置Pod中所有容器运行的组ID。
|
||
|
||
hostNetwork :使用宿主机网络,类似docker中的network=host。
|
||
|
||
workspaceVolume :用于工作空间的卷的类型。也就是kubernetes中volume的挂载类型,可以是:
|
||
|
||
emptyDirWorkspaceVolume (default)-
|
||
dynamicPVC()-
|
||
hostPathWorkspaceVolume()-
|
||
nfsWorkspaceVolume()-
|
||
persistentVolumeClaimWorkspaceVolume()
|
||
|
||
container Template配置部分参数说明
|
||
|
||
name :容器名称。-
|
||
image :容器使用的镜像。-
|
||
envVars:应用于容器的环境变量(补充和覆盖在pod级别设置的envvar)。-
|
||
envVar :同上。-
|
||
secretEnvVar :同上。-
|
||
command :容器要执行的命令。-
|
||
args :执行命令要传的参数。-
|
||
ttyEnabled :标记容器开启tty。-
|
||
livenessProbe :容器探针。-
|
||
ports :容器端口。-
|
||
alwaysPullImage :拉取镜像策略-
|
||
runAsUser :用于运行容器的用户ID。-
|
||
runAsGroup :用于运行容器的用户组ID。.
|
||
|
||
上面的参数对于脚本式和声明式pipeline都是适用的。对于声明式的语法,也可以通过在前面介绍的片段生成器生成相应的PodTemplate语法片段。
|
||
|
||
有关Pod Template和Container Template的更多参数可以参考kubernetes中资源对象pod的yaml定义,默认情况下,Pod中支持的参数,在pipeline中都是可以使用的。
|
||
|
||
了解了基本语法,下面通过一些基本的示例来加深一下理解。
|
||
|
||
语法示例
|
||
|
||
针对脚本式语法和声明式语法的不同,对于示例也会分开介绍。
|
||
|
||
脚本式语法
|
||
|
||
基础示例如下:
|
||
|
||
podTemplate {
|
||
node(POD_LABEL) {
|
||
stage('Run shell') {
|
||
sh 'echo hello world'
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
说明
|
||
|
||
对于脚本式语法,node()块是被包含在PodTemplate方法里的。
|
||
|
||
该示例会启动一个kubernetes Pod,并输出hello world。
|
||
|
||
node(POD_LABEL) :POD_LABEL用于给pipeline的agent代理(kubernetes pod)指定一个标签,确保pod名称的唯一性,这个标签可以自定义(通过podTemplate 的label参数指定),也可以像上面示例一样,使用POD_LABEL,这个是在kubernetes plugin插件的1.17及以后版本添加的功能,用于自动生成pod 标签,标签的格式为${JOB_NAME)-${BUILD_NUMBER}-hash_number。
|
||
|
||
通过上面的示例会发现,PodTemplate既没有定义Pod参数配置,也没有定义container的参数配置。在没有配置这些参数的情况下,pipeline会使用默认的PodTemplate配置,在执行上面的pipeline后,Jenkins的构建日志中会列出该Pod的yaml定义(可参考上面最初的示例),包括镜像版本,jenkins的环境变量等。由于是默认的配置,对我们编写pipeline 持续交付脚本帮助不大,我们需要自己定义Pod运行参数,所以这里就不在重复介绍默认的pod定义。有兴趣的可以执行上面的示例,结果会通过jenkins的console日志显示出来。
|
||
|
||
对于上面的基础示例,可以添加container参数,用来自定义pod内容器的启动参数,比如:
|
||
|
||
podTemplate(containers: […]) {
|
||
node(POD_LABEL) {
|
||
stage('Run shell') {
|
||
container('mycontainer') {
|
||
sh 'echo hello world'
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
该示例中,container参数定义了启动容器用到的镜像配置模板,在不填写container参数的情况,默认的jnlp代理(container)的配置参数如下所示:
|
||
|
||
containerTemplate(name: 'jnlp', image: 'jenkins/jnlp-slave:3.35-5-alpine', args: '${computer.jnlpmac} ${computer.name}')
|
||
|
||
|
||
需要注意的是:
|
||
|
||
对于${computer.jnlpmac} ${computer.name}这两个参数是jnlp-agent(该示例为jnlp-slave:3.35-5-alpine)镜像运行必须的参数,如果不写或少写一个,都会导致slave pod生成失败。
|
||
|
||
另一个要说明的点是,该contianer的名称,实际工作中我们可能需要使用自己定义的一些镜像,这些镜像如果是jnlp-agent类型(启动时通过slave agent包启动并连接Jenkins master节点)的镜像,则容器名称就必须为jnlp,否则有可能会导致pod生成失败,至于原因将在下一章节进行说明。
|
||
|
||
对于默认的jnlp代理(container)相应的声明式语法如下:
|
||
|
||
apiVersion: v1
|
||
kind: Pod
|
||
spec:
|
||
containers:
|
||
- name: jnlp
|
||
image: 'jenkins/jnlp-slave:3.35-5-alpine'
|
||
args: ['\$(JENKINS_SECRET)', '\$(JENKINS_NAME)']
|
||
|
||
|
||
通过该示例,可以知道上面脚本式语法中的两个参数是做什么用的了吧。如果还不知道,可参考jenkins的docker-jnlp-slave
|
||
|
||
默认的container配置就只包含上面的三个参数,name,image,args,除了这些参数外,还有一些经常用到的,比如用于分配伪终端的ttyEnabled参数、设置拉取镜像策略的alwaysPullImage参数、配置是否使用privileged特权模式的参数、设置容器启动内存和cpu等。
|
||
|
||
更多与容器相关的参数配置可参考如下示例:
|
||
|
||
containerTemplate(
|
||
name: 'mariadb',
|
||
image: 'mariadb:10.1',
|
||
ttyEnabled: true,
|
||
privileged: false,
|
||
alwaysPullImage: false,
|
||
workingDir: '/home/jenkins/agent',
|
||
resourceRequestCpu: '50m',
|
||
resourceLimitCpu: '100m',
|
||
resourceRequestMemory: '100Mi',
|
||
resourceLimitMemory: '200Mi',
|
||
envVars: [
|
||
envVar(key: 'MYSQL_ALLOW_EMPTY_PASSWORD', value: 'true'),
|
||
secretEnvVar(key: 'MYSQL_PASSWORD', secretName: 'mysql-secret', secretKey: 'password'),
|
||
...
|
||
],
|
||
ports: [portMapping(name: 'mysql', containerPort: 3306, hostPort: 3306)]
|
||
),
|
||
|
||
|
||
与pod Template相关的参数配置如下:
|
||
|
||
podTemplate(cloud: 'kubernetes', label: 'my-defined', containers: [....
|
||
],
|
||
volumes: [
|
||
emptyDirVolume(mountPath: '/etc/mount1', memory: false),
|
||
secretVolume(mountPath: '/etc/mount2', secretName: 'my-secret'),
|
||
configMapVolume(mountPath: '/etc/mount3', configMapName: 'my-config'),
|
||
hostPathVolume(mountPath: '/etc/mount4', hostPath: '/mnt/my-mount'),
|
||
nfsVolume(mountPath: '/etc/mount5', serverAddress: '127.0.0.1', serverPath: '/', readOnly: true),
|
||
persistentVolumeClaim(mountPath: '/etc/mount6', claimName: 'myClaim', readOnly: true)
|
||
],
|
||
namesapce: default,
|
||
serviceaccount: default,
|
||
imagePullSecrets: [ 'pull-secret' ], //用于在启动容器时从私有仓库拉取镜像,而无需在宿主机对私有仓库进行认证登录
|
||
annotations: [
|
||
podAnnotation(key: "my-key", value: "my-value")
|
||
...
|
||
])
|
||
|
||
|
||
编写pipeline脚本时对于PodTemplate方法中的某个参数如果不知道如何定义,都可以参考上面展示的模版范例,或者通过在前面的介绍的片段生成器通过jenkins ui定义来生成语法片段。
|
||
|
||
podTemplate方法的配置除了直接使用key:value方式定义外,也可以通过以yaml(kubernetes中资源对象文件的默认后缀)文件的方式定义,比如下面示例:
|
||
|
||
podTemplate(yaml: """
|
||
apiVersion: v1
|
||
kind: Pod
|
||
metadata:
|
||
labels:
|
||
some-label: some-label-value
|
||
spec:
|
||
containers:
|
||
- name: busybox
|
||
image: busybox
|
||
command:
|
||
- cat
|
||
tty: true
|
||
"""
|
||
) {
|
||
node(POD_LABEL) {
|
||
container('busybox') {
|
||
sh "hostname"
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
除了使用yaml关键字外,也可以使用yamlFile关键字,用于指定一个yaml文件,该文件通常与jenkinsfile文件一起存在于源码仓库中。通过从源码仓库中拉取Jenkinsfile文件和该参数指定的文件,下载后会自动执行。这种方式需要改变pipeline job中脚本定义的类型,默认使用Pipeline script,使用yamlFile的方式就该使用Pipeline script from SCM(前面章节有介绍)的方式了。
|
||
|
||
运行多个容器
|
||
|
||
对于不同的代码要是使用不同的构建环境怎么办?在container参数中可以定义多个containerTemplate来满足此需求。也就是在一个pod中运行多个容器,熟悉kubernetes的应该都知道,同一个pod内的容器共享主机名、网络、vloume等信息,所以在pod内运行一个或者多个容器,它们之间并没有什么区别。如下示例:
|
||
|
||
podTemplate(label:'test', containers: [
|
||
containerTemplate(name: 'maven', image: 'maven:3.3.9-jdk-8-alpine', ttyEnabled: true, command: 'cat'),
|
||
containerTemplate(name: 'golang', image: 'golang:1.8.0', ttyEnabled: true, command: 'cat')
|
||
]) {
|
||
|
||
node('test') {
|
||
stage('Get a Maven project') {
|
||
git 'https://github.com/jenkinsci/kubernetes-plugin.git'
|
||
container('maven') {
|
||
stage('Build a Maven project') {
|
||
sh 'echo build maven'
|
||
}
|
||
}
|
||
}
|
||
|
||
stage('Get a Golang project') {
|
||
git url: 'https://github.com/hashicorp/terraform.git'
|
||
container('golang') {
|
||
stage('Build a Go project') {
|
||
sh 'echo build go'
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
说明:
|
||
|
||
该示例定义多个容器,在不同的stage通过定义的容器的名称来使用该容器作为流水线执行的环境。
|
||
|
||
声明式语法
|
||
|
||
在声明式语法中使用PodTemplate方法与在脚本式语法中使用该方法的方式基本一致。主要区别在于agent的定义方式上。
|
||
|
||
首先看一个在声明式脚本中使用PodTemplate的基础示例:
|
||
|
||
pipeline {
|
||
agent {
|
||
kubernetes {
|
||
yaml """
|
||
apiVersion: v1
|
||
kind: Pod
|
||
metadata:
|
||
labels:
|
||
some-label: some-label-value
|
||
spec:
|
||
containers:
|
||
- name: maven
|
||
image: maven:alpine
|
||
command:
|
||
- cat
|
||
tty: true
|
||
- name: busybox
|
||
image: busybox
|
||
command:
|
||
- cat
|
||
tty: true
|
||
"""
|
||
}
|
||
}
|
||
stages {
|
||
stage('Run maven') {
|
||
steps {
|
||
container('maven') {
|
||
sh 'mvn -version'
|
||
}
|
||
container('busybox') {
|
||
sh '/bin/busybox'
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
说明:
|
||
|
||
使用声明式脚本,agent的type必须是”any, docker, dockerfile, kubernetes, label, none”中的一种,本节为kubernetes插件的使用,所以agent的type为kubernetes。
|
||
|
||
kubernetes使用yaml方式定义Pod和container模板配置信息,在stage阶段使用container指令指定运行的容器提供流水线执行的环境。
|
||
|
||
该pod定义未指定pod工作的namespace(命名空间),这里需要说明一下的是,如果yaml定义没有指定namespace,则默认使用在jenkins系统配置中添加kubernetes云时指定的namespace,如果这里也没设定namespace的名称,则默认使用default namespace;如果都指定了,则默认使用在pipeline脚本中定义的namespace。
|
||
|
||
上面示例 stage阶段对容器的引用方式与脚本式流水线引用容器的方式一样。
|
||
|
||
有关yaml更全面的定义,可以参考kubernetes中对资源对象pod的定义,参考kubernetes官网
|
||
|
||
除了可以在脚本式语法中使用yamlFile指令外,在声明式语法中也可以使用yamlFile指令。如下示例:
|
||
|
||
pipeline {
|
||
agent {
|
||
kubernetes {
|
||
yamlFile 'KubernetesPod.yaml'
|
||
}
|
||
}
|
||
stages {
|
||
...
|
||
}
|
||
}
|
||
|
||
|
||
说明
|
||
|
||
示例中指定KubernetesPod.yaml文件与jenkinsfile文件放在同一源码仓库的同级目录下,配置job pipeline时使用Pipeline script from SCM。如果指定的kubernetesPod.yaml文件与Jenkinsfile文件没在同一级目录,指定该文件时需要加上相对路径。
|
||
|
||
使用多个容器
|
||
|
||
默认情况下,声明式脚本的模板不会从父模板继承。所以即便在定义了全局agent代理的情况下,也可以在指定的stage单独定义agent。
|
||
|
||
如下示例,定义了全局agent,也可以在stage定义agent,互不影响。
|
||
|
||
pipeline {
|
||
agent {
|
||
kubernetes {
|
||
label 'parent-pod'
|
||
yaml """
|
||
spec:
|
||
containers:
|
||
- name: golang
|
||
image: golang:1.6.3-alpine
|
||
command:
|
||
- cat
|
||
tty: true
|
||
"""
|
||
}
|
||
}
|
||
stages {
|
||
stage('Run maven') {
|
||
agent {
|
||
kubernetes {
|
||
label 'nested-pod'
|
||
yaml """
|
||
spec:
|
||
containers:
|
||
- name: maven
|
||
image: maven:3.3.9-jdk-8-alpine
|
||
command:
|
||
- cat
|
||
tty: true
|
||
"""
|
||
}
|
||
}
|
||
steps {
|
||
container('maven') {
|
||
sh 'mvn -version'
|
||
}
|
||
}
|
||
}
|
||
stage('run go'){
|
||
steps {
|
||
container('golang') {
|
||
sh 'go --version'
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
以上就是使用kubernetes插件动态生成jenkins slave节点时用到的一些语法示例。在学会了前面jenkins与docker pipeline插件集成的语法及示例后,对于本章节的内容学习应该相对简单很多。
|
||
|
||
有关在jenkins ui中定义PodTemplate以及自定义image镜像等内容会在下一章以实际案例的方式说明。
|
||
|
||
|
||
|
||
|