Nomad是一个轻量级的工作负载编排工具。它简洁且易于使用、灵活且高性能,可以混合部署微服务、批处理、容器化和非容器化应用程序。Nomad开源项目由HashiCorp公司在2015年末启动,Nomad原生支持与Consul、Vault和Terraform无缝集成。本文的边缘计算平台项目是基于Nomad构建的分布式弹性基础架构。
目前Nomad提供的主要功能包括:
l 跨区域跨数据中心的多集群管理
l 跨动态基础设施的工作负载编排
l 多工作负载类型的混合应用部署
l 滚动更新
l 弹性伸缩
l 高扩展性的插件机制
与Consul和Vault集成后,可具服务发现、负载均衡、服务网格、机密管理、加密即服务、特权访问管理等功能。
Nomad核心概念
在描述架构之前,先介绍以下概念术语以帮助阐明所讨论的内容:
Job——作业,是由用户编写并提交的用于描述Nomad的工作负载的规范。作业是一种声明式的预期状态,用户定义作业的状态,而不用关心如何达到状态的过程。Nomad的责任是确保作业的实际状态符合用户期望的状态。一个作业由一个或多个任务组组成。
Task Group——任务组,一个任务组是必须一起运行的一组任务。例如,Web服务器可能要求负责日志传送的协同进程也始终在运行。任务组是调度的基本单位,这意味着整个任务组必须在同一客户端设备节点上运行并且不能拆分。
Driver——驱动,通过驱动的概念接入运行时,驱动指明任务是以何种方法执行的。比如Docker、QEMU、Java和静态二进制文件等。
Task——任务,是Nomad中最小的工作单元。任务由运行时驱动执行。任务在作业定义规范中需要指定使用的运行时驱动、驱动的配置、任务约束和所需资源等。
Client——客户端,是任务运行的设备节点。所有客户端都运行着Nomad代理。代理负责向Nomad Server注册、查看要分配的工作以及执行任务等。
Allocation——分配,是指将任务组分配给某个客户端执行。单个作业中可以定义数百个任务组。分配由Nomad服务器创建,作为评估期间做出的调度决策的一部分。
Evaluation——评估,是Nomad制定调度决策的机制。评估可能会导致分配发生变化。当作业的预期状态或客户端的实际状态发生变化时,Nomad会新建评估用来确定是否迁移作业。
Server——服务器,是整个集群的大脑。每个区域都有一组Nomad服务器,它们管理所有作业和客户端,运行评估并创建任务分配。服务器彼此之间同步数据并执行领导者(Leader)选举以确保高可用性。服务器跨区域联邦以使Nomad具有全局感知。
Regions和Datacenters——顾名思义,区域和数据中心。一个区域可以包括多个数据中心。多个区域之间可以邦联在一起。区域之间发出的请求将转发到适当的Nomad服务器。数据不会在区域之间进行复制。区域内的Nomad服务器管理着该区域内的状态并进行调度决策。
Bin Packing——装箱,是一种以最大程度地利用箱子的方式向箱子中填充物品的过程。具体到Nomad,客户端是就是箱子,物品就是任务组,调度器尝试在不耗尽任何维度资源的情况下使用所有设备节点的资源。
Nomad架构简介
Nomad采取C/S架构。在每个区域内,可以放置Nomad客户端和Nomad服务器。Nomad服务器负责接受来自用户的作业、管理Nomad客户端和计算任务放置。每个区域可能有来自多个数据中心的客户端,从而允许少量的Nomad服务器处理非常大规模的集群。Nomad的高层架构如图2.1所示。
图2.1 Nomad高层架构图
在某些情况下,考虑到可用性和可伸缩性,需要多个运行着的区域。边缘计算场景下普遍具有很多区域,每个区域内的边缘中心之间资源共享。Nomad支持将多个区域联邦。此时的高层架构如图2.2所示。区域彼此间完全独立,除访问控制列表外不共享应用数据和状态,但可以借助Consul进行地理位置故障转移。区域之间使用Gossip协议松散耦合,用户可以在任何区域提交作业或透明地查询任何区域的状态,请求将转发到要相应的服务器处理并返回结果。
图2.2 Nomad服务器多区域联邦
每个区域的服务器都是一个共识组的一部分。它们会共同参与选出一个Nomad服务器作为领导者来承担额外的工作职责,包括处理所有的查询和事务等。Nomad的调度器对并发是乐观的,所有服务器都并行地参与调度决策,所以领导者还需要提供额外的协调,以便安全地完成调度并确保作业不会超额认购。
每个区域预计有三个或五个服务器,这会在故障情况下的可用性与性能之间取得平衡,因为随着添加更多服务器,共识会逐渐变慢。但是,每个区域中执行任务的客户端数量没有限制。
客户端使用远程过程调用(RPC)进行和区域服务器之间的通信,进行自我注册,发送实时心跳,等待新分配以及更新分配状态。客户端向服务器注册以提供集群可用资源、属性和已安装的运行时驱动。服务器使用此信息来进行任务调度并创建分配将工作指派给客户端。
调度是Nomad的核心功能之一。在作业任务分配到客户端设备节点的过程中必须遵守作业声明的约束,并优化资源利用率。在Nomad中调度涉及四个主要的概念:作业、节点、分配和评估。作业也是要运行的任务组的声明性描述,这些任务受约束和需要资源的限制。用户通过使用Nomad CLI或REST API将作业提交到服务器。任务会被分配在Nomad客户端集群中的设备节点上运行。分配用于声明作业中的一组任务应在特定的某个节点上运行。调度则是确定适当分配的过程,并且是作为评估的一部分完成的。只要外部状态(期望状态或紧急状态)发生变化,就会创建评估。任务分配有为两个阶段,可行性检查和评估阶段。在可行性检查阶段,调度程序会通过淘汰节点来选择可用节点。淘汰的规则包括过滤不健康的节点、达不到特定约束条件、缺少运行时驱动环境等。作业约束可用于确保应用程序在适当的环境中运行。约束可以是基于硬件功能的技术需求,如硬件架构和需要使用到GPU等;也可以是软件环境需求,如操作系统类型等;还可以是业务约束,例如确保在适当的设备节点上运行PCI兼容的工作负载等。评估阶段是对过滤出来的节点进行评估,调度程序对可行节点进行评分以找到最佳拟合。评分规则主要基于装箱,用于优化应用程序的资源利用率和密度,但也通过亲和力和反关联性规则进行补充。
Nomad使用一致性协议来保证一致性,这个一致性协议是基于Raft算法[25]的。Nomad服务器组成Peer set的一部分,参与日志复制。所有客户端节点都将请求转发给服务端。Nomad中的客户端只需要知道它们的分配关系并从服务器查询该信息,而服务器需要维护集群的全局状态。由于Raft复制的特性,集群的性能受网络延迟的影响很大。因此,每个区域选择一个独立的领导者并维护一个独立的对等组。区域内所有服务器都作为对等组的一部分参与。数据按区域划分每个领导者只对其区域中的数据负责。当接收到远程区域的请求时,将该请求转发给正确的领导者。这种设计允许更低的延迟事务和更高的可用性,而不会牺牲一致性。
Nomad使用Gossip协议来管理成员资格。使用的Serf库其Gossip协议实现基于SWIM[26],并进行了一些小修改。Nomad中所有服务器都加入一个全局的WAN Gossip池。Gossip池提供的成员资格信息允许服务器执行跨区域请求。Gossip协议还用于检测同一区域中的服务器,通过一致性协议执行自动聚类。
基于Nomad的边缘计算平台
本文涉及的某公司的边缘计算平台基于Nomad。首先,介绍边缘计算平台在其边缘计算全场景规划中的位置。如图2.3所示,基于Nomad的边缘计算平台规划在计算平台层,基于虚拟化基础设施层,其上承载边缘中间件层和边缘行业应用层。
图2.3 边缘计算研究内容全场景规划
该边缘计算平台项目采取的是业界流行的MEC模型,在边缘终端附近建立边缘服务器,在边缘服务器上完成终端数据的计算任务。边缘计算平台总体概功能览如图2.4所示。
图2.4 边缘计算平台总体功能概览
基于Nomad研发的边缘计算平台,相对物端计算平台距离万物“一千米范围圈”,要远离终端设备一些,目标是“十千米范围圈”的计算场景。管理对象为具有一定数量的边缘服务器集群,有足够计算资源运行通用Linux操作系统。相对于物端计算平台,边缘计算平台能够完成更加复杂的流式数据计算、边缘AI计算等。边缘计算平台负责边缘分布式集群管理、负载均衡、服务发现、智能部署和动态调整等功能。
Nomad的插件框架
Nomad从v0.9引入了插件框架,允许用户扩展Nomad中运行时驱动和设备组件的功能。该插件系统的设计参考了Terraform和Vault中实现的插件系统。Nomad的插件框架使用go-plugin公开了语言无关的插件接口。插件实现了一组gRPC服务和方法,Nomad通过运行插件并调用已实现的RPC来对其进行管理。
在Nomad中编写运行时任务驱动包括实现DriverPlugin接口和添加main包启动插件。运行时驱动插件的生命周期很长,并且不受Nomad客户端的约束,Nomad客户端可以在不重新启动运行时驱动的情况下重新启动。Nomad将确保运行时驱动的一个实例正在运行,如果运行时驱动崩溃或终止,Nomad将启动它的另一个实例。
运行时驱动保持尽可能少的状态。任务状态由Nomad客户端在任务创建时存储,运行时驱动可以维护正在运行的任务的内存状态,必要的时候,Nomad客户端可以恢复驱动的任务状态。