跳转到: 导航, 搜索

Neutron Trunk API 性能和扩展

返回到 Neutron/TrunkPort

摘要

此页面记录了 Trunk API 的性能和扩展测量。它侧重于子端口数量与 Trunk 相关操作所需的时间之间的关系。

主要结论是

  • 在 DevStack 和大部分默认 Neutron 配置下,高达 150 个子端口的所有测试场景都完美运行。
  • 如果您在启动虚拟机之前添加了太多子端口,可能会遇到超时错误。串行绑定所有子端口的总时间应安全地低于 rpc_response_timeout,以避免超时。在此测试环境中,默认的 90 秒超时允许大约 150 个子端口。关系是线性的,因此您可以推断。
  • 如果您在虚拟机启动后添加了许多子端口,则不应逐个添加它们,因为所需总时间会比线性增长更糟糕(可能为二次方)。
  • 但是,通过批量添加子端口(例如,openstack network trunk set --subport --subport ...)可以大大减少添加子端口的总时间(虚拟机启动后)。在此测试环境中,批量大小 64 效果很好。

测试环境

底层硬件

CPU Intel(R) Core(TM) i7-4600M CPU @ 2.90GHz
RAM DDR3 同步 1600 MHz

运行 DevStack 的虚拟机

vCPU 数量 2
RAM 6 GiB

软件版本

2016 年 11 月底,大约在 Newton 发布后的 6 周

ubuntu 16.04.1
openvswitch 2.5.0-0ubuntu1
devstack a3bb131
neutron b8347e1
nova 2249898
python-neutronclient 36ad0b7
python-novaclient 6.0.0
python-openstackclient 3.3.0

local.conf

仅相关部分

[[post-config|/etc/neutron/neutron.conf]]
[DEFAULT]
service_plugins = trunk,...
rpc_response_timeout = 600
[quotas]
default_quota = -1
quota_network = -1
quota_subnet = -1
quota_port = -1

[[post-config|/etc/neutron/plugins/ml2/ml2_conf.ini]]
[ml2]
mechanism_drivers = openvswitch,linuxbridge
tenant_network_types = vxlan,vlan
[ovs]
ovsdb_interface = native
of_interface = native
datapath_type = system
[securitygroup]
firewall_driver = openvswitch

[[post-config|/etc/neutron/dhcp_agent.ini]]
[DEFAULT]
enable_isolated_metadata = false

[[post-config|/etc/neutron/l3_agent.ini]]
[DEFAULT]
enable_metadata_proxy = false

[[post-config|/etc/nova/nova.conf]]
[DEFAULT]
# NOTE this could have probably stayed the default and not make a difference
vif_plugging_timeout = 300

工作流变体

测试场景的伪代码

为了避免被内存限制,已关闭元数据和 DHCP 服务

启动前添加子端口,一次性全部添加

for 1 parent port:
    network create
    subnet create
    port create

for N subports:
    network create
    subnet create
    port create

trunk create with N subports
server create with parent port

N = 1, 2, 4, 8, 16, 32, 64, 128, 256, 512

启动后逐个添加子端口

for 1 parent port:
    network create
    subnet create
    port create

trunk create with 0 subports
server create with parent port

for N subports:
    network create
    subnet create
    port create
    trunk set (add one subport)

port list
port list --device-id server-uuid
server show

N = 1, 2, 4, 8, 16, 32, 64, 128, 256, 512

启动后批量添加子端口

for 1 parent port:
    network create
    subnet create
    port create

trunk create with 0 subports
server create with parent port

for N batches of subports:
    for M subports in a batch:
        network create
        subnet create
        port create
    trunk set (add M subports)

port list
port list --device-id server-uuid
server show

N*M = 8*64(=512), 8*128(=1024)

结果

启动前添加子端口,一次性全部添加

带有子端口的 Trunk 创建

01-before-boot-trunk-create.svg 创建带有 N 个子端口的 Trunk 的时间显然是 N 的线性函数。在测量范围内没有错误。

带有子端口的服务器启动

02-before-boot-server-create.svg 启动带有 N 个子端口的 Trunk 的服务器的时间也是 N 的线性函数。如果使用默认配置,超过大约 150 个子端口时会出现错误。

错误症状

  • Trunk 保持在 BUILD 状态
  • 子端口保持在 DOWN 状态

示例 q-agt.log

2016-11-25 14:42:56.571 ERROR neutron.agent.linux.openvswitch_firewall.firewall [req-75355670-e83b-42ac-8e99-08e50a352609 None None] Initializing port 30c12b8f-cfae-4090-bfb9-8257b4278300 that was already initialized.
2016-11-25 14:43:09.765 ERROR neutron.services.trunk.drivers.openvswitch.agent.ovsdb_handler [-] Got messaging error while processing trunk bridge tbr-fd003eb7-3: Timed out waiting for a reply to message ID 781bd8f7778941ddaf3e3b7feb166011

示例 neutron-api.log

2016-11-25 14:41:43.900 DEBUG oslo_messaging._drivers.amqpdriver [-] received message msg_id: 781bd8f7778941ddaf3e3b7feb166011 reply to reply_d020591a17164210af4973e8432edbef from (pid=7717) __call__ /usr/local/lib/python2.7/dist-packages/oslo_messaging/_drivers/amqpdriver.py:194
2016-11-25 14:43:15.977 DEBUG oslo_messaging._drivers.amqpdriver [req-75355670-e83b-42ac-8e99-08e50a352609 None None] sending reply msg_id: 781bd8f7778941ddaf3e3b7feb166011 reply queue: reply_d020591a17164210af4973e8432edbef time elapsed: 92.0763713859s from (pid=7717) _send_reply /usr/local/lib/python2.7/dist-packages/oslo_messaging/_drivers/amqpdriver.py:73

其中经过的时间(例如,上述 92 秒)超过默认的 90 秒

/etc/neutron/neutron.conf:
[DEFAULT]
rpc_response_timeout = 90

解决方法

  • 重启后,neutron-ovs-agent 似乎可以正确启动所有子端口和 Trunk,而不会出现 rpc 超时。
  • 可以提高 rpc_response_timeout。其余测试是在 rpc_response_timeout = 600 下执行的

超时 rpc 方法

2016-11-25 15:55:42.370 DEBUG neutron.services.trunk.rpc.server [req-5a3498ad-efcb-400d-a2e7-cfe19db946a9 None None] neutron.services.trunk.rpc.server.TrunkSkeleton method update_subport_bindings called with arguments ...

当然,rpc_response_timeout 不直接适用于虚拟机启动时间,因此该图表具有一定的误导性。

相关代码:https://github.com/openstack/neutron/blob/b8347e16d1db62bf79b125d89e1c7863246fcd0d/neutron/services/trunk/rpc/server.py

def update_subport_bindings
    _process_trunk_subport_bindings()
def _process_trunk_subport_bindings
    for port_id in port_ids:
        _handle_port_binding()
def _handle_port_binding
    core_plugin.update_port()

即绑定更新到父端口“级联”到所有子端口。级联更新必须在单个 update_subport_bindings rpc 调用中完成。

启动后逐个添加子端口

逐个将子端口添加到 Trunk

03-after-boot-trunk-set.svg 该图表显然是双峰的。起始部分是线性的,偏差很小。第二部分也是线性的,但斜率更高,偏差也更大。第二部分大约在 150-200 个子端口时开始,但是 150 的重新出现只是巧合。

TODO 两部分之间发生了什么变化?

双峰性可能与 Trunk 功能无关,因为在端口创建序列中也可以观察到相同的现象:04-port-create.svg

逐个将子端口添加到 Trunk 的累积时间

对于最终用户,添加子端口所花费的总时间更为重要。基于前一张图表(测量将一个子端口添加到 Trunk 的时间),我们可以预期它大致为二次方。在测量范围内没有观察到错误。

05-after-boot-one-by-one-total-trunk-set.svg

过滤端口列表的意外影响

注意 本节中描述的行为被认为是 一个错误,请参阅相关的 openstack-dev 线程。该错误已在 https://review.openstack.org/408983 中修复。

可以观察到过滤端口列表出现意外的减速,例如

  • neutron port-list --device-id ID,或
  • openstack port list --server ID

它仅在以下情况下发生:

  • 端口用作子端口
  • 如果端口列表按父端口的 device_id 过滤(列出所有端口速度很快)
  • 如果 VM ID 用于过滤引用正在使用父端口的现有 VM(过滤不存在的 ID 速度很快,过滤不相关 VM 的 ID 速度很快)

但是,即使控制器上没有并发操作,减速效应也存在。子端口的存在就足够了。

nova 在各种地方对 neutron 进行类似的调用(例如,openstack server show),因此某些 nova 操作会变慢。

TODO neutron 和 openstack CLI 之间有什么区别?
TODO 调查 sqlalchemy 如何参与。

2016-11-30 12:16:41.010 DEBUG sqlalchemy.orm.path_registry [req-b4c835fd-853f-4be4-948b-369c2734aa49 neutron 7948a752c6004c0d9131184dc7162cc4] set 'memoized_setups' on path 'EntityRegistry((<Mapper at 0x7f3aa0469fd0; Port>, <RelationshipProperty at 0x7f3a9f754230; dns>, <Mapper at 0x7f3a9fe566d0; PortDNS>))' to '{}' from (pid=15131) set /usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/path_registry.py:63
2016-11-30 12:16:41.011 DEBUG sqlalchemy.orm.path_registry [req-b4c835fd-853f-4be4-948b-369c2734aa49 neutron 7948a752c6004c0d9131184dc7162cc4] set 'eager_row_processor' on path 'Mapper|Port|ports -> Port.dhcp_opts' to '<sqlalchemy.orm.util.ORMAdapter object at 0x7f3a9df678d0>' from (pid=15131) set /usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/path_registry.py:63

06-port-list.svg

启动后批量添加子端口

减少添加子端口的总时间

逐个添加子端口的总时间会因子端口数量而迅速增加。快到您实际上应该避免逐个添加大量子端口。但是,批量添加子端口似乎可以大大减少总时间。例如

  • 逐个添加 512 个子端口:14059 秒
  • 以 64 个批次添加 512 个子端口:237 秒

最佳批量大小在编写本文时未知。但是请记住

  • 过大的批次可能导致 rpc 调用 update_port_bindings 超时(FIXME 验证启动后是否也是如此)
  • 过小的批次会导致总时间呈二次方增长

07-after-boot-one-by-one-vs-batched.svg

1024 个子端口

由于我们尚未调查的原因,测试用例 N*M = 8*128(=1024) 耗尽了内存。

2016-12-02 14:27:25.591 ERROR neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native.ovs_ryuapp [req-d8340ea4-43a1-486e-9b46-affc8cc49355 None None] Agent main thread died of an exception
...
2016-12-02 14:27:25.591 TRACE neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native.ovs_ryuapp     self.pid = os.fork()
2016-12-02 14:27:25.591 TRACE neutron.plugins.ml2.drivers.openvswitch.agent.openflow.native.ovs_ryuapp OSError: [Errno 12] Cannot allocate memory