基于 OpenShift 构建面向企业的 CI/CD
在 OpenShift 中构建应用
OpenShift 上,很多时候我们直接部署包含应用的容器镜像。但客户的开发人员在使用 OpenShift 平台时,会要求应用能够在 OpenShift 上自动化构建,并进一步实现 CI/CD。这就涉及到如何在 OpenShift 中构建应用。接下来,我们先介绍 OpenShift 上应用的构建方式,然后分析如何在 OpenShift 上实现 CI/CD。
OpenShift 上应用构建的方式
在 OpenShift 上构建应用方式包括三种:
- Dockerfile 构建
- Jenkins 构建
- Source to Image 构建(简称 S2I)
在三种方式中,书写 dockerfile 进行构建最为常见,这也是应用容器化的一种方式。但在大规模的 CI/CD 环境中,通过 Dockerfile 方式构建应用的方式显然效率较低。
在企业环境中,大多数客户的应用构建都是通过 Jenkins 方式完成的。Jenkins 面向多种构建环境,容器环境只是它的一种使用场景。在容器环境中,Jenkins 集群在 CI 流程中调用 Maven 执行构建,生成应用包。
Source to Image 是红帽 OpenShift 原创的应用构建和部署方式。红帽官方会提供很多 S2I Builder Image(如果客户需要的 Builder Image 红帽官网没有提供,则需要自行制作)。在这种模式下,我们在构建应用时,需要选择应用所在的代码库(如 git)和 S2I Builder Image(例如 tomcat),然后 OpenShift 会自动构建源码,并自动将编译打包好的软件包拷贝到容器镜像的对应目录中,完成应用容器化的步骤。
在 OpenShift 上,应用构建推荐使用 Jenkins 还是 S2I 呢?实际上,这两种方式并不冲突。在很多场景中的,它们是相互补充的。单一使用 Jenkins 构建应用的缺点是微服务多语言多版本混合构建时无隔离,此外 Jenkins 也比较消耗内存资源;单一使用 S2I 的缺点是难以实现 CI/CD 流水线管理。
接下来,我们介绍 S2I 和 Jenkins 结合使用构建应用并实现 CI/CD 的方式。
S2I 与 Jenkins 结合使用实现 CI/CD
S2I 与 Jenkins 相结合实现 CI/CD 主要有以下三种方式:
- Jenkins 负责 CI、S2I 负责 CD。
- Jenkins 管理 Pipeline,调度 OpenShift S2I 完成 CI/CD。
- 在 OpenShift 上使用 Jenkins file,调用 Jenkins 实现 CI、OpenShift 负责 CD。我们称这种方式为 OpenShift Pipeline。
在第一种方式中,Jenkins 负责 CI,输出物是应用软件包(如 Jar、War 包),然后应用包以 Binary to Image(简称 B2I)的方式注入到 Builder Image 中。本质上,B2I 是 S2I 的一个特殊使用场景,如下图 1 所示。
图 1. 第一种 CI/CD 实现方式
第二种方式中,整个 CI/CD 的动作都是由 S2I 完成,但由 Jenkins 控制整个 Pipeline。在这种方式下,Pipeline 的触发是在 Jenkins 上完成的,如下图 2 所示:
图 2. 第二种 CI/CD 实现方式
第三种即 OpenShift Pipeline 的方式。在这种方式下,通过 Jenkins file 定义整个 Pipeline,Pipeline 的触发在 OpenShift 界面上完成。CI 通过 Jenkins 完成,CD 通过 OpenShift 完成,如图 3 所示:
图 3. 第三种 CI/CD 实现方式
在以上三种方式中,我们推荐使用第三种 OpenShift Pipeline 的方式。这种方式的好处是:
- 便于管理:Pipeline 的管理由 OpenShift 完成,避免客户反复切换界面;
- 可维护性强:如果我们想要修改 CI/CD 流水线的环节(如增加 UAT),只需要修改 Jenkins file 即可。
红帽 OpenShift 平台提供 Jenkins 的容器化镜像,如图 4 所示。在 OpenShift 上部署的 Jenkins,可以实现与 OpenShift 账户的单点登录。
图 4. OpenShift 提供的 Jenkins 的镜像
如上文所述,此前 OpenShift Pileline 是通过 Jenkins 实现的。在 OpenShift 最新版本 4.2 中,OpenShift Pipeline 又有了新的实现方式。
OpenShift 4.2 中的 OpenShift Pipeline
在上文中我们提到过,CI 工具中 Jenkins 的功能较强,使用范围也很广。OpenShift 3 的 Pipeline 就是使用 Jenkins。但是对于容器和 Kubernetes 来说,Jenkins 的劣势在于:
- Jenkins 不是为容器环境构建的;
- Jenkins 不适合微服务团队结构;
- Jenkins 插件太多,造成了微服实现 CI/CD 的复杂度。
在 OpenShift 4.2 中,OpenShift Pipeline 使用云原生的 CI/CD 工具 Tekton,它具有如下优势:
- 具有基于 Tekton 的标准 Kubernetes 自定义资源(CRDs)的声明性 pipeline
- 在容器中运行 pipeline
- 使用 Kubernetes 上的容器按需扩展 pipeline 执行
- 使用 Kubernetes 工具构建 Image
- 可以自行选择(source-to-image、buildah、kaniko、jib 等)来构建容器镜像,然后部署到多个平台,如无服务器架构、虚拟机和 Kubernetes
接下来,我们先介绍 Tekton 的概念,再介绍如何在 OpenShift 上使用 Tekton 实现 CI/CD。
Tekton 简介
Tekton 是由谷歌主导的开源项目,它一个功能强大且灵活的 Kubernetes 原生开源框架,用于创建持续集成和交付(CI/CD)。通过抽象底层实现细节,用户可以跨多云平台和本地系统进行构建、测试和部署。
Tekton 将许多 Kubernetes 自定义资源(CR)定义为构建块,这些自定义资源是 Kubernetes 的扩展,允许用户使用 kubectl 和其他 Kubernetes 工具创建这些对象并与之交互。
Tekton 的自定义资源包括:
- Step:在包含卷、环境变量等内容的容器中运行命令。
- Task:执行特定 task 的可重用、松散耦合的 Step(例如,building a container image)。Task 中的 step 是串行执行的。
- Pipeline:Pipeline 由多个 tasks 组成,按照指定的顺序执行,task 可以运行在不同的节点上,他们之间有相互的输入输出。
- PipelineResource:Pileline 的资源,例如:输入(例如,git 存储库)和输出(例如,image registry)。
- TaskRun:是 CRDS 运行时,运行 task 实例的结果。
- PipelineRun:是 CRDs 运行时,运行 pipeline 实例的结果,其中包含许多 TaskRuns。
OpenShift Pipeline 的工作流程图如下所示。通过 task 定义 pipeline;Tekton 执行 pipeline,task 和 pipeline 变成运行时状态,并且 OpenShift 中启动 pod 来运行 pipeline,如下图 5 所示:
图 5. OpenShift Pipeline 工作流程
虽然 OpenShift 4.2 Pipeline 默认使用 Teckton,但 OpenShift 会继续发布并支持 Jenkins image 和 plugin,如下图 6 所示:OpenShift 4.2 的服务目录所示 Jenkins 的内容。
图 6. OpenShift 4.2 上 Jenkins 的模板
在介绍了 Tekton 的概念后,接下来我们学习如何在 OpenShift 4.2 中部署 OpenShift Pipeline。
安装 OpenShift Pipeline
OpenShift Pipelines 在 OpenShift 4.2 中,可以通过 OpenShift Operator Hub 中提供的 Operator 进行安装。在 Operator Hub 中搜索到 OpenShift Pipeline Operator,如下图 7 所示:
图 7. Operator Hub 界面
点击 install 安装 OpenShift Pipeline Operator,如下图 8 所示:
图 8. 安装 OpenShift Pipeline Operator
接下来,创建 Operator 订阅,指定要部署的项目,如下图 9 所示,点击 Subscribe:
图 9. 创建 Operator 订阅
在 OpenShift 中创建运行 OpenShift Pipeline 的项目:
[root@master ~]# oc new-project pipelines-tutorial
验证 OpenShift Pipeline
我们使用一个 Spring PetClinic 示例进行测试,这是一个简单的 Spring Boot 应用程序。
首先应用如下配置,它会创建应用的 ImageStream、DeploymentConfig、Route 和 Service 等资源。由于配置文件内容较多,我们将它放在 GitHub 上: https://github.com/ocp-msa-devops/teckton/blob/master/petclinic.yaml 。
应用配置:
# oc create -f petclinic.yaml
我们可以在 OpenShift Web 控制台中看到部署的内容,如下图 10 所示:
图 10. 查看应用部署
接下来,我们展示创建 task 的一个示例。task 含许多按顺序执行的 step。一个 task 的 steps 顺序地在在同一个 pod 中的单独容器中执行。它们具有输入和输出,以便与 pipeline 中的其他 task 进行交互。
我们创建两个 task:openshift-client 和 s2i-java task。第一个 task 执行参数传入的 oc 命令,第二个 task 是基于 Openjdk 做 S2I 的模板。配置文件内容如清单 1 和清单 2 所示:
清单 1. openshift-client-task.yaml 配置文件:
apiVersion: tekton.dev/v1alpha1 kind: Task metadata: name: openshift-client spec: inputs: params: - name: ARGS description: The OpenShift CLI arguments to run default: help steps: - name: oc image: quay.io/openshift-pipeline/openshift-cli:latest command: ["/usr/local/bin/oc"] args: - "${inputs.params.ARGS}"
清单 2. s2i-java-8-task.yaml 配置文件
apiVersion: tekton.dev/v1alpha1 kind: Task metadata: name: s2i-java-8 spec: inputs: resources: - name: source type: git params: - name: PATH_CONTEXT description: The location of the path to run s2i from. default: . - name: TLSVERIFY description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS registry) default: "true" outputs: resources: - name: image type: image steps: - name: generate image: quay.io/openshift-pipeline/s2i workingdir: /workspace/source command: ['s2i', 'build', '${inputs.params.PATH_CONTEXT}', 'registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift', '--image-scripts-url', 'image:///usr/local/s2i', '--as-dockerfile', '/gen-source/Dockerfile.gen'] volumeMounts: - name: gen-source mountPath: /gen-source - name: build image: quay.io/buildah/stable workingdir: /gen-source command: ['buildah', 'bud', '--tls-verify=${inputs.params.TLSVERIFY}', '--layers', '-f', '/gen-source/Dockerfile.gen', '-t', '${outputs.resources.image.url}', '.'] volumeMounts: - name: varlibcontainers mountPath: /var/lib/containers - name: gen-source mountPath: /gen-source securityContext: privileged: true - name: push image: quay.io/buildah/stable command: ['buildah', 'push', '--tls-verify=${inputs.params.TLSVERIFY}', '${outputs.resources.image.url}', 'docker://${outputs.resources.image.url}'] volumeMounts: - name: varlibcontainers mountPath: /var/lib/containers securityContext: privileged: true volumes: - name: varlibcontainers emptyDir: {} - name: gen-source emptyDir: {}
应用两个配置文件:
# oc create -f openshift-client-task.yaml # oc create -f s2i-java-8-task.yaml
通过 tkn 命令行查看 task,可以上面步骤创建的两个 task,如下图 11 所示:
[root@master /]# ./tkn task ls
图 11. 查看 Tekton 任务
接下来创建 Pipeline。Pipeline 定义了执行的 task 以及它们如何通过输入和输出相互交互。Pipeline 从 GitHub 获取 PetClinic 应用程序的源代码,然后使用 Source-to-Image(S2I)在 OpenShift 上构建和部署它,如下图 12 所示:
图 12. 构建过程
上图 Pipeline 的 YAML 文件(pipeline.yaml)配置如清单 3 所示。pipeline 将我们之前创建的两个 task(openshift-client 和 s2i-java task)串联。
清单 3. Pipeline 的 Yaml 配置文件:
apiVersion: tekton.dev/v1alpha1 kind: Pipeline metadata: name: deploy-pipeline spec: resources: - name: app-git type: git - name: app-image type: image tasks: - name: build taskRef: name: s2i-java-8 params: - name: TLSVERIFY value: "false" resources: inputs: - name: source resource: app-git outputs: - name: image resource: app-image - name: deploy taskRef: name: openshift-client runAfter: - build params: - name: ARGS value: "rollout latest spring-petclinic"
此 Pipeline 执行以下操作:
- 从 Git 存储库(app-git 资源)克隆应用程序的源代码。
- 使用 s2i-java-8 task 构建容器镜像,该 task 为应用程序生成 Dockerfile 并使用 Buildah 构建镜像。
- 应用程序镜像被推送到容器镜像注册表。
- 使用 openshift-cli 在 OpenShift 上部署新的应用程序镜像。
我们注意到,在上面的配置中:没有指定 PetClinic Git 的源码地址,这些将会在 Pipeline 执行的时候用参数传递进去。一个 Pipeline 中 task 的执行顺序,通过输入和输出在 task 之间定义的依赖关系以及 runAfter 定义的显式顺序确定。
通过运行以下命令创建 Pipeline:
# oc create -f pipeline.yaml
查看创建好的 Pipeline
[root@master /]# ./tkn pipeline ls NAME AGE LAST RUN STARTED DURATION STATUS deploy-pipeline 30 seconds ago --- --- --- ---
接下来,我们传入参数、启动 PipelineRun:
[root@oc132-lb /]# tkn pipeline start petclinic-deploy-pipeline \ > -r app-git=petclinic-git \ > -r app-image=petclinic-image \ > -s pipeline Pipelinerun started: petclinic-deploy-pipeline-run-gwxlz
当然,我们也可以通过 OpenShift 4.2 的 Developer 界面启动 Pipeline,如下图 13 所示:
图 13. 通过 OpenShift Developer 界面启动 Pipeline
查看 pipeline 的运行,如下图 14 所示:
[root@master /]# ./tkn pr ls
图 14. 查看 Tekton 的运行情况
查看 pipeline 的运行日志,运行正常,如下图 15 所示:
[root@master /]# ./tkn pr logs petclinic-deploy-pipelinerun-9r7hw -f
图 15. 查看 pipeline 的运行日志
红帽 OpenShift4.2 的 Developer 界面,可以监控 Pileline 运行情况,如下图 16 所示:
图 16. 查看 pipeline 的图形化界面
过一段时间后,Pipeline 运行完毕,如下图 17 所示:
图 17. Pipeline 运行成功
查看部署好的应用 pod,如图 18 所示:
图 18. 查看部署好的应用
使用 oc get route 查看应用的路由后,通过浏览器可以访问应用,如图 19 所示:
图 19. 通过浏览器访问应用
至此,我们成功通过 Tekton 的 Pipeline,在 OpenShift 上成功构建并部署应用。
结束语
在本文中,我们介绍了 OpenShift3 中通过 S2I 和 Jenkins 配合的方式,实现 CI/CD。此外,我们还介绍 OpenShift 4.2 中通过 Tekton 实现 OpenShift Pipeline 的方式,并通过具体的实验进行了展现。在未来,Tekton 会逐渐成为主流。