Tuning Ironic¶
内存利用率¶
Ironic 中的内存利用率是一个难以调整的问题,因为我们通常会被 API 消费者要求执行底层工具需要大量内存的工作。
这方面最明显的例子是镜像转换。非原始格式的镜像需要写入磁盘进行转换(如果请求),这需要转换过程生成内存映射以将镜像内容重新组合成连贯的数据流。整个过程还会给内核缓冲区和缓存带来压力。
最终,这归结为内存与性能之间的权衡,类似于性能与成本之间的权衡。
好的一面是,空闲的 Ironic 部署不需要太多的内存。坏的一面是,在大量并发部署可能被请求的高度突发环境中,应该考虑两个方面
ironic-api 服务/进程是如何设置的?是否会自动启动更多进程?
镜像是否优先考虑磁盘上的存储大小?或者它们是否被压缩并需要格式转换?
API¶
Ironic 的 API 应该具有相对稳定的内存占用,但根据 API 运行的 Web 服务器,可以启动额外的进程。
在正常情况下,截至 Ironic 15.1,ironic-api 服务/进程每个 worker 消耗大约 270MB 的内存。根据进程的启动方式,worker 的数量和每个 worker 的最大请求线程数可能会有所不同。自然地,存在配置和性能之间的权衡。
直接作为原生 Python 进程,即执行
ironic-api进程。每个 worker 允许处理和线程化多个请求,从而实现高水平的请求并发。从 Victoria 周期开始,ironic-api程序的直接调用将仅启动最多四个 worker。通过 Apache+uWSGI 等包装器启动,可以允许多个不同的 worker 进程,但这些 worker 通常限制允许执行的请求处理线程的数量。这意味着请求可以在前端 Web 服务器中堆积,并在先前的请求完成后释放到
ironic-api。在具有长时间同步调用的环境中,例如使用 vendor passthru 接口,这可能非常 problematic。作为组合的
ironic进程。在这种情况下,使用一个主进程和两个 worker 子进程。
当 Web 服务器由 API 进程直接启动时,默认值基于机器中的 CPU 套接字数量。
使用 uwsgi 启动时,这将完全取决于您的配置,但根据您的负载和需求平衡 worker/线程是强烈建议的。每个 worker 进程都是唯一的,消耗的内存比同等数量的 worker 线程多得多。
注意
具有内存去重功能的宿主操作系统应该可以在多个进程中提高整体内存占用率,但这并非开发团队所测量过的内容,并且会因多种因素而异。
一个重要的注意事项:每个 Ironic API 服务/进程确实会保留从数据库生成的哈希环的副本在内存中。这是为了帮助根据集群中各个节点及其负责的 conductor 的分配方式,在集群中分配负载。换句话说,您的内存量将随着每个 ironic conductor 管理的节点数量而增加。重要的是要理解,诸如 conductor groups 之类的功能意味着只有匹配的节点部分才会被考虑用于哈希环(如果需要)。
Conductor¶
Conductor 进程会根据需要启动许多其他进程,以完成请求的工作。最终,这意味着它可以快速消耗大量内存,因为它被要求一次完成大量工作。
从 ironic 15.1 开始的 ironic-conductor 默认情况下消耗大约 340MB 的 RAM,处于空闲配置状态。默认情况下,此进程作为单个进程运行。可以启动额外的进程,但它们必须具有唯一的、可解析的主机名和地址,用于 JSON-RPC 或使用中央 oslo.messaging 支持的消息总线,以便 Web 服务器 API 到 Conductor API 通信能够正常工作。
通常,可以触发的最耗内存的操作是部署的镜像转换,每个转换进程限制为 1GB 的 RAM。
大多数部署默认情况下都具有并发限制,具体取决于它们的 Compute(参见 nova.conf 设置 max_concurrent_builds)配置。但是,这只是每个 nova-compute worker,因此自然地,随着 worker 的增加,这种并发性会扩展。
独立用户可以轻松请求超过 Compute 服务默认最大并发构建的部署。因此,如果您的环境以这种方式使用,您可能希望仔细考虑您的部署架构。
如果单个 nova-compute 进程与单个 conductor 通话,并被要求执行十个并发部署的需要转换的镜像,则所需的内存可能超过 10GB。但是,这完全取决于镜像块结构和布局,以及使用的部署接口。
线程¶
Conductor 使用 Python 线程来实现并发。当从 API 到 conductor 的请求通过 RPC 传来时,conductor 会对其进行验证,获取节点级锁(如果需要),并启动一个处理线程以进行进一步处理。此类线程的最大数量限制为 conductor.workers_pool_size 配置选项的值。
注意
一些 worker 始终或定期被内部进程占用,例如 Power Synchronization。
达到限制后,任何新的请求都将以 HTTP 代码 503(服务不可用)被拒绝。客户端应该能够处理此代码,很可能在短延迟后重试或限制其请求。如果您看到大量此类错误,您可以尝试逐步提高限制,同时观察 conductor 的行为并确保请求不会因为在线程之间切换而开始花费更长时间。更好的选择是增加 conductor 的数量,因为这也会允许使用多个 CPU 核心。
注意
在同一台机器上运行多个 conductor 是一片未知的领域。您需要确保它们要么具有单独的 HTTP 服务器,要么共享相同的 HTTP 服务器而不会发生冲突。
如果您使用 JSON RPC,您还需要确保端口不冲突,方法是设置 json_rpc.port 选项。
从 2024.1 “Caracal” 发布周期开始,一小部分线程(由 conductor.reserved_workers_pool_percentage 选项指定)保留用于 API 请求和其他关键任务。定期任务和 agent heartbeat 不能使用它们。这确保了即使在极端内部负载下,API 仍然保持响应。
数据库¶
数据库上的查询负载是最大的潜在瓶颈之一,它可能会在部署中级联,并最终降低对 Ironic 用户的服务质量。
通常,根据负载、查询模式、定期任务等,可能需要添加额外的索引来帮助为数据库提供提示,以便它能够最有效地尝试减少需要检查的行数以返回结果集。
添加索引¶
以下示例特定于 MariaDB/MySQL,但语法应该易于修改为使用 PostgreSQL 的操作员。
use ironic;
create index owner_idx on nodes (owner) LOCK = SHARED;
create index lessee_idx on nodes (lessee) LOCK = SHARED;
create index driver_idx on nodes (driver) LOCK = SHARED;
create index provision_state_idx on nodes (provision_state) LOCK = SHARED;
create index reservation_idx on nodes (reservation) LOCK = SHARED;
create index conductor_group_idx on nodes (conductor_group) LOCK = SHARED;
create index resource_class_idx on nodes (resource_class) LOCK = SHARED;
注意
这些索引已由 Xena 版本的 Ironic 及更高版本自动添加。它们作为示例提供,操作员可以在早期版本的 Ironic 中手动添加它们。Xena 版本的 Ironic 的数据库升级仅知道跳过索引创建(如果它已经存在于 MySQL/MariaDB 上)。
注意
可以使用“LOCK = NONE”。基本测试表明这需要花费更长的时间,但不会导致数据库表在索引创建期间被写入锁定。如果数据库引擎不支持此功能,则索引创建将失败。
数据库平台还有一个称为“复合索引”的概念,其中索引与提交到数据库的精确查询模式对齐。数据库能够使用此复合索引来尝试大大减少其余查询的结果集生成时间。在撰写本文时,我们不会在 Ironic 中提供复合索引,因为我们认为最普遍的好处是单列索引,并且根据存在的数据,操作员可能希望与他们的数据库管理员一起探索复合索引,因为构建不当的复合索引也可能产生负面性能影响。
use ironic;
create index my_custom_app_query_index on nodes (reservation, provision_state, driver);
风险,以及为什么您应该聘请数据库管理员,是根据您的配置,实际索引可能需要在索引中包含一个或多个附加字段,例如所有者或租户。同时,字段匹配较少或顺序不同的查询将表现出不同的性能,因为复合索引可能无法被咨询。
索引不能解决所有问题¶
索引不是解决所有 API 或数据库性能问题的神奇万能药,但它们取决于数据访问和查询模式,它们是一个非常重要的组成部分。
底层对象层和数据转换,包括记录分页,会给手动数据库查询可能返回的结果集增加大量的开销。在 Ironic 的情况下,由于对象模型以及需要提取多个数据片段在数据模型的不同级别来处理诸如升级的情况,因此整个结果集将被下载和转换,这是您在使用命令行数据库客户端时不会遇到的开销。
BMC 交互¶
在默认配置下,Ironic 会运行一个定期任务,以将受管物理节点的电源状态与 Ironic 数据库同步。对于硬件类型 ipmi(参见 IPMI driver)以及根据节点数量、网络连接和这些查询的并行性,此同步可能会失败并触发重试。请在 Power Synchronization 部分中找到有关电源同步以及在发生过多电源同步失败时要调整哪些选项的更多详细信息。
我能做什么?¶
之前在本文档中,我们已经提出了一些架构约束和限制,但有一些事情可以完成以最大限度地提高性能。再次说明,这会因您的使用情况而异。
使用
direct部署接口。这将把任何最终镜像转换卸载到运行ironic-python-agent的主机上。此外,如果使用 Swift 或其他对象存储(如 RadosGW),则可以完全将下载与运行ironic-conductor的主机分离。使用小/紧凑的“raw”镜像。Qcow2 文件通常是压缩的,需要大量的内存才能解压缩和流式传输。
使用
[DEFAULT]memory_required_minimum设置调整 conductor 的内部内存限制。这将帮助 conductor 限制内存密集型操作。默认值应该可以防止内存不足操作,但在极端内存压力下,这可能仍然不是最佳的。在更改此设置之前,强烈建议咨询您的驻地“Unix 向导”甚至 Ironic 开发团队在 upstream IRC 中。如果网络带宽是您试图解决的问题,您可能希望探索
direct部署接口和缓存代理的组合。这样的配置在广域部署中可能非常有益。请参阅 Using proxies for image download。如果您正在使用大型配置驱动器,您可能希望确保您使用 Swift 存储它们,而不是将它们存储在数据库中。整个对象和内容会在 Ironic 需要评估整个节点时返回,这可能会产生性能影响。有关配置驱动器的更多信息,请参见 Enabling the configuration drive。