这次主要想借用CoreOS说一个概念,就是如果没有历史负担,可以有机会重新构架一个高可用的基础架构能够如何去做。

起源

故事的起源是这样的,我们现在的基础架构无论是传统的还是云的,其实是慢慢演变过来的。从最早的最简单的GUI的单机应用开始

虽然系统各有千秋,但大致上是这样的结构:
从核心、到外围的Library,然后到应用程序管理/组合,到特定功能的应用。随着Client变多,逐渐演变成C/S的基本结构。然后出现了高可用性的需求

即如何能够提供更高可用性的服务能力,而不受到后台服务的单点故障的影响。于是很自然的演变为三层的结构:

但中间的App也会出现故障,再加上考虑性能等环节,又进一步演变成

大概在2005~2010时代流行的Application Delivery 的概念就形成了,引入了更多负载均衡的组件,引入更多的应用服务器。但引入了负载均衡并没有简化这个问题,高可用架构需要各个环节都不是单点故障的。

于是涉及虚拟IP、DNS转发等方式的更复杂的架构逐渐出现。而客户也变得多样,内部的客户逐渐演变成内部、外部甚至是未知的互联网用户,那还要引入服务的概念,对外的要通过防火墙等暴露服务点。

而这样的一组服务本身可能因为location的整体瘫痪、中断而无法提供高可用的服务。于是又走向了两地三中心等模式:

这样确实能够带来最高的可用性,然而考虑到应用的分发、更新、服务的注册管理、操作系统升级更新带来了无比的复杂性。技术复杂到一定的程度时,只能不断引入管理流程和方法论。

开拓

那么问题来了,如果我们在2000年假设已经预知到15年后的世界是现在这样的,一个应用服务可能会为全世界提供访问,24x7的用户会不断访问我们,我们缺乏固化的变更窗口。
各种应用逐渐地部署在x86的通用平台之上,不再有不让人触碰的神圣的大机小机。同时,我们希望简化整个体系的运维的成本,并最终带来高可用的能够持续输出IT价值的话,我们到底应该怎么去实现整体基础架构?
我们虽然现在拥有了虚拟化、云管理等技术,但究其本质,仍然是传统的技术的衍生,例如虚拟化里的虚拟机,它的设计工作目标就是努力和实体机一模一样。例如云管理的各种provision,目标就是让我们安装传统的应用和操作系统比以前更方便。所有的技术都是预设了向前兼容的隐含目的,当然,纯理论的目标也是没有意义的,于是 CoreOS 作了一个实现。希望是能够从原理上,设计一个能和过去很不相同的企业级基础架构,我刚才那句话里所谓的企业级,这里指高可用优先、安全优先、可靠性优先。

以前有个知名的悖论:

每次技术带来的效率提升,总不带来资源的节约,而是更多的消费。例如煤炭燃烧效率提高以后,并不省下更多煤炭,而是最终消费了更多的煤炭。计算机领域也是这样,把计算效率大幅度提高,并不能节省计算资源,而是引来了更狂热的计算消费。
CoreOS的预设哲学认为效率不是最大的问题,问题是很多之前所有的计算组件的设计者没有预见到当前的这个世界的实际状况,所以CoreOS预设了一系列新的设计哲学:

操作系统最小化
如果没有以前操作系统图形化战争,KDE、Gnome……,怎么会形成一大堆尾大不掉的library和工具 取消package manager,没有本地的package。甚至取消了绝大多数的命令 所有运行对象都进行容器化
操作系统作为硬件和软件的交互层,本身应该和运行对象解耦 操作系统应该是长期保持最新的
历史遗留的操作系统由于安装配置了太多复杂项,造成补丁、重新启动都是个巨大负担。往往采用不及时更新的策略。但这个世界的安全可靠的实际威胁倒逼越来越需要更新到最新状态。 操作系统需要天生为集群设计、为高可用设计

在这些思想的指导下,CoreOS实现出这样的一个结果:
首先,操作系统的自更新是前提,而且要具备原子化,可以保持更新到最新版本、失效可以回滚

所以coreOS采用了双root设计

这是官图,CoreOS借鉴了ChromeOS的设计,root B更新后,重新启动如果失败会自动回滚到root A。
而且CoreOS的更新是很频繁的,他们的版本号不再是1.0, 2.0, 3.0之类的,取而代之的是以数字,例如623, 624, 625,指的是这个版本距离2013年10月3日发布的第一个版本之间的时间天数。
理论上,最频繁的话,他们可能会每天发一个新的正式release。由于更新如此频繁,就会触及频繁的重启动。 CoreOS 认为操作系统更新就好像应用Deployment一样,在集群的前提下是没有问题的。重启动前,系统会转移所有工作任务到集群的其他机器,然后脱离集群。重启动完成更新,再加入集群,最后接管回(/分配新)任务,系统更新事件可以看做就好像上层一个应用软件的版本更新一样。
在这样的操作系统基础的前提下, CoreOS 的上层是完全容器化的。

CoreOS最底层的最小化的核心之上,就启动直接的Docker和etcd这两种主要部件。
这里先打个岔就是CoreOS很不满意Docker的实现,所以CoreOS推出了Docker的替换做法叫做rkt (rocket)。

最基本的就是CoreOS底层 + rkt(docker的竞争工具)+容器,复杂的部署就是 CoreOS集群 + fleet (CoreOS自己的集群控制) + etcd (服务注册)。
所有的应用完全是容器化的,没有任何应用和操作系统耦合。这里值得一提的是etcd。
又涉及到故事的起源,早年间的应用开发的时候,从来没有考虑过未来可能是分布在各个数据中心,分布在全球各个位置。
所以集群一般都是由附加的工具,或者主要交付数据库自己去完成。但是越来越多的应用在这个世界现状里是需要考虑到集群的一致性和可用性的,甚至包括数据库本身,也遇到了前所未有的挑战。
以前的数据库往往只部署在保护精良的个别高级机房里,但现在还要分布在全国/全球多个数据中心,所以这种集群的分裂、合并、数据一致性、选举成为新一代应用软件都不得不考虑的部分。Service Registration(服务注册)应用是其中一个典型例子。
新设计结构里,一个能够分布的、数据是一致的、有能够应对各种异常事件的服务发现、注册、查询、注销服务是非常关键的。
作为传统方法的改良,好的实现是在数据库里增强集群功能,例如淘宝OceanBase就引入了Paxos算法,解决了Mysql的比较简单的同步机制带来的高可用vs一致性的局限性;或者是用别人写的服务注册机制,例如hadoop里会集成各种套件,服务注册就用了zookeeper。
但是作为一个新架构设计,有什么理由不在自己的应用里内置这种集群能力呢。
因为Paxos至今没有几十个人能看明白、实现清楚,但raft就极其简化,清晰可懂。最终,一个coreOS的集群可以通过native的etcd,实现小集群。

或者是分role的大集群:

换言之,在CoreOS操作系统启动以后,所有的CoreOS Node就像神经元一样自己形成了集群,具备了对外提供服务注册、查询、发现等能力。
那有了服务窗口之后,之上的应用容器如何调度? CoreOS设计了fleet功能进行集群内的应用组件加载和分担。

再打个岔,对raft感兴趣的同学们可以看https://raft.github.io 。
上面有一个很好的图形化例子解释集群出现分裂、宕机、合并等各种场景下的raft协议的工作机制,理论上可以运用在各种分布式应用中。
回归主题,到了这一步,CoreOS基本上实现了他们最初的构想的新一代架构,即从操作系统原生级别实现集群化的架构设计。
然而CoreOS仍然不满意里面核心的那一块,即应用容器App Container的部分,因为Docker是一个意外的产物,并非经过精心设计。
Docker是原先的dotCloud公司倒闭前,把内部的一个管理工具开源而得。结果没想到无心插柳柳成荫,但Docker的体系结构上完全不适合企业级。

Docker的模式所有的容器启动在Docker Engine Deamon下,是一个单一的root权限进程。
实际上任何一个container的安全breach(这是已知的未知,就是迟早会发生),就可以获得整个host的root。
Docker的fetch和build过程也是同样跑在engine daemon下,换言之build过程本身都可能成为安全问题。
再加上docker是通过DockerFile进行控制,分发的是一个文件,而不是build的结果,这造成数据中心不得不去建立DockerRegistry这样的机制,然后集中管理DockerFile和相应的build。

所以CoreOS提出了rkt,核心差异是:
1、容器跑在独立的用户上下文,没有root的问题
2、fetch build过程跑在独立的cgroup权限组下
3、结果是tar ball,可以加密、可以offline交换,默认rkt也会启用证书签字等,所以CoreOS正在推广rkt来取代企业中的需要考虑安全性和企业级交付方法的docker的使用。

时间比我预期的超过了,可能没时间讲ACI (Application Container Image)等部分的设计了。
总体上,CoreOS仍然是个非常年轻的项目,但是它体现了一个全新设计、从0开始的现代操作系统(集群)+ 新数据中心的技术哲学看法,这个团队本身也非常不错,人员很精简但产能很高。

这是我上次照的,CoreOS CEO Alex Polvi。本身很技术,说话很腼腆,但整个团队非常devote,从rkt, ACI/appc等设计上,体现了当年IBM设计早年大机的高瞻远瞩的精神,

分享人: 杨光辉 上海天旦 首席架构师