merge: bound Docker runtime build resources
This commit is contained in:
@@ -10,6 +10,9 @@ RUNTIME_MEMORY_LIMIT=4g
|
|||||||
RUNTIME_MEMORY_SWAP_LIMIT=4g
|
RUNTIME_MEMORY_SWAP_LIMIT=4g
|
||||||
RUNTIME_CPU_LIMIT=4
|
RUNTIME_CPU_LIMIT=4
|
||||||
RUNTIME_PIDS_LIMIT=256
|
RUNTIME_PIDS_LIMIT=256
|
||||||
|
# Bound Node/Rolldown build parallelism inside the runtime container.
|
||||||
|
RUNTIME_NODE_OPTIONS=--max-old-space-size=1536
|
||||||
|
RUNTIME_RAYON_NUM_THREADS=2
|
||||||
# Optional smoke-only limits used with compose.smoke.yaml (maximum supported defaults shown).
|
# Optional smoke-only limits used with compose.smoke.yaml (maximum supported defaults shown).
|
||||||
# SMOKE_RUNTIME_MEMORY_LIMIT=4g
|
# SMOKE_RUNTIME_MEMORY_LIMIT=4g
|
||||||
# SMOKE_RUNTIME_MEMORY_SWAP_LIMIT=4g
|
# SMOKE_RUNTIME_MEMORY_SWAP_LIMIT=4g
|
||||||
|
|||||||
@@ -28,6 +28,12 @@ runtime은 기본적으로 memory/swap 각각 4 GiB, CPU 4개, PID 256개 상한
|
|||||||
무제한으로 두지 않습니다. PM2 process/restart 수가 예상보다 증가하면 로그 수집보다
|
무제한으로 두지 않습니다. PM2 process/restart 수가 예상보다 증가하면 로그 수집보다
|
||||||
runtime 중지가 우선입니다.
|
runtime 중지가 우선입니다.
|
||||||
|
|
||||||
|
초기 빌드와 profile worktree 빌드는 기본 Node heap 1536 MiB,
|
||||||
|
`RAYON_NUM_THREADS=2` 안에서 실행됩니다. `RUNTIME_NODE_OPTIONS`의 heap 상한은
|
||||||
|
512~2048 MiB, `RUNTIME_RAYON_NUM_THREADS`는 1~4만 허용됩니다. 이는 빌드 중
|
||||||
|
container hard limit에 먼저 닿는 것을 막고 실행 process에도 같은 상한을
|
||||||
|
적용합니다.
|
||||||
|
|
||||||
`scripts/check.sh`는 example placeholder, 짧은 비밀값, 잘못된 domain/email,
|
`scripts/check.sh`는 example placeholder, 짧은 비밀값, 잘못된 domain/email,
|
||||||
repository credential 조합을 실제 값 출력 없이 거부합니다. 최소 길이는 DB·Redis
|
repository credential 조합을 실제 값 출력 없이 거부합니다. 최소 길이는 DB·Redis
|
||||||
password 24자, game/bootstrap token 32자, 최초 관리자 password 16자입니다.
|
password 24자, game/bootstrap token 32자, 최초 관리자 password 16자입니다.
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ services:
|
|||||||
CORE_REPOSITORY_TOKEN: ${CORE_REPOSITORY_TOKEN:-}
|
CORE_REPOSITORY_TOKEN: ${CORE_REPOSITORY_TOKEN:-}
|
||||||
CORE_SSH_PRIVATE_KEY_BASE64: ${CORE_SSH_PRIVATE_KEY_BASE64:-}
|
CORE_SSH_PRIVATE_KEY_BASE64: ${CORE_SSH_PRIVATE_KEY_BASE64:-}
|
||||||
CORE_SSH_KNOWN_HOSTS_BASE64: ${CORE_SSH_KNOWN_HOSTS_BASE64:-}
|
CORE_SSH_KNOWN_HOSTS_BASE64: ${CORE_SSH_KNOWN_HOSTS_BASE64:-}
|
||||||
|
NODE_OPTIONS: ${RUNTIME_NODE_OPTIONS:---max-old-space-size=1536}
|
||||||
|
RAYON_NUM_THREADS: ${RUNTIME_RAYON_NUM_THREADS:-2}
|
||||||
POSTGRES_HOST: postgres
|
POSTGRES_HOST: postgres
|
||||||
POSTGRES_PORT: '5432'
|
POSTGRES_PORT: '5432'
|
||||||
POSTGRES_DB: ${POSTGRES_DB:-sammo}
|
POSTGRES_DB: ${POSTGRES_DB:-sammo}
|
||||||
|
|||||||
@@ -19,6 +19,17 @@ export const validateComposeModel = (model, mode) => {
|
|||||||
if (!positive(runtime.cpus)) errors.push('runtime CPU limit must be positive');
|
if (!positive(runtime.cpus)) errors.push('runtime CPU limit must be positive');
|
||||||
if (!positive(runtime.pids_limit)) errors.push('runtime PID limit must be positive');
|
if (!positive(runtime.pids_limit)) errors.push('runtime PID limit must be positive');
|
||||||
|
|
||||||
|
const nodeOptions = runtime.environment?.NODE_OPTIONS ?? '';
|
||||||
|
const heapMatch = /(?:^|\s)--max-old-space-size=(\d+)(?:\s|$)/.exec(nodeOptions);
|
||||||
|
const heapMiB = Number(heapMatch?.[1] ?? 0);
|
||||||
|
if (!heapMatch || heapMiB < 512 || heapMiB > 2048) {
|
||||||
|
errors.push('runtime Node heap limit must be between 512 and 2048 MiB');
|
||||||
|
}
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
|
||||||
if (mode === 'development') {
|
if (mode === 'development') {
|
||||||
if (runtime.environment?.RUNTIME_MODE !== 'development') {
|
if (runtime.environment?.RUNTIME_MODE !== 'development') {
|
||||||
errors.push('development runtime mode must be literal development');
|
errors.push('development runtime mode must be literal development');
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ import assert from 'node:assert/strict';
|
|||||||
import { validateComposeModel } from '../runtime/validate-compose-model.mjs';
|
import { validateComposeModel } from '../runtime/validate-compose-model.mjs';
|
||||||
|
|
||||||
const safeRuntime = {
|
const safeRuntime = {
|
||||||
environment: { CORE_SOURCE_MODE: 'clone' },
|
environment: {
|
||||||
|
CORE_SOURCE_MODE: 'clone',
|
||||||
|
NODE_OPTIONS: '--max-old-space-size=1536',
|
||||||
|
RAYON_NUM_THREADS: '2',
|
||||||
|
},
|
||||||
restart: 'unless-stopped',
|
restart: 'unless-stopped',
|
||||||
mem_limit: String(4 * 1024 * 1024 * 1024),
|
mem_limit: String(4 * 1024 * 1024 * 1024),
|
||||||
memswap_limit: String(4 * 1024 * 1024 * 1024),
|
memswap_limit: String(4 * 1024 * 1024 * 1024),
|
||||||
@@ -21,7 +25,12 @@ test('accepts a literal, non-restarting development model', () => {
|
|||||||
services.runtime = {
|
services.runtime = {
|
||||||
...safeRuntime,
|
...safeRuntime,
|
||||||
restart: 'no',
|
restart: 'no',
|
||||||
environment: { CORE_SOURCE_MODE: 'bind', RUNTIME_MODE: 'development' },
|
environment: {
|
||||||
|
CORE_SOURCE_MODE: 'bind',
|
||||||
|
RUNTIME_MODE: 'development',
|
||||||
|
NODE_OPTIONS: '--max-old-space-size=1536',
|
||||||
|
RAYON_NUM_THREADS: '2',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
assert.deepEqual(validateComposeModel({ services }, 'development'), []);
|
assert.deepEqual(validateComposeModel({ services }, 'development'), []);
|
||||||
});
|
});
|
||||||
@@ -47,3 +56,17 @@ test('rejects an unbounded or production-mode development runtime', () => {
|
|||||||
assert.ok(errors.some((error) => error.includes('literal development')));
|
assert.ok(errors.some((error) => error.includes('literal development')));
|
||||||
assert.ok(errors.some((error) => error.includes('restart policy must be no')));
|
assert.ok(errors.some((error) => error.includes('restart policy must be no')));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('rejects missing or excessive build memory and parallelism limits', () => {
|
||||||
|
const runtime = {
|
||||||
|
...safeRuntime,
|
||||||
|
environment: {
|
||||||
|
CORE_SOURCE_MODE: 'clone',
|
||||||
|
NODE_OPTIONS: '--max-old-space-size=4096',
|
||||||
|
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('Rayon thread count')));
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user