53

从 Jenkins 迁移到 Jenkins X:一场持续交付之旅

 5 years ago
source link: https://www.infoq.cn/article/YWkOx7t4gQyO-64KZ186?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

这篇文章将介绍 dailymotion(一家总部位于巴黎的视频分享网站)从 Jenkins 迁移到 Jenkins X 的故事,包括我们遇到的问题以及我们如何解决它们。

背景

在 dailymotion,我们信奉 DevOps 最佳实践,并且重度使用了 Kubernetes。我们的部分产品(并非全部)已经部署在 Kubernetes 上。在迁移我们的广告技术平台时,为了赶时髦(作者你这么直白的吗?)我们希望完全采用“Kubernetes 方式”或云原生!这意味着我们需要重新定义我们的整个 CI/CD 管道,并使用按需分配的动态环境来替代永久性的静态环境。我们的目标是为我们的开发人员提供最好的支持、缩短产品上市时间并降低运营成本。

我们对新 CI/CD 平台的初始要求是:

  • 如果可能的话,尽量避免从头开始:我们的开发人员已经习惯使用 Jenkins 和声明性管道,目前这些东西都还好。

  • 采用公有云基础设施——谷歌云平台和 Kubernetes 集群。

  • 与 gitops 兼容——因为我们需要版本控制、评审和自动化。

CI/CD 生态系统中有不解决方案,但只有一个符合我们的要求,也就是 Jenkins X,它基于 Jenkins 和 Kubernetes,原生支持预览环境和 gitops。

Jenkins X: Kubernetes 上的 Jenkins

Jenkins X 是一个高度集成化的 CI/CD 平台,基于 Jenkins 和 Kubernetes 实现,旨在解决微服务体系架构下的云原生应用的持续交付的问题,简化整个云原生应用的开发、运行和部署过程。

你猜的没错,Jenkins X 只能在 Kubernetes 集群上运行

Jenkins X 的搭建过程非常简单,官方网站上已经提供了很好的文档。由于我们已经在使用 Google Kubernetes Engine(GKE),因此 jx 命令行工具可以自行创建所有的内容,包括 Kubernetes 集群。在几分钟内就可以获得一个完整的可运行系统真的让人印象深刻。

Jenkins X 提供了很多快速入门和模板,不过我们想重用现有代码库中的 Jenkins 管道。所以,我们决定另辟蹊径,并对我们的声明性管道进行重构,让它们与 Jenkins X 兼容。

实际上,重构工作并不是只针对 Jenkins X,而是为了能够使用 Kubernetes 插件 在 Kubernetes 上运行 Jenkins。

如果你习惯使用“经典”的 Jenkins,并在裸机或虚拟机上运行静态从节点,那么这里的主要变化是每个构建都将在自己的短存活期自定义 pod 上执行。你可以指定管道的每个步骤应该在哪个容器中执行。插件的源代码中提供了一些 管道示例

我们面临的挑战是如何定义容器的粒度,以及它们应该包含哪些工具:拥有足够多的容器让我们可以在不同的管道之间重用它们的镜像,但又不至于太多,这样容易维护——我们可不想要花太多时间重建容器镜像。

在之前,我们在 Docker 容器中运行大部分管道步骤,当我们需要自定义步骤时,就在管道中进行即时构建。

这种方式较慢,但更容易维护,因为所有内容都是在源代码中定义的。例如,升级 Go 运行时可以在单个拉取请求中完成。因此,需要预先构建容器镜像似乎是现有的设置中增加了更多的复杂性。它还具备一些优点:代码库之间的重复代码更少、构建速度更快,并且没有了因为第三方构建平台宕机而造成的构建错误。

AbiUnqy.png!web

在 Kubernetes 上构建镜像

在 Kubernetes 集群中构建容器镜像是一件很有趣的事情。

Jenkins X 提供了一组构建包,使用“Docker 中的 Docker”在容器内部构建镜像。但随着新容器运行时的出现,以及 Kubernetes 推出了 Container Runtime Interface(CRI),我们想知道其他选择是否可行。 Kaniko 是最成熟的解决方案,符合我们的需求。我们很激动,直到遇到以下 2 个问题。

  1. 第一个问题是阻塞性的:多阶段构建不起作用。通过使用搜索引擎,我们很快发现我们并不是唯一受到这个问题影响的人,而且当时还没有修复或解决方法。不过,Kaniko 是用 Go 语言编写的,而我们又是 Go 语言开发人员,所以为什么不看一下 Kaniko 的源代码呢?事实证明,一旦我们找到了问题的根本原因,修复工作就非常简单。Kaniko 维护人员很快就合并了修复,一天后,修复的 Kaniko 镜像就已经可用了。

  2. 第二个问题是我们无法使用相同的 Kaniko 容器构建两个不同的镜像。这是因为 Jenkins 并没有正确地使用 Kaniko——因为我们需要先启动容器,然后再进行构建。这一次,我们在谷歌上找到了一个解决方法:声明足够多的 Kaniko 容器来构建镜像,但我们不喜欢这个方法。所以我们又回到了源代码,在找到了根本原因后,修复就很容易了。

我们测试了一些方案,想自己为 CI 管道构建自定义的“工具”镜像,最后,我们选择使用单个代码库,每个分支使用一个镜像,也即一个 Dockerfile。因为我们的代码托管在 Github 上,并使用 Jenkins Github 插件来构建代码库,所以它可以构建所有的分支,并基于 webhook 触发事件为新分支创建新的作业,所以管理起来十分容易。每个分支都有自己的 Jenkinsfile 声明性管道文件,使用 Kaniko 构建镜像,并将构建好的镜像推送到容器注册表。Jenkins 帮我们做了很多事情,所以可以快速地添加新镜像或编辑现有的镜像。

声明所请求的资源

我们之前的 Jenkins 平台存在的一个主要问题来自于静态从属节点或执行程序,以及有时候会在高峰时段出现的长构建队列。Kubernetes 上的 Jenkins 可以轻松地解决这个问题,特别是运行在支持集群自动缩放器的 Kubernetes 集群上时。集群将根据当前的负载添加或移除节点。不过这是基于所请求的资源,而不是基于所使用资源的情况。

这意味着我们需要在构建 pod 模板中定义所请求的资源——比如 CPU 和内存。然后,Kubernetes 调度程序将使用这些信息查找匹配的节点来运行 pod——或者它可能决定创建一个新节点。这样就不会出现长队列了。

但是,我们需要谨慎定义所需资源的数量,并在更新管道时更新它们。因为资源是在容器级别而不是 pod 级别定义的,所以处理起来会更加复杂。 但我们不关心限制问题,我们只关心请求 ,所以我们只将对整个 pod 的资源请求分配给第一个容器(jnlp 那个)——也就是默认的那个。

以下是 Jenkinsfile 的一个示例,以及我们是如何声明所请求的资源的。

复制代码

pipeline {
    agent {
        kubernetes {
            label'xxx-builder'
            yaml"""
kind: Pod
metadata:
  name: xxx-builder
spec:
  containers:
  - name: jnlp
    resources:
      requests:
        cpu:4
        memory:1G
  - name:go
    image: golang:1.11
    imagePullPolicy: Always
   command: [cat]
    tty: true
  - name: kaniko
    image: gcr.io/kaniko-project/executor:debug
    imagePullPolicy: Always
   command: [cat]
    tty: true
"""
        }
    }

    stages {
    }
}

Jenkins X 的预览环境

现在我们有了所有工具,可以为我们的应用程序构建镜像,我们已准备好进行下一步:部署到“预览环境”!

通过重用现有工具(主要是 Helm),Jenkins X 可以轻松部署预览环境,只要遵循一些约定,例如镜像标签的名称。Helm 是 Kubernetes 应用程序的包管理器。每个应用程序都被打包为一个“chart”,然后可以使用 helm 命令行工具将其部署为“release”。

可以使用 jx 命令行工具部署预览环境,这个工具负责部署 Helm 的 chart,并为 Github 的拉取请求提供注释。在我们的第一个 POC 中,我们使用了普通的 HTTP,因此这种方式奏效了。但现在没有人再用 HTTP 了,那我们使用加密的吧!

多亏了有 cert-manager ,在 Kubernetes 中创建摄入资源时可以自动获取新域名的 SSL 证书。我们尝试在设置中启用 tls-acme 标志——使用 cert-manager 进行绑定——但它不起作用。

于是我们阅读了 Jenkins X 的源代码——它也是使用 Go 开发的。稍后修改一下就好了,我们现在可以使用安全的预览环境,其中包含了 let’s encrypt 提供的自动证书。

预览环境的另一个问题与环境的清理有关。我们为每个拉取请求创建了一个预览环境,在合并或关闭拉取请求时需要删除相应的环境。这是由 Jenkins X 设置的 Kubernetes 作业负责处理的,它会删除预览环境使用的命名空间。问题是这些作业并不会删除 Helm 的 release——因此,如果你运行 helm list,仍然会看到旧的预览环境列表。

对于这个问题,我们决定改变使用 Helm 部署预览环境的方式。我们决定使用 helmTemplate 功能标志,只将 Helm 作为模板渲染引擎,并使用 kubectl 来处理生成的资源。这样,临时的预览环境就不会“污染”Helm release 列表。

将 gitops 应用于 Jenkins X

在初始 POC 的某个时候,我们对设置和管道非常满意,并准备将 POC 平台转变为可投入生产的平台。第一步是安装 SAML 插件进行 Okta 集成——允许内部用户登录。它运作得很好,但几天后,我发现 Okta 集成已经不在了。我在忙其他的一些事情,所以只是问了同事一下他是否做了一些更改,然后继续做其他事情。几天后再次发生这种情况,我开始调查原因。我注意到 Jenkins pod 最近重启过。但我们有一个持久的存储,而且作业也在,所以是时候仔细看看了!

事实证明,用于安装 Jenkins 的 Helm chart 有一个启动脚本通过 Kubernetes configmap 重置了 Jenkins 配置。当然,我们无法像管理在 VM 中运行的 Jenkins 那样来管理在 Kubernetes 中运行的 Jenkins!

我们没有手动编辑 configmap,而是退后一步从大局看待这个问题。configmap 是由 jenkins-x-platform 管理的,因此通过升级平台来重置我们的自定义更改。我们需要将“定制”内容保存在一个安全的地方,并对变化进行跟踪。

我们可以使用 Jenkins X 的方式,并使用一个 chart 来安装和配置所有内容,但这种方法有一些缺点:它不支持“加密”——我们的 git 代码库中包含了一些敏感的信息——并且它“隐藏”了所有子 chart。因此,如果我们列出所有已安装的 Helm 版本,只会看到其中一个。但是还有其他一些基于 Helm 的工具,它们更适合 gitops。 Helmfile 就是其中之一,它通过 helm-secrets 插件sops 原生支持加密。

迁移

从 Jenkins 迁移到 Jenkins X 以及如何使用 2 个构建系统处理代码库也是我们整个旅程的一个很有趣的部分。

首先,我们搭建新 Jenkins 来构建“jenkinsx”分支,同时更新了旧 Jenkins 配置,用来构建除“jenkinsx”分支之外的所有内容。我们计划在“jenkinsx”分支上构建新管道,并将其合并。

对于初始 POC,这样做没有问题,但当我们开始使用预览环境时,不得不创建新的拉取请求,并且由于分支的限制,那些拉取请求不是基于新的 Jenkins 构建的。因此,我们选择在两个 Jenkins 实例上构建所有内容,只是在 Jenkins 上使用 Jenkinsfile 文件名和在新 Jenkins 上使用 Jenkinsxfile 文件名。迁移之后,我们将会更新这个配置,并重命名文件。这样做是值得的,因为它让我们能够在两个系统之间平稳过渡,并且每个项目都可以自行迁移,不会影响到其他项目。

我们的目的地

那么, Jenkins X 是否适合所有人?老实说,我不这么认为 。并非所有功能和支持的平台——git 托管平台或 Kubernetes 托管平台——都足够稳定。但是,如果你有足够的时间进行深挖,并选择了适合自己用例的功能和平台,就可以改善你的管道。这将缩短发布周期,降低成本,如果你对测试也非常认真,那么对你的软件质量也应当充满信心。

我们的旅程还没有结束,因为我们的目标仍在继续:Jenkins X 仍然处于开发阶段,而且它本身正在走向 Serverless,目前正在使用 Knative build。它的目标是云原生 Jenkins。

我们的旅程也在继续,因为我们不希望它就这样结束。我们目前的完成的一些事情并不是我们的最终目的地,它只是我们不断演化的一个步骤。这就是我们喜欢 Jenkins X 的原因:与我们一样,它遵循了相同的模式。你也可以开始你自己的旅程~

英文原文: https://medium.com/dailymotion/from-jenkins-to-jenkins-x-604b6cde0ce3


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK