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
2
3
4
resources:

Networks:
type: OS::TripleO::Network

network/networks.yaml

1
2
3
4
5
resources:

ExternalNetwork:
type: OS::TripleO::Network::External

network/external.yaml

1
2
3
4
5
6
7
8
9
10
resources:
ExternalNetwork:
type: OS::Neutron::Net
properties:
...

ExternalSubnet:
type: OS::Neutron::Subnet
properties:
...

上面自定义的资源类型的映射关系如下:

1
2
OS::TripleO::Network: network/networks.yaml
OS::TripleO::Network::External: network/external.yaml

Heat基础知识 里面我们可以知道这是嵌套了3层,总共有三层stack,三层resource,即overcloud.yaml中定义的 Networksovercloud.yaml 这个stack的resource,但同时,Networks 自己也嵌套了stack,即 network/networks.yamlnetwork/networks.yaml这个stack里又包含了ExternalNetwork这个resource,跟Networks类似,ExternalNetwork在作为resource的同时,也嵌套了自己的stack,即network/external.yaml,而network/external.yaml这个stack里面包含的ExternalNetworkExternalSubnet就是真正的资源了,因为他们对应的type已经是最小粒度的type了,没有再嵌套了,OS::Neutron::NetOS::Neutron::Subnet就分别对应的是Neutron里面的网络和子网了,这几个模板的目的,就是要在Neutron里建External Network和对应的Subnet。

NetworksExternalNetwork这样的类型的resource,因为他们具有双重身份,既是resource,又是stack,在Heat里面,这样的resource会被抽象为StackResourceStackResourceResource的子类,即将这些resource当成stack来看待。在StackResource类中会有has_nested()nested()等方法,来判断这个resource是不是一个嵌套的stack,如果是的话,可以通过nested()来将这个resource封装成一个Stack对象,所以,可以简单理解成:stack也是一种resource。

那这些关系,在数据库里面是怎么关联的呢?在数据库中对应的有两个表,分别为stackresource表,在这两个表中记录了stack和resource的关联关系,我们将上面的例子里面的stack和resource,一层一层从数据库中查出来相关的记录,就比较清楚了:

首先找最上层的stack,即overcloud:

1
2
3
4
5
6
MariaDB [heat]> select id,name from stack where id="2c4efe8b-738f-4536-a8ec-d2804dad5b88";
+--------------------------------------+-----------+
| id | name |
+--------------------------------------+-----------+
| 2c4efe8b-738f-4536-a8ec-d2804dad5b88 | overcloud |
+--------------------------------------+-----------+

然后找overcloud这个stack里面包含的资源,它包含了很多资源,我们这里只找Networks这个资源:

1
2
3
4
5
6
MariaDB [heat]> select id,nova_instance,stack_id,name from resource where stack_id="2c4efe8b-738f-4536-a8ec-d2804dad5b88" and name="Networks";
+----+--------------------------------------+--------------------------------------+----------+
| id | nova_instance | stack_id | name |
+----+--------------------------------------+--------------------------------------+----------+
| 37 | 0858496d-149e-40db-9e41-c140ea39735a | 2c4efe8b-738f-4536-a8ec-d2804dad5b88 | 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
2
3
4
5
6
MariaDB [heat]> select id,name from stack where id="0858496d-149e-40db-9e41-c140ea39735a";
+--------------------------------------+---------------------------------+
| id | name |
+--------------------------------------+---------------------------------+
| 0858496d-149e-40db-9e41-c140ea39735a | overcloud-Networks-7rk5izmwi4nz |
+--------------------------------------+---------------------------------+

同样,Networks这个stack包含了很多的资源,我们只关注ExternalNetwork这个资源:

1
2
3
4
5
6
MariaDB [heat]> select id,nova_instance,stack_id,name from resource where stack_id="0858496d-149e-40db-9e41-c140ea39735a" and name = "ExternalNetwork";
+------+--------------------------------------+--------------------------------------+-----------------+
| id | nova_instance | stack_id | name |
+------+--------------------------------------+--------------------------------------+-----------------+
| 2567 | 5edc0e4a-6b6a-4964-bb42-236f0ef834cf | 0858496d-149e-40db-9e41-c140ea39735a | ExternalNetwork |
+------+--------------------------------------+--------------------------------------+-----------------+

可以看到,上面查询出来的stack,其名字不叫Networks,而是叫overcloud-Networks-7rk5izmwi4nz,从名字上,就可以看到stack的嵌套关系,是 父stack的名字 + 对应的资源名字 + 随机字符串,形成的子stack的名字。

overcloud-Networks-7rk5izmwi4nz这个stack里面包含了一个叫做ExternalNetwork的resource,从上面我们知道,这个ExternalNetwork其实也是一个嵌套的stack,即nova_instance这个字段表示的仍然是一个stack id,我们可以继续往下查:

1
2
3
4
5
6
MariaDB [heat]> select id,name from stack where id="5edc0e4a-6b6a-4964-bb42-236f0ef834cf";
+--------------------------------------+--------------------------------------------------------------+
| id | name |
+--------------------------------------+--------------------------------------------------------------+
| 5edc0e4a-6b6a-4964-bb42-236f0ef834cf | overcloud-Networks-7rk5izmwi4nz-ExternalNetwork-q6hqzjzffbzb |
+--------------------------------------+--------------------------------------------------------------+
1
2
3
4
5
6
7
MariaDB [heat]> select id,nova_instance,stack_id,name from resource where stack_id="5edc0e4a-6b6a-4964-bb42-236f0ef834cf";
+----+--------------------------------------+--------------------------------------+-----------------+
| id | nova_instance | stack_id | name |
+----+--------------------------------------+--------------------------------------+-----------------+
| 48 | 921f17a0-c2c4-49cb-8eaf-56fb736f461c | 5edc0e4a-6b6a-4964-bb42-236f0ef834cf | ExternalSubnet |
| 49 | 00ee7810-b2cf-48d4-afd2-4f340051d6cb | 5edc0e4a-6b6a-4964-bb42-236f0ef834cf | ExternalNetwork |
+----+--------------------------------------+--------------------------------------+-----------------+

可以看到,这个ExternalNetwork对应的stack名字又多了一层,而这个stack下面有两个resource,和上面network/external.yaml模板中的内容是对应的,而这两个resource对应的nova_instance字段就不是stack id了,而是真正的在Neutron里面的network和subnet id,可以通过neutron net-show 和 subnet-show查看到。

至此,嵌套的stack查询完毕,到了最终要操作的资源。所以,理解嵌套stack,有两个关键点:

  1. stack也是一种resource
  2. 在数据库层面,通过nova_instance这个字段来对stack和resource进行关联

这种嵌套,其实就是天然的递归,可以一层一层的递归进去,比如在heat里面,查询stack中嵌套的所有资源,就是通过递归去实现的,关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def iter_resources(self, nested_depth=0, filters=None):
"""Iterates over all the resources in a stack.

Iterating includes nested stacks up to `nested_depth` levels below.
"""
for res in self._find_filtered_resources(filters):
yield res

resources = self._find_filtered_resources()
for res in resources:
if not res.has_nested() or nested_depth == 0:
continue

nested_stack = res.nested()
if nested_stack is None:
continue
# 递归查询
for nested_res in nested_stack.iter_resources(nested_depth - 1,
filters):
yield nested_res

理解了上面的原理,就可以处理一些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";

至于为什么这样,原因待查。

作者

hackerain

发布于

2018-09-23

更新于

2023-10-26

许可协议