面向云原生的应用/算力架构设计

面向云原生的应用/算力架构设计

我在 2015 年做聚石塔容器平台 EWS 这个产品的时候,云原生 (Cloud Native) 这个概念还不像今天这样为大部分人所知,那时候对于云也好,容器也好,相关的争论比起今天有过之而无不及。前几天看到一篇文章,标题叫《微服务已死,云原生永生》,不禁莞尔,想起 5、6 年前,最流行的恐怕就是「微服务」的概念,当年也有人说「SOA 已死,微服务永生」类似的话,尽管似乎没人讲得清到底所谓的传统 SOA 和 Micro Service 有什么本质区别,SOA 嘛,字面意思就是面向服务的架构,微服务算不算「服务」?再从技术实现来看,服务发现、服务治理、分布式调用,似乎还是这些东西。在漫漫容器路上,也有人质疑,容器不成熟、不安全、不稳定。

基础架构很多时候是伴随着这些技术大神间的争论快速提升的,有时在早期的时间点很难决断到底什么是「正确」的技术,亦或是根本没有所谓正确,只有合适,架构师最擅长的是什么,其实是懂得平衡和妥协。但是,技术趋势的车轮始终会向前推进,一不留神就会产生技术的代际差距,今天业界对集市的信奉远比大教堂要兴盛得多,谈到云原生,还是要回顾一些标志性事件。

想起还是 2013 年的时候,Docker Engine 刚刚被 Release 出来,当时是为了把一个环境打包成镜像,解决本地研发环境和生产环境差异化的问题,说实话我在 13 年也没看出来有什么大的价值,但这个设计改变了今天我们很多软件交付的架构。

2015 年,Docker 方兴未艾,各家争鸣,Docker 的 Swarm 号称自己有最纯正的官方血统,Twitter 的 Mesos 则以 Marathon 占据了当年容器编排的半壁江山,Google 做 Omega 的兄弟们最终没有战胜 Borg,却将自己几年的工作成果用 Kubernetes 的名义散播出来。后来的故事大家都知道了,在这一场战争中,K8S 成为了行业事实标准,Golang 成为了容器乃至云的标准语言,CNCF 成为了云计算界的 Apache,进而将「云原生」这一概念持续定义和发酵。

到了今天,所有人都在提云原生,有点像当年都在谈微服务、区块链一样,但云原生究竟是什么,有点像量子力学,有很多种诠释。

云原生事实上还是一种标准,OAM 是一种标准,CNI 也是一种标准,CNCF 提供的软件族也是标准的集合。云原生标准的价值不会亚于在工业时代所定义的市电标准电压。今天在中国,常用电器都要适配 220V 的电压,这让一件电器作为交付物可以在全国各地被自由使用,而国外则会有其它标准,所以我们还需要变压器、转接头,像不像今天全球的云计算?更具体的,我们以当前可观测性的事实标准 Prometheus 为例子,但凡实现了 Metrics 的标准,就能被无缝集成,像不像是插头和插座的关系?

对于云厂商来说,云原生代表了应用架构的未来,只有构建在云上的应用才预备包括弹性计算在内的诸多优点,最初谈上云,是先把架构原封不动地搬到云上,把基于 Xen/KVM 的虚拟机上的软件放到 ECS 上去跑,但实际上并没有充分体现云的价值,比如「免运维」,所以我们会说,在 ECS 上自己搭 MySQL 不是云原生,因为你还得自己做主备、快照、binlog 等等,而从 GAE 开始的 PaaS 则是让大家直接使用各类计算 / 存储服务,到今天,对于数据库来说,选择 RDS 乃至 PolarDB/OceanBase/TiDB 才是云原生,因为数据库的基础运维也好、扩展性也好,可以不用再被用户关心了。开发者当然是很高兴的,没有人愿意每天担惊受怕数据丢失的风险。

而对于用户来说,社区也好,Rancher 这样提供方案的公司也好,对他们来说,云原生是解决 Vendor Lock-in 的良药,也是 CNCF 在讲的故事,中间件和基础服务解耦,对于 GAE 形态的 PaaS 来说,尽管服务对外的 API 是 simple 的,但底层数据结构导致的迁移就没有那么容易了,于是服务的提供方由平台变为开源可独立部署的中间件,多云 (Polycloud) 架构开始盛行。仔细想想,Netflix 自己做那么多中间件,有一部分也是怕万一被 AWS 坑了吧。

但不管基础架构如何变迁,应用架构总是存在,好的应用架构应该是能适应乃至充分利用基础设施的变化,才能保持它的活力和技术竞争力。例如在工程技术领域。很多时候 I/O 的消耗是最大的问题,大部分业务应用本质上都是 I/O 密集型而非 CPU 密集型。为了优化针对机械磁盘的 I/O,过去有非常多的软件做了各种算法把随机读写转成顺序读写,把用户态拷贝转成内核态拷贝,为了性能有时在 Write-Back 和 Write-Through 之间做取舍,包括 Linux 的 I/O Scheduler 也是为了解决这个问题。但当 SSD 出现后,很多事情开始发生变化,Facebook 首先开始用 SSD 做 I/O Cache,后来国内一些互联网公司部分核心数据库用 Fusion IO 存储,紧接着业务数据库也开始转为使用 SSD,但当年我们切 SSD 的时候遇到了问题,最终定位是由于 CFQ 调度器导致的,再例如早期基于 LSM-Tree 的 LevelDB/RocksDB,后来针对 SSD 也都改进了读写算法,最后大家都明白了,对于新的存储介质,很多算法和设计都要演进才能发挥它的威力。

以前写过一篇文章,[《面向 GC 的 Java 编程》](https://hesey.wang/2018/11/gc-oriented-java-programming.html),其实还有非常多类似的内容值得去讲,比如面向 SSD 低延迟寻道存储的软件设计,软硬一体的架构设计往往能最大化榨干性能,想象一下,如果我们的软件不用 SIMD 去做并行计算,不用局部性原理去加速数据读取,不用 DPDK 去做网络数据流的计算,不用 FPGA 等硬件做辅助虚拟化,很难支撑今天大规模的分布式计算,延伸想一下矿机是如何软硬一体运作的。

对于云原生也是相似的,如果没有利用好云的特性,在今天各行业客户都要拓展更广阔市场的前提下,大家都想要异地多活、全球部署的能力,如果还是用老的 IDC 思路去设计应用架构,很难。

就像过去我们通过避免共享数据的方式去解决并发编程带来的复杂性问题,在云的时代,我们或许应该有一种新的编程模型和架构范式,姑且称作是 COP(Cloud-Oriented-Programming) 吧,新的范式应该能充分利用弹性和云的其它特性去解决架构的扩展性问题。举个例子,过去我们在设置系统限流时通常使用预先定义的固定值,如 load/cpu/QPS 等,好处是实现简单且有效,但当环境如机器配置等发生变化时,通常需要采用重新压测的方式定义新的限流阈值,但回想一下 TCP 是怎么做 flow-control 的,也许有人会觉得传统的慢启动效率太低,如果是 BBR 呢?很多软件已经在用 back-pressure 去更好地解决限流的问题。面向不确定性编程,机制是确定的,但参数可以是自适应的,这恰恰符合云原生的特点。

云提供的最核心能力,我的看法还是算力,不在这里展开谈弹性这些老生常谈的问题。有 2 点是我对云的算力架构的一些想法。

首先,云的算力是分布式的,而分布式的算力是需要被「调度」起来的。在操作系统中,是在线程或是进程间通信视角去调度 CPU Core 的算力;在 IDC 中,在机架、机房(可用区)视角去调度不同服务器的算力;而在云上,我们在全球数据中心的基础上,一家小公司,也能在跨大洲、大洋的维度去调度 IDC 级别的算力(当然,从非技术因素来说,GDPR 等规定给算力和数据打上了政治标签)。大公司当年做异地多活,要自建或者租用大的跨省 IDC,但不是每个小公司都能做这么大投入,今天在云上,只要架构合理,完全可以小成本完成。类似的,用户还可以把计算密集型业务放到收费低的偏远 IDC 降低成本。从这个意义上来说,云厂商的重资产基建实现了算力的普惠。而对于这种能做到跨大洲的算力调度得好不好,就要取决于应用架构面向这种维度和视角的算力的设计了。另一方面,在澎湃算力的场景下,面向弹性的设计也很重要。举个例子,单机多线程编程往往不是面向弹性的,而基于 MQ 的设计,消费者算力不够时可以通过水平扩容解决,便是面向弹性的设计。

面向弹性的确是云原生的一个特征,但并不像眼见得那么容易,有钱就能解决访问量问题的方案基本等同于水平扩展友好的方案,但这类扩容和架构的耦合性还是比较高,新浪微博花了几年时间在阿里云上打造了自动化扩容平台,给明星们的绯闻热搜保驾护航,但这个能力今天还不是大部分公司都有的能力。

其次,跨 Region 的网络延迟扩展了计算机体系的延迟数据看板 ([Latency Numbers Every Programmer Should Know](https://gist.github.com/jboner/2841832)),对于程序员来说,不同的延迟对应不同的存储用途设计,原来我们都知道从寄存器、L1/L2/L3 Cache、内存、磁盘、网络各自的延迟差了上万倍。今天在中国,跨省的网络延迟在 30~80ms 左右,通过 CN2 到香港大概在 60ms 左右,中国大陆到美国大概是 200+ms。事实上,今天在做的各种容灾架构、异地多活都是基于这些关键数据,这是基于 MapReduce 的核心观点:移动计算比移动数据快,最初同城双机房容灾约 1ms 的延迟,到杭州-上海约 10ms 延迟,再到杭州-张北约 30ms 的延迟。对架构师来说,就要去看在跨 IDC 的维度上,如何基于几十毫秒的延迟去调度算力,原来 for 循环简单搞定的,这时候可能就要命了。过去我做了很多性能优化的项目,大多数时候性能就是在和延迟死磕。

面向云原生的应用架构,从某种意义上说,因为获得了更大算力的可能性,反而需要更被约束在最佳实践的模式里,最简单的例子就是 dockerfile,一个标准化的文件可以让软件瞬间分发到几千台服务器,但写过的人都知道,你必须非常克制和准确地描述你的需求,不能像过去一样,基于中心化的基础设施,配置满天飞。也可以参考 [The Twelve-Factor App](https://12factor.net/) 里面的章节,不过我对谈到云原生应用架构就谈 12-Factor 不是很感冒,一是这个标准撰写的时候比较早了,二是它还更偏微观的工程视角。所以业界对于面向云原生的软件交付有了「不可变基础设施」这个核心原则,也是我现在在做政务中台的标准化系统交付时最核心的理念之一。

先谈这些吧,有空再写。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注