本文要点:容器编排平台是一项复杂而令人惊叹的技术,它可以帮助一些企业和团队解决一系列的问题。然而,我们经常忽略的是,容器技术还带来了一系列的挑战,企业只有克服这些挑战才能避免失败。
https://github.com/hjacobs/kubernetes-failure-stories
在讨论现状之前,让我们先了解下时至今日这项技术的发展历程。
1980 年代:chroot
1990 年代:jail
2000 年代(早期):jail > FreeBSD
2000 年代(中期):cgroups
2000 年代(后期):LXC(Linux 容器)
2010 年代(早期):Docker
2010 年代(后期):Kubernetes
如果想进一步了解其历史,请查阅 Enterprise Docker 第七章。
https://www.oreilly.com/library/view/enterprise-docker/9781491994986/
让我们从 10 年前说起,那时还没有现如今大家都知道的容器。那个时候,我们没有 / 不使用 docker、rkt 或任何其他主流的容器封装器 / 服务。为了将应用程序从源代码打包为生产部署,大多数大型公司都在构建内部系统。工程师在他们机器上运行的东西通常不是在生产环境中运行的东西,或者即使是在生产环境中运行的东西,也通常是以一种深度定制化而且非常复杂的方式一次性构建 / 打包的。
使用内部系统打包和部署应用程序需要一个大型运营团队。通常,该团队隶属于负责管理打包 / 构建流程、部署和部署后工作的平台或基础设施组织。这些角色的职责通常以操作型工作为主,包括主机故障排除、诊断 OS 补丁 / 升级中的特定依赖问题等。部署后的工作包括容量规划、订购更多服务器、上架 / 安装以及升级上面的软件,几乎不涉及自动编排。
幸运的话,有一些常规过程可以用来构建一个“黄金镜像”(想想 Hashicorp 的 Packer。这个镜像有详细的文档,甚至可能被编码,并由 Hudson(在 Jenkins 之前)这样的持续集成系统运行。这些镜像可以手动或借助某些配置管理工具自动分发到你的系统中,然后以某种顺序启动(比如用 Parallel SSH 或类似的方式)。
https://www.packer.io/
https://en.wikipedia.org/wiki/Hudson_(software)
在过去的十年里,一切都变了。我们不再使用庞大的单体应用程序,而是将服务分解为许多离散的、低耦合的部件。我们从必须构建 / 拥有自己的计算,转为拥有托管服务或公有云服务,而这个过程只需点击几下鼠标和一张信用卡。我们从垂直扩展应用程序,转为重构它们实现水平扩展。所有这一切都是同时发生的,社会也发生了变化:每个人的口袋里都有手机,网络速度在提高,全球范围内的网络延时在下降,从预约遛狗者到商品化的视频会议,一切都在网上进行。
2009 年,AWS 提供的服务还相当有限。AWS 的 EC2 服务 2008 年才完成 beta 测试并开始提供 SLA。相比之下,GCP 直到 2013 年才正式推出计算服务。
企业选择容器化他们的应用程序,是为了以快速、安全、可靠的方式提高工程输出 / 开发人员的生产力。容器化是不同于构建镜像的另一个选项,尽管有时可以将容器构建到镜像中,但这超出了本文的范围参见这里。
https://thenewstack.io/bakery-foundation-container-images-microservices/
容器使工程师在本地开发、测试和运行应用程序的方式与在其他环境(过渡环境和生产环境)中运行的方式相同或类似。容器允许描述依赖绑定关系,可以是显式的,也可以是隐式的(操作系统总是包含服务所依赖的包 $foo)。容器允许更小的服务封装和资源定义(使用 X CPU 和 Y GB 内存)。容器让你可以考虑水平伸缩应用程序,而不是垂直伸缩应用程序,从而实现更健壮的架构。
其中一些观点可能还需要进一步地讨论。为了推动对话,这些观点都有点过于大胆和发散,因为这并不是在讨论容器化或服务化(service-ification)的利弊(例如,将单个应用程序拆分为许多更小的独立运行的服务)。
虚拟化的概念是指能够在一个 OS 虚拟化系统] 上运行多个容器。容器只能看到授权给它的设备 / 资源。在 AWS 这样的托管计算平台上,你实际上是运行在一个管理程序之下,它管理运行你的操作系统和容器的 VM。
简图
虚拟化使当今的容器世界成为可能。如果没有虚拟化能力,那么现在是不可能使硬件资源在容器中运行多个应用程序的。
容器编排平台解决以下几类问题:
托管 / 标准化部署工具(部署);
根据一些定义好的启发式规则扩展应用程序(横向扩展);
当出现故障时重新调度 / 移动容器(自愈)。
有些平台可能声称他们有其他特性,如存储编排、秘密 / 配置管理和自动装箱。但实际上,如果要把它们应用于大规模安装,就需要大量的投资,要么是在分支 / 定制方面,要么是在集成与分离方面。
例如,大多数运行大型容器编排平台的人都无法使用其内置的秘密或配置管理。这些原语通常不是为几十个团队中的几百名工程师设计或构建的,而且通常不包括能够让他们稳健地管理、拥有和操作应用程序所需的控件。对于提供更强保证和控制(更不用说扩展)的系统,人们通常会把秘密管理和配置管理分开。
类似地,对于服务发现和负载均衡,将其分离出来并运行 Overlay 或抽象控制平面是很常见的。人们经常会部署 Istio 为 Kubernetes 处理这个问题。管理和运行 Istio 并不是一项简单的任务,许多现代化集群宕机都是由对这个控制平面 / 服务网格的错误配置以及对其细节缺乏理解造成的。
我们的容器编排平台是 Odin + AWS ASG(自动伸缩组)。当你在 Codeflow(我们用于部署的内部 UI)上点击 Deploy 时,Odin 将通过 Codeflow 的 API 调用被激活。Odin 启动一个 Step Function 并开始部署应用程序。AWS 上会新启动一个 VM 并将其加载到新的 ASG 中,软件都是从各种内部位置获取的,负载均衡器开始对这些新实例进行健康检查,最终,流量以蓝 / 绿方式切换到负载均衡器后面新 ASG 中的新主机上。
https://github.com/coinbase/odin
https://blog.coinbase.com/scaling-developer-productivity-d23ce491f869
我们的容器编排平台非常简单。我们启用了与 Kubernetes 相同的关键特性:Codeflow 中有一个 Deploy + Rollback 按钮,基于一些定义好的启发式规则(我们支持自定义 AWS 指标或标准 CPU 指标)进行伸缩,并能在 ASG 中的 VM 宕掉或变得不健康时重新调度 / 移动容器。
为了处理秘密和配置管理,我们构建了一个动态配置服务,它为所有内部客户提供库,第 95 百分位延迟为 6ms。它后台基于 DynamoDB,每分钟可以为成百上千个同步和异步方法请求提供服务。
为了处理服务发现和负载平衡,我们使用了 Route53(DNS)、ALB(应用程序负载均衡器)和 gRPC 客户端负载均衡(可以是原生的,也可以通过 Envoy)。我们预计今年晚些时候还会开展进一步的工作。
运行 Kubernetes 不能解决任何客户(工程)问题。相反,运行 Kubernetes 实际上会产生一系列新的问题。
我们就需要组建一支全职的计算团队。尽管随着发展,我们可能会这样做,但运行 Kubernetes 的话,我们立即就需要这样做,这样才能集中精力构建数十个集群(可能每个团队 / 组织都是分开的),开始研究 / 构建封装 / 胶水工具,开始构建抽象控制平面 / 服务网格,等等。
保护 Kubernetes 安全不是一项轻松简单或易于理解的操作。为了使我们能够拥有 / 运营 Kubernetes,我们整个平台中使用的工具和控件(Odin、ASG、Step Deployer——以及它们依赖的东西)都要有。构建相同的原语,提供与目前相同的安全级别,对于(未来的)计算团队和我们的安全团队来说都是一项非常大的投资。
托管的 Kubernetes(AWS 的 EKS、谷歌的 GKE)还处于起步阶段,拥有 / 运营 Kubernetes 的大多数挑战都尚未解决(如果有什么问题的话,就更困难了)。在 AWS,为了运行 EKS,他们正在扩充支持 / 运营团队,而在谷歌,GKE 中断数小时的情况并不少见(参见这里。你只是把一些运营问题和挑战转移给了另一个运营团队(而可见性大幅降低了)。
集群升级和管理要比我们现在所做的操作多很多。合理运行 Kubernetes 的唯一方法是让团队 / 组织拥有自己的集群(类似于让他们有自己的 AWS 帐户或 GCP 项目)。即使有了 Istio 和相关工具,升级集群和修补漏洞也不是一件容易的事。通常,你必须构建 / 运行一个辅助集群,将所有的应用程序故障转移,然后在升级后进行故障恢复。目前,这个原语还没有构建到任何抽象中。虽然托管集群(GKE)中可能提供了,但它并不总是像你预期的那样工作,并且启动后回滚通常也没有得到很好的处理。
如今,我们没有这个负担。我们运行在一个坚固的操作系统上,几乎没什么依赖。我们的 AMI 上线是从开发开始,然后经过数周的测试再继续。如果需要回滚,只需要简单地更改一行代码就可以实现。平均而言,我们每个月花在与这个领域密切相关的任何事情上的时间都不到 5 小时。
让我们看一下,在一个保存了超过 80 亿美元加密资产的企业中,运行 Kubernetes 并保证其安全的复杂性。
https://blog.coinbase.com/our-focus-on-the-institutional-space-5c8e87332268
保证 Kubernetes 集群安全的基础知识众所周知,但是如果需要对其中的每一项都做深入的研究,复杂性就来了。保护所有系统组件(etcd、kubelet)、API 服务器和任何抽象 /Overlay(Istio)的安全,就有许多东西需要理解、测试和保护。考虑到攻击面增加,必须要深入研究命名空间、seccomp、SELinux、cgroups 等。Kubernetes 非常大,它有自己的 CIS 基准测试和 InSpec 套件(谢天谢地)。
https://www.cisecurity.org/benchmark/kubernetes/
https://github.com/dev-sec/cis-kubernetes-benchmark
下面是一个简短的列表,可以作为漏洞研究的起点:
CVE-2019–5736(8.6 高):使攻击者可以改写主机的二进制文件 runc(从而获得主机 root 访问权限)。
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5736
CVE-2019–11246(6.5 中):如果容器中的二进制文件 tar 存在恶意代码,它就可以运行任何代码并输出意料之外的不良后果。
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11246
CVE-2019–11253(7.5 高) :使授权用户可以发送恶意 YAML 或 JSON 载荷,导致 API 服务器占用过多的 CPU 或内存,可能会导致崩溃或服务不可用。
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11253
Kubernetes 是一个功能强大的 PaaS 工具包,具有许多安全相关的选项,可以支持各种部署场景。当它成为大家普遍认可的 PaaS 选项时,从安全的角度来看,这是非常有价值的,因为这些安全选项中的大多数都可以抽象出来,并且必须配备辅助系统以支持其使用。
从根本上说,Kubernetes 是为工作负载编排而设计的——信任并不是 Kubernetes 中封装或部件产生的原因;多租户的目的是为了打包,而不是为了支持拓展权限边界。它提供了几个层,你可以选择在其中为不同的可执行性设置适当的边界。其中一些边界是内置的,而其他边界只是用于集成其他工具来辅助管理的集成点。下面是用于隔离工作负载的一些原语,有的 Kube 提供了,有的没有提供。
Kubernetes 集群的操作是在提供给它们的服务和网络中进行的,自然地,就会与 AWS/GCP 控制平面进行一些交互,比如配置 Ingress 负载均衡器、访问存储在 KMS 中的秘密等。随着时间的推移,团队会发展壮大,拥有独立的帐户、项目并进一步的隔离。一个单独的 AWS 帐户或 GCP 项目是实现完全 IAM 分割(segmentation)的主要原语。
另一方面,Kubernetes 集群需要在一个 AWS 帐户内操作(即使与其他地方的集群联合)。这限制了分割选项和灵活性。我们可以为每个团队或服务提供一个集群,但我们就无法利用 Kubernetes 的许多好处了,并且会带来新的管理问题,比如对所有这些集群进行元编排。
集群
集群主(API)服务器是一个次控制平面(AWS 控制平面之外),我们也需要做好安全防护。服务帐户和访问范围(容器可以假定要访问集群内外的资源)与 AWS 的 IAM 一样复杂,并且需要严格地相互映射,以便中时断不会影响 AWS 控制平面。
节点
底层节点的操作系统必须像我们现在所做的那样进行维护。事实上,我们的操作系统与谷歌用于 GKE 的基本操作系统非常相似。虽然将我们的操作系统转到 Kubernetes 不必做任何修改,但我们也不会得到任何东西。
Pod
在集群中创建 Pod,以及定义它们创建时必须满足哪些标准的规则,都是通过 PodSecurityPolicy 完成的,它的运作方式类似于 Salus 和我们现在使用的一致性管理工具。要实现干净利落的集成,我们将需要做大量的集成工作,并投资于附加的开源依赖项。
https://github.com/coinbase/salus
Pod 通过网络策略相互隔离,就像我们现在使用安全组和 / 或内部服务框架所做的那样。但在 Kubernetes 领域,Pod 相互通信所需的标识、身份验证和授权涉及大量的支持技术,如用于节点级以下标识格式和认证的 SPIFFE&SPIRE,用于授权控制的 Envoy,Istio authN 和 Z 编排,OPA 授权策略。对于其中的每一项技术,要将其标准化并应用到生产中都需要付出很大的努力。
容器
容器不是安全边界,而是资源边界。为了定义容器的安全边界,需要深入研究自定义内核命名空间、系统调用过滤、强制访问控制框架和 / 或为容器设计的基于 VM 的隔离技术,如 gVisor。
https://github.com/google/gvisor
目前,我们在这个领域的投入还不太多,因为我们还没有采用多租户的方式。如果我们转向多租户模型,我们将不得不立即进行大量的投资,通过主机 /VM 隔离技术保证类似分类的 Pod/ 容器在相同的节点上运行,不会相互干扰。
当更高级的容器编配平台有重要的用例时,我们可能会首先查看问题声明。如果该平台很容易添加到我们现有的平台上:我们可能会首先访问它,然后从那里开始探索 / 了解。如果我们认为扩展 / 添加到我们的平台上不合理,那么我们将访问所有可能的选项——而不仅仅是 Kubernetes。更可能的情况是,我们会先了解 AWS 的托管服务,如 Fargate 和 ECS,然后再了解 Kubernetes。
如果提供 Kubernetes(或任何其他容器编配平台)我们的工程师可以从中获得显著的收益时,我们才会研究提供它们。现在,提供 Kubernetes 并没有什么明显的好处。如果 Kubernetes 提供了许多我们现在没有的新特性,偿清了技术债务,或者我们的客户需要它们可以提供的新功能,而我们在可预见的将来都无法提供,这种情况就可能会改变。如果妨碍其进入我们当前平台的因素发生了显著的变化,并且它有了明显的独特之处,那么我们也会研究提供一个不同的平台。
如果 / 当我们现有的平台达到了极限,由于缺少客户需要的特性而负担太重或者可以预见将会负担太重,而扩展我们平台的工作又过于繁重,或者中断太多违反我们的 SLA,那么我们可能会重新审视不同的容器编排平台。
如果 / 当我们失去了主要上游依赖方(如 AWS 或 ASG)的支持,我们也会考虑其他选项。
这是我们可能会选择研究另一个容器编排平台的几个理由。目前,我们还没有构建、拥有、运营 Kubernetes 的计划。
Kubernetes 不是解决了像再平衡 / 自愈、自动扩展和服务发现等多方面的问题吗?我们现在是如何解决这些问题的?
在规模较小时,Kubernetes 解决了这些问题中的大部分,而且也不是很麻烦。在规模较大时,就需要更多的思考和胶水代码,并在几乎所有的东西上增加封装器 / 安全保护,以使其能够安全可靠地工作。通常,如前所述,人们倾向于添加 Istio 这样的服务网格来支持更高级的特性 / 需求。
目前,我们是这样解决的:
借助 Odin 和 ASG 实现再平衡 / 自愈;
借助 DNS 和 Envoy 实现服务发现。
Kubernetes 有存储编排,我们目前还没有,我们应该有吗?
现在,Coinbase 有两个主要的有状态应用程序——区块链节点和交易引擎,它们可能是存储编排等特性的潜在用例。对于前者(区块链节点),存储的使用定制化程度很高,我们构建了一个自定义的部署器,为它们提供所需的特性。对于后者(交易引擎),我们依赖可靠性(SRE)团队为他们的一些特定挑战提供支持。
虽然 Kubernetes 内置的存储编排对于区块链节点和交易引擎来说都是一个很好的起点,但是我们在底层技术中遇到的很多问题仍然存在。
对于部分应用程序,我们将探索并迁移到更高级的抽象服务。我们将探讨把 Fargate 和 ECS 作为这方面的候选者。目前的首要原因是利用率和成本的增加——这两者都不是很以客户为中心。我们可以选择再等等,到我们有更多以客户为中心的理由时才实施。
以客户为中心的潜在问题可能是部署时间、部署模式(除了金丝雀之外)、比目前更复杂的服务网格需求,或者在现有的工具中构建不可能 / 不合理但可以添加到 Fargate 或 ECS 的特定改进 / 特性。这些是一些潜在的以客户为中心的问题,这些问题可能会有,但目前还不知道或没有发现。
在理想情况下,向另一种底层容器技术的转移是不可见的,因为与它们交互的工具不会从根本上改变。迁移到不同的平台可能会揭示出关于现有系统隐藏的或未知的期望。如何在过渡环境和生产环境部署和调试服务仍然是抽象的,但是可能提供了一些现在没有的特性。
不。尽管存在挑战,但它是一个了不起的工具。Kubernetes 已经将我们的行业推向了一个越来越积极的方向。随着 Kubernetes 进入 v1 版本,Knative、Fargate 和 Cloud Run 的开发正在不断提高抽象级别,并解决管理 Kubernetes 的潜在挑战。未来是光明的。随着这些潜在的挑战得到解决,许多现存的问题未来可能会得到缓解。
参考阅读:
https://blog.coinbase.com/container-technologies-at-coinbase-d4ae118dcb6c
InfoQ 读者交流群上线啦!各位小伙伴可以扫描下方二维码,添加 InfoQ 小助手,回复关键字“进群”申请入群。大家可以和 InfoQ 读者一起畅所欲言,和编辑们零距离接触,超值的技术礼包等你领取,还有超值活动等你参加,快来加入我们吧!
点个在看少个 bug 👇