在 Linux 系统上 Docker 容器的性能影响
by Toradex Lucas Ferraz简介
开发人员很少看到嵌入式应用程序在本地运行与在容器内运行的性能比较。随着容器被广泛使用,许多开发人员和系统管理员可能会偏向于认为容器在性能上可能与虚拟机和其他虚拟化技术类似。 然而,这与事实相去甚远。虽然虚拟机有其应用场景,但它们在架构上与容器有所差异,这使得它们在设计上也非常不同,包括性能方面。
两种方式之间的主要区别在于容器并不运行在虚拟化引擎(又称为 hypervisor)之上。 相反,它们直接在您主机的内核上运行。在容器内启动进程与在设备本身上启动进程没有什么不同——除了虚拟化的隔文件系统。 换句话说,如果相关变量得到正确配置,容器化应用程序在设计上具有与原生应用程序完全相同的性能。
在本文中,我们将探讨其中的一些因素,并重点介绍一些常见错误以及在兼顾性能的情况下创建和开发容器的最佳实践。本文的目标不是展示相同性能的下是否真实反映实际应用程序情况(剧透了):而是展示如何充分利用容器,使 Docker 开销几乎不可察觉。 最后,我们还将展示一次启动多个容器时可能出现的一些问题,以及所有这些问题如何影响内存和启动时间。
本文使用如下硬件和软件:
硬件:Apalis iMX8QM 4GB 和 Ixora Carrier Board + 散热器
TorizonCore 5.6.0 based Yocto build,添加一些组件
关于容器的一些知识
简而言之,容器是用于打包、部署和运行应用程序的轻量级标准。它包含应用程序能够成功运行所需的一切依赖项、库文件、配置文件和其他文件。总体上它简化了再次构建编译和运行环境的步骤,并且分发整个应用程序包,主机设备除了容器引擎本身不需要安装任何依赖项。
由于它们的设计,容器具有不同的特性,因此也有着与虚拟机 (VM) 的不同的应用目标。不同于虚拟机,容器化应用相比于本地应用,前者的性能不会受到影响。如前所述,容器化进程直接在您的主机操作系统上运行,仅具有独立的文件系统和权限,因此在理论上具有相同的性能。
所以性能应该是一样的。那么,为什么我的应用程序在容器内的表现与本地的不同?这里有很多因素会产生影响,要准确指出同一个应用程序在两种环境中表现不同的原因并不容易。本质原因如下:
如果你有一个静态链接的二进制文件,它在本机和开启所有特权的容器环境中执行的表现将会是一致的,因为它们具有相同的系统配置。
结合上面的信息,我们开始逐一分析并检查可能的根本原因:
如果应用程序是动态链接的,则容器和设备上的每个共享库是否相同?它们是否具有相同的版本,是否使用相同的编译器、相同的编译选项进行编译,并启用了同样的优化?
如果您在 Debian 容器中编译了容器应用程序,在 Yocto 中编译了本机应用程序,您是否匹配了每个库的版本,检查了编译选项等?
容器是否能够访问一切必要的硬件、是否具有权限和是否正确设置访问规则?默认情况下,容器是安全的,您必须明确允许访问关键资源。
考虑到所有这些因素,我们将回顾在测量容器的内存消耗时获得的一些测试数据和结果。虽然我们不会在这篇博文中针对您遇到性能不理想的情况提供一个简单的解决方案,但 Toradex 会帮助您充分发挥容器性能。如果您需要帮助,请在我们的社区论坛上给我们留言!
测试数据和结果
首先,我们需要讨论基础资源消耗和启动时间。使用容器进行的测试发现内存使用量(在应用程序启动期间和稳定状态期间)和启动时间都略有增加。在下面的测试中,我们在Weston 之上部署了一个 Qt 应用程序,并测量了消耗的时间和内存情况 - 如上面的视频所示。部署容器软件栈的内存开销很小(我们的测试显示大约增加 23 MB),程序的启动时间增加约为 5.2 秒。所以您必须评估使用容器的好处(更短的开发时间、易用性、测试环境、可靠的构建、OTA 支持等)是否超过了这种较小的性能和启动时间影响。
此外,关于容器化应用程序,您还应该了解其他的“陷阱”。启动新容器时会发生一个情况:当 Docker 守护程序配置所需的环境并创建容器时,内存占用会出现一个短暂的峰值。在下面的示例中,该图显示了 Qt 应用程序和 Weston 的内存使用情况。第一个峰值(大约 3.8秒处)发生在Weston容器启动时,第二个峰值(大约在 15.5秒处)发生在实际的Qt应用程序启动时。对于此测试,我们手动启动每个容器,因此该时间不反映实际的启动时间。
您应该也注意到,容器的启动时间还是相当长。例如,Weston 启动时间不到1.5秒,而 Qt 应用程序启动时间不到3.5秒,总共不到5秒。相比之下,如果本机直接运行,则启动整个软件栈(Weston 和 Qt 演示)所需的时间为 2.2 秒。
但是,当一次启动多个容器时,内存峰值甚至更大,尽管启动时间不会受到太大影响(假设您的设备有足够的处理器内核)。下图中的前两个图比较了启动一个容器和启动五个容器时的内存使用情况。所有容器都相同,且仅执行简单的 sleep 10 命令。
当您需要启动多个容器时,如果它们需要使用多于计算机模块 (SoM)可用的内存来启动,那么设备将重新启动。此外,如果您的容器使用 --restart 标志设置为始终自动重启,容器会陷入无限期重启循环!若要缓解此问题,可以执行以下操作:
修改应用程序使用更少的容器(推荐);
在容器之间创建启动依赖项,使它们挨个启动,而不是一次全部启动。这将在应用程序启动时间和内存峰值之间取得平衡。上图中的第三个图说明了此方法的表现。
如果您想了解更多信息,我们的文档中有一个简短的内容,其中包含一些有用的信息。
话虽如此,在一次启动多个容器时,您必须始终注意内存使用情况,尽管启动时间应该不是太大问题。此外,请记住,在增加应用程序启动时间和降低内存峰值之间始终需要权衡。系统的可用内存和性能越低,您就越需要注意这种权衡选择。尽管如此,对于我们大多数支持 TorizonCore 的模块,能够同时启动的容器数量在大多数场景中都在确定的范围内。即便如此,我们仍在研究解决方案来改进它:请继续关注我们的发布版本并联系我们的支持人员,让我们知道您的应用场景!
总结
有很多因素可能会影响应用程序的性能。在这篇博文中,我们介绍了其中的一些,尽管对于不同的应用来说,具体指出可能导致应用程序性能不佳的原因是复杂且多样的,但我们还是提出了一些您可能面临的挑战。
尽管在运行容器化应用程序时确实存在内存消耗的小幅增加,但我们也认为,在应用程序开发中,您从 Docker 环境中获得的收益在很大程度上超过了它的不足。综上所述,Toradex 积极帮助我们的用户提高其应用程序的性能,因此,如果您在应用程序上遇到性能不佳的情况,请务必给我们留言!
提交
Verdin AM62 LVGL 移植
基于 NXP iMX8MM 测试 Secure Boot 功能
隆重推出 Aquila - 新一代 Toradex 计算机模块
Verdin iMX8MP 调试串口更改
NXP iMX8MM Cortex-M4 核心 GPT Capture 测试