因收到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:'',,,...,containers: [ ,...)>, ,...)>, .... ]){ 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 yaml """ kind: Pod metadata: name: spec: containers: - name: image: imagePullPolicy: IfNotPresent command: - xxxx tty: true volumeMounts: .... restartPolicy: Never volumes: ...... """ } } stages { stage('one') { steps { container('container_name') { } container('container_name') { } } 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镜像等内容会在下一章以实际案例的方式说明。