에셋 사전 컴파일로 인한 과도한 메모리 사용

안녕하세요,

지난 몇 년간 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인가요? 그렇다면 스왑이 설정되어 있나요?

서버 사양을 공유해 주시겠습니까?

데이비드,

추가 플러그인 목록은 다음과 같습니다:

#   - Trade buttons (Marketplace에서 사용됨)
          - git clone --depth=1 https://github.com/jannolii/discourse-topic-trade-buttons.git
          #   - Saved Searches
          - git clone --depth=1 https://github.com/discourse/discourse-saved-searches.git
          #   - Discourse Askimet
          - git clone --depth=1 https://github.com/discourse/discourse-akismet.git
          #   - Prometheus
          - git clone --depth=1 https://github.com/discourse/discourse-prometheus.git
          #   - Discourse docs
          - git clone --depth=1 https://github.com/discourse/discourse-docs.git
          #   - MSGraph poll
          - git clone --depth=1 https://github.com/CERN/msgraph-poll-discourse-plugin.git

감사합니다,

이스마엘

헬리오서지님, 안녕하세요.

노드는 8개 CPU와 30GiB의 RAM을 갖추고 있습니다. 정상적인 조건에서 포럼은 1개 CPU와 최대 2~3GB의 RAM(프리컴파일 포함)을 사용했습니다.

스왑은 설정되어 있지 않습니다. 스왑은 메모리 제약이 있을 때 사용되며, 여기서는 그런 경우가 아니어야 한다고 이해하고 있습니다. 그러나 이전에는 절대 그런 적이 없었던 메모리 사용량이 늘어난 것이 더 "걱정"됩니다.

감사합니다,

ismael

음, 서버 사양에는 스왑이 필요하지 않을 거예요. 여기 팀원 데이비드가 가장 잘 도와줄 수 있을 거예요.

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"
Plugin name is 'msgraph-polling', but plugin directory is named 'msgraph-poll-discourse-plugin'

# 여기서 영원히 멈춥니다

AssetProcessor.ember_versiondiscourse/lib/plugin/js_manager.rb at latest · discourse/discourse · GitHub 에 있는 줄에 해당합니다.

그래서 저는 이 파일(첨부됨)을 수정했는데, 기본적으로 처리 중에 어디에서 막히는지 출력하도록 하고, discourse/lib/plugin/js_manager.rb at latest · discourse/discourse · GitHubAssetProcessor.ember_version을 제거하여 5를 넣어 hex 생성을 계속하도록 했습니다.

그리고 discourse/lib/plugin/js_manager.rb at latest · discourse/discourse · GitHub (parallel_count = [Etc.nprocessors, 1].min) 에서 병렬성을 1로 줄여 상황을 단순화했습니다.

이후 bundle exec rake assets:precompile:build를 실행하니 다음과 같은 결과가 나왔습니다:

/var/www/discourse$ bundle exec rake assets:precompile:build
Plugin name is 'msgraph-polling', but plugin directory is named 'msgraph-poll-discourse-plugin'
[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...
Compiling automation...
end of files.sort
end of files.sort
        hex_digest 103dc9ebebb80a7065cb8dd41fb3356b30f151f7
########### recursive

# 여기서 영원히 멈추며, 전체 메모리를 소모합니다

ulimit 값과 관련이 있을 수 있다고 의심합니다. (우리는 ulimit -n 1048576; 같은 값 대신 unlimited으로 설정해 두었습니다.) 많은 수의 파일을 열고 그 내용을 메모리에 저장하기 때문(재귀 호출을 통해)이라고 생각합니다.

이것이 떠오르는 것이 있거나, 문제의 원인에 대한 다른 단서가 있다면 알려주세요.

감사합니다,

Ismael

js_manager.rb.txt (7.7 KB)

스왑(swap)을 사용하는 것이 항상 권장됩니다. 커널의 과대할당(overcommit)을 활성화하는 것도 매우 좋은 아이디어입니다. 이를 통해 피크 메모리 요구량을 상당히 줄일 수 있습니다.

이 두 가지를 먼저 설정한 뒤 다시 시도해 보세요. 과대할당에 대한 자세한 내용은 다음 링크를 참고하세요.

진단 측면에서는, OOM(Out Of Memory) 이벤트를 확인하기 위해 dmesg를 점검하는 것이 도움이 될 수 있으며, 이는 사후에 수행할 수 있습니다. 또한 시스템이 멈추는 시점에 vmstat을 실행하는 것도 좋습니다.

vmstat 5 5

일반적인 진단 조언은 다음과 같습니다:

Hi @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
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과는 아무 관련이 없습니다.

스왑을 추가하세요. 디스크 공간 부족이 아닌 한 추가하지 않을 이유가 없습니다. 8GB나 16GB를 추가한 뒤 다시 시도해 보세요. 먼저 정상 작동하는 상태로 만드는 것이 목표입니다. 그 후에 원한다면 메모리를 많이 사용하는 프로세스를 측정해 볼 수 있습니다.

오버커밋(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

안녕하세요,

제공해 주신 모든 팁에 진심으로 감사드립니다. 정말 큰 도움이 되었습니다. 최근 발생한 메모리 문제의 근본 원인을 파악한 것으로 보입니다.

이전에는 빌드 시점에 루트 권한으로 bundle exec rake assets:precompile:build를 실행할 때 Redis나 데이터베이스 연결이 필요하지 않았습니다. 하지만 이 동작이 변경되었습니다(참조: Introducing pre-compiled JS assets for self-hostersIntroducing a new build system for plugins ).

이러한 변경에 대응하기 위해, bundle exec rake assets:precompile:build 단계를 런타임 시점의 init 컨테이너로 이동시켰습니다(이전에는 db:migrate 등을 실행하기 전에 수행). 이를 통해 Redis와 데이터베이스 모두에 필요한 서비스 접근 권한을 가진 discourse 사용자로 실행할 수 있게 되었습니다.

그러나 실행 중 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
...
# 그리고 목록은 계속 늘어나며, 이로 인해 메모리 포화가 유발됩니다

우리의 테스트 결과, init 컨테이너를 루트 권한으로 실행하고 npm uninstall -g pnpm을 실행한 뒤 npm install -g pnpm@10.28.0을 수행하면 루프가 해결되고 플러그인 컴파일이 성공적으로 완료되는 것을 확인했습니다:

...
[Plugin::JsManager] Compiling 49 plugins...
[Plugin::JsManager] Finished initial compilation of plugins in 5.82s

따라서 인프라를 과도하게 설계하거나 디자인을 변경하기 전에, @david 님께 질문드리고 싶습니다. assets:precompile:build가 Redis나 데이터베이스 연결 없이 실행될 수 있도록 이전 동작으로 복원할 계획이 있나요?(DISCOURSE_DOWNLOAD_PRE_BUILT_ASSETS: 0 플로우에서 하시는 것과 유사하게요)

부연 설명으로, 호기심에서 질문드립니다: 루트 권한이 아닌 사용자로 node 프로세스를 실행할 때 이 재귀적 pnpm 설치 루프가 발생하는 반면, 루트 권한으로 실행하면 이를 피할 수 있는 이유는 무엇인가요?

감사합니다,
Ismael