今天的主题是讨论一下“命令式”思想和“声明式”思想在分布式系统和微服务架构运维中的应用。
主要大纲
1. “命令式”和“声明式”的概念
“命令式”和“声明式”的概念
“命令式”和“声明式”这两个概念最初来自于编程语言,这两个概念并不常见,所以我们首先将他们明确一下。
第一个是“ 命令式 ”:
“命令式”有时也被称作“指令式”,好像有一个是台湾翻译,我不确定是哪一个了。“命令式”强调的是how,如果你是在写命令式的程序,那么你将step-by-step的告诉计算机如何完成一项工作,大多数的程序都是这样的。
在命令式场景下,计算机是不具备“智能”的,只是很机械的完成你交代的事情,至于结果如何,要看你的水平。就像上图,煎饼好吃不好吃、会不会糊锅,要看厨师对原料和火候的掌握。
另一个是“ 声明式 ”:
“声明式”有时也被成为“描述式”或者“申明式”,为告诉计算机你想要什么, “声明”你想要的what,由计算机自己去设计执行路径,需要计算机或者是“运行时”具备一定的“智能”。在这种情况下,计算机显然不会实现所有你想要的what,用专业术语说就是“非图灵完备”,但是针对特定的任务,“声明式”要远比“命令式”方便,其实大多数声明式语言都是针对特定任务的领域专用语言,即DSL。当然,随着深度学习驱动的自动编程技术的兴起,我们有可能在任意领域使用“声明式”完成任务,这是后话,暂且不提。
最常见的声明式语言就是SQL —— 告诉计算机你想要的结果集,SQL语言的运行时,即数据库,帮你设计获取这个结果集的执行路径,并返回结果集。众所周知,使用SQL语言获取数据,要比自行编写处理过程去获取数据容易的多。
下面是一个最简单的例子:
在这个例子里我们可以看到,使用其他通用语言“命令式”的完成查询,要比使用SQL语言“声明式”的完成查询复杂的多。在数据量很大的时候两种方法的复杂度差距会更加明显,数据库可以帮助你收集数据分布的统计信息、维护索引和选择最佳执行路径,以保证查询性能,如果你自行编码完成这些工作,那代码量会有成千上万行。
“声明式”虽然为一个编程语言的概念,但是也可以上升到一个比较高的层面,作为一个设计软件的思想或思路,就好像你可以使用“命令式”的C语言,编写一个可以“声明式”使用的数据库一样。
但是我们很少使用“声明式”思想去设计软件。下面是我能想到的几个原因:
最重要的原因是第三个,归纳和提取完备的what,是件很困难、很技术化的工作,令人望而却步。
但是在很多特定的领域,我们往往更加喜欢实现“声明式”思想的工具,比如前面提到的SQL语言。
原因也很简单,因为这些工具的编写者,已经把实践“声明式”思想的最大障碍—— 归纳和提取what,替你完成了。
命令式思想在分布式系统和微服务架构中遇到的困境
现在我们跳出编程的概念,看下“声明式”思想在系统和微服务架构的运维还有DevOps等领域的应用。
首先看一个运维故事:
再看另外一个运维故事:
开个小玩笑,这里不是黑Puppet,也不是鼓吹Docker,我想表达的是,如果你以“命令式”的思路,编写一个过程化的脚本去部署应用,很可能会发生意想不到的事情,造成部署失败;而如果以“声明式”的思路,去制作和部署一个Docker镜像,那么成功率将大大提高。当然,Puppet也支持“声明式”,Docker也可以按“命令式”去使用,比如不太友好的Entrypoint脚本,但是这些都不是重点。
重点是: 使用“命令式”思路去部署一个分布式系统或者微服务架构,已经变得非常困难。
比如部署下面这个系统:
“命令式”运维存在这下述问题,这些问题在分布式系统和微服务架构中日趋严重。
在分布式系统 和微服务架构中,edge case更加复杂,环境更加动态,想在脚本中处理这些问题,你需要成为一个事无巨细的绝顶高手。如果同时还想实现部署结果的一致性、事务性和版本化,那简直是不可能完成的任务,就好像只是为了查询一笔数据,却要去编写一个数据库。
如果使用“声明式”的思路,问题则简单的多:
“声明式”运维的表现,就是编写一个配置文件,描述想要的部署结果,然后由平台解析这个配置文件并自动生成这个部署结果。描述部署结果的配置文件比过程化的脚本更加易于理解,可以由开发人员自行编写。由开发人员参与的Ops,才叫DevOps。
配置文件一目了然,多人维护不是问题,有错误也很容易被发现。配置文件本身就是文档。部署结果为配置文件所描述,如果达不到这个结果,那么部署失败并回退到部署之前的状态,所以部署天然具备了一致性和事务性。
当然,上述优势需要平台去保证,Kubernetes就是这样的一个平台。
以Kubernetes的设计思想为例,介绍声明式思想的优势
接下来就以Kubernetes为例,说明一下平台软件的“声明式”设计思想。
Kubernetes非常强调声明式思想,在其文档的第一篇就明确提出:
使用Kubernetes时,用户不需要去定义do A then B then C这种workflow,而是直接去描述一个desired state,然后Kubernetes帮助用户去达到这个state。至于如何达到这个state,用户不必关心。这种设计使Kubernetes更加易用和健壮,更具弹性和扩展性。
在Kubernetes的设计原则中,也把Declarative列在首位。
State your desired results, let the system actuate.
Kubernetes 实现了多个 Control Loop , 观测系统的运行状态 , 如果偏离了预期状态 ,Kubernetes 会自动进行校正。
举一个简单的例子,Kubernetes会维护一个集群系统内的可用节点数,使其等于用户的定义数量,如果有节点发生故障,Kubernetes会自行产生新节点替换故障节点,以保证可用节点数不变。
“声明式”思想在分布式系统和微服务架构中如此重要,所以我们的一位英国小伙伴在办公桌上钉了这样一个纸条时刻提醒自己:
普元的实践
可是说归说,做归做,我们该如何去实践“声明式”思想呢?
普元将“声明式”思想贯彻到了数字化企业云平台的开发工作中,与此同时,普元还有一个秘密武器,普元前些年做了一个很牛的元数据管理产品,现在把它用在了声明信息的管理上,以高效管理声明信息,帮助实现大规模分布式系统和微服务架构的“声明式”运维工作。
最后,我们有一个OneMore Thing,是声明式思想的一个延伸概念 —— Choreography。
在前面提到的Whatis Kubernetes文档中,说“Kubernetes不仅是一个orchestration system”,而是“more akin tochoreography”。那么orchestration和choreography这两个概念代表什么呢?
Orchestration的本意是乐队指挥:
Choreography的本意是舞蹈编舞:
这两个概念的最大区别就是,乐队指挥是个中央控制点,而舞蹈中是不存在这样一个控制点的—— 舞蹈演员根据舞伴的动作来确定自己的下一个动作。
这两个概念用到IT中,也是非常的贴切,差异就在系统中有无乐队指挥那样的中央控制点。
可以看到,Orchestration是由一个中央引擎执行一个工作流,来达到一个预期状态,更贴近“命令式”概念;而Choreography则定义了交互协议—— 参与方根据相关方的动作来确定自己的下一个动作,来达到一个预期状态,更贴近“声明式”概念。
所以在Whatis Kubernetes中,特别强调Kubernetes的实践思路是more akin to choreography。
另外,很多文章将Orchestration翻译为“编排”,其实并不准确。
在大规模的分布式系统和微服务架构中,是很难实现一个“上帝视角”的中央控制点的,所以在实践“声明式”思想的时候,要时刻记得choreography。
今天的分享就到这里,谢谢大家。
关于作者:
宋潇男
EAII-企业架构创新研究院 专家委员
现任普元云计算架构师, 曾在华为负责云计算产品与解决方案的规划和管理工作 。曾负责国家电网第一代云资源管理平台以及中国银联基于OpenStack的金融云的技术方案、架构设计和技术原型工作。
EAII(Enterprise Architecture Innovation Institute)企业架构创新研究院,致力于软件架构创新与实践,加速企业数字化转型。
eaworld 项目 (微信号:eaworld,长按二维码关注)
eaworld是EAII的官方微信账号。