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实现方法。在这之前,推荐大家先阅读如下资料:
- 我的另外两篇博文:《Cloudfoundry的部署工具BOSH介绍》和《在openstack上使用Bosh部署cloudfoundry集群(上)》
- openstack的CPI对底层IaaS的api调用使用的是fog。fog是一个ruby库,它包装了一整套的IaaS api调用方法,这其中基本上包括目前能够看到的所有IaaS平台。要想弄清CPI实现的底层方法,就必须对fog非常熟悉。源码github:https://github.com/fog/fog/tree/master/lib/fog/openstack
- 官方有关在vsphere上使用bosh部署cloudfoundry的相当详细的文章:http://cloudfoundry-doc.csdn.net/deploy/vSphere.html
-
前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/
从这里可以看出,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.rb和 https://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才有可能。
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 | + | |||||
+ | ||||||
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包
目录结构如下:
打开openstack_registry和aws_registry源码对比一下你就会发现,registry内容变化主要是config.rb和server_manager.rb两个文件。所以自己在做registry的时候主要是搬运工作,然后修改下这两个文件就好了。完成后加入bosh,然后改下gemfile,这一部分的工作就完成。
- 在bosh的agent里提供IaaS的支持
我们需要添加三个文件,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中添加相关支持
然后要在micro目录下添加配置文件。这个配置文件用于micro bosh stemcell的创建,内容基本变化不大。
最后记得把最新的bosh源码放到src文件下,保证一切工作是同步的。
四 总结
- 仔细研究AWS和openstack的CPI
- 实现CPI要求的所有接口并进行相关测试
- 开发CPI concole
- 在bosh中添加registry gem包
- 在bosh的agent和bosh-release中添加相关支持
- 把所用之前的工作集成在一起,创建自定义的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