Heat/DSL2
这是一个尝试演进第一个 Heat/DSL 提案的想法,结合了来自其他地方的一些相关工作/概念,包括 TOSCA 和 CAMP。这两个规范都在不断发展,并且有潜力收敛到简单、直观和强大的东西:这是朝着这种收敛的目标努力,并且被认为可以很好地映射到所有三个(Heat、TOSCA 和 CAMP),并使其易于表示其他流行的工具和技术(例如 Juju、Puppet、Chef、OpenShift)。
这是一个正在进行中的工作,旨在引导讨论而不是阻止它。欢迎发表评论和进一步演进。另请参阅 Heat/Vocabulary 表。
概述
这个变体将第一个提案 (Heat/DSL) 简化为两个基本概念
-
组件是结果模型中的组成部分,可以是 OpenStack 原语,例如 服务器 或 负载均衡器 或 子网,也可以是更高级的概念,例如 数据库 或 服务/层/自动伸缩组,或者用户提供的用于应用程序的工件 -
需求告诉引擎哪些组件和环境是合适的,如何配置它们,以及如何将它们连接在一起(例如通过查看能力)
然后,一个 蓝图 是一组组件和需求,定义了一个应用程序模板。一个 环境 是可以部署蓝图/组件的地方,而一个 部署 是蓝图在环境中的实际实例化(也称为堆栈)。
繁重的工作由定义的组件和需求类型完成。其中一些将在 OpenStack Heat 中标准化(例如 服务器),而另一些可以基于其他地方的标准模型(例如 Juju、Chef、Puppet、Salt、Rerun、RPM),或由供应商提供(例如 PaaS 或云服务),并期望可移植的需求类型在其他社区中发展(例如 TOSCA、Java 社区流程、PaaS 等)。
这个的粗略 Python 解释器可以在 [1] 找到。它没有引擎,也没有集成到 Heat 中,但这是一个开始,并且可以随着此规范的发展而发展,弄清楚如何/是否将其连接到 Heat,并挑选 Rackspace 将要捐赠给 Heat/DSL 的代码。与最初的提案的主要区别是:(1)删除了 服务 作为顶级概念,因为它可以建模为组件类型(参见下面的复杂示例);(2)删除了 提供者、接口 和 关系 作为顶级概念,因为这些可以通过需求建模。(我喜欢在第一个 DSL 中包含选项和参数的想法,但我还没有(目前)在这里这样做。)
在这个提案(以及我理解的原始提案)中,大量的元数据(关于查找值、映射等)被外部化并由需求处理。
简单示例
使用此提议的 DSL 的服务器的基本模板,带有安装脚本
name: "server running a custom install script"
# blueprint defining components, using map/dictionary syntax
components:
my_server:
type: server
run: my_script
requires:
minRam: 4gb
my_script:
type: com.example.script.ShellScript
content: http://example.com/my_install.sh
sudo: true
为了提高可读性,组件的“id”是隐式的,需求的“type”是隐式的。这是使用“list”语法在 JSON 中(也有效 YAML)的相同内容,其中这些键是显式的。
{
"name": "server running a custom install script"
"components": [
{ "id": "my_server",
"type": "server",
"requires": [
{ "minRam": "4gb" },
{ "run": "my_script" } ]
}, {
"id": "my_script",
"type": "com.example.script.ShellScript",
"content": "http://example.com/my_install.sh",
"sudo": true
} ]
}
这里是一个使用由 Heat 扩展定义的更高级组件的模板(例如 TOSCA、OpenShift 等——这些反过来映射到 OpenStack 原语,如 服务器)。这混合了列表和映射语法,并展示了如何使用全局需求来强制执行管辖权和受信任的组件。
name: "war file deployed to appserver container (vm or cluster or paas)"
# blueprint defining components, with no 'id' or 'type', and using list syntax
components:
- content: hello.war
requires:
- type: com.example.java:WarDeploymentRequirement
requires:
# force use of jetty server and openstack servers, in USA
providers: { allow: [ com.example.*, org.jetty.*, org.openstack.* ] }
jurisdiction: { iso3166: "us" }
模式定义
蓝图 的基本形状是
name: <optional string name> components: <mandatory list of components or map of components by id> requires: <optional list of global requirements or map of global requirements by type, or value of default requirement>
一个 组件 由以下属性组成
id: <optional string user-supplied identifier> type: <optional type imposing requirements> requires: <optional list of requirements or map of requirements by type> content: <optional reference to a piece of content> <other attributes as understood by the type>
组件的类型可以是显式的,也可以根据声明在其上或指向它的需求推断得出(使用需求中的“fulfillment”属性)。设想了抽象类型和子类型,例如 服务器 可能是理解某些需求的抽象类型(例如 minRam),具有具体的子类型,例如 AWS::EC2::Instance 或 OpenStack::Nova::Server。
一个 需求 由以下属性组成
type: <mandatory string known requirement type> fulfillment: <optional component(s) who fulfill this requirement> <other attributes as understood by the requirement type>
Heat 将定义一个需求库,其中一些是全局的,例如 提供者 和 管辖权(在上面的示例中),其他是特定于组件类型并被这些类型理解的,例如 minRam 和 run 在 服务器 上(在上面的脚本示例中)。为了提高可读性,在需求类型定义了默认属性(例如 minRam 和 run 在上面的脚本示例中)的情况下,可以使用简写的语法,该语法仅为该需求属性提供一个值(如果含义明确);同样,如果组件类型定义了默认需求类型,则可以省略需求的 类型。
对其他组件的引用使用“id:other_id”的表示法(或其列表),其中 other_id 是组件上的标识符,或者简单地“other_id”,如果含义明确,或者 Heat 特定的 URL 指向预先存在的组件,以指示组件必须提供与需求匹配的能力。通常可以避免它,但它是处理某些复杂现实世界情况的一种相对简单的方法,例如将两个不同的 Web 应用程序指向同一个数据库实例,或者指定对用于满足一个需求的组件的附加需求。下面包含了一些说明。
复杂示例
这是一个使用 RPM 安装的 PHP 运行集群(作为一个示例毒药——Juju、Chef、Puppet 等类似地完成),一个解包的 tarball,以及一个负载均衡器和 DNS 设置(请根据需要进行调整,以更好地映射到现有的 heat 概念)。
name: "elastic php app"
# example using 'tier' type with 'nodespec' attribute to define the node,
# 3 nodes, with "loadbalancer" and "dns" reqs which quantum fulfils
components:
my_app_tier:
type: tier # defined type in heat, understanding attributes and requirements below
initialSize: 3
nodespec:
type: server # (this might be a default for tier's 'nodespec')
requires: # set up each of the server instances
os: rhel
# these requirements point to "in-line" components
run: { type: rpm, rpms: [ php ], repos: [ http://rpms.example.com/repo/ ] }
unpack: { type: tgz, content: http://example.com/my_app.tgz, target: /var/html }
requires:
loadbalancer: # known requirement type, on the _tier_ (treated as the pool)
publicPort: 80
membersPort: 80
fulfillment: id:my_lb # point to the LB, which has an addl requirement (dns)
my_lb: # type is implied by the 'loadbalancer' requirement above
requires:
dns: # another defined requirement type
hostname: myapp.example.com
这是一个来自 CAMP 提案草案的示例,使用可以由 Heat 后端扩展支持(或在 TOSCA 等中实现)的高级组件。
name: "war file with database using CDI"
# complex 3-tier example using map syntax and 'fulfillment' to ensure
# WAR file's container(s) are injected with DB which has run SQL init
components:
hello_war: # no type
content: hello.war
requires:
com.example.java:WarDeploymentRequirement:
fulfillment: frontend
hello_sql:
content: hello.sql
type: com.example.database:Schema # here, type of component defined
requires: backend # assume Schema defines a default req type "DB"
frontend: # "platform component" implied by WarDeplReq above
requires:
database: # frontend type must recognise a named "database" req
mode: CDI # assume that req supports various injection modes
fulfillment: backend # ensure this is the same DB which ran our hello.sql
com.example.lb:LoadBalanced: # longhand req form: this is the 'type'
protocol: https # assume that type recognises these attributes
algorithm: round-robin
sticky-sessions: true
fulfillment: lb
lb: { tags: [ "load-balancer" ] } # tag it so we can monitor it afterwards
API
REST API 应该支持 POST 此 YAML,并且应该支持 POST 一个包含此 YAML 文件的 ZIP(在已知位置,例如 `heat.yaml`),其中文件引用相对于该 ZIP 解析——因此,例如,脚本、WAR 和密钥等工件可以包含在内。此外,这将使其与 TOSCA 的“CSAR”格式和 CAMP 的“PDP”格式非常容易兼容。
REST API 可以如 Heat/Open_API 中所述,或者它可以是 CAMP 的一个删减版本(它非常接近这个,有一些额外的软区别,这些区别在运行时可能很有用——应用程序组件(工件)与平台组件(由平台提供的,例如服务器或数据库);以及组件(部署中的实例)与模板(可用类型);等等)。