Cloudfoundry自动化部署工具Bosh的CPI研究分析

长平狐 发布于 2013/11/25 18:27
阅读 2K+
收藏 1

WARNING:这是一篇关于研究BOSH CPI的文档,需要有一定的相关的cloudfoundry,openstack,以及BOSH的基础。

BOSH自四月份开源以来,有关的BOSH尝试也一直没有间断。由于BOSH官方支持的IaaS平台目前有amazon的AWS和vmware自己的Vsphere,所以在这两款平台上部署搭建BOSH集群有官方文档参考。而对于目前火热的openstack,第三方的piston已经将其CPI进行了开源,随后bosh官方也在bosh和bosh-release源码中加入了对openstack的支持。这使得BOSH在openstack上部署成为可能,目前也有相当一部分成功的部署案例。

CPI其实可以理解成一套封装成BOSH可以读懂的IaaS接口,它所做的事情就是调用IaaS的api来实现BOSH对虚拟机的操作。

现在优秀的开源IaaS还有很多,比如cloudstack等,这些IaaS和openstack之间存在很多相似之处。本文通过研究openstack的CPI,我们希望能够开发一套在除官方支持的其他IaaS上的CPI实现方法。在这之前,推荐大家先阅读如下资料:

  1. 我的另外两篇博文:《Cloudfoundry的部署工具BOSH介绍》《在openstack上使用Bosh部署cloudfoundry集群(上)》
  2. openstack的CPI对底层IaaS的api调用使用的是fog。fog是一个ruby库,它包装了一整套的IaaS api调用方法,这其中基本上包括目前能够看到的所有IaaS平台。要想弄清CPI实现的底层方法,就必须对fog非常熟悉。源码github:https://github.com/fog/fog/tree/master/lib/fog/openstack
  3. 官方有关在vsphere上使用bosh部署cloudfoundry的相当详细的文章:http://cloudfoundry-doc.csdn.net/deploy/vSphere.html
  4. 前EMC的大牛们对此进行过他们的尝试,并在openstack大会上做了题为CloudFoundry弹性架构和基于OpenStack的部署实践》的专题演讲。其中内容包括了根据开源的openstack cpi,使用bosh进行cloudfoundry的集群部署实践。视频传送门:http://vdisk.weibo.com/s/aicnt ppt:http://vdisk.weibo.com/s/aicpv?t=file 

那实现自定义的一套CPI需要哪些方法呢?这个在bosh cloud base cpi中可以查看:https://github.com/cloudfoundry/bosh/blob/master/cpi/lib/cloud.rb

从这里可以看出,cloud.rb由10个方法构成:

create_stemcell/delete_stemcell
create_vm/delete_vm/reboot_vm
configure_networks
create_disk /delete_disk /attach_disk /detach_disk

一 文件结构

CPI的文件结构很简单,其核心的代码位于lib/cloud/openstack/cloud.rb

openstack CPI源码:https://github.com/piston/openstack-bosh-cpi



二 基本方法

  • 初始化(initialize):主要的工作是建立对openstack的一个连接,并对CPI registry_client进行初始化。在建立连接的过程中,相关的配置信息从文件中读取:
    openstack_params = {
        :provider => "OpenStack",
        :openstack_auth_url => @openstack_properties["auth_url"],
        :openstack_username => @openstack_properties["username"],
        :openstack_api_key => @openstack_properties["api_key"],
        :openstack_tenant => @openstack_properties["tenant"]
      }

        (这些信息是在micro_bosh.yml文件里设置的)

        然后通过这些属性,使用fog分别生成面对nova和glance的两个对象,并建立openstack的连接:

      @openstack = Fog::Compute.new(openstack_params)
      @glance = Fog::Image.new(openstack_params)
        这是两个fog封装好的对象,Fog::Compute和Fog::Image源码分别在: https://github.com/fog/fog/blob/master/lib/fog/openstack/compute.rbhttps://github.com/fog/fog/blob/master/lib/fog/openstack/image.rb

        此外,在这过程中还会对registry_client进行初始化。registry是bosh的一个gem包,bosh的agent里需要使用到这个gem包。它的作用是存储虚拟机的元数据信息,这些信息包括虚拟机启动时的一些属性,包括用户名密码,NTP等。不同的IaaS要有不同的registry支持。在CPI层面上,不同的IaaS在registry这一部分基本上是相同的,我们不用对registry进行过多的考虑。

        如果机器安装了fog客户端的话,还在终端进行简单测试。编辑root下的.fog文件,配置openstack的相关属性。

     $ vim ~/.fog

     :default:
       :openstack_auth_url: OS_AUTH_URL
       :openstack_username: OS_USERNAME
       :openstack_api_key:  OS_PASSWORD
       :openstack_tenant:   OS_TENANT_NAME
       然后调用fog客户端,这里我查看所有的flavor和image信息,并查找名字为m1.medium的flavor和名字为ubuntu的镜像: 

 $ fog
    Welcome to fog interactive!
    :default provides OpenStack and VirtualBox
      openstack = Fog::Compute.new(:provider => "OpenStack")
      flavors = openstack.flavors
      images = openstack.images
      flavor = flavors.find { |f| f.name == 'm1.medium' }
      image = images.find { |i| i.name == 'ubuntu' }

    返回的结果会在终端显示出来,说明我们已经成功使用fog建立了openstack的连接,并通过连接查看到flavor和image信息。实际上,在成功建立连接之后,基本上所有的查看类型的api调用都可以实现,比方说查看当前所有的网络,VM情况等。

     这种使用fog客户端测试的形式很适合刚开始接触fog的初学者。通过这种方式可以更好地了解CPI的调用过程,并在对IaaS的测试经验上得到积累。如果后期需要自己开发CPI,那么在写CPI的测试用例时,你会发现这个过程是很类似的。

  • create_stemcell/delete_stemcell:上传、删除stemcell。

stemcell是一个虚拟机的镜像,而openstack的镜像管理是使用的glance组件,所以create_stemcell方法实现的就是glance组件上传本地镜像的过程。这里要注意的是,如果stemcell中存在kernel文件或者ramdisk文件,要先对这两类文件进行上传。如果包含kernel文件,disk_format和container_format要设置成“aki”;如果包含ramdisk文件,设置成ari。

如果包含上述的两类文件,那么image上传是就会对相关属性进行赋值并最终上传。

   image_params = {
     :name => image_name,
     :disk_format => cloud_properties["disk_format"],
     :container_format => cloud_properties["container_format"],
     :location => root_image,
     :is_public => true
    }
   image_properties = {}
   image_properties[:kernel_id] = kernel_id if kernel_id
   image_properties[:ramdisk_id] = ramdisk_id if ramdisk_id
   image_params[:properties] = image_properties unless image_properties.empty?
   upload_image(image_params)

同样在删除stemcell过程中,也注意到这两类文件进行相关处理。当然这里我们也可以使用fog自己上传一个image,模拟其中的过程。

  • create/delete/reboot_vm:这三个方法是针对VM的创建,删除,重启的操作。这其中的实现很简单,基本上就是对虚拟机的参数设定后,调用一下api操作VM的过程。创建openstack虚拟机的参数需要:
       server_params = {
          :name => server_name,//虚拟机的名称
          :image_ref => image.id,//镜像模板的id
          :flavor_ref => flavor.id,//计算资源模板的id
          :key_name => resource_pool["key_name"] || @default_key_name,//openstack操作需要的key_name
          :security_groups => security_groups.map { |secgrp| {:name => secgrp} },//安全网络组的设置
          :user_data => Yajl::Encoder.encode(metadata)/用户信息
        }
       availability_zone = resource_pool["availability_zone"]//还需要设置虚拟机的zone
        if availability_zone
          server_params[:availability_zone] = availability_zone
        end

  这里要注意的一点是创建VM的过程中网络的配置。CPI方法中缺省的network_spec值是nil,但是bosh在调用openstack CPI创建虚拟机时,如果有sercurity_group的属性设置,那这个值必须存在。当然如果创建VM时没有特意指定sercurity_group,那么也就按照默认的设置创建虚拟机。这里的网络ip是DHCP服务器分配的ip,openstack会自动完成。

  这里也会对registry进行设置。可以把这个看作是刚出生的婴儿要注册出生证一样主要是对VM基本信息的登记。

  删除虚拟机的过程中,除了destroy掉VM之外,对registry也要就行一个删除维护。重启虚拟机的时候,openstack默认给的方法是软重启,也就是正常退出的重启。但是,CPI也提供了硬重启的方法,即虚拟机直接"断电重启"。

  • configure_networks:虚拟机网络配置。Bosh需要CPI提供两种网络类型:dynamic network和virtual network。dynamic network是一种动态网络,这种网络的ip是通过DHCP服务器分配,所以ip是不固定的人工不能干预;virtual network则不一样,这种网络下的ip是可以动态绑定和解绑的,被称作是floating ip。采用这种网络模式的代表是AWS,也正由于AWS在IaaS界的巨大影响,很多IaaS也都具备floating ip的网络。我们研究的CPI必须提供这两种网络,因此在IaaS搭建的时候需要注意,有些IaaS是不包括这种网络模式的,或者在基本功能中不支持(比如cloudstack 4.x高级功能中才具有类似功能)。如果要开发CPI,这种情况下就要先研究该IaaS支持floating,然后实现CPI才有可能。
     了解了这两种网络,我们再看看CPI的代码中做了哪些工作。首先,负责网络配置的类有Network,VipNetwork,DynamicNetwork,以及NetworkConfigurator,相关实现都在名字对应的.rb文件中。其中VipNetwork,DynamicNetwork继承自Network,代表了CPI的两种网络模式。NetworkConfigurator负责封装了网络配置的初始化及配置方法。NetworkConfigurator的initialize,我们可以看出,DynamicNetwork是必须定义的,VipNetwork是可选的。

DynamicNetwork的配置和VipNetwork的配置是处在两个不同的调用阶段,可以想象整个bosh调用CPI的过程来理解。对于openstack来讲,bosh会首先根据上传的stemcell镜像创建虚拟机,而DynamicNetwork是在虚拟机创建的时候就配置好的。这一部分的工作是openstack自动完成,所以CPI实际上没做任何处理。虚拟机创建完成之后,bosh会调用configure_networks方法,根据bosh的配置文件中设置的ip,配置虚拟机的floating ip。所以两种网络是共存的。

既然CPI主要是针对VIP网络进行的配置,那么我们就看下其中的实现。首先network_configurator.rb下的configure方法,如果没有vip network,要将所有的floating ip解绑定:

      if @vip_network
        @vip_network.configure(openstack, server)
      else
        addresses = openstack.addresses
        addresses.each do |address|
          if address.instance_id == server.id
            @logger.info("Disassociating floating IP `#{address.ip}' " \
                         "from server `#{server.id}'")
            address.server = nil
          end
        end
      end
存在vip network,就会进入vip_network.rb下的config方法,给指定的ip地址绑定一个虚拟机:

      address_id = nil
      addresses = openstack.addresses
      addresses.each do |address|
        if address.ip == @ip
          address.server = nil unless address.instance_id.nil?
          address.server = server
          address_id = address.id
          break
        end
至此网络的配置就完成了。这里要注意一点sercurity group只有在dynamic_network中才能设置,因此相关的工作是在虚拟机创建的时候就已经完成了。

  • create_disk /delete_disk /attach_disk /detach_disk:针对虚拟机的磁盘操作。create_disk /delete_disk是创建/删除volume,attach_disk /detach_disk是把volume绑定/解绑到虚拟机。创建volume需要的参数有:
       volume_params = {
          :name => "volume-#{generate_unique_name}",
          :description => "",
          :size => (size / 1024.0).ceil,
          :availability_zone => availability_zone  //这个值是通过server id得到的
        }
在绑定磁盘的时候,需要检查当前虚拟机绑定过的所有volume的device信息,如果之前绑定过就不执行绑定操作,并生成警告日志信息。

        ("c".."z").each do |char|
        dev_name = "/dev/vd#{char}"  //检查所有的dev_name
        if device_names.include?(dev_name) //之前是否绑定过
          @logger.warn("`#{dev_name}' on `#{server.id}' is taken")
          next
        end
        @logger.info("Attaching volume `#{volume.id}' to `#{server.id}', device name is `#{dev_name}'")
        if volume.attach(server.id, dev_name)
          state = volume.status
          wait_resource(volume, state, :"in-use")
          new_attachment = dev_name
        end
        break
类似的,解绑定的时候也要检查所给的volume是否已经被绑定过。


三.关于fog和开发其他IaaS CPI

openstack的CPI是建立在fog上的,通过源码我们也看出其结构非常简单,这也得益于fog的良好封装。我们可以使用fog来开发我们自己的IaaS CPI。虽然使用fog比较简单,但是其中的存在的一些问题也不容忽视。fog对openstack支持较为良好,但对于其他IaaS来说并不尽然。这是fog支持的IaaS功能:

provider cdn   compute      dns    identity storage other
atmos         +  
aws + + +   + auto_scaling, beanstalk, cloud_formation, cloud_watch, dynamodb, elasticache, elb, emr, glacier, iam, rds, ses, simpledb, sns, sqs, sts
baremetalcloud   +        
bluebox   + +      
brightbox   +        
clodo   +        
cloudstack   +        
dnsimple     +      
dnsmadeeasy     +      
dynect     +      
ecloud   +        
glesys   +        
gogrid   +        
google         +  
hp + +     +  
ibm   +     +  
joyent   +        
libvirt   +        
linode   + +      
local         +  
ninefold   +     +  
openstack   +   +   network
ovirt   +        
rackspace + + + + + block_storage, compute_v2, load_balancers, databases
serverlove   +        
stormondemand   +        
vcloud   +        
virtualbox   +        
vmfusion   +        
voxel   +        
vsphere   +        
xenserver   +        
zerigo     +      

对于某一IaaS来讲,fog的覆盖面基本上可以做到cpi所要使用的方法(因为这些方法比较基础),但是由于各个Iaas之间结构设计上的差异,仍存在个别情况。例如上文提到的cloudstack,它的网络配置需要涉及到高级功能的api,但是fog仅支持普通用户权限的api调用(fog对于cloudstack的覆盖情况可参见http://docs.cloudstack.org/CloudStack_Documentation/Design_Documents/Fog_coverage)。这就对我们实现CPI造成了些许的阻碍。两种方式都可以尝试:

1.通过自身实现fog api覆盖的扩展。这样就是调用fog的方法和工具类实现对api的支持。想贡献代码的可以看这:http://fog.io/1.7.0/about/contributing.html

2.直接封装实现IaaS api调用。针对自身的IaaS包装CPI需要使用的api其实也不难,毕竟方法的数量较少,而且都是基本的api调用。采用这种方法的前提是对IaaS的了解要相对深入,尤其是网络配置的部分。

完成了上述的CPI方法,还要开发一个CPI的用户终端,比如openstack就有openstack_concole。此外,针对每个方法,spec单元测试也是必须要的,在这过程中发现的问题要尽早解决。

是不是做到这些,BOSH就可以在IaaS上部署了呢?其实我们的工作还没有做完。到目前为止,我们只是提供了IaaS的接口,但在BOSH的Agent中还是不能识别这种接口。我们在bosh的组件中也缺少相对应的registry。所以,我们还需要做三部分工作:

  • 在bosh的中加入registry的gem包
registry存储了VM的元数据信息,在bosh的源码中我们可以看出只有AWS,vsphere和openstack的registry。所以我们要自己开发其他IaaS的registry。registry的

目录结构如下:


打开openstack_registry和aws_registry源码对比一下你就会发现,registry内容变化主要是config.rb和server_manager.rb两个文件。所以自己在做registry的时候主要是搬运工作,然后修改下这两个文件就好了。完成后加入bosh,然后改下gemfile,这一部分的工作就完成。

  • 在bosh的agent里提供IaaS的支持
在VM的agent里,BOSH会做大量工作,包括了对VM自身的管理和对registry的管理。这里我们需要添加的部分位于bosh/agent/lib/agent/infrastructure下。先来看看目前的文件结构:


我们需要添加三个文件,registry.rb,setting.rb和IaaS_Name.rb。registry.rb主要是agent需要使用的registry方法,这里要根据我们自己开发的registry gem包进行相应修改。setting.rb是agent对底层IaaS的配置方法,并由IaaS_Name.rb进行封装。相关的代码跟registry一样,都可以根据目前存在的AWS和openstack搬运修改。

  • 在bosh-release中添加相关支持
在BOSH自部署的过程中,我们需要bosh-release,因此为了能够在自己的IaaS上部署BOSH,就要在bosh-release中添加支持。 首先,在jobs和packages中添加registry的部分。这里,由于内容相对简单不做过多说明,大家可以参考openstack的有关部分。

然后要在micro目录下添加配置文件。这个配置文件用于micro bosh stemcell的创建,内容基本变化不大。

最后记得把最新的bosh源码放到src文件下,保证一切工作是同步的。

四 总结

通过openstack源码的分析,以及对后续bosh中的相关修改说明,我想现在对于开发实现自己的一套IaaS CPI已经有了一个思路。这里总结一下:
  1. 仔细研究AWS和openstack的CPI
  2. 实现CPI要求的所有接口并进行相关测试
  3. 开发CPI concole
  4. 在bosh中添加registry gem包
  5. 在bosh的agent和bosh-release中添加相关支持
  6. 把所用之前的工作集成在一起,创建自定义的micro-bosh-stemcell并部署micro bosh来进行测试

这个过程中由于文件较为分散,所以最好使用git做好源码的管理。如果这一切都OK的话,我们就能在自定义stemcell和自定义的registry的前提下,完成BOSH的部署,从而在除官方支持的三种平台上成功部署cloudfoundry。


目前,由于BOSH的相关文档较少,所以这篇文档更多出于我本人的理解,肯定在一些地方存在不足和错误。希望本文能够对开始接触BOSH的人有些帮助,如果你对BOSH感兴趣,并有更好的想法和建议,也请联系我。

我的邮箱:alan90121@zju.edu.cn  微博:@SuperAlan爱绝杀  



原文链接:http://blog.csdn.net/alan90121/article/details/8177242
加载中
返回顶部
顶部