OpenStack中的Heat分析

时间:2021-09-27 00:18:58

OpenStack中的Heat分析

声明:
本博客欢迎转发,但请保留原作者信息!
博客地址:http://lingxiankong.github.io/
内容系本人及本人团队学习、研究和总结,如有雷同,实属荣幸!
Author:华为云计算工程师 孔令贤
Date: 2013-12-02

版本:2013.2

Heat是一套业务流程平台,旨在帮助用户更轻松地配置以OpenStack为基础的云体系。利用Heat应用程序,开发人员能够在程序中使用模板以实现资源的自动化部署。Heat能够启动应用、创建虚拟机并自动处理整个流程。它还拥有出色的跨平台兼容性,能够与Amazon Web Services业务流程平台CloudFormation相对接——这意味着用户完全可以将AWS模板引入OpenStack环境当中。

所以,在分析Heat的时候,不可避免的要与AWS的CloudFormation作对比。

关于template,可以参考Amazon的文档,毕竟heat源于CloudFormation。 其实把Amazon CloudFormation的文档仔细读一遍,基本就能玩转Heat了。

需要说的就一点,虽然heat目前兼容CloudFormation,但openstack社区已经在慢慢支持自研的模板结构,叫HOT(heat orchestration template,本文暂不关注),所以目前有两种template解释器,依据模板文件开头是否是heat_template_version字段识别。

目前CloudFormation Template中,Heat仅支持如下的字段:
'AWSTemplateFormatVersion', 'Description', 'Mappings', 'Parameters', 'Resources', 'Outputs'
而CloudFormation中的'Conditions'字段目前还不支持。

在CloudFormation中,支持如下的函数:
Fn::Base64Fn::FindInMapFn::GetAttFn::GetAZsFn::JoinFn::SelectRef,目前Heat都支持。因为尚不支持Conditions字段,所以也不支持Fn::And, Fn::Equals, Fn::If, Fn::Not, Fn::Or,这些条件判断函数。

定义资源时,可以定义DeletionPolicy属性,表示在删除stack时,资源的删除策略,默认是Delete。在CloudFormation中,可以指定Retain,表示不删除该资源,对于有快照功能的资源(比如AWS::EC2::Volume),也可以指定Snapshot,表示在删除前先做快照。

显式的定义资源的依赖关系。在资源定义时已经有一些函数(比如RefFn::GetAtt)可以判定依赖关系,但除了这些,可能还需要手工指定依赖。一个典型的用法就是配合AWS::CloudFormation::WaitCondition(关于WaitCondition一个使用示例可以参见这里),它的作用是暂停stack的创建,等待某个信号,然后再恢复执行。比如在虚拟机创建完后,需要等待应用的下载和配置结束,然后才能真正认为该虚拟机创建完毕。一个例子:

<ol class="linenums" style="margin: 0px 0px 0px 25px; padding: 0px; list-style-position: initial;"><li class="L0" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="str" style="color: rgb(236, 118, 0);">"myWaitCondition"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">{</span></code></li><li class="L1" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">    </span><span class="str" style="color: rgb(236, 118, 0);">"Type"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"AWS::CloudFormation::WaitCondition"</span><span class="pun" style="color: rgb(241, 242, 243);">,</span></code></li><li class="L2" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">    </span><span class="str" style="color: rgb(236, 118, 0);">"DependsOn"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"Ec2Instance"</span><span class="pun" style="color: rgb(241, 242, 243);">,</span></code></li><li class="L3" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">    </span><span class="str" style="color: rgb(236, 118, 0);">"Properties"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">{</span></code></li><li class="L4" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">        </span><span class="str" style="color: rgb(236, 118, 0);">"Handle"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">{</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"Ref"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"myWaitHandle"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">},</span></code></li><li class="L5" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">         </span><span class="str" style="color: rgb(236, 118, 0);">"Timeout"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"4500"</span></code></li><li class="L6" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">    </span><span class="pun" style="color: rgb(241, 242, 243);">}</span></code></li><li class="L7" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pun" style="color: rgb(241, 242, 243);">}</span><span class="pln" style="color: rgb(241, 242, 243);">    </span></code></li><li class="L8" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"></code></li><li class="L9" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="str" style="color: rgb(236, 118, 0);">"myWaitHandle"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">{</span></code></li><li class="L0" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">     </span><span class="str" style="color: rgb(236, 118, 0);">"Type"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"AWS::CloudFormation::WaitConditionHandle"</span><span class="pun" style="color: rgb(241, 242, 243);">,</span></code></li><li class="L1" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">     </span><span class="str" style="color: rgb(236, 118, 0);">"Properties"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">{</span></code></li><li class="L2" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">     </span><span class="pun" style="color: rgb(241, 242, 243);">}</span></code></li><li class="L3" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pun" style="color: rgb(241, 242, 243);">}</span></code></li></ol>

在定义虚拟机资源时,可以指定userdata

<ol class="linenums" style="margin: 0px 0px 0px 25px; padding: 0px; list-style-position: initial;"><li class="L0" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="str" style="color: rgb(236, 118, 0);">"UserData"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">{</span></code></li><li class="L1" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">   </span><span class="str" style="color: rgb(236, 118, 0);">"Fn::Base64"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">{</span></code></li><li class="L2" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">       </span><span class="str" style="color: rgb(236, 118, 0);">"Fn::Join"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">[</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">""</span><span class="pun" style="color: rgb(241, 242, 243);">,</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">[</span></code></li><li class="L3" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">            </span><span class="str" style="color: rgb(236, 118, 0);">"curl -X PUT -H 'Content-Type:' --data-binary '{\"Status\" : \"SUCCESS\","</span><span class="pun" style="color: rgb(241, 242, 243);">,</span></code></li><li class="L4" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">                                                           </span><span class="str" style="color: rgb(236, 118, 0);">"\"Reason\" : \"The application myapp is ready\","</span><span class="pun" style="color: rgb(241, 242, 243);">,</span></code></li><li class="L5" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">                                                           </span><span class="str" style="color: rgb(236, 118, 0);">"\"UniqueId\" : \"myapp\","</span><span class="pun" style="color: rgb(241, 242, 243);">,</span></code></li><li class="L6" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">                                                           </span><span class="str" style="color: rgb(236, 118, 0);">"\"Data\" : \"Done\"}' "</span><span class="pun" style="color: rgb(241, 242, 243);">,</span></code></li><li class="L7" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">             </span><span class="str" style="color: rgb(236, 118, 0);">"\""</span><span class="pun" style="color: rgb(241, 242, 243);">,</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">{</span><span class="str" style="color: rgb(236, 118, 0);">"Ref"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"myWaitHandle"</span><span class="pun" style="color: rgb(241, 242, 243);">},</span><span class="str" style="color: rgb(236, 118, 0);">"\"\n"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">]]</span></code></li><li class="L8" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">   </span><span class="pun" style="color: rgb(241, 242, 243);">}</span></code></li><li class="L9" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pun" style="color: rgb(241, 242, 243);">}</span></code></li></ol>

当然,其他资源也可以依赖于WaitCondition,例如绑定弹性IP之前,需要确保应用程序部署完毕。

在CloudFormation中,仅AWS::AutoScaling::AutoScalingGroup支持UpdatePolicy属性,表示group内的虚拟机数目发生变化的约束,目前Heat还不支持。

这个比较容易理解,为资源设置一些键值对。

The environment is used to map resource names to templates, and optionally can be used to define common stack parameters, so that they don't need to be passed every time you create a stack.

environment在heat中代表了创建stack的运行时环境。里面包含了两种资源:
1、parameters. 一个参数集,会覆盖template中的参数定义。一般从创建stack的入参environment中获取,但如果创建stack时同时指定了environment文件和parameters参数,那么后者的优先级高。

2、resource_registry. 表示映射关系,有以下几种形式:

  1. 资源名称和资源类的映射
    这种映射关系一般不需要创建时指定,系统初始化时会预定义很多资源的映射关系(可以参考heat/engine/resources目录),比如虚拟机这个对象,'OS::Nova::Server'就对应于Server类。当然,你也可以根据自己的虚拟化系统中的对象模型,自定义你自己的映射关系。比如Rackspace就在contrib/rackspace/heat/engine/plugins定义了自己的映射关系。

  2. 旧名称和新名称的映射
    一个很简单的例子就是,neutron以前叫quantum,那么在新的系统中,就需要使用包含neutron的名称,这样的话对于已经在使用中的template,就需要将所有包含OS::Quantum字段的类型都转换成OS::Neutron

  3. 重新定义资源类型,例如:

    resource_registry: "AWS::EC2::Instance": file:///home/mine/my_instance_with_better_defaults.yaml

  4. 只想重新定义template中某一资源的类型,其他资源保持不变
    还是举个例子:

    resource_registry: resources: my_db_server: "OS::DBInstance": file:///home/mine/all_my_cool_templates/db.yaml

environment除了在参数指定外,也可以在系统中预定义,相关的配置项environment_dir,默认的位置是/etc/heat/environment.d

整个heat就是围绕stack在玩,管理stack的生命周期。下面逐个分析一下stack中的一些属性。

这个属性就是管理stack中的参数集。参数来源有三个:template文件中;environment中;命令行参数中。前者主要定义参数的schema,那么就要保证后两者中的参数是第一个的子集。目前heat支持的参数类型:String, Number, CommaDelimitedList, Json.

维护了stack中所有的资源对象. 那么资源对象是如何初始化的?

template中每个资源对象定义时都有一个Type字段, 是资源的类型名称。还记得environment对象么?environment中的registry中保存了系统中所有类型的映射关系, 那么就很容易的根据资源的类型初始化资源对象了。template中对于资源的定义, 可能会有一些依赖。要么依赖于某个参数(比如ref, find-in-map, join等函数), 要么依赖于其他对象。对于前者, 很容易做静态解析, 即在对象创建前就能根据参数的传递确定, 但对于对象间的依赖, 只能是在创建期间动态解析。

表示stack中的资源关联关系,heat在处理dependencies时使用了大量的内置函数,以至于这块代码看起来很费劲,可读性差。但原理其实很简单, 依据template中的资源定义, 可以知道每个资源依赖于谁, 又被谁依赖, 最终得到一个类似于树状的结构。对dependencies属性的迭代,默认会先找到叶子节点,也就是依赖关系的最底端(可能有多个),对该资源进行创建操作,该操作是同步的,也就是说第二个资源的创建会等到前一个资源创建成功后才开始创建。

在Amazon中,通常使用Ubuntu的cloud-init工具进行应用的部署(OpenStack也支持),在定义虚拟机资源时传递Userdata作为开机启动脚本,内容以#!开头,可以回顾一下Resource Attributes: DependsOn章节的例子。

基于cloud-init,CloudFormation还提供了一些帮助工具(在虚拟机内运行)来加速应用的自动部署:cfn-init, cfn-signal, cfn-get-metadata, and cfn-hup.

为什么使用帮助工具?
如果没有这些工具,那么只能在UserData中写入大量的脚本进行软件的下载、安装、配置等,远没有配置来的灵活简单,并且无法做到应用的自动更新。

cfn-init会读取虚拟机资源中的Metadata属性(主要关注类型为AWS::CloudFormation::Init的数据),根据配置

  • 安装软件包
  • 写文件
  • 配置服务启动

cfn-init的运行通常放在UserData脚本中,一个例子:

<ol class="linenums" style="margin: 0px 0px 0px 25px; padding: 0px; list-style-position: initial;"><li class="L0" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="str" style="color: rgb(236, 118, 0);">"UserData"</span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">{</span><span class="pln" style="color: rgb(241, 242, 243);"> </span></code></li><li class="L1" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">      </span><span class="str" style="color: rgb(236, 118, 0);">"Fn::Base64"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">{</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"Fn::Join"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">[</span><span class="str" style="color: rgb(236, 118, 0);">""</span><span class="pun" style="color: rgb(241, 242, 243);">,</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">[</span></code></li><li class="L2" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">      </span><span class="str" style="color: rgb(236, 118, 0);">"#!/bin/bash -v\n"</span><span class="pun" style="color: rgb(241, 242, 243);">,</span></code></li><li class="L3" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">      </span><span class="str" style="color: rgb(236, 118, 0);">"yum update -y aws-cfn-bootstrap\n"</span><span class="pun" style="color: rgb(241, 242, 243);">,</span></code></li><li class="L4" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"></code></li><li class="L5" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">      </span><span class="str" style="color: rgb(236, 118, 0);">"# Install LAMP packages\n"</span><span class="pun" style="color: rgb(241, 242, 243);">,</span></code></li><li class="L6" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">      </span><span class="str" style="color: rgb(236, 118, 0);">"/opt/aws/bin/cfn-init -s "</span><span class="pun" style="color: rgb(241, 242, 243);">,</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">{</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"Ref"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"AWS::StackName"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">},</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">" -r WebServer "</span><span class="pun" style="color: rgb(241, 242, 243);">,</span></code></li><li class="L7" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">      </span><span class="str" style="color: rgb(236, 118, 0);">"    --region "</span><span class="pun" style="color: rgb(241, 242, 243);">,</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">{</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"Ref"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"AWS::Region"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">},</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">" || error_exit 'Failed to run cfn-init'\n"</span></code></li><li class="L8" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">      </span><span class="pun" style="color: rgb(241, 242, 243);">]]}}</span><span class="pln" style="color: rgb(241, 242, 243);">        </span></code></li><li class="L9" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="pln" style="color: rgb(241, 242, 243);">  </span><span class="pun" style="color: rgb(241, 242, 243);">}</span></code></li></ol>

还记得在Resource Attributes: DependsOn章节中的UserData例子么,CloudFormation提供了cfn-signal工具来方便的完成同样的功能。如果cfn-init失败或软件安装失败,都可以调用cfn-signal来通知失败,否则,通知成功:

<ol class="linenums" style="margin: 0px 0px 0px 25px; padding: 0px; list-style-position: initial;"><li class="L0" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em;"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="str" style="color: rgb(236, 118, 0);">"# All is well so signal success\n"</span><span class="pun" style="color: rgb(241, 242, 243);">,</span></code></li><li class="L1" style="margin: 0px 0px 5px; padding: 0px; color: rgb(85, 85, 85); list-style: decimal; line-height: 1.1em; background-color: rgb(17, 17, 17);"><code style="margin: 0px; padding: 0px; border: none; font-family: Courier; line-height: 14px; word-wrap: normal; background-color: transparent;"><span class="str" style="color: rgb(236, 118, 0);">"/opt/aws/bin/cfn-signal -e 0 -r \"LAMP Stack setup complete\" '"</span><span class="pun" style="color: rgb(241, 242, 243);">,</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">{</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"Ref"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">:</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"WaitHandle"</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="pun" style="color: rgb(241, 242, 243);">},</span><span class="pln" style="color: rgb(241, 242, 243);"> </span><span class="str" style="color: rgb(236, 118, 0);">"'\n"</span></code></li></ol>

一个用来获取metadata块的帮助脚本,不需要证书就能调用。cfn-init应该就是调用了cfn-get-metadata来获取数据。

默认情况下,cfn-hup守护进程每隔10分钟运行一次,用来监听虚拟机资源中metadata的变化,启动用户自定义脚本,以便更新应用程序(修改配置,新增软件,更新软件版本等),通常配合UpdateStack使用。

我们都知道heat是openstack中的组件,那么云平台组件理所应当是nova/cinder/neutron/ceilometer这些。但是,heat绝不仅仅只用于openstack平台,假如你是cloudstack,或者是eucalyptus,只要能搞定对象建模的映射,那你也能使用heat,相关配置项cloud_backend

AWS中有AutoScalingCloudWatch,所以Heat也不好意思不支持。

Havana中的Heat没有对外实现AWS AutoScaling API,要在IceHouse实现(初步定在I-2实现),相关的BP和wiki:
https://blueprints.launchpad.net/heat/+spec/autoscaling-api
https://blueprints.launchpad.net/heat/+spec/autoscaling-api-resources
https://wiki.openstack.org/wiki/Heat/AutoScaling
目前仅仅实现了支持template中定义autoscaling group和alarm资源而已。机制图:
OpenStack中的Heat分析

AWS的AutoScaling有以下特性:

  • 一般与CloudWatch和Load Balancer配合使用
  • 伸缩内的虚拟机都是同质虚拟机
  • 伸缩的触发:手动调整group大小;手动执行策略;根据监控指标自动执行策略(与alarm关联);定时触发
  • 分为水平伸缩(个数)和垂直伸缩(配置)
  • 根据伸缩组内的虚拟机的状态(可以集成自己的健康检查系统)进行自动的删除创建
  • 伸缩组可以跨AZ(实现业务的HA),并且实现AZ间的均衡,但不能跨region
  • 策略可以是按数目扩缩、指定扩缩后的总数、按照比例扩缩
  • 貌似目前AWS尚不支持启动/停止、休眠/唤醒的方式扩缩

Cooldown Time(AWS)

AutoScaling中的默认冷却时间是300s,表示连续两次alarm触发伸缩动作的最小执行间隔,与某个scaling group相关联。而冷却时间与scaling policy相关联,优先级高于默认冷却时间,表示一个策略连续两次被触发的最小执行间隔。

增加一个虚拟机:
OpenStack中的Heat分析

增加多个虚拟机:
OpenStack中的Heat分析

当然,可以通过改变group的capacity或手动执行策略,绕过cooldown的时间限制。

AWS CloudWatch(API文档)中的alarm是一个监控特定指标的对象。alarm的三种状态:
1、OK。表示指标正常
2、ALARM。表示指标异常
3、INSUFFICIENT_DATA。表示数据不可用。
如果连续几个周期都处于ALARM状态,那么就会触发一个或多个policy,进而触发scaling group的扩缩。

AWS中alarm, group, policy的关系:
OpenStack中的Heat分析

Havana中的Heat中并没有完全实现CloudWatch API,仅实现了describe_alarms,list_metrics,put_metric_data,set_alarm_state四个API。在早期的Heat的实现中,在autoscaling group内的每个虚拟机里都会运行agent获取数据,保存到Heat的数据库里,同样,数据库也会保存alarm数据。随着时间的推移,借助Ceilometer的功能,不再需要虚拟机中的agent,而由Ceilometer进行数据的获取和策略的触发,之前的流程估计到Icehouse会被移除。

不知道为什么,在看Heat的时候,突然想到了OVF,也是模板,也是应用(vApp)的快速部署。有什么区别呢?

OVF模板是模板的一种压缩格式,用来虚拟平台之间交换虚拟设备,它极大地方便了虚拟机跨平台的操作,无论是VMware vShphere、XenServer还是Hyper-v,都可以通过OVF模板来相互转移平台。OVF 软件包将虚拟机或 vApp 的状况捕获到独立的软件包中。磁盘文件以压缩、稀疏格式存储。

Heat是一套流程,你能看到的实体就是一个template文件,该文件是可以跨平台使用的,定义资源更加的灵活,支持的资源类型比OVF要多得多,功能也比OVF强大很多,特别是资源之间的依赖关系,使用不同的云平台时,需要以不同的模板参数区别;而OVF是相对独立的东西,你能看到的东西是一个ovf包,可以跨平台使用。

其实我也没太想明白具体的、实质的区别,有待继续理解。

  1. Heat离商用还有很长的路要走,至少与AWS相比,还有很多功能特性需要补齐;
  2. 作为一个新晋的正式项目,它的不成熟正预示着开发者的贡献机会;

参考文档:
http://techs.enovance.com/5991/autoscaling-with-heat-and-ceilometer
https://wiki.openstack.org/wiki/Heat/Using-CloudWatch