‘security_group_rules_for_devices’ RPC 调用重构¶
https://blueprints.launchpad.net/neutron/+spec/security-group-rules-for-devices-rpc-call-refactor
安全组规则从 neutron-server 到 L2 agent 的同步在高密度云中扩展性差,导致在某些情况下 neutron-server 阻塞。
问题描述¶
从 L2 agent 到 neutron-server 的 security_group_rules_for_devices RPC 调用扩展性不好,因为所有安全组规则条目都会随着安全组中的每个特定 IP 地址而扩展(参见 [1]),当该组在类似规则中被引用时:
允许来自 IPv4 和 IPv6 的 ‘default’ 组的所有流量
这导致
巨大的 AMQP 消息(>20-600 MB)
在 neutron-server 端处理时间很长,当在同一租户/安全组下有很多实例时(>60 秒)
neutron-server 锁死,当 RPC 调用在 agent 端超时时,并且相同的 security_group_rules_for_devices 调用被重新发送到 neutron。
为了更详细的了解
security_group_rules_for_devices 是从 L2 agent 到 neutron-server 的 RPC 调用,参见 [1]。
此调用的参数是 device_ids 列表,device_ids 连接到端口。Neutron 构建一个安全组规则列表 [1] 并返回每个 device_id 的安全组规则列表
neutron-server 和 L2 agent 之间的消息大小将根据以下公式增长
MessageSize ~= base + L * Instances_in_host * (Instances_in_security_group-1)
其中 L = len(str(security_group_rule)) ~=440 字节
这个问题更有可能发生在大型云或更密集的云中,但这是一个问题,因为 nova 可以扩展到 10 倍的实例而不会出现问题:参见 [2]。
提议的变更¶
将 security_group_rules_for_devices 重构为新的调用 ‘security_group_rules_for_devices_compact’,它不返回 [1],而是返回可以由 l2 agent 扩展的更紧凑的结果。
新的 ‘security_groups_rules_for_devices_compact’ RPC 调用返回
{'security_groups': {'sg-id1': {'rules': [ {},{},{},{}]}
},
'security_group_member_ips': { 'sg-id1' : {'ipv4': ['192.168.11.2/32'],
'ipv6': [] },
'sg-id2-referenced-from-id1' : {...},
},
'devices': {'dev-id1': { ... ,
'fixed_ips': ['192.168.11.4'],
'security_groups': ['sg-id1', 'sg-id2', ...] }}
}
安全组规则将以非扩展的方式传递,这意味着规则中对 src 或 dst 安全组的任何引用都将存储为安全组 ID。
像这样(旧结果)
{'dev-id1':
{...,
'security_group_rules': [{'direction': u'egress',
'ethertype': u'IPv6',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e'},
{'direction': u'egress',
'ethertype': u'IPv4',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e'},
{'direction': u'ingress',
'ethertype': u'IPv4',
'protocol': u'icmp',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e'},
{'direction': u'ingress',
'ethertype': u'IPv4',
'remote_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'source_ip_prefix': '192.168.11.2/32'},
{'direction': u'ingress',
'ethertype': u'IPv4',
'remote_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'source_ip_prefix': '192.168.11.3/32'},
{'direction': u'ingress',
'ethertype': u'IPv4',
'remote_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'source_ip_prefix': '192.168.11.4/32'},
{'direction': u'ingress',
'ethertype': u'IPv4',
'remote_group_id':
u'23138476-4fde-454e-33ad-abc123456782',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'source_ip_prefix': '192.168.33.4/32'}
]
},
'dev-id2': {
...,
'security_group_rules': [{'direction': u'egress',
'ethertype': u'IPv6',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e'},
{'direction': u'egress',
'ethertype': u'IPv4',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e'},
{'direction': u'ingress',
'ethertype': u'IPv4',
'protocol': u'icmp',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e'},
{'direction': u'ingress',
'ethertype': u'IPv4',
'remote_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'source_ip_prefix': '192.168.11.2/32'},
{'direction': u'ingress',
'ethertype': u'IPv4',
'remote_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'source_ip_prefix': '192.168.11.3/32'},
{'direction': u'ingress',
'ethertype': u'IPv4',
'remote_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'source_ip_prefix': '192.168.11.4/32'},
{'direction': u'ingress',
'ethertype': u'IPv4',
'remote_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'source_ip_prefix': '192.168.11.5/32'},
{'direction': u'ingress',
'ethertype': u'IPv4',
'remote_group_id':
u'23138476-4fde-454e-33ad-abc123456782',
'security_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e',
'source_ip_prefix': '192.168.33.4/32'}
]
}
}
在新版本中会像这样
{'security_groups': {u'1809f907-4b0c-4445-a366-ff28eaab9c2e':
{'rules': [
{'direction': u'egress', 'ethertype': u'IPv6'},
{'direction': u'egress', 'ethertype': u'IPv4'},
{'direction': u'ingress',
'ethertype': u'IPv4',
'protocol': u'icmp',},
{'direction': u'ingress',
'ethertype': u'IPv4',
'remote_group_id':
u'1809f907-4b0c-4445-a366-ff28eaab9c2e'},
{'direction': u'ingress',
'ethertype': u'IPv4',
'remote_group_id':
u'23138476-4fde-454e-33ad-abc123456782'}
]
}
},
'security_group_member_ips': { u'1809f907-4b0c-4445-a366-ff28eaab9c2e' :
{u'ipv4': ['192.168.11.2/32',
'192.168.11.3/32',
'192.169.11.4/32',
'192.168.11.5/32'],
u'ipv6': []
},
u'23138476-4fde-454e-33ad-abc123456782' :
{u'ipv4': ['192.168.33.2/32'],
u'ipv6': []
}
},
'devices': {'dev-id1': { ... ,
'fixed_ips': ['192.168.11.4'],
'security_groups':
['1809f907-4b0c-4445-a366-ff28eaab9c2e'] },
'dev-id2': { ... ,
'fixed_ips': ['192.168.11.4'],
'security_groups':
['1809f907-4b0c-4445-a366-ff28eaab9c2e'] },
}
所有从设备引用的安全组都将包含在响应中。
所有来自引用组的 remote_group_id 的安全组成员 IP 地址都将包含在响应中。
旧调用可以在这个 J 周期内标记为已弃用,并在 K 周期内删除。
将重构为新的调用具有以下优势
与旧 agent 的兼容性,在 neutron-server 升级期间
能够将补丁(server/agents)分成更多步骤,因为我们将能够解决新的调用,同时保持 agent 调用旧的调用,然后在后续步骤中重构 agent。
结果消息大小将是
- MessageSize ~= base +
D * Instances_in_host + L * Referenced_security_groups + I * Instances_in_referenced_security_groups
- 其中 L = len(str(compact_security_group_rule)) ~= 220 字节
D = len(str(device_description_including_ips_and_sg_ids)) I = len(str(ip_address + ‘,’)) ~= 17 字节
在新消息格式中,没有数据被复制,因此现在有两个变量成为乘法因子。
下一步
Édouard Thuleau 提出使用每个安全组的 RPC topic [3],这将在本次迭代之后进行处理。
备选方案¶
与其在一个 rpc 调用中包含所有安全组,不如将其拆分为第二个调用 ‘security_groups_and_referenced_members’,该调用将接收安全组 ID 列表,并返回安全组列表和安全组 IP 地址列表。完全同步需要向 neutron server 发送 2 个调用。
我们可以有一个 ‘security_groups’ 和一个 ‘security_groups_members’,它将提供安全组,没有成员 IP 地址,以及每个安全组的 IPv4 和 IPv6 地址成员列表。完全同步需要向 neutron server 发送 3 个调用。但是,这种方法允许单独通信安全组中的新成员,或安全组中的新规则,从而进一步减少了这些情况下的传输信息。对于第一种替代方案,通过增加调用的数量来减少流量量似乎是一个糟糕的权衡,因为每个调用都会产生开销/延迟。
我们可以只在规则生成中压缩 CIDR 范围,这不需要修改 agent,但会增加 rpc 请求处理时间。
数据模型影响¶
无
REST API 影响¶
无
安全影响¶
无
通知影响¶
无
其他最终用户影响¶
无
性能影响¶
在以下情况下,性能影响应该非常积极
安全组更改:neutron-server 负载和 AMQP 消息大小。目前 oslo messaging 将结构序列化为 JSON,因为 AMQP 版本限制(字典的字符串大小是其中之一)。减小消息大小将减少字典构建时间和序列化时间。
新端口的创建时间
通过 ipset spec 提出的 iptables 级别优化将实现更高的性能影响 [4]。
如果传输时间成为我们的瓶颈而不是处理时间,我们可能会考虑 AMQP 级别是否可用压缩。
其他部署者影响¶
无
开发人员影响¶
我不确定是否有专有的 l2-agent 与 RPC 通信。我们将允许一些时间来升级这些 agent,将其引入为新的 RPC 调用,而不是修改现有的调用。
在删除旧调用之前,所有使用此 RPC 调用的 agent 都需要更新到新的调用。
实现¶
负责人¶
工作项¶
将 rpc 调用重构为 neutron-server 中的一个新调用,以及 agent mixin 中的匹配 rpc 调用。
添加功能测试以验证该方法。
逐步升级 agent 以使用新的调用。
分析 db 访问并提出新的规范,如果可以对此领域进行改进。
依赖项¶
无
测试¶
功能测试将验证该方法并确保可以使用旧方法获得的结果可以更快地使用新方法持续复制。使用功能测试测试这两个 rpc 调用将避免两个代码路径中的回归,而仅在 Tempest 中测试此方法只能测试默认 rpc 调用。
文档影响¶
无