-->
我们在之前的 告别爹味十足的 FinOps 吧 里提到过,云应该被看做一台单机超算。随着高性能网络的普及,云上节点之间的网络带宽已经超越上一代物理服务器的本地磁盘读写带宽。从这个时刻起,云不应该再被看成一堆虚拟机服务器的集群,而应该是一台拥有几乎无限资源的超级计算机。(其实这已经是全世界云计算从业者的共识)但是很多从业者可能只知其一,不知其二,如何看待云才是设计云上软件最关键的区别。
从单台计算机的角度看待云,和从虚拟机集群的角度看待云,有非常大的不同,让我们看看这些不同点。
在分布式集群里部署的是服务,在单机操作系统里安装的是程序。服务是永远处于运行状态的,程序是被事件触发的,具体如下:
服务部署后随时保持在线,而程序被触发的时候才启动。服务一般不考虑启动速度,只考虑运行性能;而对于一个无需部署的程序来说,启动速度是关键指标。比如你的 grep 启动的时候,不能需要 5s 预热。
服务有状态,以接口形式对外提供服务;程序无状态,以输入输出的方式对外提供服务。
即使使用 k8s 运维,服务也只能按照比例伸缩服务规模,单位服务需要的资源是完全相等的;程序并不需要成比例伸缩资源需求,而是按需分配。
服务因为资源占用是相对静态的,所以一般追求的是尽量优化单机性能,提高单个实例的处理能力;而程序很快并不会长期存活占用资源,所以追求的历史总占用资源最小化(在一段时间内占用资源的积分)。
维护一组分布式服务,还是维护操作系统上的一个程序,区别也不小:
对于跨集群应用来说,我们会认为集群和集群具有很大相似性,所以会尝试使用他们的最大交集来避免跨集群部署的时候修改代码;而跨操作系统的时候,我们会尽量利用操作系统提供的不同 api,在逻辑层面通过封装的方式来兼容差异性,举个例子,同样是高性能网络程序,linux 上的一般基于 epoll,BSD 上就基于 kqueue,而 windows 肯定要依赖 IO completion Port。
在集群视角里,虚拟机相当于一台服务器,我们应该当物理服务器一样对待,里面可以部署一组不同的服务,但是你把它看做一个进程的话,那么里面应该就一个独立的服务,用一个符合你需要的最低配置就好。
从一个进程的角度来看,aws lambda 这样的云函数显然有更快的启动速度,但是因为云函数往往只有较低的硬件配置,所以虚拟机应该被看做计算和内存资源更多,本地存储更强,运行时间更长的进程。
代码都会被不断更新,那么如何将正在运行的服务升级到新版呢?传统的服务升级有各种前摇和后摇工作,这里不再赘述;但是一个程序升级就简单的多,和在操作系统上升级软件没有区别,因为程序的运行的生命周期是有限的,所以只要下次拉起的是新代码就完成了升级。
服务主要是保证接口调用的安全,常用的手段是比较偷懒的,限制访问 ip 之类,简单粗暴容易随着部署情况的变化出现泄露;而一个临时启动的应用程序,做好它本身以及它依赖数据的权限控制,就能保证安全。
对于服务来说,多用户肯定要共享同一个服务,代码层面要做很多他们之间的内存和存储的数据的隔离,最有难度的是要限制某个用户的请求占据所有资源饿死其他用户;但是如果是程序,每个用户 launch 的都是属于自己的“进程”,不会在同一个“进程”里有不同用户不同请求的数据,天然由云这个操作系统进行了数据隔离以及资源隔离。
对于一个集群来说,用 CMDB 去管理资产,管理资源到业务的对应关系是我们传统的资产管理手段;但是如果把云看成操作系统,不同业务无法提供静态的服务器状态供 CMDB 管理,而资源的占用也是动态的,只能通过日志和账单服务来追踪费用。
如果是服务,默认是独占自己所在的服务器/容器的全部资源的;在操作系统上,即使有一个 daemon 程序,也不会占用很多资源。会在有需求的时候才分配需要的资源。
抽象和类比是我们人类利用自己的经验去理解新事物的阶梯。而如何抽象和类比就是我们认知能力的表现。
鉴于操作系统是计算机科学史上最伟大的抽象,从 “cloud is the new computer” 为出发点设计的云上系统,可以充分的利用云的弹性以及云的原生服务,最大的避免资源浪费。 在 云优先架构 里曾经提出的“云优先架构”就是基于这个抽象而设计的。 有意思的是,云不仅是一台计算机,而且还是一台 NUMA 架构的计算机。使用云的最佳实践就相当于在一台 NUMA 架构的超级计算机上编程,关于更多的细节,我们会在本系列后面的文章里详细介绍。