特斯拉的持续部署流水线建设

| 2023-01-12

简介

数字化革命对我们的日常生活和工业的各个部门都产生了巨大的影响。传统上,由机械工程主导的行业现在正转向软件驱动,我们从汽车行业的转型中就可以观察到。

现在,客户(用户)们已习惯于自动、即时且免费地获取最新的更新。这种开发方式始于敏捷开发方法在企业应用软件开发中的普及,它让发布周期加快了。

「快速失败( Fail Fast )」已成为硅谷公司创新的驱动力。现在,持续交付在某些应用领域是最先进的,它让软件开发人员只要点击一个按键,就可以向广大用户提供新版本,是软件开发和验收测试的全新概念,即:可以向有限数量的用户组提供某种新功能,从而尽早从客户那里接收即时反馈。

然而,假如当用户是司机,而不是手机用户呢?其中主要区别在于:汽车是一个「安全为先」的系统,汽车一旦出现了一个软件故障,很可能会导致人身伤亡。因此,如果汽车制造商打算「快速失败」,他们必须在把汽车交付给客户之前完成,所以,如果汽车制造商打算持续交付,则必须对其软件进行彻底的自动化检查。随着行业内开始部署自动驾驶,这一点变得越来越重要。假如客户相信,自动驾驶汽车能安全地将他们送到目的地,那么,他们的生活基本上就完全掌握在某个软件的手中。

同时,他们希望该软件能够始终处于最新的技术前沿。这些相反的要求给汽车行业带来了比任何其他行业都更大的挑战,因为它是目前唯一一个大规模生产的网络物理系统。本文将深入了解汽车行业目前如何努力克服这些挑战,使用了哪些技术,以及目前仍存在哪些限制。

分布式+嵌入式+企业级+安全为先

在将任何软件交付给客户之前,需要执行许多步骤,包括编译和组装最终产品,并在三个不同级别上进行测试,它们可以概括为部署流水线的一系列后续阶段,包括:

  1. 持续集成中可以自动执行的所有步骤;
  2. 由运行时间较长的验收测试组成;
  3. 传统意义上的发布以及用户验收测试。

每个阶段仅在前一阶段成功通过时才触发。

因此,当我们试图进行持续交付时,我们必须设法让第二阶段和第三阶段的执行速度与第一阶段执行速度相同(或至少几乎相同)。否则,软件变更和新版本产生的周期可能就超过了发布过程。这样的话,持续交付的整个想法就没有用了,因此需要将构建分组到一个版本中。

事实上,来自DevOps运动的技术和工具,如DockerPuppet 旨在实现发布的自动化,这类工具让亚马逊和 Facebook 等公司能够在其生产环境中持续部署其最新的软件版本。但是,对于像特斯拉这样生产高度嵌入且分布式系统的公司呢?汽车行业具有完全不同的结构、工艺和要求。那么,在这个领域中,(持续)交付流水线是什么样子的呢?

为了理解汽车发布流程,首先需要考虑的是车辆的硬件和软件架构。现代汽车有多达100个具有不同用途的独立ECU(电子控制单元),它们相互作用以实现类似 ADAS(高级驾驶员辅助系统)的复杂功能,如下图所示。

为了减少通信信道上的负载,传感单元本身已经执行了某些预处理:例如,ADAS ECU 以对象列表的形式接收周围环境的数据,该列表由摄像机和雷达传感器预先根据原始数据生成。因此,汽车系统不仅是嵌入式系统,而且是分布式嵌入系统。

由于原始设备制造商的组织结构,软件团队是分布且异构的。ECU 通常由承包商开发和生产。然而,也可能是混合结构:部分或全部应用软件可能由 OEM 或其他承包商提供,而 ECU 上的部署可能由另外的承包商、分包商或甚至 OEM 再次完成。这意味着交付动作可能由已经通过其自身部署流水丝的许多不同源触发。

最后,汽车是一个安全关键系统,这意味着在部署到生产环境之前,必须进行功能安全检查。通常,使用FMEA[2]或STPA[3]等分析方法。它们涉及安全分析员识别可能的危险场景,并针对发布候选进行测试。

自动驾驶系统的部署流水线

上述要求使得汽车行业的不同阶段,需要由不同的组织并行执行,如下图所示,起点始终是在单个ECU中提交的源代码。接下来是一个标准的持续集成阶段,包括静态代码分析、编译、单元和集成测试。由于一个ECU可能包含一个或多个应用软件库文件,并且始终是独立的操作系统,该操作系统为调度或通信等基本功能提供抽象层,因此必须执行进一步的集成测试。

与大型企业软件一样,当其中一个依赖项发生变化时,需要触发构建。不同的软件库文件首先需要配置并链接到操作系统。这通常是一项手动任务,由Vector 公司的达芬奇配置器等专用工具支持,其中包括提供相关库及其接口的可视化。虽然这一步通常需要专家知识,但是,它可以从体系结构信息中得到,并在建立持续交付流水线时实现自动化。此步骤的结果是一个功能完整的容器,可以在任何兼容的硬件上进行闪存。这确实与 DevOps 已知的集装箱化非常相似。

下一步涉及在单个 ECU 上执行的集成测试。这些测试旨在确保隔离控制单元的正确功能,因此仅限于接口测试。这些测试最终在定制的硬件在环或开环测试台上进行。许多不同的构建管理系统( 从著名的 Jenkins 到具有复杂测试选择方法的企业级专有定制开发系统)用于触发测试,具体的测试执行由标准工具执行。

这些工具连接到总线系统,在提供相应的架构信息时,可以监控、解释和操纵信号。它们提供接口来指定期望的行为,如来自通信 ECU 的信号的模拟输入以及成功(或失败失败)标准,例如:在给定时间内发送特定信号的预期。如果所有 ECU集成测试都通过,则编译的软件容器将提交到中央存储仓库。这一步类似于「源代码提到到代码仓库」,区别只是在于此时的提交是一个二进制工件被检入,此时会激活第二个阶段。

此次二进制的提交首先触发集成测试台上的软件部署,然后再触发测试车辆上的软件。所使用的测试台的复杂性和测试目标各不相同。然而,当涉及到功能集成时,大多数测试台的设计使其包含实现一定数量功能的所有 ECU,以及环境和其他 ECU 的复杂硬件在环仿真环境。这些功能测试将在此类测试台上执行。

当然,这一阶段的成本也是巨大的。现代车辆包含大量功能。ISO26262 要求对功能的每个要求进行测试覆盖,这导致对整个车辆进行数万次集成测试。由于它们是在目标硬件上执行的,因此它们必须实时运行,这导致每个测试的平均执行时间为几分钟。

显然,我们要努力减少这个执行时长,并且必须为精确的变更定制测试套件。在典型的手工功能集成过程中,专家知识用于分析在哪个测试用例中测试哪一个子功能。然而,在持续交付的场景中,需要用启发式程序代替专家程序

在软件工程中,已经提出了许多测试选择方法,但由于源代码不可用,这些方法大多不适用。在这种情况下,分析涉及的通信路径的测试套件的测试选择方法显示出巨大的潜力:只有当被操纵和检查的信号实际上被改变的软件块处理时,才会执行测试。这些测试在所谓的「测试场」上执行。这些是具有不同配置的类似测试台的集合,应允许并行执行。

在某些情况下,测试对某些配置有要求(例如,测试用例是为某个动力传动系统设计的)。这样的测试场必须由中央服务器系统控制。除了知道测试台的状态和排队的测试之外,这样的系统还必须包含负载均衡组件,以确保最大程度的并行化,并且每个测试都是在具有正确配置的测试台上执行的。

某些测试台在设计时就考虑到其允许在运行时重新配置,例如,通过使用继电器连接冗余 ECU 变体。在这种情况下,负载均衡器的任务成为一个复杂的调度问题。这是目前只能由企业自定义服务器系统执行的任务。

功能安全测试是允许软件在进入道路的车辆的基本要求。如前所述,使用 FMEA或 STPA 等方法分析系统的需求和架构。在目标硬件上执行测试用例。这可以像功能集成测试一样集成到持续部署流水线中,因为它们仍旧使用了相同的技术。

然而,不同之处在于,安全分析也必须是在持续执行进行。

到目前为止所述的流水线各阶段可以或多或少地集成到一个持续流水线中中,并且,如果有足够的资源和适当且有效的工具可用,则不会妨碍白天或晚上的交付。

然而,道路上的验收测试将始终是手动操作的。这些测试中的许多可以自动化并在测试场上执行,有可能将此过程缩短到几天。尽管如此,这还是会造成延迟,并且考虑到每天有数次提交,并不是每个构建都会在包括此阶段的“持续流水线”中交付。

正如特斯拉所展示的那样,最后一个阶段,即使用远程技术部署,今天已经得到了良好的测试。随着4G等高速移动数据标准的出现,即使在偏远地区,更新大量软件也不再是问题。在这个阶段唯一的问题是,汽车可能在很长一段时间内无法接受服务。在这种情况下,无法持续交付,但这一问题只在非常偏远的地区出现。

An Outlook to Palo Alto

作为一家硅谷公司,特斯拉的目标是尽快将其创新产品推向市场。事实上,他们是唯一一家持续交付的汽车制造商。但其交付背后的机制显得并不保守。

根据特斯拉论坛,用户似乎对他们的汽车是否收到了最新更新感到困惑。相同的更新版本似乎在不同的日期部署到不同的车辆上。公开可用的数据库「特斯拉固件升级跟踪器」,使得特斯拉用户能够上传他们的汽车收到的更新数据。例如,在固件版本7.1的过程中,跟踪了 1000 多辆汽车和 5000 多个单一更新。

上图显示了四个代表性版本以及每天部署的汽车数量。第一个也是最明显的观察是,每个构建有某种形式的「生命周期」,在这个生命周期中,它逐渐被部署,直到它过时,并且这些生命周期重叠,有时在很大程度上重叠。

应该注意的是,重叠的构建常常一个接一个地部署在同一辆车上,因此可以安全地假设,在部署时,并非每个构建都可以「跳过」。「生命周期」可以分为三个阶段:

  • 「发布日期」是一个版本向广大公众开放的第一天,许多汽车将在这一天更新。
  • 「升级阶段」通常包括发布日期后的几天。绝大多数需要更新的汽车将在此期间更新。延迟很可能是由于无线数据服务的可用性引起的。
  • 「淡出阶段」,有时可能需要几周到几个月,每隔一天就会有几辆车收到更新。该阶段持续时间长的原因尚不清楚,但可能是由于车辆长时间未接收无线数据、每天更新的有限时间段以及对必须首先进行的其他更新的依赖性。

这个生命周期可以在上图中观察到,这个热力图显示了2016年8月和9月最频繁部署的更新。在该图中,还可以观察到另一个异常。几乎每一个主要版本都是在发布日期前几天部署的,在某些情况下甚至提前了四周。这种异常极不可能是由车辆中的错误引起的,例如更新时间报告不准确,因为它已被如此定期地观察到。

在特斯拉固件版本7.1的26个版本中,有15个版本已在50多辆汽车上发布,在发布日期前一天发布(请注意,特斯拉未提供正式发布日期)。此外,个别车辆或模型与此类「早期部署」的发生之间没有任何联系。一辆车可能会提前收到一次更新,下一次更新会在发布日期或坡道升级阶段进行,而其他一些更新则会更晚。

我们无法观察到有同一辆车两次提前部署。我们在这里观察到的可以描述为一种金丝雀发布模式。虽然我们只能猜测这可能是什么原因,但这的确是明确的证据,表明特斯拉确实在持续部署其软件,但假设构建是特斯拉源代码的指定修订版,最重要的是固定修订版,则延迟时间长达四周或更长。

持续交付: 在路上!

如此复杂且成本高昂的持续交付流水线很容易解释从软件交付到生产环境中最终部署的延迟数周。 该流水包含从其他领域已知的完整的连续输送管道,但必须冗余传递,并且每个 ECU 单独传递。

虽然在这一层面上可以使用熟悉的技术,并且很容易实现快速处理,但该阶段只是在复杂、嵌入式、分布式、安全关键系统中部署的第一步。车辆的所有这些特性对部署流水线提出了额外的要求,这需要时间。

最重要的是,验收测试和法律批准等必要的手动步骤可能会延迟新软件的部署。在这些步骤中发现的错误需要进行长时间的分析,然后才能将固定版本发送到管道中。

然而,我们观察到特斯拉汽车上发布的产品数量和频率都非常高。这表明,从技术角度来看,汽车行业中持续交付的大多数问题都可以解决,并且正在解决。

如果过去几年的发展速度能够保持,那么在未来几年内,整个行业将建立一个快速、全面、持续的集成流水线。