Jenkins_Pipeline
要实现在 Jenkins 中的构建工作,可以有多种方式,我们这里采用比较常用的 Pipeline 这种方式。Pipeline,简单来说,就是一套运行在 Jenkins 上的工作流框架,将原来独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂流程编排和可视化的工作。
1. 核心概念
Node
:节点,一个 Node 就是一个 Jenkins 节点,Master 或者 Agent,是执行 Step 的具体运行环境,比如我们之前动态运行的 Jenkins Slave 就是一个 Node 节点Stage
:阶段,一个 Pipeline 可以划分为若干个 Stage,每个 Stage 代表一组操作,比如:Build、Test、Deploy,Stage 是一个逻辑分组的概念,可以跨多个 NodeStep
:步骤,Step 是最基本的操作单元,可以是打印一句话,也可以是构建一个 Docker 镜像,由各类 Jenkins 插件提供,比如命令:sh ‘make’,就相当于我们平时 shell 终端中执行 make 命令一样。
2. 构建Jenkins Pipeline
- Pipeline 脚本是由 Groovy 语言实现的,但是我们没必要单独去学习 Groovy,当然你会的话最好
- Pipeline 支持两种语法:Declarative(声明式)和 Scripted Pipeline(脚本式)语法
- Pipeline 也有两种创建方法:可以直接在 Jenkins 的 Web UI 界面中输入脚本;也可以通过创建一个 Jenkinsfile 脚本文件放入项目源码库中
- 一般我们都推荐在 Jenkins 中直接从源代码控制中直接载入 Jenkinsfile Pipeline 这种方法
快速创建一个简单的 Pipeline,直接在 Jenkins 的 Web UI 界面中输入脚本运行。
- 新建任务:在 Web UI 中点击
新建任务
-> 输入名称:pipeline-demo
-> 选择下面的流水线
-> 点击确定
- 配置:在最下方的 Pipeline 区域输入如下 Script 脚本,然后点击保存。
1 |
|
- 构建:点击左侧区域的
立即构建
,可以看到 Job 开始构建了
console output 我们可以看到上面我们 Pipeline 脚本中的 4 条输出语句都打印出来了,证明是符合我们的预期的。
如果对 Pipeline 语法不是特别熟悉的,可以前往输入脚本的下面的链接 流水线语法 中进行查看,这里有很多关于 Pipeline 语法的介绍,也可以自动帮我们生成一些脚本。
2.1 在Slave中构建任务
使用 Pod 模板用于创建 agent 节点,它们可以通过用户界面进行配置,也可以使用 podTemplate
步骤在管道中进行配置。无论哪种方式,它都提供对以下字段的访问:
1 |
|
现在我们重新触发立刻构建:
1 |
|
我们发现多了一个名叫 pipeline-demo-6-fvbts-298xs-2ff1v
的 Pod 正在运行,隔一会儿这个 Pod 就不再了。这也证明我们的 Job 构建完成了,同样回到 Jenkins 的 Web UI 界面中查看 Console Output
回到 Job 的主界面,也可以看到大家可能比较熟悉的 阶段视图
界面。
需要安装 Pipeline: Stage View
这个插件。
2.2 部署Kubernetes应用
上面我们已经知道了如何在 Jenkins Slave 中构建任务了,那么如何来部署一个原生的 Kubernetes 应用呢? 要部署 Kubernetes 应用,我们就得对我们之前部署应用的流程要非常熟悉才行,我们之前的流程是怎样的:
- 编写代码
- 测试
- 编写 Dockerfile
- 构建打包 Docker 镜像
- 推送 Docker 镜像到仓库
- 编写 Kubernetes YAML 文件
- 更改 YAML 文件中 Docker 镜像 TAG
- 利用 kubectl 工具部署应用
我们之前在 Kubernetes 环境中部署一个原生应用的流程应该基本上是上面这些流程吧?现在我们就需要把上面这些流程放入 Jenkins 中来自动帮我们完成(当然编码除外),从测试到更新 YAML 文件属于 CI 流程,后面部署属于 CD 的流程。如果按照我们上面的示例,我们现在要来编写一个 Pipeline 的脚本,应该怎么编写呢?
1 |
|
现在我们创建一个流水线的作业,直接使用上面的脚本来构建,同样可以得到正确的结果:
这里我们来将一个简单 golang 程序,部署到 kubernetes 环境中,代码链接:https://github.com/cnych/drone-k8s-demo。我们将代码导入到我们自己的 GitLab 仓库上去,地址:http://132.226.239.102:50080/root/drone-k8s-demo,这样让 Jenkins 和 Gitlab 去进行连接进行 CI/CD。
如果按照之前的示例,我们是不是应该像这样来编写 Pipeline 脚本:
第一步,clone 代码 第二步,进行测试,如果测试通过了才继续下面的任务 第三步,由于 Dockerfile 基本上都是放入源码中进行管理的,所以我们这里就是直接构建 Docker 镜像了 第四步,镜像打包完成,就应该推送到镜像仓库中吧 第五步,镜像推送完成,是不是需要更改 YAML 文件中的镜像 TAG 为这次镜像的 TAG 第六步,万事俱备,只差最后一步,使用 kubectl 命令行工具进行部署了
到这里我们的整个 CI/CD 的流程是不是就都完成了。但是我们这里的项目是 golang 代码的,构建需要相应的环境,如果每次需要特定的环境都需要重新去定制下镜像这未免太麻烦了,我们这里可以通过自定义 podTemplate
来进行定制。我们可以直接在 Pipeline 中去自定义 Slave Pod 中所需要用到的容器模板,这样我们需要什么镜像只需要在 Slave Pod Template
中声明即可,完全不需要去定义一个庞大的 Slave 镜像了。
然后在下面的流水线区域我们可以选择 Pipeline script
然后在下面测试流水线脚本,我们这里选择 Pipeline script from SCM
,意思就是从代码仓库中通过 Jenkinsfile
文件获取 Pipeline script
脚本定义,然后选择 SCM 来源为 Git(如果为空则需要安装 git
插件。),在出现的列表中配置上仓库地址 http://gitlab.k8s.local/root/drone-k8s-demo.git
,由于我们是在一个 Slave Pod 中去进行构建,所以如果使用 SSH 的方式去访问 Gitlab 代码仓库的话就需要频繁的去更新 SSH-KEY
,所以我们这里采用直接使用用户名和密码的形式来方式
在 Credentials
区域点击添加按钮添加我们访问 Gitlab 的用户名和密码
然后需要我们配置用于构建的分支,如果所有的分支我们都想要进行构建的话,只需要将 Branch Specifier
区域留空即可,一般情况下不同的环境对应的分支才需要构建,比如 main、dev、test 等,平时开发的 feature 或者 bugfix 的分支没必要频繁构建,我们这里就只配置 main 分支用于构建。
然后还要记得 Webhook Trigger,用于代码提交通知 Jenkins,勾选 Generic Webhook Trigger
,并添加一个名为 k8sdemo
的 Token,这个 Token 用于后面配置 Gitlab Webhook 的时候使用。
前往 Gitlab 中配置项目的 Webhook,设置 -> Webhooks
,填写上面得到的 trigger 地址:
我们这里都是自定义的域名,也没有配置 https 服务,所以记得取消配置下面的 启用SSL验证
。
我们这里都是自定义的域名,也没有配置 https 服务,所以记得取消配置下面的 启用SSL验证
。
现在就可以正常保存了,可以直接点击 测试 -> Push Event
测试是否可以正常访问 Webhook 地址,出现了 Hook executed successfully: HTTP 200
则证明 Webhook 配置成功了,否则就需要检查下 Jenkins 的安全配置是否正确了。
由于当前项目中还没有 Jenkinsfile
文件,所以触发过后会构建失败。
接下来我们直接在代码仓库根目录下面添加 Jenkinsfile
文件,用于描述流水线构建流程,整体实现流程如下图所示:
首先定义最简单的流程,要注意这里和前面的不同之处,这里我们使用 podTemplate
来定义不同阶段使用的的容器,有哪些阶段呢?
1 |
|
Clone 代码在默认的 Slave 容器中即可;单元测试我们这里直接忽略,有需要这个阶段的同学自己添加上即可;Golang 编译打包肯定就需要 Golang 的容器了;Docker 镜像构建/推送是不是就需要 Docker 环境了;最后的 Kubectl 更新服务是不是就需要一个有 Kubectl 的容器环境了,所以我们这里就可以很简单的定义 podTemplate
了,如下定义:
1 |
|
通过 podTemplate
可以来定义我们整个流水线的的模板了,每个阶段需要用到哪些容器都可以在该模板中定义,我们这里就定义了 golang、docker、kubectl 3 个容器。
注意这里我们配置了一个 DOCKER_HOST
的环境变量,这是因为我们现在的 Kubernetes 集群的容器运行时是 Containerd,这样节点上就没有 docker.sock
文件了,没有直接可用的 Docker Daemon 后端使用了,所以我们可以在集群中部署一个 Docker 后端服务,然后通过 DOCKER_HOST
环境变量来指定 Docker Daemon 的地址,这样我们就可以在容器中使用 Docker 命令构建镜像了,直接应用下面的资源清单文件即可:
1 |
|
然后在 volumes 中通过 hostPathVolume
将集群的 kubeconfig
文件挂载到容器中,这样我们就可以在容器中访问 Kubernetes 集群了,但是由于我们构建是在 Slave Pod 中去构建的,Pod 就很有可能每次调度到不同的节点去,这就需要保证每个节点上有 kubeconfig
文件才能挂载成功,所以这里我们使用另外一种方式。
通过将 kubeconfig
文件通过凭证上传到 Jenkins 中,然后在 Jenkinsfile 中读取到这个文件后,拷贝到 kubectl 容器中的 ~/.kube/config
文件中,这样同样就可以正常使用 kubectl 访问集群了。在 Jenkins 页面中添加凭据,选择 Secret file
类型,然后上传 kubeconfig
文件,指定 ID 即可:
然后在 Jenkinsfile
的 kubectl 容器中读取上面添加的 Secret file
文件,拷贝到 ~/.kube/config
即可:
1 |
|
现在我们直接将 Jenkinsfile
文件提交到 GitLab 代码仓库中,正常来说就可以触发 Jenkins 的构建了:
我们可以看到生成的 slave Pod 包含了 4 个容器,其中还包含一个 jnlp 的容器,加上我们在 podTemplate 指定的加上 slave 的镜像,运行完成后该 Pod 也会自动销毁。
当然也可以安装 Blue Ocean 插件,这样可以更加直观的看到流水线的执行情况。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!