因预编译资源导致的内存过度消耗

大家好,

过去几年,我们一直在 OpenShift 上运行自己的 Discourse 实例。最近几个月(大约从 2026 年 1 月开始,大致与 Introducing pre-compiled JS assets for self-hostersIntroducing a new build system for plugins 中介绍的新方法一致),我们观察到了以下情况:

在构建时预编译资源(bundle exec rake assets:precompile:build)时,该操作现在会失败并消耗超过 20GB 的内存:

...
gem install prometheus_exporter -v 2.2.0 -i /var/www/discourse/plugins/discourse-prometheus/gems/3.4.7 --no-document --ignore-dependencies --no-user-install
Successfully installed prometheus_exporter-2.2.0
1 gem installed
Plugin name is 'msgraph-polling', but plugin directory is named 'msgraph-poll-discourse-plugin'
[assemble_ember_build] No existing build info file found.
Fetching and extracting https://get.discourse.org/discourse-assets/2026.5.0-latest-03484cbd/production.tar.gz...
  % Total    % Received % Xferd  Average Speed   Time   Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 20.1M  100 20.1M    0     0  22.5M      0 --:--:-- --:--:-- --:--:-- 22.5M
Prebuilt assets downloaded and extracted successfully.
[assemble_ember_build] Reusing existing core ember build. All done.
Plugin name is 'msgraph-polling', but plugin directory is named 'msgraph-poll-discourse-plugin'
[Plugin::JsManager] Compiling 49 plugins...

# 在此处长时间卡住

查看内存消耗情况,我们看到:

Every 1.0s: free -h                             webapp-test-discourse-689b5fcb4d-fd2dp-debug-b7nn2: Mon May  4 14:15:57 2026

               total        used        free      shared  buff/cache   available
Mem:            28Gi        26Gi       596Mi       524Mi       2.1Gi       1.7Gi
Swap:             0B          0B          0B

而之前该过程相当快速,且内存消耗适中。

我们尝试设置环境变量如 CI=1NODE_OPTIONS="-–max-old-space-size=X",但似乎都无法缓解这种内存消耗问题。

是否有人遇到同样的问题?如果是的话,你们是如何解决的?

非常感谢!

Ismael

您能否分享已安装插件的列表?

您的总内存是 4GB 吗?如果是,是否配置了交换空间?

能否分享一下服务器配置?

你好,David,

额外插件 的列表如下:

#   - 交易按钮(用于市场)
          - git clone --depth=1 https://github.com/jannolii/discourse-topic-trade-buttons.git
          #   - 保存的搜索
          - git clone --depth=1 https://github.com/discourse/discourse-saved-searches.git
          #   - Discourse Akismet
          - git clone --depth=1 https://github.com/discourse/discourse-akismet.git
          #   - Prometheus
          - git clone --depth=1 https://github.com/discourse/discourse-prometheus.git
          #   - Discourse 文档
          - git clone --depth=1 https://github.com/discourse/discourse-docs.git
          #   - MSGraph 投票
          - git clone --depth=1 https://github.com/CERN/msgraph-poll-discourse-plugin.git

祝好,

Ismael

你好,Heliosurge,

节点配置为 8 核 CPU / 30 GiB 内存。在正常情况下,论坛通常消耗 1 核 CPU 和最多 2-3 GB 内存(包括预编译)。

未配置交换空间。我理解交换空间仅在内存受限时使用,而此处不应出现这种情况。然而,我更担心的是内存消耗量,因为以前从未出现过这种情况。

此致,

Ismael

好的,您的服务器规格应该不需要交换空间。这里的团队成员 David 可能最能提供帮助。

内存使用量是否在 assets:build 任务完成后立即下降?

你好 @david

在 assets:build 任务完成后,内存使用量会立即下降吗?

不会。我进一步排查后发现有些奇怪的地方。

在执行 precompiling:build 之前,插件列表如下:

/var/www/discourse$ ls plugins/
automation           discourse-akismet           discourse-data-explorer  discourse-hcaptcha           discourse-microsoft-auth  discourse-post-voting  discourse-saved-searches       discourse-user-notes           styleguide
chat                 discourse-apple-auth        discourse-details        discourse-lazy-videos        discourse-narrative-bot   discourse-presence     discourse-solved               discourse-zendesk-plugin
checklist            discourse-assign            discourse-docs           discourse-local-dates        discourse-oauth2-basic    discourse-prometheus   discourse-subscriptions        footnote
discourse-adplugin   discourse-cakeday           discourse-gamification   discourse-login-with-amazon  discourse-openid-connect  discourse-reactions    discourse-templates            msgraph-poll-discourse-plugin
discourse-affiliate  discourse-calendar          discourse-github         discourse-lti                discourse-patreon         discourse-rewind       discourse-topic-trade-buttons  poll
discourse-ai         discourse-chat-integration  discourse-graphviz       discourse-math               discourse-policy          discourse-rss-polling  discourse-topic-voting         spoiler-alert

通过调试代码,我注意到了以下行为:

/var/www/discourse$ script/rails runner "AssetProcessor.ember_version"
插件名称为 'msgraph-polling',但插件目录名为 'msgraph-poll-discourse-plugin'

# 在此处无限挂起

AssetProcessor.ember_version 对应于 discourse/lib/plugin/js_manager.rb at latest · discourse/discourse · GitHub 这一行。

因此,我对该文件进行了一些修改(已附上),主要是为了打印在处理过程中卡住的位置,并移除 discourse/lib/plugin/js_manager.rb at latest · discourse/discourse · GitHub 中的 AssetProcessor.ember_version,直接设置为 5 以继续生成十六进制值。

随后,我将并行度降低为 1 以简化操作,修改位置在 discourse/lib/plugin/js_manager.rb at latest · discourse/discourse · GitHub = [Etc.nprocessors, 1].min`)。

之后,我执行了 bundle exec rake assets:precompile:build,结果如下:

/var/www/discourse$ bundle exec rake assets:precompile:build
插件名称为 'msgraph-polling',但插件目录名为 'msgraph-poll-discourse-plugin'
[assemble_ember_build] 正在复用现有的核心 Ember 构建。全部完成。
插件名称为 'msgraph-polling',但插件目录名为 'msgraph-poll-discourse-plugin'
[Plugin::JsManager] 正在编译 49 个插件...
正在编译 automation...
文件排序结束
文件排序结束
        hex_digest 103dc9ebebb80a7065cb8dd41fb3356b30f151f7
########### 递归

# 在此处无限挂起,占用全部内存

我怀疑这可能与 ulimit 值有关(我们将其设置为 unlimited 而不是类似 ulimit -n 1048576; 的值),因为您打开了大量文件并将其内容存储在内存中(通过递归调用)。

请告诉我这是否让您想起了什么,或者您是否有关于问题原因的其他线索。

祝好,

Ismael

js_manager.rb.txt|附件 (7.7 KB)

建议始终启用交换空间(swap)。启用内核的内存超用(overcommit)功能也是一个非常好的主意,它可以显著降低峰值内存需求。

请先解决这两个问题,然后再试一次。关于内存超用,请参阅:

在诊断方面,之后检查 dmesg 中是否有 OOM(内存不足)事件会有所帮助,同时在系统卡顿时运行 vmstat 也很有用:

vmstat 5 5

以下是我的一般诊断建议:

@Ed_S

感谢您的留言。

我进行了诊断,结果如下:

vmstat 5 200
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 3  0      0 19595924    104 3919416    0    0  4173    32  439 1040  5  1 93  0  0
 1  0      0 19595924    104 3919416    0    0     0   154 4249 6449  1  1 98  0  0
 1  0      0 19595924    104 3919416    0    0     0    39 4399 6778  1  1 98  0  0
 1  0      0 19595924    104 3919416    0    0    12    75 5414 8640  1  1 98  0  0
 1  0      0 19595924    104 3919416    0    0    51    69 4248 6637  1  1 99  0  0
 1  0      0 19595924    104 3919416    0    0     0    83 4441 6784  1  1 98  0  0
 1  0      0 19595924    104 3919416    0    0     9    53 6111 9254  2  1 97  0  0
 1  0      0 19595924    104 3919416    0    0     0   887 4854 7373  1  1 98  0  0
 1  0      0 19595924    104 3919416    0    0     0    40 4705 7319  1  1 98  0  0
 1  0      0 19595924    104 3919416    0    0     0    37 4701 7305  1  1 98  0  0
# 我们开始预编译...
 3  0      0 19595924    104 3919416    0    0   124   902 8292 10254 19  5 75  0  0
 2  0      0 19595924    104 3919416    0    0 43073  6829 13702 16200 11  4 82  4  0
 2  0      0 19595924    104 3919416    0    0 19624   815 12340 15581 10  4 83  3  0
 2  0      0 19595924    104 3919416    0    0  1818  3953 7554 9248 13  3 84  0  0
 2  0      0 19595924    104 3919416    0    0     0    99 7475 8661 16  2 82  0  0
 2  0      0 19595924    104 3919416    0    0     0    52 7634 9084 13  2 84  0  0
 2  0      0 19595924    104 3919416    0    0   115   585 6843 8121 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     0 13139 7254 8444 13  2 84  0  0
 2  0      0 19595924    104 3919416    0    0     3  1305 8740 11091 14  2 83  0  0
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 5  0      0 19595924    104 3919416    0    0   465  9798 8403 9279 13  2 85  0  0
 3  0      0 19595924    104 3919416    0    0     6    99 7264 8993 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     0    96 7190 8627 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     0    66 6869 8299 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     0   109 7075 8521 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     3    78 8763 11295 14  2 83  0  0
 2  0      0 19595924    104 3919416    0    0     0  3075 7337 8358 13  2 85  0  0
 4  0      0 19595924    104 3919416    0    0     6   133 7016 8697 13  2 85  0  0
 3  0      0 19595924    104 3919416    0    0     0    45 7005 8370 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     0   134 7330 9011 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0    26    86 7239 8747 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0     0   127 8809 11618 15  3 83  0  0
 2  0      0 19595924    104 3919416    0    0     6  1473 7142 8352 13  2 85  0  0
 2  0      0 19595924    104 3919416    0    0  2021   136 8041 10138 13  3 84  0  0
 2  0      0 19595924    104 3919416    0    0  4457   664 6913 7927 12  3 84  0  0

内存消耗急剧上升:

               total        used        free      shared  buff/cache   available
Mem:            28Gi        26Gi       460Mi       518Mi       2.3Gi       1.8Gi
Swap:             0B          0B          0B

并且在编译插件时卡住:

/var/www/discourse$ bundle exec rake assets:precompile:build

gem install prometheus_exporter -v 2.2.0 -i /var/www/discourse/plugins/discourse-prometheus/gems/3.4.7 --no-document --ignore-dependencies --no-user-install
Successfully installed prometheus_exporter-2.2.0
1 gem installed

A new release of RubyGems is available: 3.6.9 → 4.0.11!
Run `gem update --system 4.0.11` to update your installation.

Plugin name is 'msgraph-polling', but plugin directory is named 'msgraph-poll-discourse-plugin'
[assemble_ember_build] No existing build info file found.
Fetching and extracting https://get.discourse.org/discourse-assets/2026.5.0-latest-6b98fe35/production.tar.gz...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--  0     0    0     0    0     0      0      0 --:--:-- --:--  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 20.1M  100 20.1M    0     0  20.8M      0 --:--:-- --:--:-- --:--:-- 20.8M
Prebuilt assets downloaded and extracted successfully.
[assemble_ember_build] Reusing existing core ember build. All done.
Plugin name is 'msgraph-polling', but plugin directory is named 'msgraph-poll-discourse-plugin'
[Plugin::JsManager] Compiling 49 plugins...

# 在此处挂起,直到出现 OOMKilled

您能否修改 ulimit 并查看是否会出现上述行为?

祝好,

Ismael

啊,你遇到了 OOM,很好。这已经可以确定了。ulimit 与此无关。

添加交换空间(swap)。除了磁盘空间不足外,没有理由不这样做。添加 8G 或 16G,然后重试。你的目标是让系统进入正常工作状态。之后,如果你愿意,可以尝试测量是哪个进程导致了内存耗尽。

配置内存超用(overcommit)。这是良好的实践,能减少峰值内存问题。你不需要理解它或为其辩护,只需照做即可。这是良好 Linux 配置的一部分。先检查一下,非常简单:

# uname -a
Linux ubuntu-4gb-hel1-1 6.8.0-110-generic #110-Ubuntu SMP PREEMPT_DYNAMIC
 Thu Mar 19 17:16:23 UTC 2026 aarch64 aarch64 aarch64 GNU/Linux
# cat /proc/sys/vm/overcommit_memory
1

大家好,

非常感谢大家提供的各种建议,我们非常感激。我们认为已经找到了近期内存问题的根本原因。

此前,在构建时以 root 身份运行 bundle exec rake assets:precompile:build 并不需要 Redis 或数据库连接。但这一行为已发生变化(参考:Introducing pre-compiled JS assets for self-hostershttps://meta.discourse.org/t/introducing-a-new-build-system-for-plugins/398713)。

为适应这一变化,我们将 bundle exec rake assets:precompile:build 步骤移至运行时的初始化容器(在执行 db:migrate 等操作之前)。这样可以让该步骤以 discourse 用户身份运行,并具备访问 Redis 和数据库所需的服务权限。

然而,在执行过程中,进程陷入了 lib/plugin/js_manager.rb 中的一个循环。查看 ps -fe 输出,我们发现 pnpm 反复尝试自我安装,导致内存耗尽:

...
discour+     704     688  5 11:00 pts/0    00:00:00 node /usr/bin/pnpm -C=frontend/asset-processor node build.js
discour+     718     704  5 11:00 pts/0    00:00:00 node /usr/bin/pnpm add pnpm@10.28.0 --loglevel=error --allow-build=@pnpm
discour+     729     718  6 11:00 pts/0    00:00:00 node /usr/bin/pnpm add pnpm@10.28.0 --loglevel=error --allow-build=@pnpm
discour+     740     729  6 11:00 pts/0    00:00:00 node /usr/bin/pnpm add pnpm@10.28.0 --loglevel=error --allow-build=@pnpm
discour+     754     740  7 11:00 pts/0    00:00:00 node /usr/bin/pnpm add pnpm@10.28.0 --loglevel=error --allow-build=@pnpm
...
# 列表不断增长,导致内存耗尽

在我们的测试中发现,如果将初始化容器以 root 身份运行,并依次执行 npm uninstall -g pnpmnpm install -g pnpm@10.28.0,即可解决该循环问题,使插件编译成功完成:

...
[Plugin::JsManager] 正在编译 49 个插件...
[Plugin::JsManager] 插件初始编译完成,耗时 5.82 秒

因此,在我们过度设计基础设施或可能改变架构之前,我想向 @david 请教:是否有计划恢复 assets:precompile:build 的先前行为,使其无需 Redis 或数据库连接即可运行(类似于您在 DISCOURSE_DOWNLOAD_PRE_BUILT_ASSETS: 0 流程中所做的处理)?

另外,出于好奇:为什么以非 root 用户身份运行 node 进程会触发这种递归的 pnpm 安装循环,而以 root 身份运行似乎能避免该问题?

祝好,
Ismael