基础设施即代码(IaC)的世界已经掀起了云原生的风暴,如今配置云服务和应用程序时,它已成为一种通用的最佳实践。当云操作呈指数级增长时(这在当今基于 SaaS 和云托管的按需应用程序中发生得相当快),对于仍在利用 ClickOps 的公司来说,很快就会崩溃,ClickOps 通过手动配置,但这在云应用程序中会造成漂移。
值得注意的是,虽然 IaC 是一种最佳实践,并提供了许多好处(包括避免漂移),当我们需要进行重大的基础设施迁移时,我们看到了它的真正价值。我们发现,由于我们很早就利用了 IaC 的强大功能,并在配置方面与最佳实践保持一致,因此以前复杂的迁移过程变得简单多了。(还记得像 VeloStrata 和 CloudEndure 这样的公司吗?它们就是为此目的而建立的吗?)当我们谈论云和供应商锁定时,我们已经了解到如何打包、配置和部署应用程序会直接影响它们的可移植性和可扩展性。
在本文中,我想分享我们在 Firefly 的工作,将作业从 Serverless 迁移到 Kubernetes Job 的伟大旅程、经验教训、以及帮助我们以最小的痛苦实现这一目标的技术。
第一站:Lambda 事件
Serverless(无服务器)正在成为许多新兴甚至成熟公司的热门选择架构,因为它提供了云的所有好处——规模、可扩展性、弹性——以及最小管理和维护开销的附加值。它不仅速度快、可扩展,而且构建起来还非常有趣。
Lambda、函数、连接它们的服务以及基于事件的架构是开发人员试验和快速迭代之前复杂架构的游乐场。对于专为此类工作而构建的Terraform和Terraform Modules来说尤为如此。突然之间即可通过 lambda 运行程序在数小时内构建支持大规模、并发操作的基础设施,这过去至少需要几天甚至几周。
然而,随着时间的推移,我们开始遇到事件驱动架构和设计方面的问题。 由于我们的数据和流正常工作所需的服务种类繁多——API 网关、SQS、SNS、S3、事件桥等,事件的数量及其输入/输出开始增加。这就是我们开始触及已知的Serverless超时墙的地方。由于 Serverless 本质上是短暂的运行时,它基本上只有一个 15 分钟的任务完成窗口。如果任务没有及时完成,就会失败。
我们开始意识到,蜜月期可能已经结束了,我们需要根据用例和操作的具体性质来重新思考我们的基础设施选择。当你走上微服务的道路时——在我们的案例中,我们选择了利用Go例程来实现多线程服务(所以我们谈论的是很多服务),你通常会开始失去对正在运行的作业和服务数量的控制。
我们“微服务统治一切!”的心态,在以前我们认为它是令人难以置信的可扩展性的标志,但现在是我们崩溃的根源。我们试图通过添加限制来解决超时问题,但这大大减慢了我们的流程(对 SaaS 公司来说这不是一件好事),这当然不是我们所希望的结果。当我们增加集群时,这带来了巨大的成本影响,这对于一家初创公司来说也不是理想的选择。
技术债的累积使我们开始考虑我们的选择——重写还是迁移?如果不进行重大改革,我们还可以考虑或利用哪些其他技术呢?
第二站:停靠在 ECS
我们旅程的下一站是ECS(Elastic Container Service,弹性容器服务)。选择 ECS 实际上是我们最初选择将应用程序打包并部署到 Serverless 的副产品。我们选择将所有的应用程序进行 docker 化,并通过 Terraform 进行配置。这种早期的选择最终使我们能够选择我们的架构和基础设施。
我们决定尝试一下 ECS,主要是因为它的分析能力,以及处理任务、事件和作业时没有时间限制,就像 Serverless 一样。
ECS 的优势在于它的控制机制——这是它的核心功能,AWS 在其中管理任务调度、优先级、在何处以及何时运行。然而,对我们来说,这也是一把双刃剑。
特定事件的性质要求我们在任务调度方面有更大的控制权,比如更细粒度的优先级排序、任务排序、基于预定义的指标和阈值推动动态配置,而不仅仅是编程限制,还需更具动态性并利用遥测数据的限制。例如,如果我们有一个特定的帐户或租户正在使系统超载或向系统发送垃圾邮件,我们可以更动态地限制该事件,并更好地控制每个租户的自定义配置。
当我们分析这种情况时,我们意识到缺少的是一台“计算机”,或者说是 Kubernetes 世界中的操作(operator)。(这是一篇关于如何编写第一个 Kubernetes 操作的好文章,在这里你可以了解更多详细信息)。
第三站:回到 Kubernetes 作业的旅程中
回到使用容器化 lambda 的选择上,我们意识到,由于这个选择,我们并没有局限于基于 AWS 的基础设施,突然间,一个开放的社区标准选项出现了,这对我们和我们的需求来说是正确的选择。
如果我们想看看迁移到 Kubernetes 的好处,有很多需要考虑的因素:
有了 Kubernetes 作业(job),就有了一个可以实现更动态配置的操作(operator)
作为一家以 IaC 为先的公司,Helm是配置应用程序的绝佳方式
在无限规模上进行更大、更细粒度的分析、限制和配置
对我们来说,能够手动配置和管理 CPU 和内存分配,以及通过深度分析对其进行定制和自动化,这一好处是非常重要的。特别是当我们谈论由具有高度不同使用行为的各种客户端组成的规模时,其中一个租户可以运行两个小时,而其他租户只能运行三秒。因此,这种可定制性对我们来说是一个关键特性,也是最终让我们确信这一举措的关键。
接下来是检查应用程序的不同层,以了解这种迁移的复杂性。
如何将 Lambda 转换为 Kubernetes 作业?
现在是我们进行哲学思考的时候了。最终,什么是 lambda?这是一种需要使用特定配置并一次性完成的作业,该配置运行一组 worker 来完成作业。这让我们顿悟,这听起来很像……K8s 的作业。
我们的容器化 lambda 和完全编码的配置使我们能够重用运行时和配置,在不同环境之间移动只需进行很少的调整。让我们来看看其中的一些关键元素。
网络
绝大多数网络要素都是通过容器化来覆盖的,包括安全组等等。另一方面,如果网络未配置为代码或定义不明确,那么可能会发现服务之间的通信崩溃。确保所有安全组及其资源,从 VPC 到其他任何东西,都得到了正确的配置,可以确保更加无缝的过渡,这本质上是民主化基础设施选择的基础。
权限和外部配置
另一个可以促成或破坏这种迁移的关键方面是权限和访问控制。Serverless(AWS)、ECS 和 Kubernetes 与IAM角色一起工作时,问题只在于如何设计角色,使流不会中断,然后就可以轻松地跨环境移植它们。通过这种方式,我们可以确保流程不会在过渡中中断。会存在一些小的更改和优化,例如配置信任关系; 但是,这比从头开始配置所有权限要好的多。
更改 IAM 角色的信任关系:
这使其可移植且可重复使用:
我们需要确保所涉及的其他更改也在 Kubernetes 部署中将环境变量转换成了 configmap 格式。这样,就可以连接到首选的运行时和环境上了。
令人不快的运行时流
这并不意味着不会存在另人不快的流。Docker 并不是万能的,在某些情况下会出现兼容性问题,例如基础镜像可能会随着服务的不同而变化,或者在不同的操作系统发行版本之间发生变化,此外还有 Linux 问题,比如文件目录中的依赖关系。
然而,我们可以通过使用尽可能多的抽象来构建自己的 Docker 镜像和依赖关系,从而克服这些挑战。例如,在单独的构建器镜像中编译我们的 Golang 应用程序,并在目标镜像中使用它,或者在具有显式引用的结构中管理我们的环境变量,以避免依赖任何运行时为我们注入,这些都是避免运行时问题的良好实践。
蓝/绿部署
那么最终的发布是什么样的呢?虽然有一些停机时间,但这并不重要。我们团队选择了蓝/绿的方法来进行部署,并对此进行了密切监控,以确保所有数据都能正常接收,迁移也能顺利进行。
在我们进一步深入探讨之前,先简单介绍一下监控和日志记录。这是在部署任何内容之前需要确保正确迁移的另一个方面。当涉及到监控时,我们需要确保正确地转换了一些元素。如果我们之前监控过 lambda,那么现在就需要将它们转换为监控集群和 pod。我们需要验证日志是否能正常发送和到达——CloudWatch 与 fluentd。
一旦我们把所有这些都准备好了,就可以按照蓝/绿部署来重新路由我们的流量了。我们通过 SQS 将一些事件流路由到新的基础设施,并进行持续的完整性检查,以确保业务逻辑没有中断,一切都在传输,并且监控和日志记录都在正常工作。一旦我们检查了这个流程,并在没有任何中断的情况下将流量从以前的基础设施缓慢增加到新的基础设施上,我们的迁移就已经完成了。
这可能需要几个小时或几天的时间,具体时间取决于部署规模以及操作、SLA、数据等的敏感程度。唯一明确的建议是要确保我们有适当的可见性来了解它是否在有效地工作。
原文链接:
https://www.infoq.com/articles/lambda-migration-k8s-jobs/
相关阅读:
评论