From 1d25540b1a34aa080b06d59178a247e5a9320097 Mon Sep 17 00:00:00 2001 From: hided62 Date: Sun, 16 Aug 2026 06:48:42 +0000 Subject: [PATCH] =?UTF-8?q?fix(runtime):=20=ED=84=B4=20=EB=8D=B0=EB=AA=AC?= =?UTF-8?q?=20=EC=A0=84=EC=9A=A9=20heap=20=EC=83=81=ED=95=9C=EC=9D=84=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 공용 빌드·API heap과 턴 데몬 heap을 분리하고 Compose 안전 검증과 운영 문서를 함께 갱신한다. --- .env.example | 2 ++ README.md | 9 ++++++--- compose.yaml | 1 + runtime/validate-compose-model.mjs | 9 +++++++++ test/validate-compose-model.test.mjs | 3 +++ 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index a73538d..26011b5 100644 --- a/.env.example +++ b/.env.example @@ -22,6 +22,8 @@ RUNTIME_CPU_LIMIT=4 RUNTIME_PIDS_LIMIT=256 # Bound Node/Rolldown build parallelism inside the runtime container. RUNTIME_NODE_OPTIONS=--max-old-space-size=1536 +# Optional turn-daemon-only heap. Raise the runtime memory hard limit first. +TURN_DAEMON_NODE_OPTIONS=--max-old-space-size=1536 RUNTIME_RAYON_NUM_THREADS=1 # Optional smoke-only limits used with compose.smoke.yaml (maximum supported defaults shown). # SMOKE_RUNTIME_MEMORY_LIMIT=4g diff --git a/README.md b/README.md index 67dd881..1116b87 100644 --- a/README.md +++ b/README.md @@ -63,9 +63,12 @@ runtime 중지가 우선입니다. 초기 빌드와 profile worktree 빌드는 기본 Node heap 1536 MiB, `RAYON_NUM_THREADS=1` 안에서 실행됩니다. `RUNTIME_NODE_OPTIONS`의 heap 상한은 -512~2048 MiB, `RUNTIME_RAYON_NUM_THREADS`는 1~4만 허용됩니다. 이는 빌드 중 -container hard limit에 먼저 닿는 것을 막고 실행 process에도 같은 상한을 -적용합니다. +512~2048 MiB, `RUNTIME_RAYON_NUM_THREADS`는 1~4만 허용됩니다. 턴 데몬만 더 큰 +heap이 필요하면 `TURN_DAEMON_NODE_OPTIONS`를 512~4096 MiB 범위에서 지정합니다. +이 값은 Gateway orchestrator가 생성하는 turn-daemon PM2 process에만 +`NODE_OPTIONS`로 적용되며 API·frontend·worker와 build는 계속 +`RUNTIME_NODE_OPTIONS`를 사용합니다. 큰 값을 쓰기 전에 `RUNTIME_MEMORY_LIMIT`과 +동일한 swap 상한을 함께 늘리고 실제 container 총사용량을 확인합니다. `scripts/check.sh`는 example placeholder, 짧은 비밀값, 잘못된 domain/email, repository credential 조합을 실제 값 출력 없이 거부합니다. 최소 길이는 DB·Redis diff --git a/compose.yaml b/compose.yaml index 1fd7133..cc80e04 100644 --- a/compose.yaml +++ b/compose.yaml @@ -50,6 +50,7 @@ services: CORE_SSH_PRIVATE_KEY_BASE64: ${CORE_SSH_PRIVATE_KEY_BASE64:-} CORE_SSH_KNOWN_HOSTS_BASE64: ${CORE_SSH_KNOWN_HOSTS_BASE64:-} NODE_OPTIONS: ${RUNTIME_NODE_OPTIONS:---max-old-space-size=1536} + TURN_DAEMON_NODE_OPTIONS: ${TURN_DAEMON_NODE_OPTIONS:---max-old-space-size=1536} RAYON_NUM_THREADS: ${RUNTIME_RAYON_NUM_THREADS:-1} POSTGRES_HOST: postgres POSTGRES_PORT: '5432' diff --git a/runtime/validate-compose-model.mjs b/runtime/validate-compose-model.mjs index 51246ec..90b919b 100644 --- a/runtime/validate-compose-model.mjs +++ b/runtime/validate-compose-model.mjs @@ -56,6 +56,15 @@ export const validateComposeModel = (model, mode) => { if (!heapMatch || heapMiB < 512 || heapMiB > 2048) { errors.push('runtime Node heap limit must be between 512 and 2048 MiB'); } + const turnDaemonNodeOptions = runtime.environment?.TURN_DAEMON_NODE_OPTIONS ?? ''; + const turnDaemonHeapMatch = /(?:^|\s)--max-old-space-size=(\d+)(?:\s|$)/.exec(turnDaemonNodeOptions); + const turnDaemonHeapMiB = Number(turnDaemonHeapMatch?.[1] ?? 0); + if (!turnDaemonHeapMatch || turnDaemonHeapMiB < 512 || turnDaemonHeapMiB > 4096) { + errors.push('turn daemon Node heap limit must be between 512 and 4096 MiB'); + } + if (turnDaemonHeapMiB * 1024 * 1024 >= Number(runtime.mem_limit)) { + errors.push('turn daemon Node heap limit must stay below the runtime memory limit'); + } const rayonThreads = Number(runtime.environment?.RAYON_NUM_THREADS ?? 0); if (!Number.isInteger(rayonThreads) || rayonThreads < 1 || rayonThreads > 4) { errors.push('runtime Rayon thread count must be between 1 and 4'); diff --git a/test/validate-compose-model.test.mjs b/test/validate-compose-model.test.mjs index 7a4016e..353cbfe 100644 --- a/test/validate-compose-model.test.mjs +++ b/test/validate-compose-model.test.mjs @@ -7,6 +7,7 @@ const safeRuntime = { environment: { CORE_SOURCE_MODE: 'clone', NODE_OPTIONS: '--max-old-space-size=1536', + TURN_DAEMON_NODE_OPTIONS: '--max-old-space-size=3072', RAYON_NUM_THREADS: '2', GATEWAY_IMAGE_UPLOAD_URL: 'https://sam-image.hided.net', GATEWAY_IMAGE_UPLOAD_SECRET_FILE: '/run/secrets/image_upload_core2026_secret', @@ -87,10 +88,12 @@ test('rejects missing or excessive build memory and parallelism limits', () => { environment: { CORE_SOURCE_MODE: 'clone', NODE_OPTIONS: '--max-old-space-size=4096', + TURN_DAEMON_NODE_OPTIONS: '--max-old-space-size=8192', RAYON_NUM_THREADS: '8', }, }; const errors = validateComposeModel({ services: { runtime } }, 'production'); assert.ok(errors.some((error) => error.includes('Node heap limit'))); + assert.ok(errors.some((error) => error.includes('turn daemon Node heap limit'))); assert.ok(errors.some((error) => error.includes('Rayon thread count'))); });