在本教程中,我们将使用Rancher在Kubernetes上部署和伸缩Jenkins。按照本文的步骤一步步来,你将会使用到我们用来测试实际构建作业的master-agent体系结构,创建出功能齐全的Jenkins。
介 绍
Jenkins是一个开源的持续集成和持续交付工具,它可以用来自动构建、测试和部署软件。在全世界有超过一百万的用户在使用Jenkins,它是目前最流行的自动化服务器。Jenkins的优势包括:
- 是一个拥有庞大社区支持的开源软件
- 基于Java的代码库,使其可以移植到所有主要平台
- 有超过1000个插件的丰富生态系统
Jenkins能够与主流的源代码管理系统(Git、SVN、Mercurial以及CVS)、主流的构建工具(Ant、Maven、Grunt)、shell脚本和Windows批处理命令、测试框架、报表生成器的都良好地协同工作。Jenkins的插件还提供了对Docker和Kubernetes的支持,Docker和Kubernetes能够创建、部署基于云的微服务环境,并且把它们投入到测试和生产部署中。
Jenkins支持master-agent体系结构(许多build agents/构建代理根据master服务器调度来完成任务),使其具有高度的可伸缩性。Master的工作是安排构建作业,将作业分发给代理实际执行,监视这些代理并获得构建的结果。除此之外,master服务器还可以直接执行构建作业。
代理的任务是构建从master服务器发送过来的作业。作业可以配置在指定类型的代理商运行,如果没有特别需求,Jenkins就简单地选择下一个可用代理。
Jenkins的可伸缩性可以带来许多便利:
- 并行运行多个构建方案
- 自动地挂载和移除代理,节约开销
- 分配负载
当然,尽管Jenkins包含了这种开箱即用的可伸缩性特性,配置它的过程却并不是很简单。有许多能够扩展Jenkins的选择,而其中一种强大的选择就是使用Kubernetes。
Kubernetes是什么?
Kubernetes是一个开源的容器编排工具。它主要用来帮助操作人员部署、伸缩、更新和维护服务,以及提供服务发现机制来管理节点集群上的容器化应用程序。你可以查看官方文档,了解更多关于Kubernetes的内容和用途:
https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/
Kubernetes是管理可伸缩的、基于容器的工作负载的最佳工具之一。包括Jenkins在内的大多数应用程序都可以进行容器化,而这也使得Kubernetes成为了非常好的选择。
![c557c43cc08b5324d8adc700fd7edb61.png](https://i-blog.csdnimg.cn/blog_migrate/670a32f8cbbfaf7772560511c93dd032.jpeg)
项目目标
在我们开始之前,先花一点时间描述一下我们将要构建的系统。
我们首先会将Jenkins master实例部署到Kubernetes集群上。我们将使用Jenkins的kubernetes插件。我们将使用Jenkins的kubernetes插件,通过提供动态代理来适配当前的工作负载,在集群上扩展Jenkins。该插件基于具体的Docker镜像启动代理,为每个构建创建一个Kubernetes pod。在构建完成后,Jenkins将删除pod来节省资源。代理则使用JNLP(Java Network Launch Protocol,Java网络启动协议)启动,因此容器能够在启动和运行之后自动连接到Jenkins master。
前期准备和安装
你需要准备这些东西来完成本教程:
Linux机器用于运行Rancher:我们还会使用它构建自定义的Jenkins镜像。可以按照Rancher安装入门指南在主机上安装Docker和Rancher:
https://rancher.com/quick-start/
Docker Hub账户:我们需要一个带有容器镜像仓库的账户,为Jenkins master和代理推送自定义镜像。
GCP账户:我们将在GCP上部署Kubernetes集群。谷歌云平台的free-tier应该能够完成这项工作。您实际使用其他公有云,操作也是一样的。
为Jenkins组件构建自定义的镜像
那么我们先从给Jenkins组件构建自定义镜像开始,将它们推送到Docker Hub。
登录到Linux服务器,在那里你就可以运行Rancher并构建镜像。如果还没有安装Docker和Rancher,请按照Rancher快速入门指南在主机上安装Docker和Rancher。主机准备好后,我们就可以准备dockerfile了。
编写Jenkins Master Dockerfile
我们先在当前文件夹下创建一个名为Dockerfile-jenkins-master的文件,来定义Jenkins master镜像:
![e5f315347dcc06a2a5f2fe83c095cd4c.png](https://i-blog.csdnimg.cn/blog_migrate/682739ea072897ee7bc0569589efd705.jpeg)
在文件内部,加入下面Dockerfile构建命令。这些命令使用主Jenkins Docker镜像作为基础,配置我们用于部署到Kubernetes集群的插件:
![eece6199565a50a6dcce3aeb19ef507a.png](https://i-blog.csdnimg.cn/blog_migrate/b339ed47fa639e9ef0c83c568316349d.jpeg)
完成后,保存并关闭文件
编写Jenkins代理的Dockerfiles
接下来,我们就可以为Jenkins代理创建Dockerfile文件了。我们将创建两个代理镜像,演示Jenkins如何正确识别为每个作业准备的正确代理。
在当前目录中创建一个空文件。我们将把它复制到镜像中作为正在构建的每个代理的标识符:
![8b5d7380a9eef6c7fa2dd913cc3a9f34.png](https://i-blog.csdnimg.cn/blog_migrate/21ea296393294bcfbfc816c2975b2151.jpeg)
现在,为第一个代理镜像创建Dockerfile
![e6668641373ce4c625682aa8fc147c81.png](https://i-blog.csdnimg.cn/blog_migrate/c5c90477694da5db3bf1693d64ef360d.jpeg)
该镜像将把空文件复制到一个唯一的名称,标记所使用的代理。
![6fb2534e212164558aefe4a8c0f30459.png](https://i-blog.csdnimg.cn/blog_migrate/6db21201851fb33ab257653eba6ddcf6.jpeg)
完成之后保存并关闭文件
最后,定义第二个代理。这个和前一个代理相同,不过使用了不同的文件标识符:
![5895ebe9b6b4e87e190a695f0a3717c8.png](https://i-blog.csdnimg.cn/blog_migrate/b06ba2120166c7f326d5b50009a8645b.jpeg)
保存并关闭文件
现在你的工作目录看起来应该是这样的:
![d9f0efa57d03144eb6d48a7d708326f1.png](https://i-blog.csdnimg.cn/blog_migrate/bc385dbc5a4189c5f60d5ee8b8587d93.jpeg)
构建镜像并Push到Docker Hub
有了准备好的Dockerfile,我们现在就准备构建和push镜像到Docker Hub啦。
首先构建Jenkins master的镜像:
注意:下面的命令中,将 替换成自己的Docker Hub账户名
[root@rancher-instance jenkins-kubernetes]# docker build -f Dockerfile-jenkins-master -t /jenkins-master .
当命令的结果返回之后,查看新创建的镜像:
![c48081c80d71de704c288f6c39aeac04.png](https://i-blog.csdnimg.cn/blog_migrate/850ec9b939b70452ece9ec8e094ca44b.jpeg)
使用账户凭证登录到Docker Hub:
[root@rancher-instance jenkins-kubernetes]# docker loginLogin with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username:
Password:
Login Succeeded
现在,将镜像推送到Docker Hub:
注意:下面的命令中,同样注意替换成自己的Docker Hub账户
![e3598ff45865f69f234beba33224141b.png](https://i-blog.csdnimg.cn/blog_migrate/b2f77ec3ab1165f8b6dc5f1f85e831b9.jpeg)
你可能还需要同样的命令来构建第二个镜像给Jenkins JNLP代理:
注意:下面的命令中,注意将替换成自己的Docker Hub账户名
![29336a8ebca90cac87d4eca23e80ebda.png](https://i-blog.csdnimg.cn/blog_migrate/2a309ddd59f42cb8898f84c18cad99c1.jpeg)
如果一切顺利,你就能在Docker Hub账户中看到下图这个状态:
![86699e90c7e1d334341e7392f6e1e58a.png](https://i-blog.csdnimg.cn/blog_migrate/2955760cb3605d5f77a68ee380d10915.jpeg)
使用Rancher部署集群
现在我们的镜像已经发布,就可以使用Rancher来帮助部署GKE集群了。如果您之前安装了Rancher,通过web浏览器访问服务器的ip地址就能登录到实例了。
接下来,创建新的GKE集群。这里为了创建具有访问权限的服务账户,你需要登录到谷歌云账户。使用其他的公有云服务所需的步骤也是相似的,具体可以参考文档学习如何创建服务账号,以及如何与Rancher一起部署集群:
https://rancher.com/docs/rancher/v2.x/en/cluster-provisioning/hosted-kubernetes-clusters/
在集群上部署Jenkins
在集群准备好之后,我们就可以部署Jenkins master和创建一些服务了。如果你对kubectl比较熟悉,你可以直接用命令行来实现;不过通过Rancher的UI,你也能很容易地部署所有需要的组件。
无论你选择何种方式将工作负载添加到集群上,都需要在本地计算机上创建下面的文件来定义需要创建的对象。
首先创建一个文件定义Jenkins部署:
![1f03b50c911cf9912f09607f74622cee.png](https://i-blog.csdnimg.cn/blog_migrate/19cffadae1be59ea4dfc83bdafc04b3f.jpeg)
在文件里粘贴下面的内容:
注意:下面的内容中将替换成自己的Docker Hub账户名
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: jenkins
spec:
replicas: 1
template:
metadata:
labels:
app: jenkins
spec:
containers:
- name: jenkins
image: /jenkins-master
env:
- name: JAVA_OPTS
value: -Djenkins.install.runSetupWizard=false
ports:
- name: http-port
containerPort: 8080
- name: jnlp-port
containerPort: 50000
volumeMounts:
- name: jenkins-home
mountPath: /var/jenkins_home
volumes:
- name: jenkins-home
emptyDir: {}
下面,创建一个文件配置我们需要的两个服务。
一个是LoadBalancer服务,它将提供一个公开的IP地址便于我们在Internet上访问Jenkins。另一个是ClusterIP服务,用于在master和代理之间的内部通信,之后会用到该服务:
![9133b1f6ff5ce455ba87dcc00b7c6634.png](https://i-blog.csdnimg.cn/blog_migrate/29dfda21e4574daec30baaf5707efee5.jpeg)
在文件内,复制下面的YAML结构:
apiVersion: v1
kind: Service
metadata:
name: jenkins
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080
selector:
app: jenkins
---
apiVersion: v1
kind: Service
metadata:
name: jenkins-jnlp
spec:
type: ClusterIP
ports:
- port: 50000
targetPort: 50000
selector:
app: jenkins
在Rancher上,点击自己管理的集群(例子中名为jenkins)。在左上角的菜单中,选择Default项目,然后选择Workloads选项卡。
![e2eb278879cf7b8983ccdc1028a19c88.png](https://i-blog.csdnimg.cn/blog_migrate/0240e4b47b561bfdf73e9c295231d7b7.jpeg)
现在,点击Import YAML。在接下来的页面中,点击右上角的Read from a file按钮。选择在本地创建的deployment.yml文件并点击Import。
![b4a2343dda963fcbc118ddb14968d5c4.png](https://i-blog.csdnimg.cn/blog_migrate/9a1aca111551950874d8e53ea9ed6cab.jpeg)
Rancher将在集群上面部署一个基于你Jenkins master镜像的pod
![166b971da4eb06bfaadcd8b564f91383.png](https://i-blog.csdnimg.cn/blog_migrate/83307ee28d699d7c332ede80f52caf3d.jpeg)
接下来,我们需要在Jenkins master上配置访问UI的方式。
在Load Balanced选项卡中,按照先前导入文件一样的操作。单击Import YAML按钮,接着点Read from a file按钮。然后从自己的电脑中选择service.yml文件,点击Import按钮。
![b19e2c590ea53bae9d50985560a83190.png](https://i-blog.csdnimg.cn/blog_migrate/6075af5d73a6e9ccf1006c839f7a2d79.jpeg)
Rancher会开始创建你的服务。部署负载均衡会花费一些时间。
![555153c95460c24f0bd07bf2c7b38824.png](https://i-blog.csdnimg.cn/blog_migrate/6a7345cf4e2cb296ac8adf0655ffe564.jpeg)
在service状态成为Active后,你可以点击在负载均衡器行后侧的三个垂直点,选择View/Edit YAML来找到它的公共IP地址。在这里,向下滚动界面找到在status->loadBalancer->ingress->ip下的IP地址。
![20f28b43f650765a17e60638c59def15.png](https://i-blog.csdnimg.cn/blog_migrate/89da13585fc16c16c0aee75a8eae93c1.jpeg)
这样我们就可以通过该IP地址在web浏览器中访问Jenkins UI了。
![27689c9a512ff6969d75b6618edf8a9e.png](https://i-blog.csdnimg.cn/blog_migrate/7f0430a6323a8a27c52407d60ba1affc.jpeg)
配置动态的构建代理
有了Jenkins master启动运行后,我们可以进一步配置动态构建代理,以在必要的时候可以自动启动Pods。
禁用默认Master构建代理
Jenkins UI中左侧的Build Executor Status下,默认配置了两个Executor,等待执行构建作业,它们是由Jenkins master提供的。
主实例应该只负责调度构建作业、将作业分发给代理以供执行、监视代理并获取构建结果。因为我们不希望主实例执行构建,因此要禁用这些。
点击Manage Nodes之后的Manage Jenkins。
![36620d21b25370e71b383e513a3d5fe0.png](https://i-blog.csdnimg.cn/blog_migrate/28b7f57a95b15f31085374c90fd2c417.jpeg)
单击与master行上的齿轮图标。
![c123b6ec7e8af4472110ba3a99e866ef.png](https://i-blog.csdnimg.cn/blog_migrate/79c949161dad82279788d2b048a72c22.jpeg)
接下来的页面中,把# of executors设置成0,点击Save。
![e1ea04eca1f94f9cfa51ff7ef21d3ec0.png](https://i-blog.csdnimg.cn/blog_migrate/c15896880c2df8914e0e491b70118a19.jpeg)
这两个空闲的executors将从UI左侧的Build Executor Status中删除。
收集配置信息
为了在Kubernetes集群上自动部署构建代理,我们需要一些信息来配置Jenkins。我们需要三条来自GCP账户的信息以及一条来自ClusterIP服务中的信息。
在你的GCP账户中,选择Kubernetes Engine,接着是Clusters,然后点击集群的名称。在Detail列中,复制端点IP供之后使用。这是我们需要让Jenkins连接到集群的URL:
![761f9b7a63c730e2c7d15a1f728bafaa.png](https://i-blog.csdnimg.cn/blog_migrate/16022549eab92550df2cab13db6d8791.jpeg)
下一步,点击端点右侧的Show credentials。复制Username和Password。
![3047d1a2cbc7af97a0f739743ee084d1.png](https://i-blog.csdnimg.cn/blog_migrate/90a0dfbffd8d85dca26bf8d2703223b8.jpeg)
现在,切换到Rancher UI。在左上角菜单中,选择Jenkins集群上的Default项目。在上方的导航窗口中选择Workloads选项卡,然后单击页面上的Service Discovery选项卡:
![e8a62ec9ec1676619553db0335e7b5f8.png](https://i-blog.csdnimg.cn/blog_migrate/733b134807f7c27c4a48fd539fd9b3bd.jpeg)
点击jenkins-jnlp行上垂直的三个点,然后单击View/Edit YAML。复制spec > clusterIP以及spec > ports > port中的值备用。
配置Jenkins Kubernetes插件
返回主Jenkins仪表盘,点击Manage Jenkins,然后选择Manage Plugins:
![8d3a22f327d591949b3cb5d1ae209d63.png](https://i-blog.csdnimg.cn/blog_migrate/3dd524d1b0fe0eb0f9686d9a722051ef.jpeg)
点击Installed选项卡并查看Kubernetes插件是否安装:
![90726694142aa9d277fa413c7d23d65a.png](https://i-blog.csdnimg.cn/blog_migrate/4cce0ac24e00b940ef90d73884488676.jpeg)
现在我们来配置插件。前往Manage Jenkins并选择Configure System:
![833286356fa39c8e8dacf269d5a97e8a.png](https://i-blog.csdnimg.cn/blog_migrate/d8c69680fa46593329edf655c40dd4b4.jpeg)
滑到页面底部的Cloud部分。点击Add a new cloud,选择Kubernetes。
![515cd2750bf2c8f3fbcd9c1e31f5a1cc.png](https://i-blog.csdnimg.cn/blog_migrate/76cd3fb0e6cf8d2fb6d23b99f56a5805.jpeg)
在下面的表单中,Kubernetes URL字段上输入https://,然后输入从GCP账户复制的集群端点IP地址。
在Credentials下,点击Add按钮,选择Jenkins。在出现的表单上,输入从GCP账户复制的用户名和密码,单击底部的Add按钮。
返回到Kubernetes表单,从Credentials下拉菜单中选择刚才添加的凭据并单击Test Connection按钮。如果配置正确,则会显示“Connection test successful”。
现在,向下滚动到页面底部的Images部分,单击Add Pod Template按钮,然后选择Kubernetes Pod Template。 使用唯一值填写“Name”和“Labels”字段,以标识您的第一个代理。 我们将使用标签指定应该使用哪个代理镜像来运行每个构建。
接下来,在Jenkins tunnel字段中,输入你在Rancher UI中从jenkins-jnlp服务检索到的IP地址和端口,用冒号分隔:
![6d51045c3f9b88631e339f7ea2a4067b.png](https://i-blog.csdnimg.cn/blog_migrate/fe4ed1bc9889c760d52570f5561000d0.jpeg)
填写以下字段:
- Name: jnlp(这是Jenkins代理需要的)
- Docker image:/Jenkins-slave-jnlp1(确保更改Docker Hub用户名)
- Command to run:删除这里的值
- Arguments to pass to the command:删除这里的值
其余字段保持原样。
![5a758469715ad8ba20dad49651231b74.png](https://i-blog.csdnimg.cn/blog_migrate/6257223d60a52b398b4dcba377004964.jpeg)
接下来,单击Add Pod Template按钮,再次选择Kubernetes Pod Template。对创建的第二个代理镜像重复刚才的过程,需要注意的是,在需要的时候要修改那些对应于第二个镜像的值:
![06bd2c1e04d33bcca296d80fc55566e4.png](https://i-blog.csdnimg.cn/blog_migrate/501de9b1bc41ec924287b9254ce880ac.jpeg)
单击Save按钮保存修改并继续。
测试动态构建作业
现在我们已经完成了配置工作,我们可以创建一些构建作业,保证Jenkins能够在Kubernetes之上进行伸缩。这里我们将为每个Jenkins代理创建5个构建作业。
在Jenkins主页面,单击左侧的New Item,为第一个代理的第一个构建输入名称,选择Freestyle project并单击OK按钮。
![2cc892760f43a46c5a515c97c41388e5.png](https://i-blog.csdnimg.cn/blog_migrate/094df6642ee41cde32058b4b3bc77afc.jpeg)
在下一页的Label Expression字段中,输入你为第一个Jenkins代理镜像设置的标签,如果单击字段之外,会出现一条消息,提示标签由云提供服务。
![9c70759866237cdf0795c575768ef57e.png](https://i-blog.csdnimg.cn/blog_migrate/1e911a752b18e37eb02b0b6aa1eab0fe.jpeg)
向下滚动到Build Environment部分,检查Color ANSI Console Output。
在Build部分,单击Add build step并选择Execute shell。把下面的脚本粘贴进去。
![790360d2c43db987b2d71e53ac862b5f.png](https://i-blog.csdnimg.cn/blog_migrate/8fb2803e0ebd8123e4727ebe2a3bf7c6.jpeg)
完成之后单击Save。
![ee04c959d6fe1533e55edbf1fec913e0.png](https://i-blog.csdnimg.cn/blog_migrate/b5fc584df9ae6242193cfae63ab15265.jpeg)
给第一个代理创建另外四个工作则是单击New Item,填写新名称并使用Copy from字段来从第一个构建中复制。你可以在无需对第一个构建作更改的情况下保存每个构建。
接下来,为第二个Jenkins代理配置第一个作业。单击New Item,给第二个代理的第一个作业选个名字,再一次从第一个代理中复制作业。这一次我们将在保存之前修改配置页面上的字段。
首先,修改Label Expression字段匹配第二个代理的标签。
接着,用下面的脚本替换掉Build部分文本框中的脚本:
![b4554094f37cd17a29bbc9f07f1c3da7.png](https://i-blog.csdnimg.cn/blog_migrate/d20cfed7b3679dd08f1ce46c8fe894c0.jpeg)
完成后单击Save。
![ebb4ba2bf4738e19df6f768532a02576.png](https://i-blog.csdnimg.cn/blog_migrate/6c8a98d3cd591c5569816a56b452b315.jpeg)
同样按照刚刚我们的流程,为第二个代理创建另外四个构建。
现在,转回到主页面,单击每行最右边的图标,启动全部刚刚创建的10个作业。在启动之后,它们会按照Build Queue部分的指示排队等待执行:
![d8df3ff9ab8a8ca978a03a9eb700760c.png](https://i-blog.csdnimg.cn/blog_migrate/12cb2503afe72bd2f17c7e361d996be9.jpeg)
大约几秒钟之后,会开始创建Pods来执行构建(你可以在Rancher的Workload选项卡中检验这一点)。Jenkins会为每个作业创建一个pod。在每个代理启动时,它连接到master并从队列中接收要执行的作业。
![756ea6c9e99a22584c3a2bdb88ddff51.png](https://i-blog.csdnimg.cn/blog_migrate/0c9e31f295c6ade74dc4914e7f25fd8e.jpeg)
![c3da9a05ff95f81a321a5840d1733c13.png](https://i-blog.csdnimg.cn/blog_migrate/e6000a2ba188ee97a30995f0f617108d.jpeg)
代理完成了自己的工作后,它就会自动从集群中删除:
![82b90ddff174d954df000e8c02ded4ce.png](https://i-blog.csdnimg.cn/blog_migrate/761a06dc1c115a8640960f4ab9b2a047.jpeg)
要检查作业的状态,可以单击每个代理中的一项作业。从Build History中单击构建,然后点击Console Output。由第一个代理执行的作业应该指定使用了jenkins-slave1 Docker镜像,而由第二个代理执行的构建应该指定使用了jenkins-slave2镜像:
![d89e7b7ffa5c44b4391fced8d7bc8e2e.png](https://i-blog.csdnimg.cn/blog_migrate/ff421eb5d0b5e42b964147d353acdd8a.jpeg)
![961ef2be86e719d6101117b0c2d7dffb.png](https://i-blog.csdnimg.cn/blog_migrate/adf7b719edf46ebad5f8f7386c273729.jpeg)
如果你得到了上面的输出,那么Jenkins的配置就是正确的,而且是按照预期的方式在运行的。现在你就可以开始定制自己的Kubernetes的构建系统,帮助自己的团队测试和发布软件啦。
结 论
在本文中,我们配置了Jenkins来按需自动部署构建代理,将其连接到了Rancher管理的Kubernetes集群。为此,我们完成了下面的步骤:
- 使用Rancher创建了一个集群
- 为Jenkins master和代理创建了自定义Docker镜像
- 将Jenkins master和L4 LoadBalancer服务部署在Kubernetes集群上
- 在集群上配置了Jenkins kubernetes插件,自动生成动态代理
- 使用带有专用代理镜像的多个构建作业测试场景
本文着重展现了设置Jenkins master和代理体系结构的基本的必要配置。我们了解了Jenkins如何使用JNLP启动代理,以及容器如何自动连接到Jenkins master来接受指令。为了实现这一点,我们使用Rancher创建集群、部署工作负载并监控产生的Pods。在这之后,我们又依靠Jenkins Kubernetes插件将所有不同的组件连接在了一起。
![53aec148a308f48396d5ff45f49391b8.png](https://i-blog.csdnimg.cn/blog_migrate/667272455454ca79c5429ed4019613c3.jpeg)