作者:Tim Collins
我叫 Tim Collins,是Sendible[1]的高级 DevOps 工程师。在 Sendible,我们正在着手一个计划,使我们的应用和开发堆栈更适合云原生,但我们很快发现我们现有的 CI 解决方案不能胜任这项工作。我们开始寻找替代方案,并认为记录我们的过程可能会帮助其他处于类似情况的人。
为什么?
Jenkins 可以说仍然是事实标准的 CI 工具。它是成熟的,在互联网上有大量知识渊博的人,他们可以帮助你从中得到最好的东西。然而,成熟也会带来挑战。
主要的是……
插件意大利面条
Jenkins 有大量的插件。缺点是,Jenkins 有大量的插件!找到适合自己需要的插件,评估插件的安全影响,然后保持更新/维护,这些开始成为一个真正令人头疼的问题。
不是云原生
当然,在 Kubernetes 中运行 Jenkins 是可能的,同样也可能在触发工作时启动动态 pod。然而,Jenkins 最初并不是为这种工作方式设计的,在使用它之后,我们开始清楚地发现它并不能与 Kubernetes 完全互操作。一个明显的例子是,Jenkins 的主安装只能在一个 pod 中运行,因此不存在 HA 部署以备排除或崩溃状况。
同样,Jenkins 运行作业(job)的自然方法是将所有需要的容器部署到一个 pod 中。这意味着在运行开始时启动所有需要的容器,直到运行结束时才释放它们。由于所有内容都在一个 pod 中,而 pod 不能跨越多个节点,因此如何使用节点来适应工作负载存在限制。
当然,有很多方法可以解决这个问题——有一段时间,我们用级联的 Jenkins 作业来欺骗它,让它为我们提供动态供应的 pod……但过了一段时间,我们意识到我们只是在与一个工具斗争,让它做一些它原本不打算做的事情。结果,流水线代码很快变得难以维护,调试工作变得复杂。
成本效率
在 Sendible,我们发现自己采用了越来越多的变通方法,尝试用我们熟悉的工具运行 CI,使用 Kubernetes,并降低成本。事实上,我们在维护成本上损失的时间和金钱比我们能节省的要多。
还有其他成本方面的考虑。一个使用良好的 Jenkins 控制器可能会消耗大量的系统资源,前面提到的“每个作业一个 pod”的问题意味着你可能需要提供大型服务器。如果你在 Kubernetes 之外运行 Jenkins,并且没有一个自动伸缩系统,那么可能会一直运行代理节点,这可能会增加你的成本。
为什么使用 Argo?
我们已经在 GitOps 中使用了 Argo CD,并且已经完成了一个关于 Argo Rollouts 的 POC 来管理未来的版本。因此,至少调查一下它们的兄弟项目 Workflows 和 Events 是有意义的。
很明显,与我们现有的 CI 解决方案相比,Argo Workflows 要快得多,由于有了重试选项,我们可以利用集群自动伸缩器和 AWS Spot 实例,这立即将我们的 CI/CD 成本降低了 90%!我们发现,只有在需要时才创建 pod,从而能够为相同的作业提供更小的服务器,从而节约了更多的成本。
我们还想要一些能够扩展到 CI 之外的东西。最终,我们追求的是一个灵活的“实干工具”,我们可以在多种情况下使用。除了常规的 CI 工作,我们已经在使用 Argo Workflows 和 Argo Events:
- 警报补救(从 Alertmanager 接收警报并触发工作流来补救问题)。
- 从 Slack 创建测试环境。
- 自动测试我们的备份恢复,有问题时发出警报。
花了多长时间?
与所有 DevOps 一样,这个过程正在进行中,但在最初的项目中只有一个人,只有一些 Kubernetes 知识,没有 Argo Workflows 或 Events 知识,我们在一天内就完成了基本的概念验证和运行。然后经过两周的改进,我们制作了足够生产使用的 Workflows(使它成为 HA,添加 SSO 等),以便更广泛的团队采用它。
我们一路学到了的一些东西
与所有工具实现一样,这个过程也并非没有挑战。希望下面这个简短的列表可以帮助那些踏上类似旅程的人:
忘掉“Jenkins 式”
如果你多年来一直使用 Jenkins Pipelines,那么你可能不会自然地想到云原生流水线解决方案。尽量避免在不同的工具中重写 Jenkins 流水线。相反,花点时间去理解流水线的设计目的是什么,并对其进行改进。
Argo Workflows 的动态 pod 创建意味着你必须重新处理在工作期间保存数据的方式。正式的方法是在外部存储解决方案(如 S3)中使用工件存储库,但是对于更多的瞬态数据,你可以考虑设置一个 RWM PVC 来在几个 pod 之间共享存储卷。
同样,你可以将此迁移作为重新考虑并行性和任务排序的机会。Jenkins 流水线当然提供了并行运行的步骤,但这是一个必须有意识地选择的东西。Argo Workflows 的方法是默认情况下并行运行步骤,允许你简单地定义任务之间的依赖关系。你可以以任何顺序编写工作流,然后只需调整依赖项。我们建议你继续细化这些依赖项,以找到最适合你的。
利用工作流模板
在可能的情况下,尝试将工作流中的每个步骤视为其自身的功能。你可能会发现你的各种 CI 工作都有很多共同的功能。例如:
- 从 Git 克隆
- 构建容器
- 更新票务管理系统或 Slack 的状态
将每个流程步骤编写为单独的工作流模板。这允许你通过将这些模板拼接到 DAG 中,然后将适当的参数传递给它们,从而构建一个相对地新的 CI 流程。随着时间的推移,编写新的 CI 流程主要成为将构建块组合在一起的练习。
你不需要“大爆炸”
“迁移”这个词是可怕的,而且有可能被填满 ? 符号。其实不必如此。
如果你已经有了 Jenkins,那就不要想把它撕掉,或者认为你必须一次把所有东西都换掉。你可以在 Jenkins 旁边慢慢运行 Workflows——你甚至可以让 Jenkins 触发 Workflows。当我们开始时,我们先转移自动化集成测试,然后再转移到更复杂的 CI 工作。
利用 Argo Slack 频道和 Github 讨论页面
Argo 文档很好,Github 本身也是一样的好(特别是 Github 的讨论页面),但有一群知识渊博的人以奇怪而奇妙的方式使用 Argo,他们大多在Slack 频道[2]中闲逛。
下一步?
随着我们每天使用 Argo Workflows,我们仍然在学习关于它的新东西,我们仍然处于不断重构我们的 Workflows 以获得绝对最好的结果的阶段。
版本 3.1 的 Argo Workflows 已经不远了,我们期待着即将到来的特性。需要特别注意的是,条件参数[3]将允许我们删除一些脚本步骤,容器集[4]将允许我们加快 CI 中的某些步骤。
如果你对我在 Argo Workflows 和 Argo Events 方面的经验有任何疑问,你可能会在 CNCF Slack 工作空间中找到我,或者你可以通过 Sendible 网站联系我。
参考资料
[1]
Sendible: https://www.sendible.com/
[2]
Slack 频道: https://slack.cncf.io/
[3]
条件参数: https://argoproj.github.io/argo-workflows/conditional-artifacts-parameters/
[4]
容器集: https://argoproj.github.io/argo-workflows/container-set-template/