Skip to content

Latest commit

 

History

History
78 lines (43 loc) · 12.3 KB

introduction.md

File metadata and controls

78 lines (43 loc) · 12.3 KB

前言

ZStack发布以来,已经得到越来越多的开源云计算爱好者的关注,不少公司和开发者也开始基于ZStack做二次开发。本文档详细介绍了ZStack的技术实现细节,为开发者提供第一手的ZStack开发资料.

虽然该文档是以ZStack的设计与实现为主题的,但其覆盖的大型程序设计的方方面面会引出非常多有意思的话题,例如“什么样的架构能够承担百万级代码?”、“如何在一个全异步的程序中实现exception safe?”、“进程内微服务架构是个什么鬼?”、“在Java中,为什么使用interface的funcional style编程比使用lambda更灵活?” ... 诸如此类。这都是我们在开发ZStack的过程中通过不断实践总结下来的经验,也能被一般大型程序所借鉴。

围绕metadata(元数据)而生

几年前在跟Darren Shepherd(前同事,现容器初创企业Rancher CTO)有一次聊天,他提到IaaS管控软件(例如你们熟悉的OpenStack,CloudStack,还有ZStack)的本质是metadata管理软件,我们所做的一切,不过是尽力维护数据中心各种物理设备和虚拟设备的元数据与中心数据库的一致。虽然该观点不一定是Darren首创,但他确实非常准确的指出了编排软件的本质。无论媒体或从业者把云描述的如何神奇,冠以各种高大上的词汇,例如“数据中心操作系统”、“云OS”,在程序员眼中,编排软件(无论是IaaS还是容器)无非是大型的配置管理程序,它根据用户的指令配置数据中心的虚拟、物理资源,将它们的元数据设置到合适的状态,并尽力在数据库中维护这些元数据的一致性。

你已经注意到我多次用了“尽力”二字。是的,在一个大型分布式系统中维护元数据的一致性是非常难的,在后面的章节中,你们将看到ZStack为此所做的努力。

什么是元数据

元数据(metadata)的通常定义是:data that provides information about other data,即用来描述其它数据的数据。在IaaS的语境中,元数据用来描述数据中心中的物理设备、虚拟设备的状态。在后面的内容中,“状态改变”即指“元数据改变”。

我们用一个例子来说明这种状态的改变,以及维护状态一致性的难点。当用户启动一个虚拟机时,系统中通常会发生如下步骤:

  1. 调度算法分配物理机
  2. 在主存储上创建虚拟磁盘
  3. 配置网络,设置DHCP、DNS等网络服务
  4. 一切都准备好后,在物理机上新建虚拟机

在这个例子中,创建虚拟磁盘会改变存储系统的状态(新建了一个文件/块设备,可用磁盘容量减少),配置网络改变网络系统的状态(占用了新的IP地址,配置了DHCP、DNS等条目),新建虚拟机改变计算系统的状态(物理机上出现一个新的虚拟机)。这一切的改变都需要被记录在数据中,否则编排软件无法得知系统状态。当某个步骤发生错误时,例如在最后一步虚拟机创建失败了,前面步骤所执行的操作就应该回滚恢复之前的状态,否则系统状态就会跟数据库状态不一致。当这种不一致累积到一定程度的时,就会最终导致系统崩溃。

架构的选择

没有完美

编排软件通常也是系统集成软件,其特性是粘合各种不同的、独立的子系统。

In information technology, systems integration is the process of linking together different computing systems and software applications physically or functionally, to act as a coordinated whole.

从定义上就可以知道,程序员是很难在在编写此类软件时获得愉悦感的。每个子系统都不受你控制,他们有的可能不提供API,有的是20年前的设计,有的BUG丛生,有的干脆就是猪设计。你要做的一切就是不断的粘合,不断想workaround,为了让整个系统能够完整而正常的工作,如果做的更好一点,健壮的工作。所以你明白为啥会没有愉悦感了,因为你总是在为别人的错误埋单,即使你已经在脑海里抱怨了一百遍:It's not my fault, it's the fucking system not having APIs,你还是得找出一条路让它能够跟你的程序集成起来。奋斗吧少年!含着眼泪帮别人把屁股擦完,写出高质量的code吧!

在动手写第一行code之前,我花了数月的时间来思考架构,因为经历告诉我集成软件不好写。如你所知,我是CloudStack早期员工,在加入Cloud.com一年之后,我已经完成了诸如Overlay Network on Openvswitch、Baremetal as a Service等项目,对CloudStack的架构了然于心。这并不是说我很牛,而是CloudStack的代码耦合的很紧,如铁板一块,如果不至上而下的了解清楚所有细节,是无法完成老板交给我的任务的。

2011年时Citrix收购了Cloud.com,OpenStack也在此时声名鹊起。作为OpenStack的早期贡献者,Citrix内部展开了一场关于真理的大讨论:继续投入OpenStack社区还是推刚2.3亿美金买来的CloudStack上位?老美的逻辑你永远不懂,刚花2.3亿美金买了个公司,不到一个月就开始讨论是不是刚买的东西不要了,把人全部转去做另一个开源项目。我当时都觉得人才真是太值钱了,2.3亿美金啊!后来回到天朝创业后我才知道,这事在天朝只要3000万美金就足够了,把下面干事的人全挖过来。扯远了。由于当时的大讨论很激烈,老大要求几位主架构师一个月内出一份OpenStack和CloudStack的技术对比报告,以供圣断。几位架构师都是CloudStack的创始工程师,结果可想而知,但平心而论,2011年的OpenStack还太早期,很多IaaS必备的功能都不齐全,Citrix选CloudStack从技术上讲并没有什么错误。也是接这个大讨论的机会,我花了大量时间学习研究OpenStack。当时它完全不同于CloudStack的架构设计让我觉得很新奇,我也跟打了鸡血似的读了很多关于SOA、关于微服务架构的书籍,像什么《Enterprise Integration Patterns》、《Service Oriented Architecture》等,还有一堆《xxx In Actions》的工具书。所以我对OpenStack的基础架构设计还是很了解的。

所以在我要准备做ZStack,我花了很长时间来思考要设计一个什么样的架构。CloudStack和OpenStack的设计教会我了一个道理:没有完美的架构。他们都有自己的优点和缺点,但都没能很好的解决如今IaaS软件面临的问题,这也是为什么我们要做ZStack的原因。

架构选择第一步是明确软件的受众,即你的用户是谁。目前流行的企业软件设计可以粗分为两类:互联网软件和传统软件。互联网软件就是我们常见的运行在公有云里的xxx as a service。这类软件的特点是服务于超大规模的多租户环境,软件本身的使用者是软件商自己的运维团队,用户购买软件提供的服务。传统软件就是我们几十年来一直熟悉的模式,付费购买一个带许可证的软件拷贝,用户自己安装使用并维护。互联网软件使用者就是自己的特点决定了它可以在做架构设计的时候做很多假设,例如假设用户不会以错误的方式使用软件,假设用户有手动恢复错误的能力,假设软件只运行在指定的硬件环境下,诸如此类。这些假设可以省掉架构设计中很多复杂的冗余,或者牺牲一些特性(如易用性)来保障核心功能(如服务超大规模多租户环境)。传统软件就没有这么幸运,因为它们面对的是水平参差不齐的用户,所有你没设想到的错误用例都会在客户那里遇到,不能做任何假设,反而要增加大量的冗余来为你未能设想的场景留条后路。当然,传统软件通常不会被用作互联网软件面临的超大规模环境,性能上的牺牲也可以在很大程度上减少架构设计的复杂性。

ZStack设计之初的商业模式是私有云和混合云,所以属于传统软件,由用户自己安装部署运维。另一方面,从一开始我就没想过给ZStack贴上只能管理xxx台物理机的标签,相反的,我希望它能解决之前IaaS软件性能问题,能以单节点管理数万物理机、百万虚拟机、并且响应数万并发API。

现在你知道我为什么要思考数月之久了,我需要设计一个即能像互联网软件一样服务于超大规模环境,又能像传统软件一样易用稳定,服务于技术水平一般的普通用户。

Hybrid:混合之美

前面说到CloudStack和OpenStack各有优缺点。CloudStack的有点是简单易用,它就是一个自包含的软件,下载下来经过简单的配置就可以用起来,也不需要花太多精力运维。其缺点主要是代码耦合太紧,导致加新功能和修老bug都太难,往往是牵一发而动全身,修掉一个老bug,引出一堆新bug。这种情况导致的最直接后果就是不稳定。CloudStack用户都怕升级,一升级老问题解决了无数个新问题又来了,系统需要好长一段时间才能稳定下来。一个日本的CloudStack大客户直到CloudStack都已经出到了4.x版本了,还在用5年前的2.2.14版本。

在第一次接触OpenStack时,它松耦合的微服务架构让我印象深刻,在经历了修改CloudStack铁板一块的代码后,这种松耦合的架构一度让我认为它是IaaS软件的未来。但随着时间的推移,OpenStack使用中的各种问题让我对它的架构开始产生怀疑,特别是在我花费一周时间搭建的OpenStack环境在一个周末莫名崩溃后,我认识到这种设计是无法打造一个健壮的软件产品的。

我将ZStack的架构设计目标列为以下几点:

  • 能打造一个健壮、易使用的软件产品,受众是具有一般运维水平的IT管理人员
  • 代码结构要高度松耦合,最好能做到增加和删除新功能都不修改已有代码
  • 支持超大规模环境,能响应大量并发API
  • 尽量减少用户配置,即使需要,用一个配置文件,一切以API交付
  • 分布式,能水平扩展
  • 功能高度模块化,开发人员能够快速理解所有功能的代码结构
  • 能够无缝升级
  • 不需要写太多代码

这些目标中,最让我头疼的是如何既让软件简单易部署,又要避免代码级别的紧耦合。Monolithic application在近年来微服务架构流行的大背景下常常以反面星星出现,人们似乎忘了它易部署、自包含的优秀特性。Microservices分布式的特点让它天生具有松耦合的特性,这是程序员梦寐以求的。但当你试图用微服务架构打造一个软件产品时,你就会发现前面有无数大坑在等待着你。阅读《Microservices - Not A Free Lunch!》能让你快速了解这些坑,《Building Microservices》一书让你更为深入的了解什么是微服务,以及应该如何正确的使用它。

Hybrid是另一个流行的词汇,混血的似乎总是最好的。为了即能保留monolithic application的易用性,又能获得微服务架构带来代码松耦合,ZStack最终采用了一种介于两者之间的Hybrid架构,我将它称为进程内微服务架构

如名字所暗示,进程内微服务架构将所有微服务运行在同一个进程之中。这样的好处是,对于用户,软件个像monolithic application一样易于部署和维护;对于程序员,每个服务都高度自治和松耦合,还能获得程序位于同一个进程地址空间的种种好处,例如实现基于Observer Pattern的插件结构。

小结

在本章中,我阐述了IaaS编排软件的本质:维护系统元数据一致性,以及ZStack架构设计的目标和选择进程内微服务架构的原因。ZStack是一个粘合各个子系统的集成软件,即要能像传统软件一样简单易用,又能像互联网软件一样管理超大规模数据中心。这两个设计目标贯穿在ZStack的各个实现当中,在后面的章节里,我将会为你一一介绍。