爲了應對諸如驚人的操做開銷、重複的努力、可測試性等微服務一般面臨的挑戰,以及得到諸如代碼解耦,易於橫向擴展等微服務帶來的好處,ZStack將全部服務包含在單個進程中,稱爲管理節點,構建一個進程內的微服務架構。java
構建一個IaaS軟件是很難的,這是一個已經從市場上現存的IaaS軟件得到的教訓。做爲一個集成軟件,IaaS軟件一般須要去管理複雜的各類各樣的子系統(如:虛擬機管理器hypervisor,存儲,網絡,身份驗證等)而且須要組織協調多個子系統間的交互。例如,建立虛擬機操做將涉及到虛擬機管理模塊,存儲模塊,網絡模塊的合做。因爲大多數IaaS軟件一般對架構考慮不夠全面就急於開始解決一個具體問題,它們的實現一般會演變成:node
隨着一個軟件的不斷成長,這個鐵板一塊的架構(monolithic architecture)將最終變爲一團亂麻,以致於沒有人能夠修改這個系統的代碼,除非把整個系統從頭構建。這種鐵板一塊的編程問題是微服務能夠介入的完美場合。經過劃分整個系統的功能爲一個個小的、專注的、獨立的服務,並定義服務之間交互的規則,微服務能夠幫助轉換一個複雜笨重的軟件,從緊耦合的、網狀拓撲架構,變成一個鬆耦合的、星狀拓撲的架構。python
由於服務在微服務中是編譯獨立的,添加或者刪除服務將不會影響整個系統的架構(固然,移除某些服務會致使功能的缺失)。 微服務遠比咱們已經討論的內容更多:微服務的確有不少引入注目的優勢,尤爲是在一個的開發運維 流程(DevOps process)中,當涉及到一個大機構的不少團隊時。咱們不打算討論微服務的全部支持和反對意見,咱們肯定你能夠在網上找到大量的相關文章,咱們主要介紹一些咱們認爲對IaaS軟件影響深遠的特性。web
雖然微服務能夠解耦合架構,但這是有代價的。閱讀Microservices - Not A Free Lunch!和Failing at Microservices會對這句話有更深的理解。在這裏,咱們重點強調一些咱們認爲對IaaS軟件影響重大的事情。spring
建立Microservices架構的挑戰之一是決定應該把哪一部分的代碼定義爲服務,一些是很是明顯的,好比說,處理主機部分的邏輯代碼能夠被定義爲一個服務。然而,管理數據庫交互的代碼很是難以決定應不該該被定義爲服務。數據庫服務可使得整個架構更加清晰明瞭,可是這樣會致使嚴重的性能降低。一般,相似於這樣的代碼能夠被定義爲庫,庫能夠被各個服務調用。鑑於全部服務通常在互相隔離的目錄下開發和維護,建立一個給不一樣的單一的軟件提供接口的虛擬的庫,要求開發者必須具備良好的和各個不一樣組的開發者溝通協調的能力。綜上,服務很容易重複造輪子和致使沒必要要的重複作功。數據庫
服務,尤爲是那些分散在不一樣進程和機器上的,是難以部署和升級的。用戶一般必須去花費幾天甚至幾周去部署一個完整的可運行的系統,並懼怕升級一個已經構建好的穩定的系統。儘管一些相似puppet的配置管理軟件必定程度上緩解了這個問題,用戶依舊須要克服陡峭的學習曲線去掌握這些配置工具,僅僅是爲了部署或者升級一個軟件。管理一個雲是很是困難的,努力不該該被浪費在管理這些本來應該使生活更輕鬆的軟件上。 服務的數量確實很重要:IaaS軟件一般有許許多多的服務。拿著名的openstack舉個例子,爲了完成一個基礎的安裝你將須要:Nova, Cinder, Neutron, Horizon, Keystone, Glance。除了nova是在每臺主機都須要部署的,若是你想要4個實例(instances),而且每一個服務運行在不一樣機器上,你須要去操縱20臺服務器。雖然這種人造的案例將不太可能真實地發生,它依舊揭示了管理相互隔離的服務的挑戰。編程
運行在不一樣服務器上的服務,分別維護着它們散亂在系統各個角落的配置副本。在系統範圍更新配置的操做一般由臨時特定的腳本完成,這會致使由不一致的配置產生的使人費解的失敗。設計模式
爲了跟蹤系統的健康情況,用戶必須付出額外的努力去監控每個服務實例。這些監控軟件,要麼由第三方工具搭建,要麼服務自身維護,仍然受到和微服務面臨的問題所相似的問題的困擾,由於它們仍然是以分佈式的方式工做的軟件。api
插件這個詞在微服務的世界中不多被聽到,由於每一個服務都是運行在不一樣進程中一個很小的功能單元(function unit);傳統的插件模式(參考The Versatile Plugin System)目標是把不一樣的功能單元相互掛在一塊兒,這在微服務看來是不可能的,甚至是反設計模式的。然而,對於一些很天然的,要在功能單元間強加緊密依賴的業務邏輯,微服務可能會讓事情變得很是糟糕,由於缺少插件支持,修改業務邏輯可能引起一連串服務的修改。服務器
意識到上述的全部問題,以及這麼一個事實,即一個能夠正常工做的IaaS軟件必須和全部的編排服務一塊兒運行以後,ZStack把全部服務封裝在單一進程中,稱之爲管理節點。除去一些微服務已經帶來的如解耦架構的優勢外,進程內的微服務還給了咱們不少額外的好處:
由於全部服務都運行在同一進程內,軟件只須要一份支持軟件(如:database library, message library)的拷貝;升級或改變支持庫跟咱們對一個單獨的二進制應用程序所作的同樣簡單。
服務能夠專一於它們的業務邏輯,而不受各類來自於高可用、負載均衡、監控的干擾,這一切只由管理節點關心;更進一步,狀態能夠從服務中分離以建立無狀態服務,詳見ZStack's Scalability Secrets Part 2: Stateless Services。
因爲在一個進程中,全部的服務共享一份配置文件——zstack.properties;用戶不須要去管理各類各樣的分散在不一樣機器上的配置文件。
部署,升級或者維護一個單一的管理節點跟部署升級一個單一的應用程序同樣容易。橫向擴展服務只須要簡單的增長管理節點。
由於運行在一個單一的進程中,插件能夠很容易地被建立,和給傳統的單進程應用程序添加插件同樣。 進程內的微服務並非一個新發明: 早在90年代,微軟在COM(Component Object Model)中把server定義爲遠程、本地和進程內三種。這些進程內的server是一些DLLs,被應用程序在同一進程空間內加載,屬於進程內的微服務。Peter Kriens在四年前就聲稱已經定義了一種老是在同一進程內通訊的服務,OSGi µservices。 ##服務樣例 在微服務中,一個服務一般是一個可重複的業務活動的邏輯表示,是無關聯的、鬆耦合的、自包含的,並且對服務的消費者而言是一個「黑盒子」。簡單來講,一個傳統的微服務一般只關心特定的業務邏輯,有本身的API和配置方法,並能像一個獨立的應用程序同樣運行。儘管ZStack的服務共享同一塊進程空間,它們擁有這些特色中的絕大多數。ZStack很大程度上是一個使用強類型語言java編寫的項目,可是在各個編排服務之間沒有編譯依賴性,例如:計算服務(包含VM服務、主機服務、區域服務、集羣服務)並不依賴於存儲服務(包含磁盤服務、基礎存儲服務、備份存儲服務、磁盤快照服務等),雖然這些服務在業務流程中是緊密耦合的。 在源代碼中,一個ZStack的服務並不比一個做爲一個獨立的jar文件構建的maven模塊多任何東西。每個服務能夠定義本身的APIs、錯誤碼、全局配置,全局屬性和系統標籤。例如KVM的主機服務擁有本身的APIs(以下所示)和各類各樣的容許用戶本身定義配置的方式。
<?xml version="1.0" encoding="UTF-8"?> <service xmlns="http://zstack.org/schema/zstack"> <id>host</id> <message> <name>org.zstack.kvm.APIAddKVMHostMsg</name> <interceptor>HostApiInterceptor</interceptor> <interceptor>KVMApiInterceptor</interceptor> </message> </service>
##經過全局配置來配置
備註:這裏只簡單展現一小部分,用戶可使用API去更新/獲取全局配置,在這裏展現一下全局配置的視圖。
<?xml version="1.0" encoding="UTF-8"?> <globalConfig xmlns="http://zstack.org/schema/zstack"> <config> <category>kvm</category> <name>vm.migrationQuantity</name> <description>A value that defines how many vm can be migrated in parallel when putting a KVM host into maintenance mode.(當一個KVM主機變成維護模式的時候,這裏的值定義了能夠被併發遷移的虛擬機的數量)</description> <defaultValue>2</defaultValue> <type>java.lang.Integer</type> </config> <config> <category>kvm</category> <name>reservedMemory</name> <description>The memory capacity reserved on all KVM hosts. ZStack KVM agent is a python web server that needs some memory capacity to run. this value reserves a portion of memory for the agent as well as other host applications. The value can be overridden by system tag on individual host, cluster and zone level(全部的KVM主機預留的內存容量。ZStack中的KVM代理運行時是一個須要一部份內存容量去運行的python的web服務器,這個值爲代理和其餘主機應用程序預留了一部份內存,在單一主機上的、集羣上的、區域上的系統標籤能夠覆蓋這個值)</description> <defaultValue>512M</defaultValue> </config> </globalConfig>
備註:如下代碼對應zstack.properties文件夾中相應的屬性
@GlobalPropertyDefinition public class KVMGlobalProperty { @GlobalProperty(name="KvmAgent.agentPackageName", defaultValue = "kvmagent-0.6.tar.gz") public static String AGENT_PACKAGE_NAME; @GlobalProperty(name="KvmAgent.agentUrlRootPath", defaultValue = "") public static String AGENT_URL_ROOT_PATH; @GlobalProperty(name="KvmAgent.agentUrlScheme", defaultValue = "http") public static String AGENT_URL_SCHEME; }
##經過系統標籤配置
備註:如下代碼對應數據庫中相應的系統標籤。
@TagDefinition public class KVMSystemTags { public static final String QEMU_IMG_VERSION_TOKEN = "version"; public static PatternedSystemTag QEMU_IMG_VERSION = new PatternedSystemTag(String.format("qemu-img::version::%s", QEMU_IMG_VERSION_TOKEN), HostVO.class); public static final String LIBVIRT_VERSION_TOKEN = "version"; public static PatternedSystemTag LIBVIRT_VERSION = new PatternedSystemTag(String.format("libvirt::version::%s", LIBVIRT_VERSION_TOKEN), HostVO.class); public static final String HVM_CPU_FLAG_TOKEN = "flag"; public static PatternedSystemTag HVM_CPU_FLAG = new PatternedSystemTag(String.format("hvm::%s", HVM_CPU_FLAG_TOKEN), HostVO.class); }
##載入服務 服務在Spring的bean的xml文件中聲明自身,例如,kvm的部分聲明相似於:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:zstack="http://zstack.org/schema/zstack" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://zstack.org/schema/zstack http://zstack.org/schema/zstack/plugin.xsd" default-init-method="init" default-destroy-method="destroy"> <bean id="KvmHostReserveExtension" class="org.zstack.kvm.KvmHostReserveExtension"> <zstack:plugin> <zstack:extension interface="org.zstack.header.Component" /> <zstack:extension interface="org.zstack.header.allocator.HostReservedCapacityExtensionPoint" /> </zstack:plugin> </bean> <bean id="KVMHostFactory" class="org.zstack.kvm.KVMHostFactory"> <zstack:plugin> <zstack:extension interface="org.zstack.header.host.HypervisorFactory" /> <zstack:extension interface="org.zstack.header.Component" /> <zstack:extension interface="org.zstack.header.managementnode.ManagementNodeChangeListener" /> <zstack:extension interface="org.zstack.header.volume.MaxDataVolumeNumberExtensionPoint" /> </zstack:plugin> </bean> <bean id="KVMSecurityGroupBackend" class="org.zstack.kvm.KVMSecurityGroupBackend"> <zstack:plugin> <zstack:extension interface="org.zstack.network.securitygroup.SecurityGroupHypervisorBackend" /> <zstack:extension interface="org.zstack.kvm.KVMHostConnectExtensionPoint" /> </zstack:plugin> </bean> <bean id="KVMConsoleHypervisorBackend" class="org.zstack.kvm.KVMConsoleHypervisorBackend"> <zstack:plugin> <zstack:extension interface="org.zstack.header.console.ConsoleHypervisorBackend"/> </zstack:plugin> </bean> <bean id="KVMApiInterceptor" class="org.zstack.kvm.KVMApiInterceptor"> <zstack:plugin> <zstack:extension interface="org.zstack.header.apimediator.ApiMessageInterceptor"/> </zstack:plugin> </bean> 管理節點,做爲全部服務的容器,將在啓動階段讀取它們的XML配置文件,載入每個服務。 # 總結 在這篇文章中,咱們演示了ZStack的進程內微服務架構。經過使用它,ZStack擁有一個很是乾淨的,鬆耦合的代碼結構,這是建立一個強壯IaaS軟件的基礎。