OpenStack Heat嵌套Stack解析
Stack是Heat里面抽象出来的概念,是一组资源的集合,Stack之间是可以嵌套的,也就是说一个Stack可以有子Stack,子Stack又可以有子Stack,关于Heat Stack的基础知识,见这篇文章:Heat基础知识
这篇文章重点介绍下嵌套stack在heat里面的实现,因为在实际使用heat时,模板稍微复杂一点,就会用到嵌套的stack,当嵌套的层数非常多时,里面的关系就会变得非常复杂,一旦遇到一些问题,如果不懂其实现的原理,就会束手无策。
所谓嵌套,其实就是需要在数据库里面记录清楚各个资源之间的关系,谁是父,谁是子,把数据库的关联关系搞清楚了,嵌套stack的原理也就清楚了,所以重点是数据库模型是怎么建模的。
Heat里面有两个最重要的概念,一个是刚刚提到的stack,还有一个是resource,resource代表的是某个stack里面封装的资源,也就是说某个stack里面有哪些resource,嵌套的关键点就在这里,因为stack可以嵌套stack,所以被嵌套的stack其实也是一个resource。
我们以tripleo里面的几层嵌套stack为例,来进行下解析。TripleO里面关于基础网络的相关的逻辑,被封装在多个模板中,这些模板通过互相引用,形成了嵌套stack,有如下几个模板:
overcloud.yaml #该模板是tripleo最上层的模板
1 | resources: |
network/networks.yaml
1 | resources: |
network/external.yaml
1 | resources: |
上面自定义的资源类型的映射关系如下:
1 | OS::TripleO::Network: network/networks.yaml |
从 Heat基础知识 里面我们可以知道这是嵌套了3层,总共有三层stack,三层resource,即overcloud.yaml中定义的 Networks
是 overcloud.yaml
这个stack的resource,但同时,Networks
自己也嵌套了stack,即 network/networks.yaml
,network/networks.yaml
这个stack里又包含了ExternalNetwork
这个resource,跟Networks
类似,ExternalNetwork
在作为resource的同时,也嵌套了自己的stack,即network/external.yaml
,而network/external.yaml
这个stack里面包含的ExternalNetwork
和ExternalSubnet
就是真正的资源了,因为他们对应的type已经是最小粒度的type了,没有再嵌套了,OS::Neutron::Net
和OS::Neutron::Subnet
就分别对应的是Neutron里面的网络和子网了,这几个模板的目的,就是要在Neutron里建External Network和对应的Subnet。
像Networks
和ExternalNetwork
这样的类型的resource,因为他们具有双重身份,既是resource,又是stack,在Heat里面,这样的resource会被抽象为StackResource
,StackResource
是Resource
的子类,即将这些resource当成stack来看待。在StackResource
类中会有has_nested()
和nested()
等方法,来判断这个resource是不是一个嵌套的stack,如果是的话,可以通过nested()
来将这个resource封装成一个Stack
对象,所以,可以简单理解成:stack也是一种resource。
那这些关系,在数据库里面是怎么关联的呢?在数据库中对应的有两个表,分别为stack
和resource
表,在这两个表中记录了stack和resource的关联关系,我们将上面的例子里面的stack和resource,一层一层从数据库中查出来相关的记录,就比较清楚了:
首先找最上层的stack,即overcloud:
1 | MariaDB [heat]> select id,name from stack where id="2c4efe8b-738f-4536-a8ec-d2804dad5b88"; |
然后找overcloud这个stack里面包含的资源,它包含了很多资源,我们这里只找Networks
这个资源:
1 | MariaDB [heat]> select id,nova_instance,stack_id,name from resource where stack_id="2c4efe8b-738f-4536-a8ec-d2804dad5b88" and name="Networks"; |
注意,这里有一个字段叫做nova_instance
,这是个关键点,这个字段并不像字面的意思一样,只表示nova里面instance的id,它真实的含义其实是:physical resource id,它适用于所有的资源,当这个resource代表的是真实的资源时,比如Network, Subnet, Instance, Port等,nova_instance
则记录这些资源真实的uuid,通过这些uuid可以show出来对应的资源;而当这个resource代表的不是真实的资源,也即代表的是stack时,nova_instance
则表示的是这个stack的uuid,它虽然不是真实的资源,但是可以通过openstack stack show xxx
来查询出来这个stack,从这个角度上来说,它也是一个资源,只不过是heat里的stack而已,这跟neutron里的network, subnet, port是一个道理。
从上面的分析我们知道,0858496d-149e-40db-9e41-c140ea39735a
是一个stack uuid,我们把这个stack和其包含的resource,从数据库中查出来:
1 | MariaDB [heat]> select id,name from stack where id="0858496d-149e-40db-9e41-c140ea39735a"; |
同样,Networks
这个stack包含了很多的资源,我们只关注ExternalNetwork
这个资源:
1 | MariaDB [heat]> select id,nova_instance,stack_id,name from resource where stack_id="0858496d-149e-40db-9e41-c140ea39735a" and name = "ExternalNetwork"; |
可以看到,上面查询出来的stack,其名字不叫Networks
,而是叫overcloud-Networks-7rk5izmwi4nz
,从名字上,就可以看到stack的嵌套关系,是 父stack的名字
+ 对应的资源名字
+ 随机字符串
,形成的子stack的名字。
overcloud-Networks-7rk5izmwi4nz
这个stack里面包含了一个叫做ExternalNetwork
的resource,从上面我们知道,这个ExternalNetwork
其实也是一个嵌套的stack,即nova_instance
这个字段表示的仍然是一个stack id,我们可以继续往下查:
1 | MariaDB [heat]> select id,name from stack where id="5edc0e4a-6b6a-4964-bb42-236f0ef834cf"; |
1 | MariaDB [heat]> select id,nova_instance,stack_id,name from resource where stack_id="5edc0e4a-6b6a-4964-bb42-236f0ef834cf"; |
可以看到,这个ExternalNetwork
对应的stack名字又多了一层,而这个stack下面有两个resource,和上面network/external.yaml
模板中的内容是对应的,而这两个resource对应的nova_instance
字段就不是stack id了,而是真正的在Neutron里面的network和subnet id,可以通过neutron net-show 和 subnet-show查看到。
至此,嵌套的stack查询完毕,到了最终要操作的资源。所以,理解嵌套stack,有两个关键点:
- stack也是一种resource
- 在数据库层面,通过
nova_instance
这个字段来对stack和resource进行关联
这种嵌套,其实就是天然的递归,可以一层一层的递归进去,比如在heat里面,查询stack中嵌套的所有资源,就是通过递归去实现的,关键代码如下:
1 | def iter_resources(self, nested_depth=0, filters=None): |
理解了上面的原理,就可以处理一些heat里常遇到的一些奇怪问题了,比如在tripleo里,经常会遇到这样的错误:
1 | Stack CREATE FAILED (overcloud-Networks-7rk5izmwi4nz-ExternalNetwork-hl6wiafrngab): Resource CREATE failed: Conflict: resources.ExternalNetwork: Unable to create the flat network. Physical network external is in use. |
遇到这样的错误,说明overcloud-Networks-7rk5izmwi4nz-ExternalNetwork-hl6wiafrngab
这个stack对应的resource在heat里面找不到了,也就是父stack找不到原来的子stack了,关联关系被破坏掉了,所以在执行stack的更新时,会再去建这些resource,但是实际上这些resource是存在的,就创建失败了,导致报了这样的错。解决思路就是按照上面的解析,把这些stack和resource的关系一步一步给梳理出来,然后可以修改相关的字段,主要是nova_instance
字段,将正确的关系重新梳理正确,让heat能够找到正确的resource,就可以更新成功了。
修改完之后,如果还遇到这样的问题:
1 | 2019-01-05 10:21:51Z [overcloud]: UPDATE_FAILED resources.Networks: resources.TenantNetwork: The Stack (overcloud-Networks-atpivemskzkd-TenantNetwork-wgbvjqwnbxby) could not be found. |
可以在数据库中将对应的现在的stack name修改为现在找不到的stack name:
1 | update stack set name="overcloud-Networks-atpivemskzkd-TenantNetwork-wgbvjqwnbxby" where name="overcloud-Networks-atpivemskzkd-TenantNetwork-4arueh4pzole"; |
至于为什么这样,原因待查。
OpenStack Heat嵌套Stack解析
https://hackerain.me/2018/09/23/openstack/tripleo/heat-nested-stack.html