Netflix容器化实践

在已有云原生基础设施中采用容器

新兴容器技术

Netflix自2008年起全力投入云迁移,并开始将其内部托管基础设施全部迁移到AWS(亚马逊网络服务)。如今,几乎所有Netflix服务都在AWS的虚拟机(VM)上运行。客户的目录浏览体验、内容推荐计算以及支付均由AWS提供支持。

多年来,Netflix帮助构建了许多云原生模式,例如松散耦合的微服务和不可变基础设施,这些已成为行业最佳实践。全面迁移到云对Netflix来说取得了巨大成功。尽管已经拥有成功的云原生架构,Netflix仍在投资容器技术。

容器技术使Netflix能够遵循许多已用于虚拟机的相同模式,但以一种更简单、更灵活且高效的方式实现。推动这一投资的一些因素包括:

  • 端到端应用打包 :用于本地开发的容器镜像与在生产环境中运行的容器镜像相同(或至少非常相似)。这种打包方式使开发人员能够在类似生产环境的条件下更轻松地构建和测试应用程序,从而提高可靠性并减少开发开销。

  • 灵活打包 :Netflix历来提供以Java虚拟机(JVM)为中心的开发和部署环境,使用将应用程序配置“固化”到其中的通用虚拟机镜像。对于非JVM应用程序,正确配置此镜像可能较为困难。容器镜像提供了一种简便的方法,可构建仅包含应用程序所需内容的应用程序专用镜像。

  • 更简单的云抽象 :部署Netflix应用程序到虚拟机时,需要选择一个大致合适大小的虚拟机实例类型,并配置其运行和管理应用程序。许多因素会影响最佳实例类型的选择,包括硬件(例如中央处理器、内存、磁盘)规格、定价、区域可用性以及高级功能支持(例如专用网络或存储功能)。对许多开发人员而言,这是一个令人困惑的以机器为中心的步骤,容易出错。容器通过提供仅需声明应用程序需求的以应用为中心的部署方式,使该过程变得更加简单。

  • 更快更高效的云资源 :容器是轻量级的,这使得构建和部署它们更加便捷,比使用虚拟机基础设施更快。此外,由于容器仅包含单个应用程序所需的组件,因此它们体积更小,可以在虚拟机上更密集地部署,从而减少整体基础设施占用空间。

这些因素并未改变Netflix现有云原生基础设施的模式或方法。相反,容器提高了开发人员的生产力,使他们能够更快地进行开发、部署和创新。容器在行业内也正逐渐成为部署和运行云原生应用的事实上的技术。对容器的投资确保了Netflix的基础设施与关键行业趋势保持一致。

尽管提升开发人员生产力的价值推动了公司的战略投资,但投资容器的一个重要实际原因是Netflix团队已经开始使用容器。这些团队不仅提供了如何从容器中受益的具体证据,还凸显了内部容器支持的不足。

Netflix容器的独特挑战

在许多公司,容器采用通常发生在构建新的绿地应用时,或作为更大规模基础设施重构的一部分,例如迁移到云环境或将单体应用拆分为微服务。Netflix的容器采用则有所不同,因为它是由已在云原生基础设施上运行的应用程序所驱动的。这一独特环境在多个方面影响了我们所构建技术的方式以及内部采用的管理方式:

  • 由于应用程序尚未进行重构,因此在迁移到容器时无需进行重大更改就显得尤为重要。
  • 由于Netflix的文化提倡自下而上的决策,因此并没有强制要求团队采用容器。因此,我们最初只专注于少数希望尝试容器并能从采用中获得显著收益的内部用户和使用场景。
  • 我们预计一些应用程序将继续在虚拟机中运行,而另一些则在容器中运行,因此确保它们之间的无缝连接非常重要。
  • 早期的容器采用使用场景包括传统的微服务和各种批处理作业。因此,目标是支持这两种类型的工作负载。
  • 由于应用程序将从稳定的AWS EC2(弹性计算云)环境迁移到运行在EC2之上的新容器管理层,因此提供适当的可靠性至关重要。

现有云基础设施中的容器

Netflix的独特需求促使我们开发了Titus,这是一个面向Netflix云基础设施的容器管理系统。Titus的设计侧重于以下几个关键领域:

  • 使现有的Netflix应用程序能够在容器中无需修改即可运行。
  • 使这些应用程序能够轻松使用现有的Netflix和AWS云基础设施及服务。
  • 在同一资源池上调度批处理和作业任务。
  • 高效且可靠地管理云容量。

Titus是在Apache Mesos之上构建的一个框架,Mesos是一个跨机器集群协调可用资源的集群管理系统。Mesos使我们能够控制我们认为重要的方面,例如调度和容器执行,同时处理诸如存在哪些机器以及有哪些可用资源等细节。此外,当时已有其他多家大型公司在大规模使用Mesos。其他开源容器管理系统,例如Kubernetes和Docker Swarm,在Titus开发期间也相继推出,它们提供了各自的容器调度和执行方式。鉴于上述特定需求,我们认为很快就会偏离这些系统的通用功能,从而限制其带来的优势。

Titus包含一个可复制的、通过选举产生主节点的调度器,称为Titus主控系统,负责将容器部署到大量名为Titus代理的EC2虚拟机上,而这些代理负责管理每个容器的生命周期。Zookeeper负责管理主节点选举,Cassandra用于持久化存储主控器的数据。

示意图0

Titus架构如图1所示。

在Titus中,工作通过作业规格来描述,其中详细说明了要运行的内容(例如容器镜像和入口点)、元数据(例如作业的用途及其所有者)以及运行所需的资源,如中央处理器、内存或调度约束(例如可用区均衡或主机亲和性)。作业规格被提交至主控器,包含多个任务,每个任务代表一个正在运行的应用程序实例。主控器将任务调度到Titus代理上,由Titus代理根据任务的作业规格启动容器。

设计以实现轻松采用容器

大多数Netflix微服务和批处理应用都是基于Netflix云基础设施、AWS服务或两者的一部分构建的。Netflix云基础设施由多种系统组成,为在云端运行的Netflix应用程序提供核心功能。例如,Eureka是一种服务发现系统,Ribbon是一种进程间通信库,它们提供了连接服务的机制。Atlas是一种时序遥测系统,Edda是一种针对云资源的索引服务,它们为服务的监控和分析服务提供了工具支持。这些系统中的许多都可作为开源软件使用。类似地,许多Netflix应用程序使用AWS服务,例如S3(简单存储服务)或SQS(简单队列服务)。

为了避免使用这些服务的应用程序在采用容器时需要进行更改,Titus与许多Netflix云和AWS服务集成,使容器化应用能够轻松访问和使用这些服务。通过这种方法,应用程序开发人员可以继续依赖这些现有系统,而无需采用功能类似但不同的替代基础设施。这与其他容器管理系统不同,后者通常提供自己的服务或使用新的、专用于容器的基础设施服务。

与现有云环境集成

在某些情况下,通过Titus实现对Netflix云基础设施系统的访问非常简单。例如,许多基于Java的平台服务客户端仅需要Titus在容器中设置特定的环境变量即可。这样做便可自动启用分布式配置服务、实时数据管道系统以及其他服务。

其他集成需要对基础设施服务本身进行更改,以便能够与Titus控制平面(通常是在EC2之外)通信,或理解容器级别的数据。例如,Eureka客户端已更新,以支持来自EC2虚拟机以及Titus容器的服务注册。类似地,健康检查轮询系统也进行了修改,以查询Titus并为容器提供健康检查轮询功能,而不仅仅是虚拟机。实例上的Atlas遥测代理也已修改,以收集和发送来自Titus代理的容器级系统指标(例如,中央处理器和内存使用)。此前,它仅收集整个主机的指标。

除了让Netflix应用程序更轻松地在容器中运行外,这些集成还降低了Netflix内部采用容器所需的学习成本。无论用户和团队使用的是虚拟机还是容器,他们都可以继续使用自己熟悉的工具和流程。例如,拥有现有Atlas遥测仪表板和警报的团队可以将其应用程序从虚拟机迁移到容器,同时保持其遥测和运维系统不变。

与Netflix云基础设施集成还使Titus开发团队无需专注于重建现有的内部云组件。在几乎所有情况下,与现有Netflix服务集成的工作量远比实现或引入该服务的新的容器专用版本要小得多。

我们选择利用Netflix的持续交付工具Spinnaker,而不是在Titus中实现各种部署策略(如红黑部署或滚动升级)。Spinnaker提供了cloud provider的概念,使其能够协调在Titus和EC2上的应用部署。除了为Titus提供熟悉的部署工具外,使用Spinnaker还使得专注于持续交付的Spinnaker团队能够实现部署编排逻辑,而Titus开发团队则可以专注于容器调度和执行。

当然,Netflix云的某些方面要么与Titus的工作方式不同,要么无法与Titus协同工作。通过与现有的Netflix组件集成,而不是要求使用新的组件,Titus提供的每一项集成都逐步降低了部分团队和用户的采用难度。

启用AWS集成

使容器采用更加便捷的另一个关键方面是实现对AWS服务的使用。许多Netflix应用程序都是围绕各种AWS服务(如S3或SQS)构建的。使用AWS服务需要正确的IAM(身份和访问管理)凭证来授权服务调用。

对于在EC2虚拟机中运行的应用程序,身份和凭证信息通过元数据服务从一个众所周知的IP地址提供的实例元数据获取。该元数据服务以EC2虚拟机为粒度提供凭证,这意味着同一虚拟机上的容器化应用必须共享主机的IAM凭证(这违反了最小权限原则),或者不使用AWS服务。

Titus使用在每个代理虚拟机上运行的元数据服务代理,为容器提供其特定的IAM凭证。Titus作业会声明所需IAM角色。当容器化应用向元数据服务IP发起IAM请求时,代理会通过主机路由规则拦截这些请求,并将请求重定向至自身。代理从Titus提供的任务配置信息中提取容器的IAM角色,然后使用主机的IAM assume role功能获取特定的IAM凭证,并将其返回给容器。

IAM assume role允许主体的IAM角色(在本例中为主机的)临时承担另一个主体(在本例中为容器的)的身份权限。该方法通过使用与EC2虚拟机相同的IAM角色,仅向容器提供其IAM凭证。除了IAM角色外,代理还向容器提供Titus实例身份信息,而非EC2身份信息。此类信息将由Eureka客户端等客户端库使用。

通用网络是关键

许多集成的重要推动力是通用网络基础设施。容器化应用与现有基础设施之间的无缝网络通信,消除了大量集成和采用过程中的障碍。

一种常见的容器网络解决方案是提供覆盖网络,该网络在现有网络之上创建一个独立的网络。这种方法具有吸引力,因为它解耦了两个网络,且不需要更改底层网络。然而,覆盖网络将容器的网络空间与现有网络隔离,需要通过网关或代理来连接它们。

另一种常见的方法是从主机IP地址向容器分配特定端口。虽然这使得容器的IP地址可以成为现有网络IP空间的一部分,但容器只能使用必须预先知晓的特定端口,限制了使用相同端口的容器共存,并且使容器暴露于主机的网络策略之下。此外,应用程序和基础设施必须以不同于处理虚拟机网络(IP地址)的方式来处理容器网络(端口)。由于许多Netflix系统知晓IP地址,但不了解端口,因此向必要系统中retrofitting额外的端口数据将会是一项重大工作。

Titus通过将容器连接到与虚拟机相同的AWS虚拟私有云(VPC)网络,为每个容器提供唯一的IP地址。使用通用VPC可让容器与虚拟机共享相同的IP地址空间,并使用相同的网络策略和功能,例如AWS安全组(SGs)。这种方法避免了管理端口的需求,因为每个容器都有自己的IP地址和完整的端口范围,以及网络网关。

在启动请求了可路由IP地址的容器时,Titus会将一个AWS ENI(弹性网络接口)附加到运行该容器的代理虚拟机上。附加ENI会在虚拟机上创建一个新的网络接口,可以从该接口分配多个IP地址。这些地址从与VPC中虚拟机相同的无类别域间路由(CIDR)范围内分配,这意味着容器和虚拟机可以直接访问彼此的IP地址。通过ENI分配IP地址,Titus无需管理VPC中可用的IP地址或直接修改VPC路由表。

当Titus准备启动一个新容器时,会为该容器创建一个网络命名空间,从弹性网络接口分配一个特定的IP地址,并使用虚拟以太网(veth)接口将容器的网络命名空间连接到主机的网络命名空间。主机上的路由规则会将该IP地址的所有流量路由到veth接口,而容器内部的路由规则则负责处理流入和流出的网络数据。

网络命名空间为容器配置分配的IP地址。

容器与虚拟机共享同一VPC网络的另一个优势是,它们可以使用通用的网络安全策略,例如提供虚拟防火墙功能的AWS安全组。每个弹性网络接口(ENI)都可以配置为使用一组安全组(SGs)防火墙规则,这些规则适用于进出该接口的所有流量。Titus会将容器请求的安全组(SGs)应用到与该容器关联的弹性网络接口(ENI)上,从而对其流量执行安全组防火墙规则。

可附加到虚拟机的弹性网络接口(ENI)数量有限,可能少于Titus可分配给该虚拟机的容器数量。为了更高效地使用弹性网络接口,Titus允许容器共享同一个弹性网络接口。然而,这种共享仅在容器使用相同的安全组配置时才可行,因为安全组(SGs)只能针对整个弹性网络接口进行配置。在此情况下,每个容器都将拥有一个唯一的IP地址,其流量将应用相同的防火墙规则。 示意图1 展示了共享弹性网络接口的一个示例。在该示例中,三个容器各自拥有从主机所附弹性网络接口分配的唯一IP地址。但容器1和容器2可以通过同一弹性网络接口传输流量,因为它们都仅使用安全组X。

Titus主控系统通过将安全组(SGs)和弹性网络接口(ENIs)视为两级资源来实现资源共享,从而能够将具有相同安全组配置的容器调度到现有的弹性网络接口之后。Titus还通过Linux流量控制为每个容器提供保证的网络带宽,根据容器请求的带宽设置令牌桶速率。这些网络功能避免了更改使应用程序迁移到容器,并让在容器或虚拟机内运行的应用程序对外部服务透明。

拥有通用网络基础设施有助于容器采用。需要连接到应用程序的外部服务无需关心应用程序所使用的技术。这种透明性使得现有系统能够更轻松地与容器化应用协同工作,并使同时包含虚拟机和容器的混合环境更易于管理。

支持批处理和服务工作负载

早期的Netflix容器使用场景包括批处理作业和服务应用程序。这些工作负载的不同之处在于,批处理作业旨在运行至完成,其运行时间可能从几秒到几天不等,而服务则需要“永久运行”。通过容器隔离,无需使用两个不同的系统来管理这两种类型的工作负载,而是可以将这些作业共置在同一集群中,从而提高整体集群利用率并减轻运维负担。

由于这两种作业类型具有不同的生命周期和管理需求,Titus主控系统将作业管理与任务分配的角色分开。作业管理负责每种作业类型的生命周期,例如批处理作业的最大运行时间和重试策略,或服务作业的扩展策略。任务分配则负责将任务分配到集群中的空闲资源,并且只需考虑任务所需的资源以及可用区均衡等调度约束。

对于任务分配,Titus使用Fenzo,这是一个用于Mesos框架的可扩展调度器库。Fenzo由Netflix开发,已被内部一个名为Mantis的流处理系统使用。Fenzo将任务分配给Mesos提供的资源提议,并支持多种可配置和扩展的调度目标。

Titus使用Fenzo的装箱算法,并结合其代理自动扩展功能,根据工作负载需求动态地扩展和缩减代理池。通过自动扩展代理池,Titus能够将空闲的已购买AWS预留实例释放给其他内部系统使用,限制使用AWS更昂贵的按需资源池。Fenzo支持fitness calculator的概念,允许调整调度决策的质量。Titus使用此功能来权衡调度速度和分配质量。

尽管Titus主控系统是一个单体式调度器,但这种解耦是一种有用的模式,因为它借鉴了两级调度器设计的某些方面。Fenzo充当集中式资源分配器,而作业管理器则允许对不同类型的作业进行解耦管理。这为每个作业管理器提供了统一的任务分配和代理管理策略,使其只需专注于作业生命周期。其他调度器也具有类似的职责分离,但Fenzo提供了一个丰富的应用程序编程接口,使作业管理器能够支持多种使用场景,并且有可能扩展以支持具有特殊需求的作业类型。

构建一个具有独立作业管理器的单体式调度器,不同于其他基于Mesos的系统,在那些系统中不同类型的作业由不同的Mesos框架管理。在这些情况下,每个框架都充当该作业类型的完整且独立的调度器。Titus被设计为Mesos集群上的唯一框架,从而避免了资源锁定以及多个框架可能导致的资源可见性问题,并允许利用完整的集群状态进行任务分配。避免这些问题有助于Titus主控系统更快地进行调度,并做出更优的任务分配决策。

异构容量管理

通过Titus使用容器的优势之一是它抽象了应用程序在虚拟机中进行的大量以机器为中心的管理。在许多情况下,用户只需告诉Titus“运行此应用程序”,而无需担心容器将在哪个位置或哪种实例类型上运行。然而,用户仍然希望获得一些关于其应用程序是否以及何时能够运行的保证。当运行具有不同目标和优先级的应用程序时,这些保证尤为重要。例如,微服务需要知道自身能够根据流量增加而扩展其容器数量,即使此时批处理作业可能正在同一集群上启动数千个任务并消耗大量资源。

此外,在EC2虚拟机中运行的应用程序已经习惯了AWS预留实例的概念,即提前购买即可保证虚拟机容量。为了在虚拟机和容器之间实现更一致的容量概念,Titus提供了层级和容量组的概念。

Titus目前提供两个层级:一个确保Titus代理虚拟机已启动并准备好运行容器,另一个则允许代理池根据工作负载的变化进行扩展和缩减,如图3所示。两者之间的主要区别在于启动容器所需的时间。第一个层级称为关键层级,在图中以实线边框表示。该层级使Titus能够立即启动容器,而无需等待EC2配置虚拟机。此层级以运行比应用程序当前所需更多的虚拟机为代价,优化了启动延迟。

第二层,称为灵活层级,仅提供足以处理当前工作负载的代理虚拟机(尽管会保留少量空闲实例作为余量,以避免过于激进的扩缩容)。通过在灵活层级中对代理虚拟机进行扩展,Titus能够减少资源消耗,但在需要先配置EC2虚拟机才能启动容器时,可能会导致任务的启动延迟。关键层级通常用于微服务,这些微服务的应用程序需要能够快速扩缩容以应对流量变化,或用于包含人工交互元素的批处理作业——例如,当用户期望作业给出实时响应时。代理的数量会根据需要进行扩缩容,如图中虚线边框所示。

示意图2

容量组是位于每一层之上的逻辑概念,可为一个应用程序或一组应用程序提供某种保证专用容量的数量。例如,一个微服务可能希望保证其能够扩展以满足峰值流量需求,或者一个批处理作业可能希望保证一定的任务吞吐量。在引入容量组之前,Titus上的应用程序可能会因其他应用程序耗尽所有集群资源而面临资源饥饿问题(通常这些资源饥饿是由提交作业的脚本中的错误或用户未考虑其作业所需容量所导致的)。此外,容量组帮助用户思考并明确其预期的容量需求,从而有助于指导Titus自身的容量规划。

结合容量组和层级,可使应用程序在成本(为应用程序预留可能未使用的代理资源)与可靠的任务执行(确保一个应用程序不会被另一个应用程序耗尽资源)之间进行权衡。这些概念在某种程度上类似于AWS的预留实例和按需实例。提供类似的容量概念,有助于容器采用,使用户能够以类似于虚拟机容量的方式来思考容器容量。

容器采用管理

对于大多数公司而言,开始采用新技术是困难的,Netflix也不例外。早期存在相互矛盾的容器采用问题:要么容器采用速度过快,导致Titus在成熟过程中出现规模和可靠性问题;要么容器采用仅限于少数使用场景,不足以证明投入的合理性。

尽管存在这些担忧且缺乏内部支持,已有少数团队开始采用容器并获得实际收益。这些早期用户提供了具体的使用场景,其中容器正在解决实际问题,因此初期重点放在了这些场景上。我们假设这些早期采用者将展示容器和Titus的价值,同时使我们能够构建一套可推广至未来使用的功能基础。希望这种方法能推动容器技术的自然普及,并缓解之前提到的担忧。

这些早期团队运行了各种临时的批处理作业,由于以下几个原因,他们成为Titus的初始用户是合理的。首先,他们的使用场景对Titus早期有限的可用性和性能具有较高的容忍度;Titus出现中断不会影响Netflix的客户体验。其次,这些团队已经在使用容器,因为他们数据处理框架和编程语言使得容器镜像成为一种便捷的打包方案。第三,许多用户是数据科学家,简化的接口满足了他们不想管理基础设施的需求。

其他团队也对Titus感兴趣,但被有意拒绝,因为他们并不适合使用Titus。这些团队要么无法从容器中获得显著收益,要么在此阶段有Titus难以满足的需求。

早期用户推动了我们对Netflix和AWS集成、调度性能以及系统可用性的关注,这帮助了其他早期采用者。随着这些方面的改进,我们开始着手服务作业支持。早期采用服务的包括多语言应用以及需要快速开发迭代非常重要的应用程序。

Titus目前每天启动约150,000个容器,其代理池由分布在多个AWS区域的数千台EC2虚拟机组成。随着使用量的增长,对运维的投入也随之增加。这一重点提升了Titus的可靠性与可扩展性,并增强了内部团队对其的信心。因此,Titus支持着越来越多样的内部使用场景。它为构成客户互动流媒体体验的服务、驱动内容推荐和购买决策的批处理任务,以及辅助工作室和内容制作的应用程序提供支持。

未来重点领域

到目前为止,Titus一直专注于为Netflix应用程序使用容器提供基本功能和功能。随着更多使用场景采用容器以及规模的扩大,开发重点预计将发生转移。Netflix计划投入的关键领域包括:

  • 尽管当前的容器技术提供了重要的进程隔离机制,但它们并未完全消除噪声邻居干扰。共享CPU资源可能导致上下文切换和缓存争用开销,并非所有共享的内核组件(例如网络文件系统内核模块)都具备容器感知能力。我们计划在用户空间和内核级别上提升Titus代理所提供的隔离性。

  • 对于批处理和服务应用程序,有许多高级调度器功能可以提高其可靠性和效率。例如,Titus目前在任务放置后不会重新调度任务。随着代理池更改或其他任务完成后,主控器最好重新考虑任务的最佳放置位置,例如改善其在可用区之间的平衡。

  • 除了更密集地部署EC2虚拟机外,Titus还可以通过更智能地利用资源来提高云资源使用率。例如,当容量组被分配但未被使用时,Titus可以在这些空闲资源上运行可抢占的、尽力而为的批处理任务,并在需要时将其让位于预留应用。

同样,Netflix在其已购买但空闲的EC2预留实例之间,通过一些内部使用场景进行资源协调。Titus可以通过建立一个低成本、临时性代理池,使更多内部团队更便捷地利用这些实例。

尽管只是一小部分Netflix的内部应用使用Titus,我们相信这种方法使Netflix能够快速采用并从中受益。尽管具体细节可能与Netflix相关,但通过与现有基础设施集成并携手合适的早期采用者来实现低摩擦的容器采用,对于任何希望采用容器的组织而言,都可能是一种成功的策略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值