Jenkins架构

来自Linux78|wiki

Jenkins架构

Jenkins采用的是“master agent”架构(有时也称为“master slave”架构),如图所示。Jenkins master负责提供界面、处理HTTP请求及管理构建环境;构建的执行则由Jenkins agent负责(早期,agent也被称为slave。目前还有一些插件沿用slave的概念)。

Jenkins

基于这样的架构,只需要增加agent就可以轻松支持更多的项目同时执行。这种方式称为Jenkins agent的横向扩容。

对于Jenkins master,存在单节点问题是显而易见的,但是目前还没有很好的解决方案。

在学习Jenkins的过程中,发现各种文档中掺杂着node、executor、agent、slave 4个术语,新手很容易被它们弄得一头雾水。它们分别是什么意思呢?

• node:节点,指包含Jenkins环境及有能力执行项目的机器。master和agent都被认为是节点。

• executor:执行器,是真正执行项目的单元。一个执行器可以被理解为一个单独的进程(事实上是线程)。在一个节点上可以运行多个执行器。

• agent:代理,在概念上指的是相对于Jenkins master的一种角色,实际上是指运行在机器或容器中的一个程序,它会连接上Jenkins master,并执行Jenkins master分配给它的任务。

• slave:“傀儡”,与agent表达的是一个东西,只是叫法不同。

理解node、executor、agent、slave之间的关系,对于做好分布式构建很重要。

总而言之,executor的概念是相对于node的,没有node也就谈不上executor了。node通常指的是机器(不论是物理的还是虚拟的)。agent有时指一个程序,有时指一种角色(相对于master而言),这取决于上下文。对于slave,可以等同于agent。

增加agent

实现分布式构建最常用、最基本的方式就是增加agent。Jenkins agent作为一个负责执行任务的程序,它需要与Jenkins master建立双向连接。连接方式有多种,这也代表有多种增加agent的方式。

在真正介绍如何增加agent前,我们需要了解标签(label)在分布式构建中的作用。

对agent打标签

当agent数量变多时,如何知道哪些agent支持JDK 8、哪些agent支持Node.js环境呢?我们可以通过给agent打标签(有时也称为tag)来确定。

通过标签将多个agent分配到同一个逻辑组中,这个过程被称为打标签。同一个agent可以拥有多个标签。在标签名中不能包含空格,也不能包含!、&、|、<、>、(、)这些特殊字符中的任何一个。因为包含特殊字符的标签名与标签表达式(用于过滤agent)冲突。

对于支持JDK 8的agent,我们打上jdk8标签;对于支持Node.js的agent,我们打上nodejs标签;如果一个agent同时支持JDK 8和Node.js,那么就两个标签都打上。

在打标签时,可以根据以下几个维度来进行。

• 工具链:jdk、nodejs、ruby;也可以加上工具的版本,如jdk6、jdk8。

• 操作系统:linux、windows、osx;或者加上操作系统的版本,如ubuntu18.04、centos7.3。

• 系统位数:32bit、64bit。

可以根据实际项目情况新增维度。

对于不同的增加agent的方式,打标签的方式也不同。我们会在讲解如何增加agent的同时,介绍如何打标签。

通过JNLP协议增加agent

Java网络启动协议(JNLP)是一种允许客户端启动托管在远程Web服务器上的应用程序的协议。Jenkins master与agent通过JNLP协议进行通信。而Java Web Start(JWS)可以被理解为JNLP协议的一个客户端。现实中,人们常常将JNLP和JWS看成是一种东西。

接下来,我们来看看通过JNLP协议增加agent的具体步骤。

(1)进入Manage Jenkins→Global Security→TCP port for JNLP配置页面,如图14-2所示。我们可以选择开放固定端口或者随机开放Jenkins master的一个端口来提供JNLP服务。


选择开放端口

随机开放端口不利于自动化,所以选择开放固定端口。此端口用于master与agent之间的TCP通信,与访问Jenkins界面时的端口有别。

(2)进入Manage Jenkins→Manage Nodes→New Node页面,如图14-3所示。选项“Permanent Agent”指的是常驻代理客户端。

Jenkins添加节点

单击“OK”按钮后,进入node配置页面,如图所示。

jenkins配置节点

• Name:agent名称。

• Remote root directory:agent机器上的工作目录(Jenkins master不关心),使用绝对路径。

• Labels:agent的标签。

• Usage:agent的使用策略。有两种:

◦ Use this node as much as possible,尽可能使用此agent。

◦ Only build jobs with label expressions matching this node,只有当构建任务符合本agent的标签时,才使用此agent。

• Launch method:agent的运行方式。JNLP协议的agent选择“Launch agent via Java Web Start”。配置完成后进入节点列表页面,此时master节点的状态显示是在线的,即可用的,如图所示。

jenkins节点列表

当节点不可用时,如node1节点,Jenkins master不再分配任务给它,如图所示。

jenkins节点不可用

(3)单击节点列表中的node1,跳转到“Agent node1”页面,显示详情如图所示。

JNLP协议agent连接Jenkins master还有3种方式。

一是在agent机器的浏览器中打开此页面,单击“Launch”按钮。

二是通过javaws命令从master节点下载Java Web Start程序。

三是无界面方式连接。

第3种方式不需要界面操作,我们毫不犹豫地选择它,因为只有这样才方便自动化。

jenkins Agent node详情

(4)SSH登录到Jenkins agent机器,下载agent.jar文件(JNLP协议的客户端),下载路径为:<Jenkins master地址>/jenkins/jnlpJars/agent.jar。假设这台机器已经安装好JDK,则执行命令:java-jar agent.jar-jnlpUrl http://192.168.23.11:8667/jenkins/computer/node1/slave-agent.jnlp-workDir "/app"。其中-workDir参数用于指定agent的工作目录。

当命令提示连接成功后,我们打开Jenkins master页面,查看node1的详情页,如图所示,表示已经连接成功。

jenkins agent连接master成功

细心的读者会发现,agent与master之间的连接过程没有任何权限控制。这是因为我们没有设置Jenkins的安全控制(默认Jenkins向匿名用户开放所有权限)。当设置了安全控制后,新建node,我们将在node的详情页看到连接master的命令就变成了:

jenkins-agent

其中-secret******就是agent与master之间的连接凭证。每一个JNLP客户端的凭证都不一样。

提示:升级Jenkins后,也需要重新下载agent.jar。agent.jar需要与Jenkins master同步升级。

最后,我们看到通过JNLP协议增加agent的方式是需要在Jenkins界面上进行手动操作的(增加节点的操作)。这部分是无法自动化的,因此,我们只在以下场景中使用这种方式。

• 在安全性要求相对较高的情况下,只能手动增加agent。

• 增加Windows agent。14.2.3 通过JNLP协议增加Windows agent

其实,增加Jenkins Windows agent与增加Jenkins Linux agent没有什么差别,都需要提前准备好JDK环境,然后通过运行agent.jar与Jenkins master建立连接。

通过Swarm插件增加agent

Swarm插件可以帮助我们更好地增加agent。安装此插件后,增加agent就不需要在Jenkins界面上进行手动操作了。只需要启动Swarm客户端(指定Jenkins master地址),master与agent就会自动建立连接。

具体步骤如下:

(1)安装Swarm插件(https://plugins.jenkins.io/swarm)。

(2)确保Jenkins agent机器上安装有JDK。

(3)在Jenkins agent机器上下载Swarm客户端(https://repo.jenkins-ci.org/releases/org/jenkins-ci/plugins/swarm-client/3.9/swarm-client-3.9.jar)。

(4)在Jenkins agent上启动swarm-client连接服务器端。命令如下:

java -jar swarm-client-3.9.jar --username admin --password admin --master http://192.168.23.11:8667/ jenkins --name swarm-nnode

当日志显示连接成功后,在节点列表页面可以看到Swarm客户端连接成功,如图所示。

jenkins Swarm客户端连接成功

以下是swarm-client部分参数的介绍。

-deleteExistingClients:如果Jenkins master上已经存在同名的node,则先删除。(慎用)
-description:描述。
-disableClientsUniqueId:默认Swarm会在node名称后加上一个唯一ID。加上此参数后,代表取消加上唯一ID。
-disableSslVerification:取消SSL校验。
-executors N:设置executor的个数。
-labels VAL:分配给agent的标签,如果有多个,则使用空格分隔。注意,这是给agent打标签。
-master VAL:指定Jenkins master的URL。
-mode MODE:Jenkins master分配项目给agent时使用的格式,即有两种格式,即normal (尽可能分配job)和exclusive(当与指定label匹配时才分配项目)。
-username VAL:连接时使用的用户名。
-password VAL:连接时使用的密码。不推荐使用。
-passwordEnvVariable VAL:从环境变量中读取密码。推荐使用。
-passwordFile VAL:从文本文件中读取密码。推荐使用。
-retry N:最大重连次数,默认无次数限制。
-retryInterval N:每次重连间隔时长,单位为秒。默认值为10秒。

想要了解更多参数,可以通过java-jar swarm-client-3.9.jar-help命令。

最后,我们发现使用Swarm插件后,手动操作部分就剩下启动Swarm客户端了。

提示:-labels VAL参数即为此agent打标签。如果有多个标签,请加上引号,如-labels"jdk8 windows"。

agent部分详解

打完标签后,如何在pipeline中使用标签呢?其实从一开始就使用了标签,只是我们一直没有详细介绍。agent部分描述的是整个pipeline或在特定阶段执行任务时所在的agent。换句话说,Jenkins master根据此agent部分决定将任务分配到哪个agent上执行。agent部分必须在pipeline块内的顶层定义,而stage块内的定义是可选的。

any

到此为止,pipeline的agent部分都是这样写的:

pipeline {
   agent any
	//...
}

agent any告诉Jenkins master任何可用的agent都可以执行。

agent部分的定义可以放在阶段中,用于指定该stage执行时的agent。

pipeline {
    agent any
	stages {
	    stage ('build'){
		    agent any
			steps {
			echo "build"
			}
		}
	}
}

注意:pipeline块内的agent部分是必需的,不能省略。

通过标签指定agent

当pipeline需要在JDK 8环境下进行构建时,就需要通过标签来指定agent。代码如下:

pipeline {
    agent {
	    label 'jdk8'
	}
	stages {
	   stage('build') {
	       step {
		       echo "build"
		   }
	   }
	}
}

事实上,agent {label'jdk8'}是以下定义方式的简略写法。

agent {
    node {
	label 'jdk8'
	}
}

有些构建任务是需要在JDK 8及Windows环境下执行的。也就是说,我们需要过滤同时具有windows和jdk8标签的agent。可以这样写:

agent {
    label 'windows && jdk8'
}

使用&&代表并且关系。

上文中,在增加agent时,已经配置好了该agent上的默认工作目录路径,但是agent部分允许我们对工作目录进行自定义。node除了label选项,还提供了另一个选项——customWorkspace,自定义工作目录,写法如下:

agent {
    node {
	    label 'jdk8'
		customworkspace '/var/lib/custom'
	}	
}

customWorkspace选项除了写绝对路径,还可以写相对于默认工作目录路径的相对路径。

不分配节点

以上介绍的是如何分配agent,其实还可以指定不分配agent,写法很简单:agent none,指的是不分配任何agent。

没有真正遇到过使用场景,可能就很难想象在什么时候使用agent。如果希望每个stage都运行在指定的agent中,那么pipeline就不需要指定agent了。示例如下:

pipeline {
    agent none
	stages {
	    stage('Example build'){
		    agent { label 'mvn' }
			steps {
			    echo "echo ,build"
			}
		}
		stage(test) {
		    agent { label 'test' }
			steps {
			    echo "hell test"
			}
		}
	}
}

when指令的beforeAgent选项

在默认情况下,阶段内所有的代码都将在指定的Jenkins agent上执行。when指令提供了一个beforeAgent选项,当它的值为true时,只有符合when条件时才会进入该Jenkins agent。这样就可以避免没有必要的工作空间的分配,也就不需要等待可用的Jenkins agent了。

在某些场景下,beforeAgent选项通常用于加速pipeline的执行。示例如下:

pipeline {
    agent onoe
	stages {
	    stage('build') {
		    steps {
			    echo "hello world"
			}
		}
		stage('deploy') {
		    agent {
			    label "some-label"
			}
			when {
			    beforeAgent true
				branch 'production'
			}
			steps {
			    echo 'deploy'
			}
		}
	}
}

只有分支为production时,才会进入“Example Deploy”阶段。这样就可以避免在some-label的agent中拉取代码,从而达到加速pipeline执行的目的。

小结

增加Jenkins agent还是需要做不少工作的。在实际工作中笔者会自动化这个过程。虽然写自动化脚本需要一点时间,但是这点成本是非常值的。自动化增加Jenkins agent的脚本可以带来以下好处:

• 下次对Jenkins agent扩容时,只需要执行一遍自动化脚本就可以了。

• 可以快速重建Jenkins,比如迁移Jenkins、搭建新的Jenkins环境等。]