Why Heat
关于Heat是什么东西,我已经在前面的文章《在Keystoen v2的Mitaka上部署HEAT服务》中有所提及。既然Heat 是一个基于模板来编排复合云应用的服务,那么引入编排会对我现在有什么改变呢?我想了下,改变大致如下:
- 更快更有效的管理OpenStack的资源
目前大部分业务场景都是研发或测试的同事在申请虚拟机的同时会额外申请数据卷作为日志盘。在不引入Heat前,虚拟机和数据卷的映射挂载方式为用户手动确立。所以线上数据卷的维护往往都会出现利用ip地址作为映射关系的标准。
Volume管理页面不是说这样不好,只是做一方面是用户体验方面不太友好,数据卷少还好,一旦出现批量创建的情况,需要人为一个一个去挂载连接虚拟机,需要时间。另一方面也不利于虚拟机开机后的初始化的,我们知道正常情况下创建卷的速度是快于虚拟机的,要让虚拟机开机初始化数据卷,还是用传统的人为方式,明显是不可靠的。这方面,Heat的作用就比较大了。
- 更小的研发成本
大部分不是做云计算的公司往往不会有专门从事这方面的研发人员,而往往他们处于成本的综合考量会倾向于选择OpenStack来作为自己的私有云解决方案。引入Heat,对于之前不了解OpenStack的研发者来说可以更快的接入现有的业务系统。对于开发者更关心的是授权认证和对虚拟资源的增删改,而对于底层的状态并不用太多关系。
Heat 编排
Heat 目前支持两种格式的模板,一种是基于 JSON 格式的 CFN 模板;另外一种是基于 YAML 格式的 HOT 模板。CFN 模板主要是为了保持对 AWS 的兼容性。HOT 模板是 Heat 自有的,资源类型更加丰富,更能体现出 Heat 特点的模板。
一个典型的 HOT 模板由下列元素构成:
- 模板版本:必填字段,指定所对应的模板版本,Heat 会根据版本进行检验。
- 参数列表:选填,指输入参数列表。
- 资源列表:必填,指生成的 Stack 所包含的各种资源。可以定义资源间的依赖关系,比如说生成 Port,然后再用 port 来生成 VM。
- 输出列表:选填,指生成的 Stack 暴露出来的信息,可以用来给用户使用,也可以用来作为输入提供给其它的 Stack。
本文会主要用 HOT 模板。HOT 模板的全称是 Heat Orchestration Template,是 Heat 发展的重心。
一个简单的官方demo如下:
#模板版本模板版本
heat_template_version: 2015-10-15
description: Launch a basic instance with Debian image using the
``m1.mediumtiny`` flavor`` one network.
#参数列表
parameters:
NetID:
type: string
description: Network ID to use for the instance.
#资源列表
resources:
server:
type: OS::Nova::Server
properties:
image: Debian-Wheezy-7.11
flavor: m1.medium
networks:
- network: { get_param: NetID }
#输出列表
outputs:
instance_name:
description: Name of the instance.
value: { get_attr: [ server, name ] }
instance_ip:
description: IP address of the instance.
value: { get_attr: [ server, first_address ] }
novnc_console_url:
value: { get_attr: [server, console_urls, novnc] }
description: all available console URLs for the server
模板版本和参数列表没什么讲的,这里最重要的资源列表和输出列表,这两个东西直接影响你创建资源的类型以及资源返回的信息。
我们首先简单看下OS::Nova::Server
的资源类型
$ openstack orchestration resource type show OS::Nova::Server
- Field: support_status
Value: {message: null, previous_status: null, status: SUPPORTED, version: null}
- Field: attributes
Value:
accessIPv4: {description: The manually assigned alternative public IPv4 address
of the server., type: string}
accessIPv6: {description: The manually assigned alternative public IPv6 address
of the server., type: string}
addresses: {description: 'A dict of all network addresses with corresponding port_id.
Each network will have two keys in dict, they are network name and network
id. The port ID may be obtained through the following expression: "{get_attr:
[<server>, addresses, <network name_or_id>, 0, port]}".', type: map}
console_urls: {description: 'URLs of server''s consoles. To get a specific console
type, the requested type can be specified as parameter to the get_attr function,
e.g. get_attr: [ <server>, console_urls, novnc ]. Currently supported types
are novnc, xvpvnc, spice-html5, rdp-html5, serial.', type: map}
instance_name: {description: AWS compatible instance name., type: string}
name: {description: Name of the server., type: string}
networks: {description: 'A dict of assigned network addresses of the form: {"public":
[ip1, ip2...], "private": [ip3, ip4], "public_uuid": [ip1, ip2...], "private_uuid":
[ip3, ip4]}. Each network will have two keys in dict, they are network name
and network id.', type: map}
show: {description: Detailed information about resource., type: map}
- Field: properties
Value:
admin_pass: {description: The administrator password for the server., immutable: false,
required: false, type: string, update_allowed: true}
availability_zone: {description: Name of the availability zone for server placement.,
immutable: false, required: false, type: string, update_allowed: false}
block_device_mapping:
description: Block device mappings for this server.
immutable: false
required: false
schema:
'*':
immutable: false
required: false
schema:
delete_on_termination: {description: Indicate whether the volume should
be deleted when the server is terminated., immutable: false, required: false,
type: boolean, update_allowed: false}
device_name: {description: A device name where the volume will be attached
in the system at /dev/device_name. This value is typically vda., immutable: false,
required: true, type: string, update_allowed: false}
snapshot_id:
constraints:
- {custom_constraint: cinder.snapshot}
description: The ID of the snapshot to create a volume from.
immutable: false
required: false
type: string
update_allowed: false
volume_id:
constraints:
- {custom_constraint: cinder.volume}
description: The ID of the volume to boot from. Only one of volume_id
or snapshot_id should be provided.
immutable: false
required: false
type: string
update_allowed: false
volume_size: {description: 'The size of the volume, in GB. It is safe
to leave this blank and have the Compute service infer the size.',
immutable: false, required: false, type: integer, update_allowed: false}
type: map
update_allowed: false
type: list
update_allowed: false
block_device_mapping_v2:
description: Block device mappings v2 for this server.
immutable: false
required: false
schema:
'*':
immutable: false
required: false
schema:
boot_index: {description: Integer used for ordering the boot disks., immutable: false,
required: false, type: integer, update_allowed: false}
delete_on_termination: {description: Indicate whether the volume should
be deleted when the server is terminated., immutable: false, required: false,
type: boolean, update_allowed: false}
device_name: {description: A device name where the volume will be attached
in the system at /dev/device_name. This value is typically vda., immutable: false,
required: false, type: string, update_allowed: false}
device_type:
constraints:
- allowed_values: [cdrom, disk]
description: 'Device type: at the moment we can make distinction only
between disk and cdrom.'
immutable: false
required: false
type: string
update_allowed: false
disk_bus:
constraints:
- allowed_values: [ide, lame_bus, scsi, usb, virtio]
description: 'Bus of the device: hypervisor driver chooses a suitable
default if omitted.'
immutable: false
required: false
type: string
update_allowed: false
image_id:
constraints:
- {custom_constraint: glance.image}
description: The ID of the image to create a volume from.
immutable: false
required: false
type: string
update_allowed: false
snapshot_id:
constraints:
- {custom_constraint: cinder.snapshot}
description: The ID of the snapshot to create a volume from.
immutable: false
required: false
type: string
update_allowed: false
swap_size: {description: 'The size of the swap, in MB.', immutable: false,
required: false, type: integer, update_allowed: false}
volume_id:
constraints:
- {custom_constraint: cinder.volume}
description: The volume_id can be boot or non-boot device to the server.
immutable: false
required: false
type: string
update_allowed: false
volume_size: {description: 'Size of the block device in GB. If it is omitted,
hypervisor driver calculates size.', immutable: false, required: false,
type: integer, update_allowed: false}
type: map
update_allowed: false
type: list
update_allowed: false
config_drive: {description: 'If True, enable config drive on the server.', immutable: false,
required: false, type: boolean, update_allowed: false}
diskConfig:
constraints:
- allowed_values: [AUTO, MANUAL]
description: Control how the disk is partitioned when the server is created.
immutable: false
required: false
type: string
update_allowed: false
flavor:
constraints:
- {custom_constraint: nova.flavor}
description: The ID or name of the flavor to boot onto.
immutable: false
required: true
type: string
update_allowed: true
flavor_update_policy:
constraints:
- allowed_values: [RESIZE, REPLACE]
default: RESIZE
description: Policy on how to apply a flavor update; either by requesting a
server resize or by replacing the entire server.
immutable: false
required: false
type: string
update_allowed: true
image:
constraints:
- {custom_constraint: glance.image}
description: The ID or name of the image to boot with.
immutable: false
required: false
type: string
update_allowed: true
image_update_policy:
constraints:
- allowed_values: [REBUILD, REPLACE, REBUILD_PRESERVE_EPHEMERAL]
default: REBUILD
description: Policy on how to apply an image-id update; either by requesting
a server rebuild or by replacing the entire server.
immutable: false
required: false
type: string
update_allowed: true
key_name:
constraints:
- {custom_constraint: nova.keypair}
description: Name of keypair to inject into the server.
immutable: false
required: false
type: string
update_allowed: false
metadata: {description: Arbitrary key/value metadata to store for this server.
Both keys and values must be 255 characters or less. Non-string values will
be serialized to JSON (and the serialized string must be 255 characters or
less)., immutable: false, required: false, type: map, update_allowed: true}
name: {description: Server name., immutable: false, required: false, type: string,
update_allowed: true}
networks:
description: An ordered list of nics to be added to this server, with information
about connected networks, fixed ips, port etc.
immutable: false
required: false
schema:
'*':
immutable: false
required: false
schema:
fixed_ip:
constraints:
- {custom_constraint: ip_addr}
description: Fixed IP address to specify for the port created on the
requested network.
immutable: false
required: false
type: string
update_allowed: false
floating_ip: {description: ID of the floating IP to associate., immutable: false,
required: false, type: string, update_allowed: false}
network:
constraints:
- {custom_constraint: neutron.network}
description: Name or ID of network to create a port on.
immutable: false
required: false
type: string
update_allowed: false
port:
constraints:
- {custom_constraint: neutron.port}
description: ID of an existing port to associate with this server.
immutable: false
required: false
type: string
update_allowed: false
port_extra_properties:
description: Dict, which has expand properties for port. Used only if
port property is not specified for creating port.
immutable: false
required: false
schema:
admin_state_up: {default: true, description: The administrative state
of this port., immutable: false, required: false, type: boolean,
update_allowed: true}
allowed_address_pairs:
description: Additional MAC/IP address pairs allowed to pass through
the port.
immutable: false
required: false
schema:
'*':
immutable: false
required: false
schema:
ip_address:
constraints:
- {custom_constraint: net_cidr}
description: IP address to allow through this port.
immutable: false
required: true
type: string
update_allowed: false
mac_address:
constraints:
- {custom_constraint: mac_addr}
description: MAC address to allow through this port.
immutable: false
required: false
type: string
update_allowed: false
type: map
update_allowed: false
type: list
update_allowed: false
binding:vnic_type:
constraints:
- allowed_values: [normal, direct, macvtap]
description: The vnic type to be bound on the neutron port. To support
SR-IOV PCI passthrough networking, you can request that the neutron
port to be realized as normal (virtual nic), direct (pci passthrough),
or macvtap (virtual interface with a tap-like software interface).
Note that this only works for Neutron deployments that support
the bindings extension.
immutable: false
required: false
type: string
update_allowed: true
mac_address:
constraints:
- {custom_constraint: mac_addr}
description: MAC address to give to this port.
immutable: false
required: false
type: string
update_allowed: false
port_security_enabled: {description: 'Flag to enable/disable port
security on the port. When disable this feature(set it to False),
there will be no packages filtering, like security-group and address-pairs.',
immutable: false, required: false, type: boolean, update_allowed: true}
qos_policy:
constraints:
- {custom_constraint: neutron.qos_policy}
description: The name or ID of QoS policy to attach to this port.
immutable: false
required: false
type: string
update_allowed: true
value_specs:
default: {}
description: Extra parameters to include in the request.
immutable: false
required: false
type: map
update_allowed: true
type: map
update_allowed: false
subnet: {description: 'Subnet in which to allocate the IP address for
port. Used for creating port, based on derived properties. If subnet
is specified, network property becomes optional.', immutable: false,
required: false, type: string, update_allowed: false}
uuid:
constraints:
- {custom_constraint: neutron.network}
description: ID of network to create a port on.
immutable: false
required: false
type: string
update_allowed: false
type: map
update_allowed: false
type: list
update_allowed: true
personality:
default: {}
description: A map of files to create/overwrite on the server upon boot. Keys
are file names and values are the file contents.
immutable: false
required: false
type: map
update_allowed: false
reservation_id: {description: A UUID for the set of servers being requested.,
immutable: false, required: false, type: string, update_allowed: false}
scheduler_hints: {description: Arbitrary key-value pairs specified by the client
to help boot a server., immutable: false, required: false, type: map, update_allowed: false}
security_groups:
default: []
description: List of security group names or IDs. Cannot be used if neutron
ports are associated with this server; assign security groups to the ports
instead.
immutable: false
required: false
type: list
update_allowed: false
software_config_transport:
constraints:
- allowed_values: [POLL_SERVER_CFN, POLL_SERVER_HEAT, POLL_TEMP_URL, ZAQAR_MESSAGE]
default: POLL_SERVER_CFN
description: How the server should receive the metadata required for software
configuration. POLL_SERVER_CFN will allow calls to the cfn API action DescribeStackResource
authenticated with the provided keypair. POLL_SERVER_HEAT will allow calls
to the Heat API resource-show using the provided keystone credentials. POLL_TEMP_URL
will create and populate a Swift TempURL with metadata for polling. ZAQAR_MESSAGE
will create a dedicated zaqar queue and post the metadata for polling.
immutable: false
required: false
type: string
update_allowed: true
user_data: {default: '', description: User data script to be executed by cloud-init.,
immutable: false, required: false, type: string, update_allowed: true}
user_data_format:
constraints:
- allowed_values: [HEAT_CFNTOOLS, RAW, SOFTWARE_CONFIG]
default: HEAT_CFNTOOLS
description: How the user_data should be formatted for the server. For HEAT_CFNTOOLS,
the user_data is bundled as part of the heat-cfntools cloud-init boot configuration
data. For RAW the user_data is passed to Nova unmodified. For SOFTWARE_CONFIG
user_data is bundled as part of the software config data, and metadata is
derived from any associated SoftwareDeployment resources.
immutable: false
required: false
type: string
update_allowed: false
user_data_update_policy:
constraints:
- allowed_values: [REPLACE, IGNORE]
default: REPLACE
description: Policy on how to apply a user_data update; either by ignorning
it or by replacing the entire server.
immutable: false
required: false
type: string
update_allowed: true
- {Field: resource_type, Value: 'OS::Nova::Server'}
attributes
这里我们就可以看到attributes
下主要用来定义Server类型的输出信息,里面包括字符串类型的accessIPv4
、accessIPv6
、instance_name
、name
,字典类型的addresses
、console_urls
、networks
和show
。关于他们的用法,demo实例里面也可以窥知一二。
demo实例的输出
#输出列表
outputs:
instance_name:
description: Name of the instance.
value: { get_attr: [ server, name ] }
instance_ip:
description: IP address of the instance.
value: { get_attr: [ server, first_address ] }
novnc_console_url:
value: { get_attr: [server, console_urls, novnc] }
description: all available console URLs for the server
很明显,stack创建成功后,他的属性里面就包含虚拟机的名称、虚拟机的ip地址以及novnc的访问地址。
properties
从上面的OS::Nova:Server
可以看到Server类型的属性很多,其中我们常用的可能也就几个,例如availability_zone
、flavor
、image
、networks
和security_groups
Demo实例中的属性
#资源列表
resources:
server:
type: OS::Nova::Server
properties:
image: Debian-Wheezy-7.11
flavor: m1.medium
networks:
- network: { get_param: NetID }
这里可以看到,启动一个基本的Server资源至少需要3个属性,image
、flavor
和Network
。
正题
明白了attributes和properties的用途,对于Heat的编排相当重要,那么下面我简单模拟下需要实现的一个需求。
需求1
1.创建一个虚拟机,要求虚拟机自定义镜像、flavor、network、可用域和安全组;
2.创建一个数据卷,要求数据卷自定义存储类型、可用域和块容量;
3.将数据卷挂载给虚拟机;
4.要求所有资源通过参数传入;
5.要求返回属性包含虚拟机uuid、ip地址和vnc地址;
这个需求很简单,我们只需要查下Heat支持的模板类型,很快就可以找到只需要三个类型就可以满足需求
- type: OS::Nova::Server
- type: OS::Cinder::Volume
- type: OS::Cinder::Volume
模板如下:
CREATE_VM_VOLUME_AND_ATTACHE.yaml
heat_template_version: 2013-05-23
description: Template for Create VM
parameters:
image_id:
type: string
description: Image ID or image name to use for the server
constraints:
- custom_constraint: glance.image
secgroup_id:
type: string
description : Id of the security groupe
network_id:
type: string
description: network id
flavor_id:
type: string
description: Flavor for the server to be created
constraints:
- custom_constraint: nova.flavor
server_az:
type: string
description: Availablity zone of the VM
default: nova
volume_size:
type: number
description: Size of volume to attach to VM
default: 1
constraints:
- range: { min: 1, max: 200 }
volume_type:
type: string
description: If specified, the type of volume to use, mapping to a specific backend.
volume_az:
type: string
resources:
server:
type: OS::Nova::Server
properties:
availability_zone: { get_param: server_az }
image: { get_param: image_id }
flavor: { get_param: flavor_id }
networks:
- network: { get_param: network_id }
security_groups:
- { get_param: secgroup_id }
volume:
type: OS::Cinder::Volume
properties:
size: { get_param: volume_size }
volume_type: { get_param: volume_type }
availability_zone: { get_param: volume_az }
description: Volume for stack
volume_attachment:
type: OS::Cinder::VolumeAttachment
properties:
volume_id: { get_resource: volume }
instance_uuid: { get_resource: server }
outputs:
server_id:
value: { get_resource: server }
server_ip:
description: Network IP address of server
value: { get_attr: [ server, first_address ] }
novnc_console_url:
value: { get_attr: [server, console_urls, novnc] }
description: novnc console URLs for the server
创建也很简单
$ openstack stack create -t CREATE_VM_VOLUME_AND_ATTACHE.yaml \
--parameter volume_type=ceph \
--parameter volume_az=nova \
--parameter volume_size=10 \
--parameter image_id=908ea299-8eb2-4c92-b868-10e2c6120d10 \
--parameter secgroup_id=858dbd7e-8dd1-4fdd-8d3f-8c32ccdef07b \
--parameter network_id=163df3b0-13f2-4f2e-8401-e82088e8dc07 \
--parameter flavor_id=m1.tiny \
--parameter server_az=nova \
cirros
需求2
1.在需求1的基础上添加批量创建资源,要求自定义数量
2.要求引入需求1的模板
3.要求返回一个批次stack里面的虚拟机uuid、虚拟机ip和vnc请求地址
这里我们需要学习一个新的资源类型OS::Heat::ResourceGroup
,从名称就能够大概猜到该资源的作用:资源组,组内可以包括一个或多个相同的嵌套资源。
$ openstack orchestration resource type show OS::Heat::ResourceGroup
- Field: support_status
Value: {message: null, previous_status: null, status: SUPPORTED, version: '2014.1'}
- Field: attributes
Value:
attributes: {description: 'A map of resource names to the specified attribute
of each individual resource. Requires heat_template_version: 2014-10-16.',
type: map}
refs: {description: A list of resource IDs for the resources in the group., type: list}
show: {description: Detailed information about resource., type: map}
- Field: properties
Value:
count:
constraints:
- range: {min: 0}
default: 1
description: The number of resources to create.
immutable: false
required: false
type: integer
update_allowed: true
index_var:
constraints:
- length: {min: 3}
default: '%index%'
description: A variable that this resource will use to replace with the current
index of a given resource in the group. Can be used, for example, to customize
the name property of grouped servers in order to differentiate them when listed
with nova client.
immutable: false
required: false
type: string
update_allowed: false
removal_policies:
default: []
description: Policies for removal of resources on update.
immutable: false
required: false
schema:
'*':
description: Policy to be processed when doing an update which requires
removal of specific resources.
immutable: false
required: false
schema:
resource_list:
default: []
description: 'List of resources to be removed when doing an update which
requires removal of specific resources. The resource may be specified
several ways: (1) The resource name, as in the nested stack, (2) The
resource reference returned from get_resource in a template, as available
via the ''refs'' attribute. Note this is destructive on update when
specified; even if the count is not being reduced, and once a resource
name is removed, it''s name is never reused in subsequent updates.'
immutable: false
required: false
type: list
update_allowed: false
type: map
update_allowed: false
type: list
update_allowed: true
resource_def:
description: Resource definition for the resources in the group. The value of
this property is the definition of a resource just as if it had been declared
in the template itself.
immutable: false
required: true
schema:
metadata: {description: Supplied metadata for the resources in the group.,
immutable: false, required: false, type: map, update_allowed: false}
properties: {description: Property values for the resources in the group.,
immutable: false, required: false, type: map, update_allowed: false}
type: {description: The type of the resources in the group., immutable: false,
required: true, type: string, update_allowed: false}
type: map
update_allowed: true
- {Field: resource_type, Value: 'OS::Heat::ResourceGroup'}
这里可以看到OS::Heat::ResourceGroup
的属性如下
properties
- 定义资源组包含子资源个数的数字类型
count
- 定义索引替换字符串的字符串类型
index_var
- 定义要从资源组中删除的子资源的list类型
removal_policies
- 子资源定义的字典类型
resource_def
attributes
- 输出子资源指定属性信息
attributes
- 输出所有子资源ID列表
refs
- 输出所有子资源详细信息
show
模板如下:
**Multi_Num_VM_VOLUME_ATTACHE.yaml **
heat_template_version: 2013-05-23
description: Template for Create VM Volume,the Volume will be auto Attached by heat.
parameters:
num_resources:
type: number
description: Numbers of Resrouce
default: 1
constraints:
- range: { min: 1, max: 10 }
image_id:
type: string
description: ID of the image to use for the instance to be created.
constraints:
- custom_constraint: glance.image
secgroup_id:
type: string
description : Id of the security groupe
network_id:
type: string
description: network id
flavor_id:
type: string
description: Flavor for the server to be created
constraints:
- custom_constraint: nova.flavor
server_az:
type: string
description: Availablity Zone of the VM
default: nova
volume_size:
type: number
description: Size of volume to attach to VM
default: 1
constraints:
- range: { min: 1, max: 200 }
volume_type:
type: string
description: If specified, the type of volume to use, mapping to a specific backend.
volume_az:
type: string
description: Availability Zone of the Volumes
resources:
resgroup:
type: OS::Heat::ResourceGroup
properties:
count: { get_param: num_resources }
resource_def:
type: CREATE_VM_VOLUME_AND_ATTACHE.yaml #在这里引入了需求1的模板
properties:
image_id: { get_param: image_id }
secgroup_id: { get_param: secgroup_id }
network_id: { get_param: network_id }
flavor_id: { get_param: flavor_id }
server_az: { get_param: server_az }
volume_size: { get_param: volume_size }
volume_type: { get_param: volume_type }
volume_az: { get_param: volume_az }
outputs:
myrefs:
value: {get_attr: [resgroup, refs]}
server_ids:
value: {get_attr: [resgroup, server_id]}
server_ips:
value: {get_attr: [resgroup, server_ip]}
server_novnc_urls:
value: {get_attr: [resgroup, novnc_console_url]}
创建命令如下:
$ openstack stack create -t Multi_Num_VM_VOLUME_ATTACHE.yaml \
--parameter volume_type=ceph \
--parameter volume_az=nova \
--parameter volume_size=10 \
--parameter image_id=908ea299-8eb2-4c92-b868-10e2c6120d10 \
--parameter secgroup_id=858dbd7e-8dd1-4fdd-8d3f-8c32ccdef07b \
--parameter network_id=163df3b0-13f2-4f2e-8401-e82088e8dc07 \
--parameter flavor_id=m1.tiny \
--parameter server_az=nova \
--parameter num_resources=3 \
cirros1
更多
经过以上两个实例,我相信大多数人对Heat编排都有一个大致了解,其实编排不难,难的是资源逻辑的整合。现在社区里面有一些开源的Heat模板,里面几乎包含了大部分的高级用法。感兴趣的朋友可以去了解下
$ git clone git://git.openstack.org/openstack/heat-templates
另外,目前最新的Pike版本中Heat的代码的贡献前五其中有两家中国公司Huawei和EasyStack。不简单啊,OpenStack在国内市场的普及,一个重要的指标还是看这家公司在社区里面的贡献,总之还是挺佩服他们的。
Heat commit统计
网友评论