背景
Meta的服务(facebook, instagram, whatsapp, messengers)都依赖于数据中心的服务。
然而,静默数据错误(SDC)无法被大型系统所发现,在硬件层面无法被捕获,有可能会随着软硬件栈一直传播,最终形成应用层面的数据损失。这些损失可能需要数个月来修复,是大型基础计算设施中潜在的问题。
以下在数据中心中出现的重大事故都与SDC问题相关:
- AWS S3 因 bit corruption 造成 36 小时的服务中断
- Facebook 遇到 10%-15% 的图片无法访问 (48 小时)
- Netflix 宕机超过 3 天
潜在研究方向
我们固然可以执行非常严厉的计划来避免 SDC 问题,然而无论多么精巧的设计都不能 100% 避免问题的发生,而且会急剧降低系统的整体性能。这就要求系统设计者学会在性能与可靠性之间做工程上的平衡。
针对以上前沿研究问题,Meta的系统和基础架构实验室已经开展了大量的工作并运用于云服务场景中以保证服务交付的准确性。经过多年的研究,Meta认为解决SDC问题有以下四个方向:
- 从**计算架构(Architecture)**层面处理SDC问题
- 架构层面上处理和转移SDC的方法,例如enhanced compute block ECC机制
- 自测试(self-test)的架构模块和模式,例如lockstep computing, checkpointing和redundant computing,这些方法都需要评估计算成本和性能代价
- 新颖的处理计算和内存错误的架构解决方案,包括但不仅限于增强传统的RAS架构。
- 从**分布式计算(Distributed Computing)**层面处理SDC的传播
- 遏制SDC传播的分布式计算弹性模型和解决方案
- 跨越多个子系统的错误检测能力
- 分布式规模的错误控制和测试机制
- 自测试SDC的分布式系统架构和恢复方案
- 从**软件(Software)**层面提供弹性服务
- 软件层面实现对SDC的弹性,例如冗余机制、概率和算法的容错机制
- 实现抗SDC的通用计算和数据搬运库
- 针对SDC的实时检测和遏制软件方法(需要评估计算成本和性能开销)
- 从过去的SDC中归纳SDC解决算法
- 从**硅设计(Silicon Design)**层面提供更可靠的硬件工艺
- 面向SDC问题,优化硅设计和制造策略
- 用于硅制造过程的进阶模拟、仿真和测试策略
- 硅测试的覆盖率评估、硅模块内发生故障的概率评估模型
- 用于SDC检测的生产过程中的测试例程开发
- 硅模块使用过程中逐渐衰退的模型和评估机制
Facebook在大规模集群中对SDC问题的理解
以下内容来自论文《Silent Data Corruptions at Scale》(Arxiv) @Facebook。
补充背景
以往的工作往往研究由于辐射或者合成错误注入产生的软错误问题,Meta的研究则注意到,由于设备的特性,SDC可能会发生并且大规模重复,这种SDC是可复现、非瞬态的。此外,在之前的错误注入模型的研究中,CPU的SDC概率被定为到百万分之一的几率级别,而在真实场景下,由于CPU功能模块中为了性能往往采用最小纠错机制,CPU SDC的概率要比预计高几个数量级。
产生缺陷的原因
设备缺陷(Device Errors)
在生产和设计阶段,设备本身有可能会有潜在的缺陷:
- 有些设计有可能有corner case。例如,在某个特定的功耗状态下管理cache控制器的模块,其功能会受到限制,有可能导致设备运行被卡住或者产生功能上发生错误;
- 在CPU布局布线期间,不能确定信号的到达时间,有可能导致错误的位翻转。例如timing path error
- 制造过程中,有可能所有的晶体管的蚀刻不够可靠,使得晶体管没有相同的峰值工作电压或功率阈值,各个设备的模块特点不同,导致制造上的错误。
早期故障(Early Life Failures)
有些故障在生产过程中就被发现了,有些故障要等到硬件真正运行服务的过程中才会出现。根据晶体管内部缺陷的类型,故障可能会在运行前几周、几个月或预期设备寿命结束前的任何时间出现。 这些故障全部被归类为早期故障(Early Life Failures)。
设备老化(Degradation)
随着频繁的使用,设备会逐渐老化。频繁使用的计算部分比 CPU 的其他部分老化的更快。与早期故障相比,这些由于设备老化而产生的故障并不常见,但仍有相关的例子,例如针对 DDR4 内存的RowHammer攻击。 芯片内部使用纠错机制(ECC),可以防止设备内部性能下降。
晚期磨损(End-of-Life Wear-Out)
当设备在现场服务工作负载一段时间后,超出其额定寿命,整个硅开始出现磨损,这在大多数组件中都可以观察到。
Case Study:应用层看SDC造成的影响
Facebook中有大量不同类型的应用。我们以查询设施为例,最基本的查询基础设施是用来获取并执行SQL查询的设施,例如Spark, Presto, Hive等。下面以spark为例来描述SDC会给应用带来什么样的影响。
Spark
在Spark架构中,数据集被分为多个部分,这种数据集叫做弹性分布式数据集(Resilient Distributed Dataset, RDD),每个数据集是单独并行执行的。执行过程是每个worker node上会分布有几个RDD块,每个worker首先做map,然后在shuffle reduce阶段将结果进行综合,最后再对用户提供请求的结果。
FaceBook解压缩应用
FaceBook使用Spark进行压缩。这里,我们主要讨论解压缩的部分。当解压缩请求运行时,多组压缩文件会被输入到解压缩流水线中。在解压缩之前,系统会检查压缩文件的大小看是否大于0。如果结果大于0,才会执行解压缩。
在Spark中,解压缩流水线会提供文件的尺寸作为解压缩算法的输入,算法将执行Scala库的幂函数。有趣的是,FB在日常维护中发现了这样一个case study:幂函数会对一个已知具有非零大小的压缩文件返回0值。由于文件大小为0,因此这个文件不会被解压,这意味着随机丢失了一些文件。接下来,为压缩文件保留着k-v对的应用会发现这个错误,并且注意到此错误已经不可还原了,使得应用程序出错。
对以上问题做debug
所有工程团队用日志记录下每一个worker的每一步的结果,并复现以上的错误→从日志中锁定出错机器→单机上复现错误→锁定错误只在某一核心上被某一组特定值触发→scala无法使用GDB调试,但可以用JVM兼容JAVA字节码
工具
为了找出根本原因,Meta团队希望在不改变复现实验的含义的情况下,将编程语言从Scala转换为Java,再转换为JAVA字节码,最终到达指令级别来再通过GDB调试,以锁定错误出现的范围。但是java是即时编译的,并不能深入到指令级别,所以还需要一个提前的编译器编译出这些指令;或者需要一个探测器,可以在执行JAVA字节码时提供已经执行的二进制指令。
- 语言转换:Scala Compiler(scalac),转换后可以生成交叉编译的java类型文件,可以当做java字节码
- 提前(ahead-of-time)编译器:GCJ,可以将java字节码转换成对象文件和二进制码,二进制码可以用GDB来debug。然而这个工具已经很久没有维护了,其他也没有更好的编译器工具。
- **探测器(probe)**:JAVA提供+printassembly选项,用HotSpot追踪可以打印出已经运行出的汇编代码。
通过以上三个工具实现了GDB对代码进行debug。但是出来的都是汇编代码,且数量非常大(430K),为了定位到错误的汇编代码,本文通过先将相关的函数筛选出来,再进行反向工程,实现对错误的定位。
很好的一些调试经验(略过了,太工程,大部分谷歌翻译了)
绝对地址引用:将绝对地址留在代码中跳转会导致分段错误。如果发现汇编的该部分对再现性没有依赖性,则最好消除绝对地址引用。
意外的分支:如果意外的分支和跳转调用没有被映射,代码会因分段错误而崩溃。最好限制复现器的可变性。
外部库引用:建议最好不要依赖外部库。
编译器优化:高性能代码具有多次编译器优化功能,观察数学方程式的优化有助于理解复制器所需的关键组件。 在逐步执行汇编指令时,优化可能不直观。
冗余指令:最好消除冗余指令,例如stub指令
输入/输出寄存器:我们需要为关键指令识别数据输入和结果寄存器。识别后,必须添加额外的指令来提供用户输入、获得结果。 这实现了稳定的reproducer代码,并能够识别SDC的数据依赖性。
管理堆栈帧:独立的Reproducer需要适当地管理堆栈帧。 管理堆栈帧中的事务以防止缓冲区溢出或下溢对于稳定性至关重要。 没有堆栈框架,复制器代码无法管理基于堆栈的请求或函数调用。
内存偏移量引用:寄存器通常在指令中使用内存偏移量。 必须适当地初始化偏移量。 如果未计算和初始化偏移量,我们将遇到由于未初始化数据而导致的分段错误或复制器损坏。
特殊功能单元:需要监控特殊功能单元(如 ALU、DSP、FPU、AVX 等)的事务,它们会带来近似值。 此外,特殊功能单元利用不同的位宽、特殊功能寄存器和堆栈结构。
主框架:如果没有合适的主框架和功能框架,一个独立的reproducer是不完整的。 这使得代码可执行。
通过逆向工程,可以获得一个更简单的reproducer代码,之后通过GDB可以获得导致错误的指令,最终锁定在60行的汇编代码中。
重新审视应用的错误
此后,本文注意到具有不同精度的不正确值,结果不尽相同。因此,应用程序可能解压缩了大小不正确的文件,并且将文件在没有在EoF终止符的时候下被错误截断。 这会导致文件节点悬空、数据丢失等无法跟踪的错误。 因为复杂的数据依赖以及数据的输入,如果无法没有复现代码,这种数据损坏几乎不可能被检测并溯源,尤其是在集群拥有数十万台机器、每秒执行几百万次计算的情况下。因此,以上的方法可以更快地溯源集群内SDC的根本原因。
对抗SDC的硬件方法
我们观察到,在大规模基础设施中,SDC并不是局限于百万分之一概率的罕见事件。 这些错误是系统性的,不像其他故障模式那样容易理解。以下几种硬件方法可以降低处理器内的软错误率,对SDC也有效:
- 保护数据路径:使用ECC增强设备内的块,保护数据路径,提高设备的弹性。
- 专用的筛选模式:在制造流程中,设计专用的筛选和测试模式
- 了解大规模下SDC的行为:与大规模使用设备的客户密切合作,了解和评估SDC的影响。 研究发生率、生产故障时间、对频率、电压和环境条件的依赖性,有助于深入了解 SDC 的表现形式。
- 架构优先级:将来架构选择中,会优先考虑防止SDC的架构。
探测SDC的方法(不增加其他额外开销)
为了探测到SDC,我们需要额外的机器来进行计算,之后与参考值比较结果。以下三种方法可以实现对SDC的探测:
找机会
找机会利用处于维护状态的机器,并使用随机数据输入,执行指令级准确性验证。 这里的挑战在于其覆盖范围主要取决于机器有机会处于维护状态的频率。 在大型的集群中,我们并不希望有很大比例的机器处于这些状态。
周期性
实施一个调度程序,定期监视机器的SDC覆盖率,然后根据定期计时器安排机器进行测试。这种方案的的开销很高。
对生产友好的方案
当测试可以优化为最小的大小和运行时间时,可以使测试指令与机器上的工作负载同时执行。 结果被发送到收集器以通知机器的通过或失败状态。 此方法需要与工作负载密切配合,以免对生产工作负载产生任何不利影响。
软件容错机制
为了处理静默错误,我们需要重新考虑基础架构软件设计理念和软件抽象的鲁棒性。
冗余机制
防止应用程序级故障的更好方法是实施软件级冗余,并定期验证正在计算的数据在多个检查点是否准确。在将这些方法应用于大规模数据中心时,要考虑精确计算的成本。 冗余的代价对资源有着直接的影响:架构越冗余,重复资源池的需求就越大。 但是冗余为应用程序提供了容错的概率。
容错库
将容错机制添加到 PyTorch 等知名开源库中将极大地帮助应用程序防止暴露于SDC。 构建容错的算法会增加应用程序的额外开销。因此,如果在性能下降可忽略不计的情况下,这一点可以被实现。 这项工作需要SDC研究社区和软件库社区之间的密切合作。
Facebook检测SDC问题的方法
以下内容来自论文《Detecting silent data corruptions in the wild》(Arxiv) @Facebook。
硅测试流程
在投入使用之前,基于硅的电子设备要经历不同的开发阶段。因此,我们要了解不同开发阶段所使用的测试策略,以理解集群范围内的与测试有关的开销,由此了解为什么测试是一件很难的事情。测试流程看重三点:测试量、测试时间、在该阶段出错产生的影响。
设计和验证(Design and Verification)
采用模拟和仿真(Simulation and Emulation)的手段来进行方案的测试。测试的时间会很长,但是由于方案不断改变、新的器件会加入,所以测试周期一般会很短。在该阶段出错的代价相对来说是很低的
硅上验证(Post Silicon Validation)
此阶段产生少量的样本进行验证。与之前一个阶段相比,设计加入了生产过程的变量。从开销上来说,这一阶段的验证会引入生产的开销。如果出错,需要对原始方案推倒重新设计,此外,在真实电路上开展测试的测试开销也很大。通过测试的方案被视为可以进行大规模生产。
生产厂商测试(Manufacturer testing)
大规模生产之后,每个设备将用更加高级的测试设备进行自动测试。测试时间对生产通量有明显的影响,测试总量也从之前的几百个上升到上百万的量级。测试的开销随着数量也线性扩展。在这个阶段如果出错,开销会更大,往往导致重新设计或者重新生产该芯片。
集成测试
这一阶段设备被运送到终端消费者手中,消费者用此设备集成到自己的开发环境中,集成阶段往往由集成者来协调。在这一阶段,需要跟其他不同的设备相配合,测试的复杂度也升高了;此外,测试的开销也从单设备到多种设备、多种配置的综合测试。此阶段出错,可能会导致不同的机架重新组装或者重新安装。
基础设施引入测试
这一阶段主要是将机器接入网络,进行应用层面的测试。往往要持续几个小时到一天的时间。由于故障的来源多,所以锁定错误要更难一些。
基础设施集群测试
正常的测试流程到上面就结束了。但是因为有SDC,如果不运行某个特定的测试程序是找不到SDC问题,以保护基础架构上运行的应用的。因此,按期进行检查是很有必要的。这种测试所需要花费的开销会更大,因为需要在保证程序正常运行的情况下对程序进行复杂的编排和调度。
此外,由于硬件的复杂度和配置的复杂度,分类和溯源错误是很昂贵的。因此,需要花费比较昂贵的开销,采用更多高级的方法来探测SDC。
SDC为什么是一个很难的问题?
SDC可能的来源:
数据相关:SDC有可能天然与数据相关,也就是说这个CPU坏了,有可能大部分的计算还是正确的,但是就是一小部分是错误的。这使得测试的空间非常大。
电气因素:改变的电压、频率和电流会引起更多的错误,虽然在某一组电气配置下结果对了,但是其他情况不保证。这也使得测试空间非常大。
环境因素:地理位置的差异也会加速SDC问题的出现,这与温度、湿度都有很强的相关性。在大型集群中,也有可能因为workload不均匀一些机器温度高,使得某些计算中心结果和其他不一样。
寿命因素:随着时间,硅片的性能和可靠性也会改变。此外,SDC问题可能跟现有模型的预测有出入。因此今天计算正确,明天就不一定正确了。
从以上四点,本文总结保护集群以对抗SDC的唯一方法是通过不断改进的测试例程和高级测试模式生成来反复测试基础设施。
基础设施集群测试的方法(Infrastructure Fleet Testing)
两个主要的测试方法:
- 停产测试(Out-of-production Testing)
- 不停产测试(In-production Testing)
停产测试(Out-of-production Testing)
停产测试是指让机器接受一组固定特征的输入,将其输出与已知参考值进行比较的过程。这种测试往往在机器没有执行生产负载的时候执行,并且测试要经历不同的温度、电压、机器条件和地域环境等复杂条件。
测试的特征往往是根据生产经验以及对硅架构本身的理解来生成的,以匹配常见的一些缺陷。一般来说,在大型基础设施中,总是会有一部分机器处于维护状态,在维护开始之前,机器上的负载会被迁移下来,这叫做draining phase。之后会进行维护,例如固件升级、内核升级、环境配置、修理等措施,之后再重新将工作负载迁移回来。
之后进行停产测试:用于停产测试的工具是Meta开发的Fleetscanner。Fleetscanner会找机会识别那些进入和退出维护状态的机器,并且使已经完成维护的机器进入停产测试SDC阶段。在所有情况下,根据可用的测试时间、识别的机器类型,fleetscanner会运行优化的测试,并提供设备对敏感的架构代码路径的响应快照,并验证计算是否准确。此时,大量的机器相关的参数会被瞬间捕捉到,以在分析过程中理解设备出错的具体条件。任何被鉴别为出SDC错误的机器会被放进quarantine pool中,以待进一步的观察和测试。
不停产测试(In-production Testing)
不停产测试是指在集群内机器仍然运行工作负载的情况下,同时进行测试。如果不对测试例程和工作负载有细致的了解,则很难实现这一点。Meta实现了一种叫做ripple的测试方法,与工作负载同时存在,并且每次实现毫秒级别的测试时长。在停产测试中使用的测试序列被修改,以通过ripple测试。
影子测试
Meta实施了可以跨多组工作负载的ripple,并且精心设计了ripple测试架构,以防止整个集群的错误。 影子测试的一个主要挑战是实现托管。 基于工作负载的扩展,测试规模必须缩小。 对于 Meta 的每种类型的工作负载,确定了缩放因子的评估过程。此外,Meta还建立了与测试相关的开销分析,每种工作负载类型的托管都会提供开销,该工具的目标是将其开销降至特定阈值以下。通过反复试验,我们建立了控制结构和保障措施,以便为不同的工作负载启用不同的选项,然后将解决方案扩展到整个集群。
持续不停产
持续不停产的纠错机制始终处于开启状态,只有它运行的规模是通过集群内的配置动态控制的。 这种方法在查找需要对相同数据输入进行数千次迭代的缺陷,以及识别正在退化的设备等方面非常有效。 在几周内,通过测试随机化和缺陷模式匹配,可以将设备中识别的新签名扩展到整个设备群。 这种方法在识别硅过渡缺陷方面也非常有效。
效果对比
根据三年的真实测试对比两种策略。
- 使用 ripple 可以在 15 天内完成 70% 的常见覆盖检测,Fleetscanner 在 6 个月内将覆盖范围扩大到剩余的 23%
- 独特的 7% 覆盖率是通过集群内重复的ripple实例实现的。
- 通过不断地重新审视和评估这些覆盖率指标,可以知道围绕测试向量、测试节奏和测试运行时间的集群范围内的测试策略。 对于不同类型的缺陷,覆盖率会有所不同。
- 本文作者: Zhang Xinmiao
- 本文链接: https://recoderchris.github.io/2023/02/06/Meta关于SDC的项目基金和已有的研究成果调研/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!