设计全球级的分布式、任务关键型应用 - 从实际项目中得来的教训(下)

jopen 9年前

 

此篇为设计下一代智能DNS和流量管理平台的 NSONE 公司的创建者和CEO Kris Beevers 的客座文章的 第二部分 。点此阅读 第一部分

集成和功能测试非常关键

每一个软件开发的课程都会反复强调单元测试的重要性。无论你是正在进行测试驱动的开发还是只想炫耀一下代码,没有进行单元测试的情况下,你仍然无法肯定代码的功能。而且,随着代码的修改,单元测试要不断进行,从而保证代码的功能正确性。

在一个分布式应用中,即使你拥有全世界最好的功能测试覆盖,系统仍然有可能出问题。 单元测试永远不够!

你需要测试 不同子系统之间的交互情况 。例如,如果配置数据的一个特殊部分变化,会有什么影响呢?它会不会影响到子系统A与子系统B之间的通信呢?又如,如果你改变了一种消息格式,产生和处理这些消息的子系统还可以继续与其他子系统进行通信吗?代码修改后,需要依靠4个不同后端子系统给出的结果的一类特殊请求还会得到正确反馈吗?

单元测试回答不了这些问题,但是集成测试可以。在集成测试环境中投入时间和精力,并且在研发和部署过程中要适时进行集成测试。理想情况下,最好一直在产品系统中运行集成测试。

没有中断维护这种东西

如果你正在设计一个真正的任务关键型应用(客户需要依赖该应用来打理生意),那么该应用可能不需要开关。它永远不需要停止工作。那么,你永远都不会有终端维护的权利和可能。即使是世界上最复杂的后端架构也会有需要改变的时候。

这就是你需要 认真思考架构 的其中一个原因。几个小时的认真思考可能可以避免后期几个月的漏洞弥补时间。

Kris就遇到过一个真实的情况。NSONE的团队从一开始在架构设计阶段就投入了大量精力,使得项目沿着正确的方向前进。但是, 该团队仍然有没有考虑到的问题——平台接收高频率的数据反馈 。这些反馈会影响到如何对复杂流量管理配置下DNS记录的请求进行回答。数据反馈可以应用到多个DNS记录中,也就是说一个服务器负载信息的反馈可以通知牵涉到服务器上多个网站的流量路由决策。当时,NSONE团队假设只有一个单独的数据反馈连接到若干个DNS记录。那么,就可以通过把到达系统的数据反馈根据每个所连接的DNS记录发送到相应的边沿位置来节约大量时间和精力。然而,团队的假设是不成立的。一些客户把数据反馈连接到了数千个DNS记录。前期的偷懒导致内部发生DoS,而且随着系统的扩展,问题会越来越严重。

其问题在于:团队不能通过发送比较少的控制消息来解决以上问题。Kris等需要修改数据模型、消息模型和一些一直在运行的系统。前期只需要2-3个小时就可以避免的问题却引起了 6周的“马拉松” ——头脑风暴式的会话、相当复杂的反应、大量正确性测试以及一系列的小心翼翼的部署和迁移等。所有这些都只是为了在不中断系统的情况修复以上的问题。

在极端情况下,新框架或者代码的 每一次部署都会是无缝的 ——仔细的规划、滚动重启和集成测试等。一旦系统在服务用户,就永远不能关机。

非常小心的进行自动部署和配置管理

现在的开发生态系统拥有大量的自动部署和配置管理的工具,包括Chef、Puppet、Ansible、SaltStack、Terraform等数不胜数。选择使用哪个工具就像决定哪个模型更好一样根本无关紧要。 真正有关系的是你要使用这些工具。 即使是在公司的初创阶段,即使事情看起来非常容易搞定,你也千万不要手动管理配置或者部署——你会犯错、你会限制自身能力的提升以及使得以后的自动化变得十分艰难。

但是,要小心:权力越大,责任越大。 自动部署工具使得你可以把平台迅速搞砸。

利用Chef管理所有主机的IP表? 一个疲惫工程师的一个无意的按键动作就可以使你的平台全部瘫痪。 把一个经过反复测试的特性部署到开发用的环境中?一个真实环境和模拟环境中略有差别的流量就可以使得端到端自动部署的产品崩溃。要善用自动化工具!

Kris等就利用Ansible管理NSONE的配置和部署。 这是一个非常好的工具。 NSONE团队本可以把一切自动化,实现一键部署新的DNS传输代码到所有的边缘位置。但是,他们没有这么做。 他们选择从最低流量到最高流量,一个设备一个设备的部署。在一个设备内部,他们甚至挨个核或者挨个服务器,运行复杂的单步功能测试。 在移动到更加关键的设备前,该团队还会花费数十个小时或者几天时间来研究度量,确认没有潜在的问题。而且在部署之前,他们还会进行应用程序代码以及Ansible配置等的仔细审查。

对于你的团队和应用而言,要适时的使用自动化。但是也别忘记,自动化加速项目发展的同时,也可以加速项目的死亡!

做好“消防演习”

坏的事情总会发生。每一个科技公司都会遇到服务器崩溃的情况。NSONE团队在项目之初就想到了 各种服务器崩溃的情况 ——磁盘失效、网卡失效、RAM失效、内核崩溃、虚拟环境中的邻域效应等等。各种情况都可能非常容易的引起服务器失效。

此外,电源供应也可能出问题。还记得飓风桑迪吗?NSONE的员工不得不不断加注柴油,才能保证系统在发电机的帮助下保持工作。

而且,光缆也可能会断。BGP有可能被黑。你也可能遭遇64k的ICMP报文、DNS和NTP放射放大攻击以及等等。你该怎么办呢?

唯一能做的就是提前进行演练。在坏的事情发生之间进行演练。Netflix的Chaos Monkey就是一个很著名的例子。你不可能直接模拟可能遇到的每一种状况,但是你可以尽力去模拟你的反应。唯一能够保证你在遇到状况时可以保持镇定的方法就是,使用你已经提前放置好的工具,然后快速反应。

最小化表面积

随着你的应用程序变得越来越分布,暴露给恶意攻击的表面积也会在不经意间迅速增加。 看好你的系统,并最小化系统暴露给互联网的表面积。

架构中的每一个角色都应该只向那些允许访问的系统集暴露其服务接口。面向互联网的系统只提供面向用户的那些服务。在后端,系统之间应该尽可能的通过私有 IP空间进行通信。否则,也应该通过加密的通道进行数据传输。无论是使用像AWS的安全组这样的提供商的工具,还是使用路由器ACL或已经防火墙进行ip 表规则的自动管理,你应该尽可能的是防火墙。 首先拒绝,然后在逐个按情况放行。

永远都不要允许通过SSH等对产品系统进行直接访问。人们应该通过采用多元素认证、端口试探、IP白名单等重重保护的主机来进入系统。确保主机分布于各种网络、多个地域。进入产品架构的权限应该也限制为只有堡垒似的主机才可以。

有很多种策略可以帮助你看好系统。你只要去寻找,然后从一开始就考虑到系统的安全,使得安全成为系统的一部分即可。千万不要随着系统的扩展而淡化安全因素。

了解供应商环境

几乎每一个现代技术公司都选择把第一代产品放置在AWS、DigitalOcean或者其他低价低门槛的云服务提供商中。互联网一直在快速发展。由此,云服务提供商也在近些年迅速增多。

绝大部分公司都没有必要在扩张时着急放弃AWS。但是,每一个公司都应该尽可能的保留可选性。

你或许会发现应用程序中的一些负载在裸机中运行效率最高,也可能发现你需要一个在澳大利亚的CDN,或者在没有AWS存在的市场中获得很多关键性的用户。那么,花一些时间来让自己熟悉框架生态系统的各个方面——IaaS供应商、场地出租、DNS、CDN以及监控等等。在早期设计架构时,就考虑到以后可能需要迁移的地方。 时刻准备着迅速迁移。

NSONE公司就操作着一个全球任意播的DNS传输网络。当建立原型的时候,AWS起了很大的帮助。但是在平台部署之前,他们就不得不迁移到别的地方来满足网络需求。设计一个非常具有竞争力的任意播DNS网络很大程度上依靠公司与框架供应商、网络供应商等进行协商,获得尽可能多的控制权。在很多情况下,Kris等人不得不帮助供应商来找到合适的合作方式。

千万别忘了:基础设施运营商也都是怪胎,他们也经常欢迎各种新的想法和挑战。时刻了解生态系统,做好准备工作。

总结

Kris已经分享了自己在设计和扩展分布式、任务关键系统时的经验和教训。或许,你不能在项目初创之时就把一些都规划好。但是,你可以努力把自己放在一个更好的位置,来迎接公司扩张所带来的挑战。

此外,正在设计互联网产品的公司也应该认真思考如何以分布式的方法来设计产品架构和扩展之路,从而为用户提供最大的性能、最好的可靠性和安全性。