Cloud foundry基础

  • VMware突然发布了业内第一个开源的PaaS——CloudFoundry。 第一部份主要介绍CloudFoundry的架构设计,从它所包含的模块介绍起,到各部份的消息流向,各模块如何协调合作;

      第二部份会在第一部份的基础上,以怎样在你的数据中心里面用CloudFoundry部署一个私有PaaS为目标,把第一部分介绍到的架构知识使用起来。

      第一部份讲的很多内容,会引用Pat在10月12日的VMwareCloud Forum上面关于CloudFoundry架构的演讲。Pat是CloudFoundry Core的负责人,他的那次演讲很值得一听。若你当时在场,且理解他所说的内容,本部份可以选择直接跳过。除了会把说的内容讲具体点外,我不太可能可讲得比他好。

      一、架构及模块

      从总体地看,CloudFoundry的架构如图1所示:

    Cloud foundry基础

      图1

      这个架构图以及下文所用到的各模块架构图均来自Pat的PPT。从上图能够看到CloudFoundry主要有以下几大组件组成:

      1、 Router

      Router组件在CloudFoundry中是对所有进来的Request进行路由。进入Router的request主要有两类:首先是来自VMCClient或者STS的,由CloudFoundry使用者发出的,管理型指令。

      例如:列出你所有apps的vmcapps,提交一个apps等等。这类request会被路由到AppLife Management组件,又叫CloudController组件去;第二类是外界对你所部署的apps访问的request。这部份requests会被路由到Appexecution,又或者叫做DEAs的组件去。所有进入CloudFoundry系统的requests都会经过Router组件,看到这里可能会有朋友会担心Router成为单点,从而成为整个云的瓶颈。

      但CloudFoundry作为云系统,其设计的核心就是去单点依赖,组件平行扩充,并且可替代的以保证扩展性,这是CloudFoundry,甚至所有云计算系统的设计原则,后文会讨论CloudFoundry怎样做到这点,目前只要知道,系统可部署多个Routers共同处理进来的requests,但Router上层的LoadBalance不在CloudFoundry的实现范围,CloudFoundry只保证所有的request是无状态的,这样就使上层均衡附载选择面非常非常大了,例如可通过DNS做,也可部署硬件的LoadBalancer,或简单点,弄台ngnix作负载均衡器,都是可行的。

      Router组件,目前版本是对nginx的一个简单封装。熟悉ngnix的朋友应该知道,它可一个套接字文件(.sock文件)作为输入输出。所有安装CloudFoundry的Router组件服务器都会安装一个nginx,其ngnix.conf文件有以下配置,如图2所示:

    Cloud foundry基础

      图2

      从整体的来看,Router组件的结构如图3所示:

    Cloud foundry基础

      图3

      2、DEA(Droplet Execution Agency)

      首先要解析下什么叫做Droplet。Droplet在CloudFoundry的概念里面是指一个把你提交的源代码,以及CloudFoundry配套好的运行环境,再加上一些管理脚本,例如Start/Stop这些小脚本全部压缩好在一起的tar包。还有一个概念,叫做Stagingapp,就是指制作上面描述这个包,然后把它存储好的过程。CloudFoundry会自动保存这个Droplet,直到你start一个app时,一台部署了DEA模块的服务器会来拿一个Droplet的copy去运行。因此如果你扩展你的app到10个instances,那这个Droplet就被会复制十份,让10个DEA服务器拿去运行。

      如图4所示,是DEA模块的架构图:

    Cloud foundry基础

      图4

      Cloud Controller模块(下面会介绍)会发送start/stop等基本的apps管理请求给DEA,dea.rb接收这些请求,然后从NFS里面找到合适的Droplet。前面说到Droplet其实是一个带有运行脚本的,带运行环境的tar包,DEA只需要把它拿过来解压,并即行里面的start脚本,就可以让这个app跑起来。到此,app算是可访问,并start起来了,那即是说就是有这台服务器的某一个端口已经在待命,只要有request从这个端口进来,这个app就可接收并返回正确的信息。

      接着dea.rb要做些善后的工作:

      1、把这个信息告诉Router模块。我们前面说到,所有进入CloudFoundry的requests都是由Router模块处理并转发的,包括用户对app的访问request,一个app起来之后,需要告诉router,让它根据loadbalance等原则,把合适的request转进来,使这个app的instance能够干起活;

      2、一些统计性的工作,例如要把这个用户又新部署了一个app告诉CloudController,以作quota控制等;3、把运行信息告诉HealthManager模块,实时报告该app的instance运行情况。此外DEA还要负责部份对Droplet的查询工作,譬如,若用户通过CloudController想查询一个app的log信息,那DEA需要从该Droplet里面取到log返回等。

      3、CloudController:CloudController是CloudFoundry的管理模块。主要工作包括:

      a. 管理service,包括service与app的绑定等;

      b. Cloud环境的管理;

      c. 修改Cloud的用户信息;

      d. 对apps的增删改读;

      e. 启动、停止应用程序;

      f. Staging apps(把apps打包成一个droplet);

      g. 修改应用程序运行环境,包括instance、mem等;

      h. 查看Cloud Foundry,以及每一个app的log信息。

      这似乎有点复杂,简单的说,可很简单:就是与VMCyu STS交互的服务器端。VMC与STS与CloudFoundry通信采用的是restful接口,另一方面CloudController是一个典型的Rubyon Rails项目,从VMC或STS接到JSON格式的协议,然后写入CloudController Database,并且发消息到各模快去控制管理整个云。同其他ROR项目一样,CloudController的所有API可从conf/routes.rb里看到。开放的Restful接口好处在于第三方应用开发与集成,像阿里云这样企业在用CloudFoundry部署私有云时,可通过这些接口来自动化控制管理整个Cloud环境。这部份内容将在第二部份论述。

      如图5所示,是Cloud Controller的架构图:

    Cloud foundry基础

      图5

      图中Health Manager与DEA是外部模块,CCDatabase就是CloudController Database,这个是整个CloudFoundry不能做HP的地方。CloudController Database的并发性不会很多,应用级别的数据库访问是由底下的Service模块处理的,这个数据库存的是Cloud的配置信息。读操作主要来自DEA启动,作为初始化DEA的依据;以及healthmanager模块会从这里读取预期的状态信息,这部份数据会与从DEA得到的实际状态信息进行比对。

      NFS是多个CloudController的共享存储,CloudController其中一个重要工作就是StagingApps。Droplets的存储是在集群环境的唯一的。而CloudController是集群运行,换言之,就是每一个控制Request可能由不同的CloudController处理,假设一个简单的用户场景:我们需要部署一个app到CloudFoundry中。我们在敲完那条简单的push命令后,VMC开始工作,在做完一轮的用户鉴权、查看所部署的apps数量是否超过预定数额,问了一堆相关app的问题后,需要发4个指令,分别是:

      (1)发一个POST到”apps”,创建一个app;

      (2)发一个PUT到”apps/:name/application”,上传app;

      (3)发一个GET到”apps/:name/”,取得app状态,看看是否已经启动;

      (4)如果没有启动,发一个PUT到”apps/:name/”,使其启动。

      若第2与第4步由不同的Cloud Controller来处理,而又无法保证他们能找到同一个Droplet,那第4步将会由于找不到对应的Droplet而启动失败。怎样保证这一连串指令过来所指向的Droplet都是同一个呢?使用NFS,使CloudController共享存储是最简单的方法。但这个方法在安全性等方面并不完美。在10月12日的VMwareCloud Forum上,Pat告诉我们下一版本的CloudFoundry这里将会有大调整,但在那部份代码公开前,我不方便在这评价太多。

      (5)HealthManager: 做的事情不复杂,简单的说是从各个DEA里面拿到运行信息,然后进行统计分析,报告等等。统计数据会和CloudController的设定指标进行比对,并且提供Alert等。目前HealthManager模块还不是十分完善,但CloudManage栈里面,自动化health管理、分析是一个很重要的领域,而这方面可扩展的地方也很多,结合OrchestrationEngine可使云自管理、自预警;而与BI方面技术结合,可统计运营情况,合理分配资源等。这方面CloudFoundry还在发展中。

      (6)Services:Cloud Foundry的Service模块从源代码控制上看就知道是一个独立的、可Plugin的模块,以方便第三方把自己的服务整合入CloudFoundry生态系统。在Github上看到service是与CloudFoundry Core项目vcap独立的一个repository,为vcap-service。Service模块其中设计原则是方便第三方服务提供商提供服务。在这方面CloudFoundry做得很成功,从Github上看,已有以下服务提供:

      a)MongoDB;

      b) mysql;

      c) neo4j;

      d) PostgreSql;

      e) RabbitMQ;

      f) Redis;

      g)vBlob。

      基类都是放在base文件夹中。

      第三方若需要自己开发CloudFoundry的服务,需要继承改写它里面的两个基础类:Node与Gateway;而里面一些操作,如:Provision,可在base的provisioner.rb基础上加入自己的逻辑,同样的还有Service_Error与Service_Message等。关于怎样写自己的Service,ELC的博客会推出相应文章详细论述,并不在本文的讨论范围里面,从架构了解上来说,只要知道服务间的关系,知道个服务同base间透过继承关系来横向扩充,而CloudFoundry和apps调用Service是通过base来完成这一简单的架构方法即可。

      (7)NATS(Message bus): 从CloudFoundry的总架构图看,位于各模块中心位置的是一个叫nats的组件。NATS是由CloudFoundry的架构师Derek开发的一个轻量级的,支持发布、订阅机制的消息系统。Github开源地址是:https://github.com/derekcollison/nats。其核心基于EventMachine开发,代码量不多,可下载下来慢慢研究。

      CloudFoundry是一个多模块的分布式系统,支持模块自发现,错误自检,且模块间低耦合。其核心原理就是基于消息发布订阅机制。每个台服务器上的每个模块会根据自己的消息类别,向MessageBus发布多个消息主题;而同时也向自己需要交互的模块,按照需要的信息内容的消息主题订阅消息。譬如:一个DEA被加入CloudFoundry集群中,它需要向大家吼一下,以表明它已准备好服务了,它会发布一个主题是”dea.start”的消息:

    Cloud foundry基础

      图6

      @ hello_message_json中包括DEA的UUID,ip, port, 版本信息等内容。

      再例如,CloudController需要启动一个Droplet的instance:

      a)首先一个DEA在启动的时候,会首先会对自己UUID的消息主题进行订阅。

    Cloud foundry基础

      图7

      其他模块需要通过’’dea.#{uuid}.start”这个主题发送消息来使它启动,一旦这个DEA接收到消息,就会触发process_dea_start(msg)这个方法来处理启动所需要的工作。

      b)Cloud Controller或者其他模块发送消息,让UUID为xxx的DEA启动。

    Cloud foundry基础

      图8

      c)DEA模块接收到消息后,就会触发process_dea_start(msg)方法。msg是由其他模块发送过来的消息内容,包括:droplet_id,instance_index, service, runtime等内容,process_dea_start会取得这些启动DEA必须的信息,然后进行一系列操作,例如从NFS中取得Droplet,解压,修改必要环境配置,运行启动脚本等等。等一切都准备好后,然后需要给Router发个消息,告诉它这个Droplet已经随时准备好报效国家,以后有相应的request记得让它来处理。

    Cloud foundry基础

      图9

      d)Router模块在启动时就已经订阅”router.register”消息主题。

    Cloud foundry基础

      图10

      收到前面DEA发出的信息后,会触发register_droplet方法,去绑定Droplet。到此启动一个Droplet的instance工作完成。

      我们可看到整个CloudFoundry的核心就是一套消息系统,若想了解CloudFoundry的来龙去脉,去跟踪它里面复杂的消息机制是非常好的方法。另一方面,CloudFoundry是一套基于消息的分布式系统,面向消息的架构是它节点横向扩展,组件自发现等云特性的基础。

      Cloud Foundry的架构简单介绍至此,其实作为第一款开源的PaaS,CloudFoundry架构有很多可学习借鉴的地方,很多细节上的处理是很精妙的,这些内容若有可能会在后续文章继续探讨,本文题虽为深入CloudFoundry,实际上也只是浅尝即止,把总体架构介绍一下,目标在于使我们有足够的背景知识去用CloudFoundry搭建企业内部的私有PaaS。总结一下,笔者从CloudFoundry的结构中学到的东西:

      1、基于消息的多组件架构是实现集群的简单、且有效方法。消息可以使集群节点间解耦,使自注册,自发现这些在大规模数据中心中很重要的功能得到实现;

      2、适当的抽象层,模板模式的使用,方便第三方可以方便在CloudFoundry开发扩展功能。CloudFoundry在DEA及Service层都做了抽象层处理,相对应地使开发者可以容易地为CloudFoundry开发Runtime和Service。例如,在CloudFoundry刚推出的时候,只支持Node.js,Java, Ruby,但第三方提供商、开源社区快速跟进,为CloudFoundry添加了PHP,Python的支持。这得益于CloudFoundry精巧的DEA架构设计,如何开发新的Runtime支持,会在后续博文中有所论述.

      二、源码导读

      笔者一直觉得深入理解一个技术的最好方法就是读它的源码,而CloudFoundry是完全开源的PaaS平台,而因为刚发展起来,代码量不多,主要作者们的代码功力也相当不错,读起来很舒服,很适合研读。而不得不再次表扬一下它完全基于消息机制的架构设计,对组件扩展性,第三方接入等方面做得很好,读者可以从中学到不少思想性的东西。笔者很推荐大家去读一下它的源代码。你可以在Github上找到CloudFoundry的全部代码:https://github.com/cloudfoundry,你会看到几个不同的Repositories,它们分别是:

      1、vcap: Cloud Foundry的Core,又或者称作Kernel;

      2、vcap-service: Cloud Foundry的Service组件。Cloud Foundry的service是作为插件提供的,这出于它方便第三方开发service而设计的;

      3、vmc: VMware Cloud CLI. 是一个Ruby应用,与Cloud Foundry的CLI交互。主要通过分析用户输入的CLI,向CloudFoundry发送Restful请求;

      4、vcap-java: 如果你的app是用java开发,且需要与Cloud Foundry交互,例如取得当前serviceserver的ip地址等,你可能需要这个jar,里面对我们Java开发常用框架有所支持,它底层也是对CloudFoundry的Restful请求的包装;

      5、vcap-java-client: Cloud Foundry的Restful API的Java封装,与上面的项目不一样,它只是个简单的读取CloudFoundry信息,并放如JavaBean中;

      6、vcap-test: Cloud Foundry的test cases;

      7、vcap-test-assets: Cloud Foundry一些apps示例。




Cloud Foundry 组件概述

Cloud Foundry 组件包括了一个自服务的应用执行引擎、一个用于应用部署和生命周期管理的自动化引擎、一个脚本命令行接口(CLI)、以及与开发工具的集成以简化部署过程。Cloud Foundry 有一个开放的架构,其中包括一个用于添加框架的 buildpack 机制、应用服务接口和云提供商接口(CPI)。

Cloud foundry基础

路由

Router

Router 将传入的流量路由到适当的组件,要么是一个Cloud Controller 组件,要么是在Diego Cell 上运行的托管应用。

Router 定期查询 Diego Bulletin Board System(BBS),以确定每个应用当前运行的 cell 和容器。使用这些信息,Router 根据每个cell 虚拟机(VM)的IP地址和 cell 容器的主机端端口号重新计算新的路由表。

认证

OAuth2 Server (UAA) 和 Login Server

OAuth2 Server (UAA) 和 Login Server 一起工作以提供身份管理。

应用生命周期

Cloud Controller 和 Diego Brain

Cloud Controller (CC) 指导应用的部署。为了将一个应用发布到 Cloud Foundry 上,需要target Cloud Controller。然后,Cloud Controller 通过CC-Bridge 组件将 Diego Brain 引导到协调各个Diego cell 来stage和运行应用。

Cloud Controller 还维护orgs、spaces、user roles、services 等的记录。

nsync、BBS 和 Cell Reps

为了使应用可用,云部署必须不断地监控它们的状态,并将它们与预期的状态进行协调,并根据需要启动和停止进程。

Cloud foundry基础

nsync、BBS和Cell Rep组件一起工作在一个链上,以保持应用的运行。一端是用户,另一端是在广泛分布的vm上运行的应用实例,它们可能会崩溃或不可用。

  • 当用户scale应用时,nsync会从Cloud Controller 收到一条消息,它会将实例的数量写入Diego BBS数据库的DesiredLRP中。
  • BBS使用它的convergence进程来监控DesiredLRP和ActualLRP的值。它将启动或杀死应用实例,以确保ActualLRP数量与DesiredLRP数量匹配。
  • Cell Rep监视容器并提供ActualLRP值。

应用存储和执行

Blobstore

blobstore是大型二进制文件的存储库,Github无法轻松管理,因为Github是为代码设计的。Blobstore二进制文件包括:

  • 应用代码包
  • Buildpacks
  • Droplets

可以将blobstore配置为一个内部服务器或一个外部S3或与S3兼容的端点。

Diego Cell

应用实例、应用任务和staging任务都在Diego Cell VM 上作为Garden容器运行。Diego cell rep 组件管理这些容器的生命周期,并在它们中运行进程,将它们的状态报告给Diego BBS,并将它们的日志和指标发送给Loggregator。

服务

Service Brokers

应用通常依赖于诸如数据库或第三方SaaS提供商之类的服务。当开发人员提供服务并将服务绑定到应用时,该服务的service broker 负责提供服务实例。

通信

Consul 和 BBS

Cloud Foundry组件虚机内部通过HTTP和HTTPS协议相互通信,共享存储在两个位置的临时消息和数据:

  • Consul server 存储较长期的控制数据,例如组件IP地址和分布式锁,这些锁可以防止组件复制操作。
  • Diego 的Bulletin Board System (BBS)存储了更频繁的更新和一次性数据,如cell和应用状态、未分配的工作和心跳消息。BBS在MySQL中存储数据,使用MySQL驱动程序。

route-emitter组件使用NATS协议将最新的路由表广播到router。

指标和日志

Loggregator

Loggregator (log aggregator)系统将应用日志记录给开发人员。

Metrics Collector

Metrics Collector 从组件收集指标和统计信息。操作人员可以使用这些信息来监控Cloud Foundry 的部署。

Cloud Foundry使用GitHub上的git系统来版本控制源代码、buildpack、文档和其他资源。平台上的开发人员也可以使用GitHub用于自己的应用、自定义配置和其他资源。为了存储大型二进制文件,例如droplets,CF维护一个内部或外部的blobstore。为了存储和共享临时信息,例如内部组件状态,CF使用MySQL、Consul和etcd。

Cloud Foundry是业界领先的PaaS云平台,可以为应用提供高可用的运行平台,现在很多运行商都在使用Cloud Foundry为用户提供应用服务,如IBM、AWS等。自Cloud Foundry面世以来便一直使用BOSH来创建和更新生产服务。BOSH是一条开源工具链,用于对大规模分布式服务进行发行版工程处理、部署和生命周期管理。它包含一个云提供商接口(CPI),如VMWare,通过调用CPI完成整个Cloud Foundry的创建、部署和升级。


Kubernetes经过几年的发展越来越成熟,遍布全球的数百位开发者纷纷投身到Kubernetes的快速迭代中。越来越多的企业开始采用Kubernetes支撑生产业务,实现了高可用的容器化微服务。


但原来用户如果想继续在Kubernetes上使用Cloud Foundry,那该怎么做呢?现在业界没有一个标准的做法。较为正规的方式是开发一套完整的Kubernetes的CPI,然后利用BOSH将Cloud Foundry部署在Kubernetes上。但BOSH设计初衷是面向IaaS的部署工具,它首先需要一个Linux的虚拟机模板 Stemcell,Cloud Foundry的不同组件通过BOSH安装在基于这个Stemcell构建的虚拟机上。这种部署方式在Kubernetes上有一个很大的问题,就是当部署的Cloud Foundry组件,如Cloud Contorller或者Router的Pod因为一些原因坏了或者重启了,那么重启回来的Pod只是一个空的Stemcell,没有任何的组件信息,只能通过BOSH re-create为组件创建新的虚拟机:


Cloud foundry基础


使用BOSH在Kubernetes上部署Cloud Foundry还有一些其他问题和弊端:


  1. BOSH现在主要适用于IaaS

  2. 需要开发新的Kubernetes CPI

  3. 部署时间过长

  4. 需要手工执行许多配置,如网络和资源等


现在业界有很多厂商都在研究如何更好更快地将Cloud Foundry部署在Kubernetes上,如SUSE的SCF和Cisco的Container CF等。虽然现在还没有发布用于生产环境的版本,但思路相比BOSH更贴近Kubernetes的使用方式,也更融合Kubernetes。主要的方式就是通过制作相对应的Cloud Foundry组件的Docker Image,部署的时候直接部署对应的Docker Image,然后在运行容器之前,通过配置信息和环境变量,设置容器参数使整个Cloud Foundry组件之间可以相互通信。即使哪个Cloud Foundry组件坏了或者被重启了,组件使用已创建的Docker Image可以快速启动,只是在启动时需要重新配置一下参数。它相对之前BOSH的部署方式,好处显而易见:


Cloud foundry基础


那么我们就以SUSE的SCF为例,看看它到底是如何做到的?


Cloud foundry基础


  1. 首先我们需要将BOSH的工程通过SUSE的转换工具fissile将其编译并制作成Docker Image。

  2. 之后我们需要通过Helm将预设的参数转换成Helm或者Kubernetes的配置资源文件。

  3. 最后通过Helm将整个CF部署到Kubernetes上。


下图是和传统的BOSH CPI的部署方式的比较,绿色部分为新的Fissile + Helm 方式改变的步骤。但这些绿色步骤只需要管理员执行一次来创建Docker Image,之后就可以重复使用了。最终用户只需要执行橙色步骤,修改自己配置文件,下载相应的Docker Image,部署整个Cloud Foundry。整个部署Cloud Foundry的时间大概在15-20分钟,这和之前BOSH部署4-6个小时相比,快了很多:


Cloud foundry基础


接下来我们就来看看如何利用SCF在Kubernetes上快速部署一个Cloud Foundry,如下图:


Cloud foundry基础


  1. 首先我们需要一个性能较好的机器并且可以连接外网并安装Vagrant。

  2. 克隆整个SCF的Git项目 https://github.com/SUSE/scf

  3. 执行相应的Vagrant命令启动构建环境。

  4. 进入Vagrant创建的虚拟机,执行 make vagrant-prep 命令创建工程、编译并制作Docker Image,这个过程大概需要2-3个小时,但只需要执行一次。

  5. 上一步正确完成后,执行make kube,根据预设配置,创建Helm或Kubernetes配置资源文件。

  6. 最后执行make run将整个Cloud Foundry部署到Vagrant自带的Kubernetes上。

  7. 你也可以通过它自带的pod-status来观察整个Cloud Foundry的状态。大概15-20分钟之后,所有的Pod都启动了,Cloud Foundry就可以使用了。


所有相关信息都可以在SCF的Git中找到:https://github.com/SUSE/scf。


下图为部署成功后SCF显示的环境信息:


Cloud foundry基础


下图为部署在Kubernetes上所有Cloud Foundry组件的Pod的运行状况:


Cloud foundry基础


当然,SCF现在做的只能将Cloud Foundry运行在Vagrant创建的Kubernetes上。你也可以把创建的Cloud Foundry组件的Docker Image上传到外部的Docker Hub上,然后使用Kubernetes的配置资源文件将你的Cloud Foundry部署到你自己的Kubernetes上。