关键字:漏洞修复;系统更新;云计算
#1
前 言
eBay是全球最大的电子商务平台,连接数亿用户的在线交易。eBay云平台作为承载买卖家之间巨大交易数据的基础架构,其安全性一直是可靠交易的根基。本文将围绕eBay云安全团队(以下简称云安全团队)如何针对eBay私有云的漏洞修复和系统更新进行展开。
#2
背景 & 挑战
随着云平台的服务器数量的增加,其安全性能也受到密切关注。特别是在eBay开启支付业务之后,任何与支付相关的服务器都需要满足安全部门严格的合规要求,因此eBay云平台需要不断地针对eBay服务器节点进行漏洞修复和系统更新。面对数万台的eBay服务器节点(以下简称节点),如何更快更安全地实现漏洞修复和系统更新,我们面临着以下挑战:
eBay云平台(TESS)管理着十万级台的物理服务器节点以及一百多万的应用负载。于此同时,其物理服务器节点个数还在不断地增加中,各个集群的服务器节点运行着不同版本的底层操作系统。
每一轮的eBay云服务器节点的漏洞修复和系统升级,都意味着一次重启服务器的操作。重启服务器这一动作会影响到这台节点上运行的所有工作负载----在每次重启服务器期间,该服务器上运行的所有应用负载都会受到中止服务的影响。因此,在漏洞修复和系统升级过程中,任何线上服务宕机事件都有可能发生。云上的用户业务就有可能受到严重的影响,而影响随着漏洞修复和系统更新的频率的增加而增加。
节点上安全相关的漏洞一旦发现,暴露的时间越长,安全风险越高, 需要在尽可能短的时间内修复。于是要求在一个月内完成几乎所有的eBay私有云节点的漏洞修复和系统更新。
#3
方案设计
问题描述
为了应对以上问题,云安全团队需要以eBay云环境的所有应用负载和部署方式为切入点,结合eBay云的实际部署情况来对整个系统进行分析调查。除此之外,云安全团队不仅需要考虑当前已存在的所有应用负载,还要针对未来即将部署到eBay云平台的应用进行安全考虑。
eBay云平台上用户应用负载(以K8s的Pod的形式存在:Kubernetes的最原子的应用负载的集合)数目已经达到百万级,每个Pod都运行在云平台的节点上。具体的部署方式各不相同,其中要考虑的问题之一就是应用容灾。最原始方式容灾方式即“应用冗余”和“数据备份”,即将同一份应用分别部署在至少两个机架(Rack)上。此类静态的应用部署和数据备灾方式对基础架构部门来说,在进行漏洞修复和系统更新之时,只需严格按照一个机架一个机架去维护更新即可。
为了最大化地使用云计算资源, 在实际的云平台中,每个节点可以同时运行多个用户的Pod,每个用户的一个或者多个Pod尽可能且均匀部署在多个节点上,即用户应用和节点是多对多的映射关系。最简单的一种模型:用户1,用户2,用户3各自有两个Pod,为了防止某台节点宕机而影响某用户的所有Pod,某个用户n的podn.1和Podn.2都会尽量调度到不同的节点上。因此三个用户的pod分别以下图的方式进行调度部署。
作为基础架构部门,每轮的漏洞修复和系统更新操作都可能对用户的应用产生扰动(Disruption)。以往,我们采取按照Rack静态进行维护更新的方式。但这种方式已经不能满足当前Pod与Node不确定调度关系的应用场景。如何在保证对上层应用影响最小的前提下最快速地完成漏洞修复和系统更新是我们亟需解决的问题。
解决方案
解决思路:化静为动,化被动为主动
云安全团队采用“动态”地以某种方式选择若干台节点进行重启的方式展开,只要该组节点能够保证用户服务不受影响即可。
为此,云安全团队使用Kubernetes PDB(Pod Disruption Budget [1])来作为应用与云平台之间的协议载体。所有云用户可以通过自己的应用部署情况来创建并配置PDB。云安全团队可通过PDB来了解用户的应用负载Pod运行节点的大概部署和保护方式,依此按照用户的意愿来保护好Pod。PDB的基本功能很简单:
如上所示, PDB 通过spec.selector.matchLabels字段选择所辖的Pod。spec.minAvailable 描述了用户对自己Pod的保护程度和保护方式。PDB的作用之一就是对Pod的保护,让应用所有者根据其设置要求定义应用的最低可容忍要求,以便它即使在中断期间也能保持稳定。比如上面的PDB实例所要表达的意思就是Pod副本数量不能少于所设置的10,一旦用户的Pod数量小于等于10,任何对Pod的删除等操作都将会被PDB所拒绝,依次来起到对应用的保护作用。
PDB的应用:
最大独立集合Maximal Independent Set
云安全团队采用PDB作为连接用户与云安全团队之间的接口,让系统了解到云上应用的部署结构和最低应用的可容忍中断的要求条件。通过采用Maximal Independent Set(mis) [2]“最大独立集”算法思想,如何最大化地在不违背所有PDB的情况下进行建模来寻找满足重启条件的节点。
下图展示了应用与节点之间的在集群上的简化关系图。若集群内所有应用端PDB均能按照用户自身应用的实际情况来配置“可被打断重启的子应用个数”,此时云安全团队进行挑选节点并升级的系统叫做系统升级控制器(后称为“升级控制器”),升级控制器通过收集所有集群内PDB即可得知整个集群内所有应用部署在漏洞修复和系统升级中的应用终端预算(DisruptBudget),那么通过计算得到的节点集合即为安全的节点集合。
升级控制器根据mis升级算法将所有Pod,节点Node,以及Pod与节点的关系通过PDB存储为的“图”模型G。
G是一个二元组G =(V,E): V表示为集群中的服务器节点,E为应用与服务器节点的依赖关系。举一个最简单的例子:我们将“应用1 的两个备份分别运行在在A节点和B节点”记为(app1,(A, B)),且所有应用都可中断其中一个应用,那么上图的部署情况某个集群应用部署为:
通过升级控制器计算和建模,那么对应的G=(V,E) 应该表示为:
根据mis理论,对于无序图G,如果选中A节点,那么集合s =(B, E, F)就不能同时被选中,也就是说A节点和集合s必须是互斥的,而此时对于C节点或者D节点对于A节点来说相互独立的。由于同时C和D是邻居节点同享边app3,C和D也是互斥的。综上,可以将无序图G分成以下三组独立集合S1, S2, S3:
因此通过该结论,这6个节点可以通过3组进行重启:分别是 AC, E, FBD.
在应用到漏洞修复和系统更新中,升级控制器通过升级策略算法将6个节点依次分为若干个独立集合(Independent Set,IS)来进行升级重启即可。理论上通过这种方式选择的服务器节点升级重启,所有用户的应用的最低可容忍的中断条件都可以被满足而不会带来任何影响。同时为了加快升级,只需要计算当前集群中节点的最大独立集合即可。目前计算出图中的最大独立集合问题属于NP难题,并无最优解。但经过分析观察发现:图中各个节点的度[3]直接反映了该节点被应用的依赖程度,度越高代表被应用依赖程度越大,反之越小。比如度为零的节点可以被直接重启,由此升级控制器可以根据应用的部署情况对该mis升级策略进行优化,依此最大化地优化该“无最优”问题,其中这些优化条件为:
应用备份多,所设置的可被打断(disrupt)的数量越小的应用优先被选中升级
对应用加权,权重越多的节点优先被选中
如果某个应用所有冗余全都处于“down”的状态,直接被选中升级
…
有了以上“优化条件因素”,升级控制器可以根据TESS实际集群内的应用部署情况来选择最优化的节点编排方式,可以尽可能快最安全的选择一个个节点独立集合来重启,实现eBay私有云服务器的又快又安全的漏洞修复和系统更新。
PDB的配置约束
通过介绍PDB的功能和升级控制器的MIS升级策略算法可知,理论上,只要每个用户通过配置合适PDB来保护自身应用,那么在实际升级中,任何应用都可被安全稳定地重启。细心的同学可能就会发现,这种情况的确能够保证应用的安全,但如果用户设置的PDB不当的话,可能会出现可重启节点被“锁死”的情况。
比如用户A设置的PDB含义表述为“最大能挂掉的Pod个数为0”。这样最简单情况等同于该用户的Pod都不能被“打断”。那么在升级控制器看来,这个PDB所辖Pod所在的所有节点都不能被选中升级。这些节点就是那些被PDB锁死的节点。在云环境的基础架构中,底层的更新维护比如漏洞修复、系统升级、底层硬件维护等是很常见的。因此所有的云用户需要遵从一些约定来创建PDB:
该二则条约是最基础的条约。条约一能够最直接地解决节点被“锁死”不能被选中的问题。而条约二旨在解决由于用户Pod的“经不起”底层节点重启而暴露出的应用健壮性的问题,因此条约二从侧面也对用户的应用健壮性起到了监督作用。
PDB的加强1:
应用健康检测器HealthMonitor(以下简称健康检测器)
问题描述
通过PDB的引入,使得云安全团队对“云上”用户的应用部署情况有一定的了解,而且可以有信心地选择相应的节点进行重启。但是通过几轮的系统升级发现仅仅通过PDB还会存在一些问题:
PDB不能完全表达出用户的业务的运行的详细情况。当前的PDB仅能保证应用Pod的数量问题,没有考虑到Pod内运行的容器应用逻辑层面。具体来说,就是用户Pod状态已经为“Ready“,但是由于网络时延,CPU调度等原因,Pod内的容器业务并没有正常工作,于是就会出现PDB认为用户Pod是正常的,然而实际的Pod内的容器运行的业务却并没准备好的“代沟”。这样的“代沟”就有一定的概率让这个PDB所辖的某个Pod下的节点被重启。而此刻如果某一个Pod状态已经是“Ready”的但是实际业务没有“Ready“,那么升级控制器会误以为这些被重启后的Pod已经正常工作,转而选择新的节点进行重启。那么,这个PDB所在的业务就有一定的概率出现全部被中断服务的可能。举个例子:
比如某一应用Pod状态处于“Ready”的时刻为T1, Pod状态为“Ready”且其对外真正提供服务的开始时刻为T2,理想的情况是只有到了T2,该Pod的应用才是真正提供服务的,也就是说只有在T2这个时刻之后的任意时刻,这个Pod所在PDB的其他Pod所在节点才能够被重启。
由于目前社区的PDB并没有考虑到这一小小的时间差, 仅仅通过PDB作为云安全团队和云上用户的中间媒介的话,T2-T1的时间间隔越长,那么用户应用被影响的概率就越大。
解决方案:PDB + HealthMonitor
为了弥补社区PDB对云应用上的表达力不足的问题。云安全团队对升级控制器的MIS算法和PDB进行了加强和补充:为了能够对应用负载的健康状况进行彻底的探测,云安全团队使用健康检测器(后称HealthMonitor)来辅助PDB对云上应用进行保护。HealthMonitor是eBay特有的定制化的k8s模型,其主要作用就是表达一个应用提供服务的健康情况:
HealthMonitor与PDB共同绑定不仅弥补了PDB仅仅关注健康Pod的数量而没有关注实际应用健康状况的空缺,还有一个很重要的特征是:HealthMonitor可以反馈数据库集群比如Mysql集群,ElasticSearch集群等跨集群应用的健康状况。当云安全团队进行系统维护过程中,由于某个服务压力突然剧增,导致本来健康的ElasticSearch集群或者Mysql集群出现了服务压力,云安全团队就可以根据HealthMonitor来停止对这些应用集群的节点重启行为,从而提前避免由于基础架构的系统更新和漏洞修复而导致云上应用的事故发生。
同样引入HealthMonitor之后,用户需要将本身应用的情况细节通过HealthMonitor来告知云安全团队其应用的健康状况。于是有了以下条约:
条约四和上面的条约二具有类似的逻辑,由于升级控制器通过HealthMonitor来了解到云用户的信息,如果HealthMonitor却一直为“不健康”状态,对于升级控制器来说,再进行选择节点进行重启的话,这对云上应用是很危险的操作,因此应用实例如果长期处于“Unhealthy”状态,云服务器节点的漏洞修复和系统更新进程将会被严重的阻塞。
PDB的加强2:
提醒机制Notification
问题描述
PDB和HealthMonitor的结合在反馈用户应用署情况和保证应用安全上几乎无懈可击。但是云环境中还有这样的“短期任务”型应用模型,其主要有以下几个特点:
专门处理短期任务
处理任务时间有长有短,长的任务周期可达数周
任务处理过程中一旦任务被打断必须重新执行
…
针对这种任务类型的应用,用户通过某种方式动态配置相应PDB的方式可以完全保证应用的安全。但是对于漏洞修复和系统升级过程来说,却有严重的被阻塞进行的风险。
对于升级控制器来说,有两种阶段:
Calculating:每次通过计算寻找一组独立集IS,然后对这个独立集的节点进行重启
Rebooting:在Calculating阶段结束之后,会选出一组节点,该阶段对这组节点进行升级重启。
两个阶段持续时间长度没有巨大的变化。在升级过程中也就是说只有在Calculating的过程中PDB没有“锁着”的节点是可以被控制器选中并在Rebooting阶段进行升级的。通过沟通得知,对于“任务”类型的应用时,这种应用也大概有两种主要状态:
Running: 有新的任务处理,开始处理任务
No job: 应用处于“闲置”状态,没有任务处理
对于这种应用,只有应用处于“No job”的时候才能开启PDB,允许节点被重启。
通过以上的阐述,那么细心同学会发现,升级控制器有很大概率被这种“任务”类型的PDB配置方式严重阻塞。如下图所示,实际升级会有以下几种情况发生:
T1两个任务开始执行,由于应用处于“Running”状态,因此升级控制器在“Calculating”阶段不会选择这些正在执行任务的节点(假设为Node1)。在T2时,应用执行完任务,处于空闲期,但是升级控制器已经进入“Rebooting”状态,此时会根据“Calculating”计算出来的节点进行升级,这样Node1就失去了第一次被选中的机会;
T3时期T1的情况完全相同,那么Node1失去第二次被选中的机会;
T4应用处于“No job”的状态,此时的升级控制器已经进入“Calculating”状态,并持续到T5结束,那么中间只有(T4, T5)这个时间间隔,Node1 才有被选中的可能。
通过以上分析,不考虑其他应用PDB对Node1的锁定情况。仅考虑只有一个“任务”类型应用的运行在该节点的情况下,升级控制器在4轮的“Calculating”中,Node1才仅仅只有一次被选中的可能。可想而知,实际的云环境的服务器节点要比这中情况复杂得多,那么这个Node1节点被选中升级的机会微乎其微。这样一来,所有这类节点将会被严重阻塞。
解决方案:通知模型
通过以上分析了解,由于“任务”类型的状态不确定性以及升级控制器MIS算法的特点,该类型应用所在的节点有很大概率被阻塞,为了解决这个问题,云安全团队使用一种“Notification”机制来尽可能同步应用端与升级控制器的“步伐”。其中主要的流程如下:
对于升级控制器来说,Notification模块每次升级检查PDB的过程只需要检查这个PDB的相关节点是否被“锁死”而且是否被升级完毕,当且进行这个节点没有被升级选中且被PDB配置“锁死”,那么就针对该PDB发送“start” 的Notification信号,告诉该PDB表示想要升级某些节点;当这个PDB下面的节点全部被升级完毕,且PDB是“开锁”状态,那么就发送“complete” Notification信号,告知该PDB的应用“这些节点已经被升级完毕,可以继续应用的正常逻辑”。
而对于应用的Notification板块,其逻辑图如下:
通过以上逻辑,“任务”类型的应用的节点升级问题迎刃而解,通过Notification信号机制同步升级控制器的“节奏”,使得PDB的“锁”可以在短时间被打开,增加节点被选中的概率,从而导致所有服务器的漏洞修复和系统升级能够顺利进行。另一方面不仅仅是“任务”类型的应用,所有云上应用都可以通过“Notification”机制来获取底层的服务器维护的大致“步调”,保证减少应用自身“任务”应用被打断的几率。
以上就是云安全团队动态升级节点策略中MIS以及MIS算法基于eBay云的相关加强的大致实现过程。所提供的动态升级策略的最大亮点就是将用户应用负载在集群中的分布信息进行收集,能够实现百分之百用户应用的安全性。除此之外,动态分组升级是一个质的突变,它不仅能够根据用户应用负载分布信息自动判断某个节点是否可以重启升级,使得系统升级控制器由原来的“盲人摸象,坐井观天”变为“见微知著,洞若观火”;系统升级控制器也能够根据这些策略信息最大化挖掘集群中符合条件的节点来重启,这将大大激发升级速度的潜力,使得“鱼与熊掌兼得”成为现实。
目前eBay的很多技术团队,比如大数据Hadoop、Mysql,ElasticSearch等部门都在通过该方法与云安全团队进行对接实现云服务器节点的漏洞修复和系统更新。除了MIS策略,云安全团队还有一些其他的动态升级策略,这些策略可以动态通过系统升级控制器横向扩展来完成,关于其他升级策略以及系统升级控制器设计在这里不再一一赘述。
#4
未来工作
如何解决MIS升级长尾效应?
随着系统升级的进行,我们发现每一轮升级都会有着严重的长尾效应,长尾效应带来的影响是前50%的系统维护时间升级了80%的节点,而剩余的20%的节点却也花费了整个过程50%的时间。如何针对后面20%节点的长尾问题分析和解决将通过后续文章进行探讨和研究。
针对跨集群部署的应用如果保证其安全和系统更新的速度矛盾问题?
当前的升级控制器的管辖范围只控制在一个集群,当某些应用跨集群部署的时候,仅仅集群内部的升级控制器所掌握的信息是完全不够的,因此需要一个跨集群的全局控制器来获取更多信息,主要目的有以下几点:
保证跨集群应用的安全性
增加集群与集群之间增加漏洞修复和系统更新的并发度
实现更高效的漏洞修复和系统更新的自动化
参考文献
PDB [1] https://kubernetes.io/docs/concepts/workloads/pods/disruptions
MIS[2] https://en.wikipedia.org/wiki/Maximal_independent_set
度[3] https://zh.m.wikipedia.org/zh-hans/%E5%BA%A6_(%E5%9B%BE%E8%AE%BA)#:~:text=%E8%AE%B0%E4%BD%9C%20%E6%88%96-,%E3%80%82,5%EF%BC%8C%E6%9C%80%E5%B0%8F%E5%BA%A6%E4%B8%BA0%E3%80%82
Healthmonitor[3] https://tess.io/userdocs/tess-cluster-osupgrade/healthmonitor/