@xuemingdeng
2021-11-01T08:59:53.000000Z
字数 8655
阅读 291
摘要:
Dan Woods在这篇文章中分享了为总统竞选构建技术平台所面临的独特挑战。Woods还描述了机器学习如何帮助他们降低运营成本,以及做出了哪些架构选型。
正文:
在2020年美国总统大选中,我担任拜登阵营的首席技术官。在2020年11月的QCon Plus大会上,我做了一个演讲,分享了我们的团队为竞选而做的架构以及为了解决各种问题而开发的特殊工具。这篇文章是这次演讲的提炼总结。
作为首席技术官,我领导着竞选阵营的技术团队。我负责整体的技术运营工作,与我的优秀团队一起构建出了总统竞选活动中最好的技术栈。
我们涉足了所有东西,从软件工程到IT运营,再到网络安全,以及它们之间的方方面面。
我作为Target的一名工程师加入竞选阵营,专注于基础设施和运营,构建高可伸缩且可靠的分布式系统。在2016年希拉里竞选美国总统期间,我作为Groundwork的员工参与了竞选技术平台的工作。Groundwork是一家专门为竞选活动提供技术支持的公司。
错综复杂的竞选组织结构对技术选型的方方面面都有很大的影响。
图1列出了竞选组织的各个部门,让你对各个部门的职责有一个大致的感受。
图1:竞选活动的组织结构
每一个团队都有自己关注的方面。尽管技术受到媒体的关注,但从政治角度来看,它并不是最重要的。我们的目标是接触选民,让他们发声,让尽可能多的人加入民主进程。
在竞选活动中,技术需要完成任何需要它完成的事情。竞选活动的几乎每一个方面都需要一种技术形态。我的团队需要负责构建和管理我们的云平台、所有的IT运营工作、供应商入驻,等等。
简单地说,我们构建技术的方式就是通过“胶水”将供应商和各种系统“粘合”在一起。我们所处的环境让我们不能像开发成熟产品那样做。
如果我们需要某种工具,但没有相应的供应商可以提供或者找不到开源的,也没有足够的预算,我们就自己开发。
竞选活动技术要做的大部分事情是将数据从A点移动到B点。严格来说,就是创建了很多S3桶。
另外,我们在过去几年开发了很多东西。我们构建了数十个网站,有大型的,也有小型的。我们为外勤团队开发了一个移动应用。我们为竞选团队成员开发Chrome插件,帮助他们减轻工作负担。我们还开发了Word和Excel宏来提高工作效率,尽管这些事情很枯燥无味。在总统竞选活动中,时间就是一切,我们所做的每一件可以节省时间的事情,哪怕只节省了一分钟,也是值得我们投入的。
我们所做的大部分工作是通过自动化任务减轻竞选团队的工作负载。竞选活动技术的精华部分可以提炼成:数据、数字化、IT或网络安全。实际情况是,竞选活动的技术都是关于这些东西,它是竞选活动重要的组成部分。
我们的团队不大,但我们生气勃勃,都是充满斗志的技术狂人。我们接下所有的需求,并尽力做到最好。在竞选活动期间,我们构建并交付了超过100个服务。我们构建了超过50个Lambda Function,提供各种各样的功能。我们还为初选开发了一个叫作“Team Joe”的关系结构组织移动应用,可以把数千名热切的选民和志愿者跟他们认识的人联系起来。
我们进行了超过10000次部署,零停机,并保证了稳定性和可靠性。
我们基于云机器学习实现了自有搜索平台,具备健壮的自动化能力,为活动节省了数万小时的人工工作量,并为我们带来非常有深度的见解。我们基于强大的机器学习基础设施构建了很多服务。
竞选活动一开始就承若,我们将以高标准来要求自己,并确保不接受来自伤害地球的组织或别有用心的个人的捐赠。为了信守承诺,我们建立了一个自动化框架,几乎可以实时地审查我们的捐助者是否遵守联邦选举委员会的规定和竞选承诺。
我们赢得了初选,并希望在大选的网站上打造一个新的品牌。所以我们为joebiden.com带来了一种全新的体验。
竞选活动的一个重要方面是直接接触选民,让他们知道他们所在地区发生了什么,或者是什么时候可以投票。为此,我们建立了一个全国范围内的短信推广平台。在这一过程中,我们节省了数百万美元的运营费用。除此之外,竞选总部和州总部也开始IT化运作,最终在疫情隔离开始时成为一个可以完全远程运作的组织。
我们通过这些确保了我们拥有世界级的网络安全,这是我们所做一切的核心。
但不管怎样,竞选活动中的任何一项工作都不只是个体的责任,每个人都需要在任何时候、任何地点提供帮助,无论是打电话让选民去投票、发送短信还是收集签名让他们参加投票。这些我们都做了。
我们所做的一切都依赖于云基础设施。最重要的是,我们没有花太多宝贵的时间在重新创造轮子上。我们使用Pantheon来托管主网站joebiden.com。对于非网站的工作负载、API和服务,我们把它们部署在AWS上。此外,我们在AWS部署了一个小型Kubernetes,帮助我们更快地交付简单的工作负载(主要用于CRON作业)。
我们仍然需要快速交付大规模服务。作为一个小团队,我们的构建和部署管道的可重复性就变得非常重要。对于持续集成,我们使用了Travis CI。对于持续交付,我们使用了Spinnaker。
服务在云端部署并开始运行了之后,它们都需要一组核心功能,比如如何找到其他服务并安全地访问配置和秘钥。为此,我们使用了HashiCorp的Consul和Vault,这帮助我们构建了完全不可变和差异化的开发环境和生产环境,很少需要手动操作服务器——不是完全不需要,但真的非常少。
很大一部分技术工作是由分析团队完成的。为了确保他们能够获得最好的工具和服务,我们将分析数据保存在AWS Redshift中。它提供了一个高度可伸缩的环境,可以对资源利用做到细粒度控制。
我们将PostgreSQL作为服务的后端数据存储。
从运维的角度来看,我们希望应用程序的日志记录活动有一个中心视图,以便我们能够快速地排除和诊断问题,实现最快的故障恢复。为此,我们部署了一个ELK栈,并使用AWS Elasticsearch来存储日志。日志对于应用程序来说非常重要。指标提供了服务运行状态的洞见,在与我们的轮岗待命机制集成时,它们是非常关键的信息来源。在服务水平指标方面,我们部署了Influx和Grafana,并将它们连接到PagerDuty,确保不会遇到未知的中断。很多自动化工作负载和任务并不适合使用传统的部署模型,对于这些,我们会尽可能使用AWS Lambda。我们还将Lambda用于需要与AWS生态系统其他部分集成的工作负载,以及基于数据呈现的扇出作业。
我们构建了一个真正的多语言环境。我们用各种语言和框架构建了服务和自动化机制,我们能够以无与伦比的弹性和速度做到这些。
我们在竞选期间涉及了很多领域。要把我们所做的每一件事都彻底细化需要花费一生的时间,所以我将挑选一些有趣的架构,这些架构是由负责软件工程的技术团队做起来的。当我深入讨论架构时,请注意,对于我所讨论的每个细节,至少还可以做十几次分享,它们涵盖了我们整个技术团队所完成的工作的广度。
正如前面提到的,竞选活动一开始就承诺拒绝某些组织和个人的捐款。要大规模做到这一点,唯一的办法是让一群人定期梳理捐款,通常是每季度梳理一次,并标出可能匹配筛选条件的捐款者。
这个过程很困难,很耗时,而且容易出错。如果是手工操作,通常需要为个人捐赠的金额设定一个门槛,然后研究捐赠者是否符合我们标记的有问题的类别。
为了提高效率,我们构建了一个高度可伸缩的自动化流程,将捐赠的细节与我们想标记的一组标准相关联。我们每天都会启动一次流程。它将NGP VAN(这是所有捐款信息的真实数据来源)中的捐款数据导出到CSV文件,并转储到S3中。将CSV文件存储到S3将会触发一个SNS通知,该通知反过来将激活一系列Lambda Function。这些Lambda Function将文件分割成更小的块,将它们重新导入到S3,并启动捐款者审查流程。在这个工作流执行时,我们可以看到有大规模的Lambda,多达1000个并行执行的捐款者审查代码。
例如,我们承诺不接受来自天然气和石油行业的说客和高管的捐款。这个过程只需要几分钟,就可以完成对捐赠者的全面审查,并将其与说客、外国代理以及油气公司高管的黑名单进行核对。
在这个流程完成之后,标记的条目将被整理成单个CSV文件,然后将该文件重新导到S3,供下载使用。随后,SES将向开发人员Danielle发送一封电子邮件,表明标志已准备好,可以进行进一步的验证。验证之后,结果被转发给合规团队,他们将采取适当的行动,可能是进行退款或做进一步的调查。
图2:自动化捐款者审核流程
如果只是说这个过程为竞选节省了大量的时间,那也太轻描淡写了。捐款者审查管道很快成为技术平台的核心组件和竞选活动的重要组成部分。
在一开始,我们的团队规模还很小,但我们有很多供应商和云服务,没有足够的时间和人手来检查这些服务的安全状态。我们需要一种简单的方法来制定规则,针对关键的面向用户的系统,确保我们总是遵循网络安全最佳实践。
Tattletale正是为此而开发的一个框架。Tattletale是我们在竞选活动中构建的最重要的技术之一。
我们设定了一组任务,利用供应商系统API来确保开启了双因子身份验证之类的功能,或者如果用户帐户在系统中是活跃的,但该用户有一段时间没有登录,我们就会收到通知。休眠帐户存在安全风险,因此我们希望确保所有配置都是面向最低权限的。
此外,Tattletale中的规则可以检查负载均衡器是否无意中暴露在互联网上、IAM权限的范围是否太广,等等。在审计规则集之后,如果Tattletale发现了违规行为,它会通过Slack频道发送通知,让相关人员进行进一步的调查。它还可以通过电子邮件通知用户有违规行为发生,这样用户就可以自行采取纠正措施。如果超出了某个阈值,Tattletale会在Grafana中记录一个指标,触发PagerDuty升级策略,并立即通知待命的技术人员。
图3:Tattletale架构
当我们没有时间或资源查看安全问题时,Tattletale就成了我们的网络安全之眼。它还确保了我们遵循一套公共的网络安全标准,并尽可能保持最高标准。
当我们的内部工具达到一定的复杂性和广度时,我们需要更好地管理这些API。为了管理那些系统,我们需要合并我们所构建的UI。我们还需要标准化安全模型,这样就不会到处都是自定义身份认证和授权。
因此,我们创建了Conductor(因为拜登喜欢火车,就用售票员来命名)、我们的内部工具UI,以及Turbotots(我们根据土豆来命名的众多工具之一),也就是我们的平台API。
图4:Conductor和Turbotots
图4实际上是对一个复杂架构的简化图,但这些概略的描述足以让你了解Conductor和Turbotots做了哪些事情。
Conductor成为我们为竞选工作人员开发的所有内部工具的统一入口。换句话说,这是所有参与竞选活动的人想要访问我们提供的服务都必须经过的地方。Conductor是一个React Web应用,通过S3来部署,并通过CloudFront来分发。
Turbotots是一个统一的API,它为我们所做的一切提供了通用的身份认证和授权模型,与Conductor通信也是通过它。我们在AWS Cognito上构建了Turbotots的AuthN和AuthZ部分,这节省了大量工作,并通过G Suite/Google Workspace提供了简单的单点登录(SSO)功能。身份验证是通过解析JWT令牌来实现的。为了在前端管理好它们,我们使用了AWS Amplify的React绑定,它被无缝地集成到应用程序中。
图4右侧的内容有点难以理解,我会尽量简化。正向API是一种API网关,包含了完整的代理资源。API网关可以很容易与Cognito集成,这帮助我们实现了API请求的安全性。与API网关集成的Cognito授权器也会在请求通过代理之前执行JWT验证。我们可以通过它清楚地知道请求在发送到后端之前已经过完整的验证了。
在开发API网关代理资源时,你可以通过VPC网络负载均衡器让运行在VPC中的代码连通起来。这很复杂,但据我所知,它有效地在AWS内部的API网关和你的私有VPC之间创建了一个弹性网络接口。反过来,NLB被附加到一个包含一组NGINX实例的自动伸缩组中,作为我们的统一API。这就是Turbotots的主要部分,本质上就是在VPC中运行的所有内部服务的反向代理。在这种模式下,我们不需要向公网公开任何VPC资源。我们可以依赖AWS内置的安全装置,这让我们大家都轻松多了。
当请求到达Turbotots时, Turbotots中的轻量级Lua脚本就会提取JWT令牌的用户信息部分,并将该数据作为新的请求负载的一部分传递给下游服务。当请求到达目标服务时,它就可以检查用户信息,看看是否对该用户的请求做了验证。
用户可以被添加到Cognito的授权组中,这样他们就可以在下游服务中获得不同级别的访问权限。最小权限原则在这里仍然适用,并且没有默认权限。
Conductor和Turbotots作为一个统一的用户界面,为内部工具提供了与G Suite帐户的无缝SSO集成。在下一节,我们将介绍如何使用相同的架构向非内部用户公开部分API。
Pencil是一个点对点短信平台,从一系列简单的早期需求开始,发展成为一个我们为之投入了大量时间的庞大架构。
自己构建P2P短信平台的初衷是为了节省成本。我知道,我们可以通过Twilio发送短信,这比任其他何一个供应商的收费都要低。但是,在一开始,我们并不需要供应商提供的大量功能,我们很容易就能构建一个简单的短信系统来满足当时的需求。
随着项目越来越流行,它的规模在急剧扩张。Pencil被成千上万的志愿者用来接触数以百万计的选民,并成为我们选民外联工作流程中的一个重要组成部分。你收到的来自竞选志愿者的短信很可能是通过Pencil发送的。
Pencil的外部架构看起来有点眼熟,我们重用了Conductor架构,只做了一些配置修改,不需要修改代码。
图5:Pencil的架构看起来很像Conductor
Pencil的用户组件是一个通过S3部署并通过CloudFront分发的React Web应用程序。React应用程序反过来与连接到Cognito授权器的API网关资源对话。
用户通过电子邮件被邀请到Pencil平台,这个在Cognito注册账户的过程是独立的。在用户第一次登录时,Pencil会自动将他们添加到Cognito的用户组中,这样他们就可以访问Pencil的用户API。
从用户的角度来看,是他们点击了一个注册链接触发了短信发送。经过竞选活动数字团队的简单操作之后,用户(志愿者)就可以开始工作了。
这是非常容易做到的,因为它是建立在先前的Turbotots基础设施之上。
Tots的架构与Turbotots不同,它位于Turbotots的下游。在之前的图中,Tots解析了Turbotots下面列出的很多服务,这些服务是Tots架构的一部分。
图6:Tots平台
Tots是一个重要的平台。随着我们构建了越来越多的机器学习工具,我们很快意识到,我们需要合并逻辑和简化架构。
在各种项目中,机器学习最主要的应用场景是从文本块中提取词袋。我们将这些数据保存到Elastic的索引中,让数据主题可用于快速检索。我们使用AWS Comprehend从文本中提取词袋。这是一个很棒的服务——给它一段文本,它就会告诉你在这段文本中出现的人、地点、主题等等。文本以多种形式进入平台,包括多媒体、新闻文章和文档格式。
图6Tots平台中的很多机制都涉及在将内容发送给Comprehend之前需要如何处理。这个平台上的流程为竞选活动节省了成千上万个小时的人力工作,包括花时间转录现场活动内容(如辩论),并将它们转换成文本格式,以便竞选人员日后阅读。
CouchPotato帮助我们解决了计算机科学中最难的问题:在Linux上制作音频。CouchPotato对我们来说非常重要,因为它为我们制作音频和视频材料节省了大量时间。这也是我们所构建的最强大的技术平台之一。
图7:竞选活动用CouchPotato处理多媒体任务
CouchPotato是图7所示的架构的主要角色,但从连接多个独立服务的连线可以看出,在整个过程中,它也需要其他的支持角色。
CouchPotato的主要功能是将URL或媒体文件作为输入,在隔离的X11 Virtual Frame Buffer中打开媒体文件,在PulseAudio播放设备(FFmpeg)上收听回放,然后记录X11会话的内容。这就产生了一个MP3文件,然后将它发送到AWS Transcribe进行自动语音识别(ASR)。
在ASR完成之后,它会过一遍生成的文本,纠正一些常见错误。例如,它很少能把市长Pete Buttigieg的名字写对。我们用RegEx做一些常见的文本替换。
在完成这些工作之后,转录文本将被发送到Comprehend,它将提取其中的词袋。最后,文本被索引到Elasticsearch中。
CouchPotato的关键之处在于它能够利用FFmpeg的分段功能生成更小的音频块。它会为每个片段执行整个过程,并确保被索引到Elasticsearch中时保持统一。
这种分段是CouchPotato最初的特性之一,因为我们用它来实时记录辩论内容。通常情况下,在竞选活动中,会有一群实习生观看辩论,并把辩论内容打出来。但我们没有一大批实习生来做这个事情,所以就有了CouchPotato。
不过,分段带来了顺序问题。有时候,Transcribe会在前一个片段完成之前完成后一个片段的ASR,这意味着所有需要异步完成的工作在处理完毕之后需要按照正确的顺序重新编译。对我来说,这听起来就像是一个反应式编程问题。
我们花了很多时间来解决异步处理事件的顺序问题。这很复杂,但我们做到了。
这个时候,DocsWeb就派上用场了。DocsWeb将CouchPotato的片段转录输出发送到Google Doc,让我们可以几乎实时地与竞选活动的其他成员分享转录文本——除了从Transcribe到Elasticsearch有点延迟,但这并不算太糟。我们记录下了每一场辩论以及大量其他媒体内容,这些内容需要一群实习生花上一辈子的时间才能完成。
解决这些片段的顺序问题,其中有一部分工作是弄清楚如何替换CouchPotato实例——比如,在部署期间或实时转录事件已经在运行的时候。关于这个话题还有很多要说的,比如我们如何制作音频帧拼接,以便可以逐步淘汰旧的实例,并逐步引入新的实例。这涉及了太多的内容。要知道,HotPotato做了很多工作,让我们可以在零部署和零状态损失的情况下对CouchPotato进行热部署。
KatoPotato是整个架构的切入点。它是一个编排引擎,协调CouchPotato、DocsWeb和Elastic之间的数据移动,并充当启动CouchPotato工作负载的主要API。此外,它还负责监控CouchPotato的状态,并决定HotPotato是否需要启动新实例。
有时候,Linux上的音频捕捉会罢工。CouchPotato能够向KatoPotato报告它是否真的在捕捉音频。如果它没有在捕捉音频,KatoPotato可以启动新的CouchPotato实例,并通过HotPotato进行替换。
CouchPotato是一个建立在云机器学习之上的平台,可以让我们快速了解多媒体内容,不必花费宝贵的人力时间在这样的任务上。这是一个伟大的工程,我很高兴它能够为我们带来价值。
关于我们所构建的技术,还有很多其他东西,比如关系组织应用程序、将S3和RDS连接到Redshift的数据管道,或者我们在整个过程中创建的微型网站。我还可以分享我们是如何在一个快节奏和充满活力的环境中合作的。
我已经分享了一些我们为2020年总统竞选所做的最有趣的架构。很高兴能与大家分享这些,我也期待今后能与大家分享更多内容。
Dan Woods是Shipt的首席信息技术官和网络安全副总裁。在担任2020年总统竞选首席技术官之前,他是Target的一名杰出的工程师,专注于基础设施和运营,构建大规模、可靠的分布式系统。在加入Target之前,他参与了2016年希拉里竞选团队的技术平台工作。在参与总统竞选技术工作之前,Woods曾在Netflix的运营工程部门担任高级软件工程师,并帮助创建了Netflix的开源持续交付平台Spinnaker。Woods还是Ratpack Web框架的开源团队成员。他是《Learning Ratpack》(于2016年由O'Reilly出版)一书的作者。