Categories
程式開發

Coinbase:我们为什么不用Kubernetes?


容器编排平台是一项复杂而令人惊叹的技术,可以帮助一些企业和团队解决一系列的问题。然而,我们经常忽略的是,容器技术还带来了一系列的挑战,必须克服这些挑战才能避免失败

这篇文章改编自一篇内部博客,我看到网上像这样的文章并不多。我对这篇文章稍微做了些调整,为了便于读者理解,还添加了图片。如果你对我们下面讨论的内容感兴趣,欢迎加入我们的基础设施团队

历史

Coinbase:我们为什么不用Kubernetes? 1

在讨论现状之前,让我们先了解下时至今日这项技术的发展历程。

如果想进一步了解其历史,请查阅Enterprise Docker第七章。

让我们从10年前说起,那时还没有现如今大家都知道的容器。那个时候,我们没有/不使用docker、rkt或任何其他主流的容器封装器/服务。为了将应用程序从源代码打包为生产部署,大多数大型公司都在构建内部系统。工程师在他们机器上运行的东西通常不是在生产环境中运行的东西,或者即使是在生产环境中运行的东西,也通常是以一种深度定制化而且非常复杂的方式一次性构建/打包的。

使用内部系统打包和部署应用程序需要一个大型运营团队。通常,该团队隶属于负责管理打包/构建流程、部署和部署后工作的平台或基础设施组织。这些角色的职责通常以操作型工作为主,包括主机故障排除、诊断OS补丁/升级中的特定依赖问题等。部署后的工作包括容量规划、订购更多服务器、上架/安装以及升级上面的软件,几乎不涉及自动编排。

幸运的话,有一些常规过程可以用来构建一个“黄金镜像”(想想Hashicorp的Packer)。这个镜像有详细的文档,甚至可能被编码,并由Hudson(在Jenkins之前)这样的持续集成系统运行。这些镜像可以手动或借助某些配置管理工具自动分发到你的系统中,然后以某种顺序启动(比如用Parallel SSH或类似的方式)。

在过去的十年里,一切都变了。我们不再使用庞大的单体应用程序,而是将服务分解为许多离散的、低耦合的部件。我们从必须构建/拥有自己的计算,转为拥有托管服务或公有云服务,而这个过程只需点击几下鼠标和一张信用卡。我们从垂直扩展应用程序,转为重构它们实现水平扩展。所有这一切都是同时发生的,社会也发生了变化:每个人的口袋里都有手机,网络速度在提高,全球范围内的网络延时在下降,从预约遛狗者到商品化的视频会议,一切都在网上进行。

2009年,AWS提供的服务还相当有限。AWS的EC2服务2008年才完成beta测试并开始提供SLA。相比之下,GCP直到2013年才正式推出计算服务。

为什么企业会选择容器化他们的应用程序?

企业选择容器化他们的应用程序,是为了以快速、安全、可靠的方式提高工程输出/开发人员的生产力。容器化是不同于构建镜像的另一个选项,尽管有时可以将容器构建到镜像中,但这超出了本文的范围(参见这里)。

容器使工程师在本地开发、测试和运行应用程序的方式与在其他环境(过渡环境和生产环境)中运行的方式相同或类似。容器允许描述依赖绑定关系,可以是显式的,也可以是隐式的(操作系统总是包含服务所依赖的包$foo)。容器允许更小的服务封装和资源定义(使用X CPU和Y GB内存)。容器让你可以考虑水平伸缩应用程序,而不是垂直伸缩应用程序,从而实现更健壮的架构。

其中一些观点可能还需要进一步地讨论。为了推动对话,这些观点都有点过于大胆和发散,因为这并不是在讨论容器化或*服务化(service-ification)*的利弊(例如,将单个应用程序拆分为许多更小的独立运行的服务)。

虚拟化呢?

虚拟化的概念是指能够在一个OS虚拟化系统上运行多个容器。容器只能看到授权给它的设备/资源。在AWS这样的托管计算平台上,你实际上是运行在一个管理程序之下,它管理运行你的操作系统和容器的VM。

Coinbase:我们为什么不用Kubernetes? 2

简图

虚拟化使当今的容器世界成为可能。如果没有虚拟化能力,那么现在是不可能使硬件资源在容器中运行多个应用程序的。

容器编排平台(Mesos、Kubernetes、Docker Swarm)有什么问题需要解决?

容器编排平台解决以下几类问题:

  • 托管/标准化部署工具(部署)
  • 根据一些定义好的启发式规则扩展应用程序(横向扩展)
  • 当出现故障时重新调度/移动容器(自愈)

有些平台可能声称他们有其他特性,如存储编排、秘密/配置管理和自动装箱。但实际上,如果要把它们应用于大规模安装,就需要大量的投资,要么是在分支/定制方面,要么是在集成与分离方面。

例如,大多数运行大型容器编排平台的人都无法使用其内置的秘密或配置管理。这些原语通常不是为几十个团队中的几百名工程师设计或构建的,而且通常不包括能够让他们稳健地管理、拥有和操作应用程序所需的控件。对于提供更强保证和控制(更不用说扩展)的系统,人们通常会把秘密管理和配置管理分开。

类似地,对于服务发现和负载均衡,将其分离出来并运行Overlay或抽象控制平面是很常见的。人们经常会部署Istio为Kubernetes处理这个问题。管理和运行Istio并不是一项简单的任务,许多现代化集群宕机都是由对这个控制平面/服务网格的错误配置以及对其细节缺乏理解造成的。

我们用什么作为容器编排平台?

我们的容器编排平台是Odin + AWS ASG(自动伸缩组)。当你在Codeflow(我们用于部署的内部UI)上点击Deploy时,Odin将通过Codeflow的API调用被激活。Odin启动一个Step Function并开始部署应用程序。AWS上会新启动一个VM并将其加载到新的ASG中,软件都是从各种内部位置获取的,负载均衡器开始对这些新实例进行健康检查,最终,流量以蓝/绿方式切换到负载均衡器后面新ASG中的新主机上。

我们的容器编排平台非常简单。我们启用了与Kubernetes相同的关键特性:Codeflow中有一个Deploy + Rollback按钮,基于一些定义好的启发式规则(我们支持自定义AWS指标或标准CPU指标)进行伸缩,并能在ASG中的VM宕掉或变得不健康时重新调度/移动容器。

为了处理秘密和配置管理,我们构建了一个动态配置服务,它为所有内部客户提供库,第95百分位延迟为6ms。它后台基于DynamoDB,每分钟可以为成百上千个同步和异步方法请求提供服务。

为了处理服务发现和负载平衡,我们使用了Route53(DNS)、ALB(应用程序负载均衡器)和gRPC客户端负载均衡(可以是原生的,也可以通过Envoy)。我们预计今年晚些时候还会开展进一步的工作。

我们为什么不使用Kubernetes?

运行Kubernetes不能解决任何客户(工程)问题。相反,运行Kubernetes实际上会产生一系列新的问题。

  1. 我们就需要组建一支全职的计算团队。尽管随着发展,我们可能会这样做,但运行Kubernetes的话,我们立即就需要这样做,这样才能集中精力构建数十个集群(可能每个团队/组织都是分开的),开始研究/构建封装/胶水工具,开始构建抽象控制平面/服务网格,等等。
  2. 保护Kubernetes安全不是一项轻松简单或易于理解的操作。为了使我们能够拥有/运营Kubernetes,我们整个平台中使用的工具和控件(Odin、ASG、Step Deployer——以及它们依赖的东西)都要有。构建相同的原语,提供与目前相同的安全级别,对于(未来的)计算团队和我们的安全团队来说都是一项非常大的投资。
  3. 托管的Kubernetes(AWS的EKS、谷歌的GKE)还处于起步阶段,拥有/运营Kubernetes的大多数挑战都尚未解决(如果有什么问题的话,就更困难了)。在AWS,为了运行EKS,他们正在扩充支持/运营团队,而在谷歌,GKE中断数小时的情况并不少见(参见这里)。你只是把一些运营问题和挑战转移给了另一个运营团队(而可见性大幅降低了)。
  4. 集群升级和管理要比我们现在所做的操作多很多。合理运行Kubernetes的唯一方法是让团队/组织拥有自己的集群(类似于让他们有自己的AWS帐户或GCP项目)。即使有了Istio和相关工具,升级集群和修补漏洞也不是一件容易的事。通常,你必须构建/运行一个辅助集群,将所有的应用程序故障转移,然后在升级后进行故障恢复。目前,这个原语还没有构建到任何抽象中。虽然托管集群(GKE)中可能提供了,但它并不总是像你预期的那样工作,并且启动后回滚通常也没有得到很好的处理。
  5. 如今,我们没有这个负担。我们运行在一个坚固的操作系统上,几乎没什么依赖。我们的AMI上线是从开发开始,然后经过数周的测试再继续。如果需要回滚,只需要简单地更改一行代码就可以实现。平均而言,我们每个月花在与这个领域密切相关的任何事情上的时间都不到5小时。

关于拥有/运营Kubernetes & Istio的复杂性,下面是其他的一些参考资料:

  • OpenStack(规模为900个节点时的Kubernetes问题)
  • OpenAI(将Kubernetes扩展到2500个节点)
  • Civis(破坏Kubernetes:我们如何破坏并修复我们的K8s集群)
  • k8s.af

Kubernetes安全保护

让我们看一下,在一个保存了超过80亿美元加密资产的企业中,运行Kubernetes并保证其安全的复杂性。

组件

保证Kubernetes集群安全的基础知识众所周知,但是如果需要对其中的每一项都做深入的研究,复杂性就来了。保护所有系统组件(etcd、kubelet)、API服务器和任何抽象/Overlay(Istio)的安全,就有许多东西需要理解、测试和保护。考虑到攻击面增加,必须要深入研究命名空间、seccomp、SELinux、cgroups等。Kubernetes非常大,它有自己的CIS基准测试和InSpec套件(谢天谢地)。

漏洞

下面是一个简短的列表,可以作为漏洞研究的起点:

  • CVE-2019–5736(8.6 高):使攻击者可以改写主机的二进制文件runc(从而获得主机root访问权限)。
  • CVE-2019–11246(6.5 中):如果容器中的二进制文件tar存在恶意代码,它就可以运行任何代码并输出意料之外的不良后果。
  • CVE-2019–11253(7.5 高) :使授权用户可以发送恶意YAML或JSON载荷,导致API服务器占用过多的CPU或内存,可能会导致崩溃或服务不可用。

概述

Kubernetes是一个功能强大的PaaS工具包,具有许多安全相关的选项,可以支持各种部署场景。当它成为大家普遍认可的PaaS选项时,从安全的角度来看,这是非常有价值的,因为这些安全选项中的大多数都可以抽象出来,并且必须配备辅助系统以支持其使用。

从根本上说,Kubernetes是为工作负载编排而设计的——信任并不是Kubernetes中封装或部件产生的原因;多租户的目的是为了打包,而不是为了支持拓展权限边界。它提供了几个层,你可以选择在其中为不同的可执行性设置适当的边界。其中一些边界是内置的,而其他边界只是用于集成其他工具来辅助管理的集成点。下面是用于隔离工作负载的一些原语,有的Kube提供了,有的没有提供。

Coinbase:我们为什么不用Kubernetes? 3

控制平面(AWS账号/ GCP项目)

Kubernetes集群的操作是在提供给它们的服务和网络中进行的,自然地,就会与AWS/GCP控制平面进行一些交互,比如配置Ingress负载均衡器、访问存储在KMS中的秘密等。随着时间的推移,团队会发展壮大,拥有独立的帐户、项目并进一步的隔离。一个单独的AWS帐户或GCP项目是实现完全IAM分割(segmentation)的主要原语。

另一方面,Kubernetes集群需要在一个AWS帐户内操作(即使与其他地方的集群联合)。这限制了分割选项和灵活性。我们可以为每个团队或服务提供一个集群,但我们就无法利用Kubernetes的许多好处了,并且会带来新的管理问题,比如对所有这些集群进行元编排。

集群 &节点 &Pod&容器

集群

集群主(API)服务器是一个次控制平面(AWS控制平面之外),我们也需要做好安全防护。服务帐户和访问范围(容器可以假定要访问集群内外的资源)与AWS的IAM一样复杂,并且需要严格地相互映射,以便中时断不会影响AWS控制平面。

节点

底层节点的操作系统必须像我们现在所做的那样进行维护。事实上,我们的操作系统与谷歌用于GKE的基本操作系统非常相似。虽然将我们的操作系统转到Kubernetes不必做任何修改,但我们也不会得到任何东西。

Pod

在集群中创建Pod,以及定义它们创建时必须满足哪些标准的规则,都是通过PodSecurityPolicy完成的,它的运作方式类似于Salus和我们现在使用的一致性管理工具。要实现干净利落的集成,我们将需要做大量的集成工作,并投资于附加的开源依赖项。

Pod通过网络策略相互隔离,就像我们现在使用安全组和/或内部服务框架所做的那样。但在Kubernetes领域,Pod相互通信所需的标识、身份验证和授权涉及大量的支持技术,如用于节点级以下标识格式和认证的SPIFFE&SPIRE,用于授权控制的Envoy,Istio authN和Z编排,OPA授权策略。对于其中的每一项技术,要将其标准化并应用到生产中都需要付出很大的努力。

容器

容器不是安全边界,而是资源边界。为了定义容器的安全边界,需要深入研究自定义内核命名空间、系统调用过滤、强制访问控制框架和/或为容器设计的基于VM的隔离技术,如gVisor

目前,我们在这个领域的投入还不太多,因为我们还没有采用多租户的方式。如果我们转向多租户模型,我们将不得不立即进行大量的投资,通过主机/VM隔离技术保证类似分类的Pod/容器在相同的节点上运行,不会相互干扰。

我们何时会运行Kubernetes?Kubernetes在我们未来的计划中吗?

当更高级的容器编配平台有重要的用例时,我们可能会首先查看问题声明。如果该平台很容易添加到我们现有的平台上:我们可能会首先访问它,然后从那里开始探索/了解。如果我们认为扩展/添加到我们的平台上不合理,那么我们将访问所有可能的选项——而不仅仅是Kubernetes。更可能的情况是,我们会先了解AWS的托管服务,如Fargate和ECS,然后再了解Kubernetes。

如果提供Kubernetes(或任何其他容器编配平台)我们的工程师可以从中获得显著的收益时,我们才会研究提供它们。现在,提供Kubernetes并没有什么明显的好处。如果Kubernetes提供了许多我们现在没有的新特性,偿清了技术债务,或者我们的客户需要它们可以提供的新功能,而我们在可预见的将来都无法提供,这种情况就可能会改变。如果妨碍其进入我们当前平台的因素发生了显著的变化,并且它有了明显的独特之处,那么我们也会研究提供一个不同的平台。

如果/当我们现有的平台达到了极限,由于缺少客户需要的特性而负担太重或者可以预见将会负担太重,而扩展我们平台的工作又过于繁重,或者中断太多违反我们的SLA,那么我们可能会重新审视不同的容器编排平台。

如果/当我们失去了主要上游依赖方(如AWS或ASG)的支持,我们也会考虑其他选项。

这是我们可能会选择研究另一个容器编排平台的几个理由。目前,我们还没有构建、拥有、运营Kubernetes的计划。

Kubernetes不是解决了像再平衡/自愈、自动扩展和服务发现等多方面的问题吗?我们现在是如何解决这些问题的?

在规模较小时,Kubernetes解决了这些问题中的大部分,而且也不是很麻烦。在规模较大时,就需要更多的思考和胶水代码,并在几乎所有的东西上增加封装器/安全保护,以使其能够安全可靠地工作。通常,如前所述,人们倾向于添加Istio这样的服务网格来支持更高级的特性/需求。

目前,我们是这样解决的:

  • 借助Odin和ASG实现再平衡/自愈;
  • 借助DNS和Envoy实现服务发现。

Kubernetes有存储编排,我们目前还没有,我们应该有吗?

现在,Coinbase有两个主要的有状态应用程序——区块链节点交易引擎,它们可能是存储编排等特性的潜在用例。对于前者(区块链节点),存储的使用定制化程度很高,我们构建了一个自定义的部署器,为它们提供所需的特性。对于后者(交易引擎),我们依赖可靠性(SRE)团队为他们的一些特定挑战提供支持。

虽然Kubernetes内置的存储编排对于区块链节点和交易引擎来说都是一个很好的起点,但是我们在底层技术中遇到的很多问题仍然存在。

如果Kubernetes不是容器编排平台的未来,那么什么才是?

对于部分应用程序,我们将探索并迁移到更高级的抽象服务。我们将探讨把Fargate和ECS作为这方面的候选者。目前的首要原因是利用率和成本的增加——这两者都不是很以客户为中心。我们可以选择再等等,到我们有更多以客户为中心的理由时才实施。

以客户为中心的潜在问题可能是部署时间、部署模式(除了金丝雀之外)、比目前更复杂的服务网格需求,或者在现有的工具中构建不可能/不合理但可以添加到Fargate或ECS的特定改进/特性。这些是一些潜在的以客户为中心的问题,这些问题可能会有,但目前还不知道或没有发现。

在理想情况下,向另一种底层容器技术的转移是不可见的,因为与它们交互的工具不会从根本上改变。迁移到不同的平台可能会揭示出关于现有系统隐藏的或未知的期望。如何在过渡环境和生产环境部署和调试服务仍然是抽象的,但是可能提供了一些现在没有的特性。

Coinbase:我们为什么不用Kubernetes? 4

我/我们讨厌Kubernetes吗?Kubernetes是一个失败的容器平台吗?

不。尽管存在挑战,但它是一个了不起的工具。Kubernetes已经将我们的行业推向了一个越来越积极的方向。随着Kubernetes进入v1版本,KnativeFargateCloud Run的开发正在不断提高抽象级别,并解决管理Kubernetes的潜在挑战。未来是光明的。随着这些潜在的挑战得到解决,许多现存的问题未来可能会得到缓解。

原文链接:

Container technologies at Coinbase