在上一节中介绍了jenkins使用不同的安装方式配置连接kubernetes集群的方法以及简单的示例,根据kubernetes插件用途的不同,对于插件语法的介绍也会区分开来,本节主要介绍一下使用kubernetes集群动态生成Jenkins slave pod的插件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镜像等内容会在下一章以实际案例的方式说明。
© 2019 - 2023 Liangliang Lee. Powered by gin and hexo-theme-book.