์ฝ˜ํ…์ธ ๋กœ ์ด๋™
OmniRoute source

๐Ÿณ Docker Guide โ€” OmniRoute (ํ•œ๊ตญ์–ด)

๋ช…๋ น์–ด ํ•˜๋‚˜๋กœ ์…€ํ”„ ํ˜ธ์ŠคํŒ…ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?
์…€ํ”„ ํ˜ธ์ŠคํŒ… ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š” โ€” docker compose -f docker-compose.selfhost.yml up -d(๊ฒŒ์‹œ๋œ ์ด๋ฏธ์ง€ + Redis, ๋ฃจํ”„๋ฐฑ ์ „์šฉ, ํ”„๋กœํ•„ ์„ ํƒ ์—†์Œ). ์•„๋ž˜์˜ ๋น ๋ฅธ ์‹คํ–‰์€ ์ด๋ฏธ ๋‹ค๋ฅธ ๊ณณ์—์„œ Redis๋ฅผ ์‹คํ–‰ ์ค‘์ธ ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•œ ๋‹จ์ผ ์ปจํ…Œ์ด๋„ˆ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

ํ„ฐ๋ฏธ๋„ ์ฐฝ
docker run -d \
--name omniroute \
--restart unless-stopped \
--stop-timeout 40 \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
ํ„ฐ๋ฏธ๋„ ์ฐฝ
# ๋จผ์ € .env๋ฅผ ๋ณต์‚ฌํ•˜๊ณ  ํŽธ์ง‘ํ•ฉ๋‹ˆ๋‹ค
cp .env.example .env
docker run -d \
--name omniroute \
--restart unless-stopped \
--stop-timeout 40 \
--env-file .env \
-p 20128:20128 \
-v omniroute-data:/app/data \
diegosouzapw/omniroute:latest
ํ„ฐ๋ฏธ๋„ ์ฐฝ
# ๊ธฐ๋ณธ ํ”„๋กœํ•„(CLI ๋„๊ตฌ ์—†์Œ)
docker compose --profile base up -d
# CLI ํ”„๋กœํ•„(Claude Code, Codex, OpenClaw ๋‚ด์žฅ)
docker compose --profile cli up -d
# ํ˜ธ์ŠคํŠธ ํ”„๋กœํ•„(Linux ์šฐ์„ , ํ˜ธ์ŠคํŠธ CLI ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ๋งˆ์šดํŠธ)
docker compose --profile host up -d
# ์›น ํ”„๋กœํ•„(์›น ์„ธ์…˜ ์ œ๊ณต์ž๋ฅผ ์œ„ํ•œ Chromium/Playwright)
docker compose --profile web up -d
# CLI + CLIProxyAPI ์‚ฌ์ด๋“œ์นด ์กฐํ•ฉ
docker compose --profile cli --profile cliproxyapi up -d

OmniRoute๋Š” ์ฃผ์š” ๋ฐฐํฌ ๊ตฌ์„ฑ์— ๋งž๋Š” Compose ํ”„๋กœํ•„์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํ™˜๊ฒฝ์— ์ ํ•ฉํ•œ ํ”„๋กœํ•„์„ ์„ ํƒํ•˜์„ธ์š”.

ํ”„๋กœํ•„ ์„œ๋น„์Šค ์‚ฌ์šฉ ์‹œ๊ธฐ ๋ช…๋ น์–ด
base (๊ธฐ๋ณธ๊ฐ’) omniroute-base ํ—ค๋“œ๋ฆฌ์Šค ์„œ๋ฒ„ / ์ตœ์†Œ ๋Ÿฐํƒ€์ž„์ด ํ•„์š”ํ•˜๋ฉฐ provider CLI๊ฐ€ ๋ฒˆ๋“ค๋กœ ํฌํ•จ๋˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒฝ์šฐ docker compose --profile base up -d
cli omniroute-cli omniroute providers/setup/doctor์™€ ๋ฒˆ๋“ค CLI(Codex, Claude Code, Droid, OpenClaw)๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ docker compose --profile cli up -d
host omniroute-host ~/.local/bin, ~/.codex, ~/.claude ๋“ฑ์„ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ๋งˆ์šดํŠธํ•˜์—ฌ ํ˜ธ์ŠคํŠธ CLI์— network_mode์™€ ์œ ์‚ฌํ•œ ๋ฐฉ์‹์œผ๋กœ ์ ‘๊ทผํ•˜๋ ค๋Š” Linux ํ˜ธ์ŠคํŠธ docker compose --profile host up -d
cliproxyapi cliproxyapi ์—…์ŠคํŠธ๋ฆผ CLI ํ”„๋ก์‹œ๋ฅผ ์œ„ํ•ด ํฌํŠธ 8317์—์„œ CLIProxyAPI ์‚ฌ์ด๋“œ์นด ์‹คํ–‰ docker compose --profile cliproxyapi up -d
web omniroute-web ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ•„์š”ํ•œ ์›น ์„ธ์…˜ provider: gemini-web, claude-web, claude-turnstile (runner-web ๋นŒ๋“œ, Chromium ํฌํ•จ) docker compose --profile web up -d

์—ฌ๋Ÿฌ ํ”„๋กœํ•„์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: docker compose --profile cli --profile cliproxyapi up -d.

OmniRoute๊ฐ€ Docker์—์„œ ์‹คํ–‰๋  ๋•Œ ํ˜ธ์ŠคํŠธ CLI ๋„๊ตฌ ๊ตฌ์„ฑํ•˜๊ธฐ

์„น์…˜ ์ œ๋ชฉ: โ€œOmniRoute๊ฐ€ Docker์—์„œ ์‹คํ–‰๋  ๋•Œ ํ˜ธ์ŠคํŠธ CLI ๋„๊ตฌ ๊ตฌ์„ฑํ•˜๊ธฐโ€

omniroute setup-codex, setup-claude, config set <tool> ๋ฐ ๋Œ€์‹œ๋ณด๋“œ์˜ ๊ตฌ์„ฑ ์ €์žฅ ๋ฒ„ํŠผ์€ ๋ชจ๋‘ ~/.codex/*.config.toml๊ณผ ๊ฐ™์€ ํŒŒ์ผ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ๋กœ๋Š” CLI๊ฐ€ ์‹ค์ œ๋กœ ์‹คํ–‰๋˜๋Š” ๋จธ์‹ ์—์„œ๋งŒ ์˜๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ ์‹คํ–‰ํ•˜๋ฉด ์ปจํ…Œ์ด๋„ˆ ์ž์ฒด์˜ ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ(/home/node โ€” ์ด๋ฏธ์ง€๋Š” USER node๋กœ ์‹คํ–‰๋จ)์— ํŒŒ์ผ์ด ์ž‘์„ฑ๋˜๋ฏ€๋กœ, ํ˜ธ์ŠคํŠธ CLI๊ฐ€ ์ด๋ฅผ ์ฝ์„ ์ˆ˜ ์—†์œผ๋ฉฐ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์žฌ์ƒ์„ฑ๋˜๋Š” ์ฆ‰์‹œ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค.

OmniRoute๋Š” ์ด๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ์„ฑ๊ณต ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ณ ํ•˜๋Š” ๋Œ€์‹  ์•ˆ๋‚ด์™€ ํ•จ๊ป˜ ์“ฐ๊ธฐ๋ฅผ ๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค. CLI๋Š” 2๋กœ ์ข…๋ฃŒ๋˜๊ณ  API๋Š” containerEphemeralTarget: true์™€ ํ•จ๊ป˜ 422๋กœ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค.

๊ถŒ์žฅ: CLI๋Š” ํ˜ธ์ŠคํŠธ์—์„œ, OmniRoute๋Š” Docker์—์„œ ์‹คํ–‰

์„น์…˜ ์ œ๋ชฉ: โ€œ๊ถŒ์žฅ: CLI๋Š” ํ˜ธ์ŠคํŠธ์—์„œ, OmniRoute๋Š” Docker์—์„œ ์‹คํ–‰โ€

์ปจํ…Œ์ด๋„ˆ๋Š” API๋ฅผ ์ œ๊ณตํ•˜๊ณ , CLI๋Š” ํ˜ธ์ŠคํŠธ ๋„๊ตฌ๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.

ํ„ฐ๋ฏธ๋„ ์ฐฝ
docker compose --profile base up -d
npm install -g omniroute
omniroute connect http://localhost:20128 # CLI๊ฐ€ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ์„ค์ •
omniroute setup-codex # ํ˜ธ์ŠคํŠธ์˜ ์‹ค์ œ ~/.codex์— ์ž‘์„ฑ

์ด๋Š” Codex, Claude Code, Cursor ๋˜๋Š” ์œ ์‚ฌํ•œ ๋„๊ตฌ๋ฅผ ๋…ธํŠธ๋ถ์—์„œ ์‹คํ–‰ํ•  ๋•Œ ์ ํ•ฉํ•œ ์„ ํƒ์ด๋ฉฐ, ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ์„ค์ •์ž…๋‹ˆ๋‹ค.

๋Œ€์•ˆ: ํ˜ธ์ŠคํŠธ ๊ตฌ์„ฑ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋ฐ”์ธ๋“œ ๋งˆ์šดํŠธ(host ํ”„๋กœํ•„)

์„น์…˜ ์ œ๋ชฉ: โ€œ๋Œ€์•ˆ: ํ˜ธ์ŠคํŠธ ๊ตฌ์„ฑ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋ฐ”์ธ๋“œ ๋งˆ์šดํŠธ(host ํ”„๋กœํ•„)โ€

์ปจํ…Œ์ด๋„ˆ ์ž์ฒด์—์„œ ํ˜ธ์ŠคํŠธ ๊ตฌ์„ฑ์„ ์ž‘์„ฑํ•˜๋„๋ก ํ•˜๋ ค๋ฉด ํ•ด๋‹น ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋งˆ์šดํŠธํ•˜๊ณ  CLI_CONFIG_HOME์ด ๋งˆ์šดํŠธ ๋ฃจํŠธ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. host ํ”„๋กœํ•„์—๋Š” ์ด๋ฏธ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

environment:
- CLI_CONFIG_HOME=/host-home
- CLI_ALLOW_CONFIG_WRITES=true
volumes:
- ~/.codex:/host-home/.codex:rw
- ~/.claude:/host-home/.claude:rw

๋ฐ”์ธ๋“œ ๋งˆ์šดํŠธ๋Š” ๊ฒฝ๋กœ๋ฅผ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด ์ค๋‹ˆ๋‹ค. OmniRoute๋Š” /proc/self/mountinfo๋ฅผ ์ฝ๊ณ  ๋งˆ์šดํŠธ๋œ ๊ฒฝ๋กœ๋ฟ ์•„๋‹ˆ๋ผ ํ•˜์œ„ ํ•ญ๋ชฉ์ด ๋งˆ์šดํŠธ๋œ ๋””๋ ‰ํ„ฐ๋ฆฌ(์œ„์˜ /host-home ๊ตฌ์กฐ๊ฐ€ ์ •ํ™•ํžˆ ์ด์— ํ•ด๋‹นํ•จ)์— ๋Œ€ํ•œ ์“ฐ๊ธฐ๋„ ํ—ˆ์šฉํ•˜๋Š” ํ•œํŽธ, ๋งˆ์šดํŠธ๋˜์ง€ ์•Š์€ ๊ฒฝ๋กœ์— ๋Œ€ํ•œ ์“ฐ๊ธฐ๋Š” ๊ณ„์† ๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์™ธ ์ฒ˜๋ฆฌ: ์ปจํ…Œ์ด๋„ˆ ์ž์ฒด์˜ CLI ๊ตฌ์„ฑ(์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉ)

์„น์…˜ ์ œ๋ชฉ: โ€œ์˜ˆ์™ธ ์ฒ˜๋ฆฌ: ์ปจํ…Œ์ด๋„ˆ ์ž์ฒด์˜ CLI ๊ตฌ์„ฑ(์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉ)โ€

CLI๊ฐ€ ์‹ค์ œ๋กœ ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์— ์žˆ๋Š” ๊ฒฝ์šฐ(cli ํ”„๋กœํ•„)์—๋Š” ์“ฐ๊ธฐ๊ฐ€ ์˜๋„๋œ ๋™์ž‘์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  setup-* ๋ช…๋ น์— --allow-container-write๋ฅผ ์ „๋‹ฌํ•˜๊ฑฐ๋‚˜ ์„œ๋ฒ„์— OMNIROUTE_ALLOW_CONTAINER_CONFIG_WRITE=true๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ํ•ด๋‹น ์“ฐ๊ธฐ ๋‚ด์šฉ์ด ์ปจํ…Œ์ด๋„ˆ ์ข…๋ฃŒ ํ›„ ์œ ์ง€๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒฝ๊ณ ์™€ ํ•จ๊ป˜ ์“ฐ๊ธฐ๊ฐ€ ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค.

๋ณด์•ˆ ๊ฒฝ๊ณ  โ€” cli ํ”„๋กœํ•„ + docker.sock ๋งˆ์šดํŠธ. cli ํ”„๋กœํ•„์€ ์ปจํ…Œ์ด๋„ˆ ๋‚ด ์ž๋™ ์—…๋ฐ์ดํ„ฐ๊ฐ€ ํ˜ธ์ŠคํŠธ ๋ฐ๋ชฌ์„ ํ†ตํ•ด ์Šคํƒ์„ ์žฌ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก /var/run/docker.sock์„ ๋ฐ”์ธ๋“œ ๋งˆ์šดํŠธํ•ฉ๋‹ˆ๋‹ค. (src/lib/system/autoUpdate.ts๋Š” ํ•ด๋‹น ์†Œ์ผ“์„ ํƒ์ง€ํ•˜๊ณ , ์†Œ์ผ“์ด ์—†์œผ๋ฉด Docker ๊ฒฝ๋กœ๋ฅผ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค.) ์ด ์†Œ์ผ“์€ ํ˜ธ์ŠคํŠธ root ๊ถŒํ•œ์— ๋Œ€ํ•œ ์‹ ๋ขฐ ๊ฒฝ๊ณ„์ž…๋‹ˆ๋‹ค. ์ด ์†Œ์ผ“์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๊ฒƒ์€ ํ˜ธ์ŠคํŠธ Docker ๋ฐ๋ชฌ์„ root ๊ถŒํ•œ์œผ๋กœ ์ œ์–ดํ•˜๋ฏ€๋กœ ํ˜ธ์ŠคํŠธ์˜ ๋ชจ๋“  ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ƒ์„ฑ, ๊ฒ€์‚ฌ, ์ค‘์ง€ ๋ฐ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•œ ์ฃผ์˜ ์‚ฌํ•ญ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  1. cli ํ”„๋กœํ•„์˜ ํฌํŠธ๋ฅผ ๋„คํŠธ์›Œํฌ์— ์ ˆ๋Œ€๋กœ ๋…ธ์ถœํ•˜์ง€ ๋งˆ์„ธ์š”. 127.0.0.1์— ๊ฒŒ์‹œํ•˜์„ธ์š”(ports: "127.0.0.1:${DASHBOARD_PORT:-20128}:..."). LAN์—์„œ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ cli ํ”„๋กœํ•„์€ ๋Œ€์‹œ๋ณด๋“œ ์ˆ˜์ค€์˜ ๋ชจ๋“  RCE๋ฅผ ํ˜ธ์ŠคํŠธ ์ „์ฒด ์นจํ•ด๋กœ ํ™•๋Œ€ํ•ฉ๋‹ˆ๋‹ค.
  2. ์ถ”๊ฐ€ ํ˜ธ์ŠคํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ cli ํ”„๋กœํ•„์— ๋ฐ”์ธ๋“œํ•˜์ง€ ๋งˆ์„ธ์š”. Docker ์†Œ์ผ“๊ณผ ์ถ”๊ฐ€ ๋งˆ์šดํŠธ๊ฐ€ ํ•จ๊ป˜ ์žˆ์œผ๋ฉด ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ํŒŒ์ผ ์‹œ์Šคํ…œ๊ณผ ํ˜ธ์ŠคํŠธ ๊ตฌ์„ฑ์— ๋Œ€ํ•œ ์™„์ „ํ•œ ์ฝ๊ธฐ/์“ฐ๊ธฐ ๊ถŒํ•œ์„ ๊ฐ–๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋„๊ตฌ๊ฐ€ ํ”„๋กœ์ ํŠธ๋ฅผ ํ™•์ธํ•ด์•ผ ํ•œ๋‹ค๋ฉด CLI ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ•˜๊ณ , ํ”„๋กœ์ ํŠธ๋ฅผ cli ์ปจํ…Œ์ด๋„ˆ์— ๋งˆ์šดํŠธํ•˜์ง€ ๋งˆ์„ธ์š”.

์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€ ์ž๋™ ์—…๋ฐ์ดํŠธ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๋ฉด cli ํ”„๋กœํ•„์„ ๋น„ํ™œ์„ฑํ™”ํ•˜์„ธ์š” (COMPOSE_PROFILES=core,redis ๋˜๋Š” ๋” ์งง๊ฒŒ ์„ค์ •). ๋‹ค๋ฅธ ํ”„๋กœํ•„์€ Docker ์†Œ์ผ“์„ ๋งˆ์šดํŠธํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

MITM ๊ด€๋ จ ์œ„ํ˜‘ ๋ชจ๋ธ์€ docs/security/MITM-TPROXY-DECRYPT.md(git์— ์žˆ์œผ๋ฉฐ /docs์—๋Š” ์ปดํŒŒ์ผ๋˜์ง€ ์•Š์Œ)๋ฅผ, codex/claude-code/droid/openclaw ๋ฐ”์ด๋„ˆ๋ฆฌ ์ถœ์ฒ˜ ์ฒด์ธ์€ docs/security/SUPPLY_CHAIN.md๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

OmniRoute๋Š” ๋ถ„์‚ฐ ์†๋„ ์ œํ•œ๊ธฐ์™€ ๊ณต์œ  ์บ์‹œ๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด Redis๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. redis ์„œ๋น„์Šค๋Š” docker-compose.yml์— ํ•ญ์ƒ ์ •์˜๋˜์–ด ์žˆ์œผ๋ฉฐ(ํ”„๋กœํ•„ ์ œํ•œ ์—†์Œ), ๋‹ค๋ฅธ ์–ด๋–ค ํ”„๋กœํ•„๊ณผ๋„ ํ•จ๊ป˜ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.

์„ธ๋ถ€ ์ •๋ณด ๊ฐ’
์ด๋ฏธ์ง€ redis:7-alpine
์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„ omniroute-redis
๋‚ด๋ถ€ ํฌํŠธ 6379
ํ˜ธ์ŠคํŠธ ํฌํŠธ(์žฌ์ •์˜) REDIS_PORT (๊ธฐ๋ณธ๊ฐ’: 6379)
ํ˜ธ์ŠคํŠธ ๋ฐ”์ธ๋”ฉ(์žฌ์ •์˜) REDIS_BIND_HOST (๊ธฐ๋ณธ๊ฐ’: 127.0.0.1)
๋ณผ๋ฅจ omniroute-redis-data โ†’ /data
์ƒํƒœ ํ™•์ธ redis-cli ping (10์ดˆ ๊ฐ„๊ฒฉ)

๊ด€๋ จ ํ™˜๊ฒฝ ๋ณ€์ˆ˜:

  • REDIS_URL โ€” ์•ฑ์— ์ฃผ์ž…๋˜๋Š” ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด(๊ธฐ๋ณธ๊ฐ’: redis://redis:6379).
  • REDIS_PORT โ€” Redis ์ปจํ…Œ์ด๋„ˆ์˜ ํ˜ธ์ŠคํŠธ ์ธก ํฌํŠธ ๋งคํ•‘.
  • REDIS_BIND_HOST โ€” ํฌํŠธ๊ฐ€ ๊ฒŒ์‹œ๋˜๋Š” ํ˜ธ์ŠคํŠธ ์ธํ„ฐํŽ˜์ด์Šค. ๊ธฐ๋ณธ๊ฐ’์€ 127.0.0.1์ž…๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฃจํ”„๋ฐฑ์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ : ์‚ฌ์ด๋“œ์นด๋Š” requirepass ์—†์ด ์‹คํ–‰๋˜๋ฉฐ, ์•ฑ ์ปจํ…Œ์ด๋„ˆ๋Š” compose ๋„คํŠธ์›Œํฌ(redis:6379)๋ฅผ ํ†ตํ•ด ์‚ฌ์ด๋“œ์นด์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ๊ฒŒ์‹œ๋œ ํฌํŠธ๋Š” ํ˜ธ์ŠคํŠธ ์ธก ๋„๊ตฌ(redis-cli, ๋กœ์ปฌ npm run dev)์—์„œ๋งŒ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. 0.0.0.0์— ๊ฒŒ์‹œํ•˜๋ฉด ์ธ์ฆ๋˜์ง€ ์•Š์€ Redis๊ฐ€ LAN์˜ ๋ชจ๋“  ํ˜ธ์ŠคํŠธ์— ๋…ธ์ถœ๋ฉ๋‹ˆ๋‹ค. REDIS_BIND_HOST=0.0.0.0์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๊ฒฝ์šฐ ์„œ๋น„์Šค์˜ command:์—๋„ --requirepass๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

Redis ๋น„ํ™œ์„ฑํ™”๋Š” ๊ถŒ์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(์†๋„ ์ œํ•œ๊ธฐ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ ๋‚ด ํด๋ฐฑ์œผ๋กœ ์„ฑ๋Šฅ ์ €ํ•˜๋จ). ๋ฐ˜๋“œ์‹œ ๋น„ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•œ๋‹ค๋ฉด docker-compose.yml์—์„œ redis: ์„œ๋น„์Šค ๋ธ”๋ก์„ ์ œ๊ฑฐํ•˜๊ฑฐ๋‚˜ ์ฃผ์„ ์ฒ˜๋ฆฌํ•˜๊ฑฐ๋‚˜, ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ธ์Šคํ„ด์Šค ์ˆ˜๋ฅผ 0์œผ๋กœ ์กฐ์ •ํ•˜์„ธ์š”.

ํ„ฐ๋ฏธ๋„ ์ฐฝ
docker compose up -d --scale redis=0

๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ํ•จ๊ป˜ ์‹คํ–‰๋˜๋Š” ๊ฒฉ๋ฆฌ๋œ ํ”„๋กœ๋•์…˜ ์Šค๋ƒ…์ƒท์—๋Š” docker-compose.prod.yml์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

์„ธ๋ถ€ ์ •๋ณด ๊ฐ’
ํŒŒ์ผ docker-compose.prod.yml
๊ธฐ๋ณธ ๋Œ€์‹œ๋ณด๋“œ ํฌํŠธ PROD_DASHBOARD_PORT=20130 (๋‚ด๋ถ€ ${DASHBOARD_PORT:-20128}์— ๋งคํ•‘)
๊ธฐ๋ณธ API ํฌํŠธ PROD_API_PORT=20131
์ด๋ฏธ์ง€ omniroute:prod (runner-cli ๋Œ€์ƒ์—์„œ ๋นŒ๋“œ)
Redis ์ปจํ…Œ์ด๋„ˆ omniroute-redis-prod (redis:8.6.2, ์ „์šฉ redis-prod-data ๋ณผ๋ฅจ)
๋ฐ์ดํ„ฐ ๋ณผ๋ฅจ omniroute-prod-data (๋ช…๋ช…๋œ ๋ณผ๋ฅจ, ์žฌ๋นŒ๋“œ ํ›„์—๋„ ์œ ์ง€๋จ)
์ƒํƒœ ํ™•์ธ node healthcheck.mjs + redis-cli ping, Redis ์ƒํƒœ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ œํ•œ๋œ depends_on

์‚ฌ์šฉ ๋ฐฉ๋ฒ•:

ํ„ฐ๋ฏธ๋„ ์ฐฝ
# ํ”„๋กœ๋•์…˜ ์Šคํƒ ๋นŒ๋“œ ๋ฐ ์‹œ์ž‘
docker compose -f docker-compose.prod.yml up -d --build
# ๋กœ๊ทธ ์ŠคํŠธ๋ฆฌ๋ฐ
docker compose -f docker-compose.prod.yml logs -f
# ์ข…๋ฃŒ(๋ณผ๋ฅจ ์œ ์ง€)
docker compose -f docker-compose.prod.yml down

ํ”„๋กœ๋•์…˜ ์Šคํƒ์€ ๊ฐœ๋ฐœ์šฉ compose์™€ ์„œ๋กœ ๋‹ค๋ฅธ ์ปจํ…Œ์ด๋„ˆ ์ด๋ฆ„, ํฌํŠธ ๋ฐ ๋ณผ๋ฅจ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰๋˜๋ฏ€๋กœ, ํ”„๋กœ๋•์…˜์„ ๊ณ„์† ์‹คํ–‰ํ•˜๋ฉด์„œ ๋กœ์ปฌ์—์„œ ๊ฐœ๋ฐœ์„ ์ด์–ด๊ฐˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ์ €์žฅ์†Œ๋Š” ๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ Dockerfile(Dockerfile)์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋„ค ๊ฐœ์˜ ์Šคํ…Œ์ด์ง€๊ฐ€ ๊ณต๊ฐœ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์‚ฌ์šฉ ์‚ฌ๋ก€์— ๋งž๋Š” target์„ ์„ ํƒํ•˜์„ธ์š”.

์Šคํ…Œ์ด์ง€ ๋ฒ ์ด์Šค ์ด๋ฏธ์ง€ ์šฉ๋„
builder node:26-trixie-slim ์ข…์†์„ฑ์„ ์„ค์น˜ํ•˜๊ณ (npm ci --legacy-peer-deps) npm run build๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค(๊ธฐ๋ณธ๊ฐ’์€ Turbopack โ€” ์•„๋ž˜์˜ ๋นŒ๋“œ ์‹œ ๋ฆฌ์†Œ์Šค ์ฐธ์กฐ).
runner-base node:26-trixie-slim Next.js ๋…๋ฆฝ ์‹คํ–‰ํ˜• ์ถœ๋ ฅ์ด ํฌํ•จ๋œ ํ”„๋กœ๋•์…˜ ๋Ÿฐํƒ€์ž„์ž…๋‹ˆ๋‹ค. ํ”„๋กœ๋ฐ”์ด๋” CLI๋Š” ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
runner-cli runner-base git, docker.io, docker-compose ๋ฐ ์ „์—ญ CLI์ธ @openai/codex, @anthropic-ai/claude-code, droid, openclaw๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์—์ด์ „ํŠธํ˜• ์›Œํฌํ”Œ๋กœ์—๋Š” ์ด๊ฒƒ์„ ์„ ํƒํ•˜์„ธ์š”.
runner-web runner-base ์›น ์„ธ์…˜ ํ”„๋กœ๋ฐ”์ด๋”์ธ gemini-web, claude-web, claude-turnstile์„ ์œ„ํ•ด Playwright์™€ Chromium ๋ธŒ๋ผ์šฐ์ €(--with-deps)๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํ”„๋กœ๋ฐ”์ด๋”๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์„ ํƒํ•˜์„ธ์š” โ€” ์ผ๋ฐ˜ ์ด๋ฏธ์ง€๋Š” ์ด๊ฒƒ์ด ์—†์œผ๋ฉด ์š”์ฒญ ์‹œ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค(๋ฆด๋ฆฌ์Šค ์ฑ„๋„ ์•„๋ž˜์˜ -web ์ฐธ๊ณ  ์‚ฌํ•ญ ์ฐธ์กฐ).

ํŠน์ • ๋Œ€์ƒ์„ ์ˆ˜๋™์œผ๋กœ ๋นŒ๋“œํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์‹คํ–‰ํ•˜์„ธ์š”.

ํ„ฐ๋ฏธ๋„ ์ฐฝ
docker build --target runner-base -t omniroute:base .
docker build --target runner-cli -t omniroute:cli .
docker build --target runner-web -t omniroute:web .

์„ธ ๊ฐ€์ง€ ๋นŒ๋“œ ์ธ์ˆ˜๊ฐ€ builder ์Šคํ…Œ์ด์ง€์˜ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰์„ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค. ์ด ์ธ์ˆ˜๋“ค์€ ๋นŒ๋“œ ์‹œ์—๋งŒ ์ ์šฉ๋˜๋ฉฐ โ€” OMNIROUTE_MEMORY_MB(์•„๋ž˜ ์ฐธ์กฐ)๋Š” ๋ณ„๋„์˜ ๋Ÿฐํƒ€์ž„ ์กฐ์ • ์˜ต์…˜์ž…๋‹ˆ๋‹ค.

๋นŒ๋“œ ์ธ์ˆ˜ ๊ธฐ๋ณธ๊ฐ’ ํšจ๊ณผ
OMNIROUTE_USE_TURBOPACK 1 0์ด๋ฉด ๋Œ€์‹  webpack์œผ๋กœ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค. ์ตœ๋Œ€ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์€ ์ค„์ง€๋งŒ ๋” ๋А๋ฆฝ๋‹ˆ๋‹ค.
OMNIROUTE_BUILD_MEMORY_MB 6144 ์ƒ์„ฑ๋œ next build์˜ V8 ํž™ ์ƒํ•œ(--max-old-space-size)์ž…๋‹ˆ๋‹ค.
OMNIROUTE_BUILD_WORKERS 2 CIRCLE_NODE_TOTAL์— ์ „๋‹ฌ๋˜๋ฉฐ, Next๋Š” ํŽ˜์ด์ง€ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘์— ์‚ฌ์šฉํ•  workers = N - 1์„ ๋„์ถœํ•ฉ๋‹ˆ๋‹ค.

OMNIROUTE_BUILD_WORKERS๋Š” ๋Œ€๊ทœ๋ชจ ๋นŒ๋”์—์„œ ๋Š˜๋ ค์•ผ ํ•˜๋Š” ๊ฐ’์ด๋ฉฐ, ๋ฆฌ์†Œ์Šค๊ฐ€ ์ œํ•œ๋œ ๋นŒ๋“œ๊ฐ€ โœ“ Compiled successfully ์ดํ›„์— ์ข…๋ฃŒ๋  ๋•Œ ์˜์‹ฌํ•ด์•ผ ํ•˜๋Š” ๊ฐ’์ž…๋‹ˆ๋‹ค. ๊ฐ ํŽ˜์ด์ง€ ๋ฐ์ดํ„ฐ ์›Œ์ปค๋Š” ์ž์ฒด ํ”„๋กœ์„ธ์Šค์ด๋ฉฐ, ์ƒ์œ„ next build ์ž์ฒด๋„ ๋ณ„๋„์˜ ํ”„๋กœ์„ธ์Šค์ž…๋‹ˆ๋‹ค. ์‹ค์ œ VPS ์žฌํ˜„(issue #7518)์—์„œ ๊ฐ ํ”„๋กœ์„ธ์Šค์˜ ์ตœ๋Œ€ RSS๊ฐ€ NODE_OPTIONS ํž™ ํ”Œ๋ž˜๊ทธ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ ์•ฝ 4.5 GB๋กœ ์ธก์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค(Turbopack์€ V8 ํž™ ์™ธ๋ถ€์˜ ๋„ค์ดํ‹ฐ๋ธŒ/Rust ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ปดํŒŒ์ผํ•ฉ๋‹ˆ๋‹ค). ๊ธฐ๋ณธ๊ฐ’ 2(โ†’ ์›Œ์ปค 1๊ฐœ, ์ด ํ”„๋กœ์„ธ์Šค 2๊ฐœ)๋Š” ๊ฒŒ์‹œ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ์‚ฌ์šฉํ•˜๋Š” 16 GB / 4 vCPU GitHub ํ˜ธ์ŠคํŒ… ๋Ÿฌ๋„ˆ์— ๋งž์ถฐ ์„ค์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค. 8(โ†’ ์›Œ์ปค 7๊ฐœ)์—์„œ๋Š” ํ•ด๋‹น ๋Ÿฌ๋„ˆ์˜ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ๋ถ€์กฑํ•ด์กŒ๊ณ  buildkit์ด ResourceExhausted: ... cannot allocate memory์™€ ํ•จ๊ป˜ ๋‹จ๊ณ„๋ฅผ ์‹คํŒจ ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์„ธ์Šค๋ณ„ RSS๋ฅผ ์ถ”๋ก ํ•˜์ง€ ์•Š๊ณ  ์ง์ ‘ ์ธก์ •ํ•œ ๊ฒฐ๊ณผ, 3(โ†’ ์›Œ์ปค 2๊ฐœ)๋„ ์—ฌ์ „ํžˆ ๋ฉ”๋ชจ๋ฆฌ์— ๋งž์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. tests/unit/docker-build-memory-budget.test.ts๋Š” ์ธก์ •๊ฐ’์„ ๊ธฐ์ค€์œผ๋กœ ๊ณ„์‚ฐํ•˜๋ฉฐ, ์–ด๋А ์กฐ์ •๊ฐ’์ด๋“  ๋Ÿฌ๋„ˆ์˜ ์šฉ๋Ÿ‰์„ ์ดˆ๊ณผํ•˜๋ฉด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

Turbopack์€ V8 ํž™ ์™ธ๋ถ€์— ์กด์žฌํ•˜๋Š” ๋„ค์ดํ‹ฐ๋ธŒ Rust ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ปดํŒŒ์ผํ•˜๋ฏ€๋กœ OMNIROUTE_BUILD_MEMORY_MB๋กœ ์ด๋ฅผ ์ œํ•œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ฉ”๋ชจ๋ฆฌ ์ƒํ•œ์ด ์žˆ๋Š” ํ˜ธ์ŠคํŠธ์—์„œ๋Š” ์˜ค๋ฅ˜ ํ…์ŠคํŠธ๊ฐ€ ์ „ํ˜€ ํ‘œ์‹œ๋˜์ง€ ์•Š์€ ์ฑ„ OOM ํ‚ฌ๋Ÿฌ์— ์˜ํ•ด ๋นŒ๋“œ๊ฐ€ SIGKILL๋ฉ๋‹ˆ๋‹ค. ์ฆ‰, Creating an optimized production build ๋„์ค‘์— ๋‹จ์ˆœํžˆ ์ค‘์ง€๋˜๋ฏ€๋กœ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ์ด ์•„๋‹ˆ๋ผ ๋ฉˆ์ถ˜ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค. ๋นŒ๋“œ ํ˜ธ์ŠคํŠธ์˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ œํ•œ๋œ ๊ฒฝ์šฐ ๋ฒˆ๋“ค๋Ÿฌ๋ฅผ ์ „ํ™˜ํ•˜์„ธ์š”.

ํ„ฐ๋ฏธ๋„ ์ฐฝ
docker build --target runner-base \
--build-arg OMNIROUTE_USE_TURBOPACK=0 \
-t omniroute:base .

webpackBuildWorker๊ฐ€ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ next build๋Š” ์ƒ์œ„ ํ”„๋กœ์„ธ์Šค์™€ ๋ณ„๋„์˜ ์›Œ์ปค ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹คํ–‰ํ•˜๋ฉฐ, ๊ฐ ํ”„๋กœ์„ธ์Šค๋Š” OMNIROUTE_BUILD_MEMORY_MB๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ค€์ˆ˜ํ•ฉ๋‹ˆ๋‹ค. ์ปจํ…Œ์ด๋„ˆ ์ƒํ•œ์€ ํ•ด๋‹น ๊ฐ’์˜ ํ•œ ๋ฐฐ๊ฐ€ ์•„๋‹ˆ๋ผ ๋Œ€๋žต ๋‘ ๋ฐฐ๋ณด๋‹ค ๋†’๊ฒŒ ์„ค์ •ํ•˜์„ธ์š”.

์ด ํŠธ๋ฆฌ์—์„œ ์ธก์ •ํ•œ ๊ฒฐ๊ณผ(--target runner-base, OMNIROUTE_BUILD_MEMORY_MB=6144):

๋ฒˆ๋“ค๋Ÿฌ ์ปจํ…Œ์ด๋„ˆ ์ƒํ•œ ๊ฒฐ๊ณผ
Turbopack 8 GiB / 16 GiB ๋‘ ๊ฒฝ์šฐ ๋ชจ๋‘ ์กฐ์šฉํžˆ OOM ์ข…๋ฃŒ๋จ
webpack 8 GiB ๋นŒ๋“œ ์›Œ์ปค๊ฐ€ SIGKILL๋จ
webpack 12 GiB ์„ฑ๊ณต, ์ตœ๋Œ€ 11.1 GiB ์‚ฌ์šฉ

runner-base์—์„œ ๋‚ด๋ณด๋‚ด๋Š” ๊ธฐ๋ณธ๊ฐ’: PORT=20128, HOSTNAME=0.0.0.0, OMNIROUTE_MEMORY_MB=1024, NODE_OPTIONS=--max-old-space-size=1024, DATA_DIR=/app/data, OMNIROUTE_MIGRATIONS_DIR=/app/migrations.

Docker์—์„œ์˜ ๋ฉ”๋ชจ๋ฆฌ ๋™์ž‘:

  • ์ด๋ฏธ์ง€๋Š” OMNIROUTE_MEMORY_MB=1024๋ฅผ ์„ค์ •ํ•˜๊ณ , ์—ฌ๊ธฐ์—์„œ NODE_OPTIONS=--max-old-space-size=1024๋ฅผ ํŒŒ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • ์‹ค์ œ ์„œ๋ฒ„ ํ”„๋กœ์„ธ์Šค๋Š” ๋…๋ฆฝ ์‹คํ–‰ํ˜• ๋Ÿฐ์ฒ˜์— ์˜ํ•ด ์‹œ์ž‘๋˜๋ฉฐ, ๋Ÿฐ์ฒ˜๋Š” OMNIROUTE_MEMORY_MB๋ฅผ ์ฝ๊ณ  --max-old-space-size=<OMNIROUTE_MEMORY_MB>๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  • Node๋Š” ๋ฐ˜๋ณต๋œ --max-old-space-size ๊ฐ’ ์ค‘ ๋งˆ์ง€๋ง‰ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, OMNIROUTE_MEMORY_MB๋ฅผ ์„ค์ •ํ•˜๋ฉด ์‹ค์งˆ์ ์ธ Docker ํž™ ์ œํ•œ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ด๋ฏธ์ง€์—์„œ ์ด ๊ฐ’์„ ํ•ญ์ƒ ์„ค์ •ํ•˜๋ฏ€๋กœ Docker์—์„œ๋Š” ๋Ÿฐ์ฒ˜ ์ž์ฒด์˜ RAM ๊ธฐ๋ฐ˜ ๋ณด์ • ํด๋ฐฑ์ด ์ ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์›Œํฌ๋กœ๋“œ์— ๋งž๊ฒŒ ๋ช…์‹œ์ ์œผ๋กœ ๋Š˜๋ฆฌ์‹ญ์‹œ์˜ค(์•„๋ž˜ ํ‘œ ์ฐธ์กฐ). ์ฝ”๋”ฉ ์—์ด์ „ํŠธ์˜ /v1/responses์—๋Š” 2048๋„ ์—ฌ์ „ํžˆ ๋„ˆ๋ฌด ์ž‘์Šต๋‹ˆ๋‹ค.

Docker ๊ธฐ๋ณธ๊ฐ’์ธ 1โ€ฏGiB๋Š” ๋Œ€์‹œ๋ณด๋“œ/๊ฐ€๋ฒผ์šด ์ฑ„ํŒ…์„ ์œ„ํ•œ ์ตœ์ € ๊ธฐ์ค€์ด์ง€, ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์— ์ ํ•ฉํ•œ ํฌ๊ธฐ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ธด POST /v1/responses ๋ณธ๋ฌธ(์ˆ˜๋ฐฑ ๊ฐœ์˜ ๋ฉ”์‹œ์ง€์™€ ์ˆ˜์‹ญ ๊ฐœ์˜ ๋„๊ตฌ)์€ ์••์ถ• ์ค‘์— ์—ฌ๋Ÿฌ ์ธ๋ฉ”๋ชจ๋ฆฌ ๊ทธ๋ž˜ํ”„๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋กœ ๊ฒน์ณ ์‹คํ–‰๋œ ์•ฝ 3โ€ฏMiB / ์•ฝ 750k ํ† ํฐ ์š”์ฒญ ๋‘ ๊ฑด์€ 12โ€ฏGiB old-space์—์„œ V8์„ ์ค‘๋‹จ์‹œ์ผฐ์œผ๋ฉฐ(FATAL ERROR: Reached heap limit), 16โ€ฏGiB cgroup OOM๋„ ๋ฐœ์ƒ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. #7849๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

cgroup --memory๋Š” ํž™๋ณด๋‹ค ํฌ๊ฒŒ ์„ค์ •ํ•˜์‹ญ์‹œ์˜ค โ€” ๋„ค์ดํ‹ฐ๋ธŒ ๋ฒ„ํผ, SQLite ๋ฐ ์••์ถ• ์ค‘๊ฐ„ ๋ฐ์ดํ„ฐ๋Š” V8 ์™ธ๋ถ€์— ์œ„์น˜ํ•ฉ๋‹ˆ๋‹ค.

์›Œํฌ๋กœ๋“œ OMNIROUTE_MEMORY_MB ์ปจํ…Œ์ด๋„ˆ / cgroup ์ฐธ๊ณ 
๋Œ€์‹œ๋ณด๋“œ, ๊ฐ€๋ฒผ์šด ์ฑ„ํŒ… ํ•˜๋‚˜ 1024 (์ด๋ฏธ์ง€ ๊ธฐ๋ณธ๊ฐ’) โ‰ฅ2โ€ฏGiB
์ฝ”๋”ฉ ์—์ด์ „ํŠธ ํ•˜๋‚˜(Claude/Codex/Grok) 8192 โ‰ฅ10โ€ฏGiB ์ผ๋ฐ˜์ ์ธ ๋‹จ์ผ ์„ธ์…˜ /v1/responses
๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๊ธด /v1/responses ๋‘ ๊ฑด 10240โ€“12288 โ‰ฅ12โ€“16โ€ฏGiB ์•ฝ 12โ€ฏGiB ํž™์—์„œ V8 ์ค‘๋‹จ์ด ์ธก์ •๋จ
๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๊ธด ์ปจํ…์ŠคํŠธ ์„ธ ๊ฐœ ์ด์ƒ ๋‹จ์ผ ํ”„๋กœ์„ธ์Šค์—์„œ ์‹คํ–‰ํ•˜์ง€ ๋ง ๊ฒƒ ์ง๋ ฌํ™” / RAM ์ฆ์„ค ๊ธฐ๋ณธ ๊ณ ๋ถ€ํ•˜ ์ž‘์—… ํ—ˆ์šฉ๋Ÿ‰์€ ์‹คํ–‰ ์ค‘ 1๊ฑด์ด๋ฉฐ, RAM์„ ๋Š˜๋ฆฌ์ง€ ์•Š๊ณ  ์ด ๊ฐ’์„ ๋†’์ด๋ฉด ์ค‘๋‹จ์ด ๋‹ค์‹œ ๋ฐœ์ƒํ•จ

๋ฒ ์–ด ๋ฉ”ํƒˆ์—์„œ omniroute serve๋Š” OMNIROUTE_MEMORY_MB๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ RAM์˜ ์•ฝ 35%๋กœ ๋ณด์ •ํ•ฉ๋‹ˆ๋‹ค([512, 4096] ๋ฒ”์œ„๋กœ ์ œํ•œ). Docker๋Š” ํ•ญ์ƒ 1024๋ฅผ ์„ค์ •ํ•˜๋ฏ€๋กœ ๊ณต์‹ ์ด๋ฏธ์ง€์—์„œ๋Š” ์ด ๋ณด์ •์ด ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ„ฐ๋ฏธ๋„ ์ฐฝ
docker run -d --name omniroute --restart unless-stopped --stop-timeout 40 \
-e OMNIROUTE_MEMORY_MB=8192 --memory=10g \
-p 127.0.0.1:20128:20128 -v omniroute-data:/app/data diegosouzapw/omniroute:latest

ENVIRONMENT.md์— ๋ฌธ์„œํ™”๋œ ๊ธฐ๋ณธ๊ฐ’ ์™ธ์—๋„ Docker์—์„œ ์‹คํ–‰ํ•  ๋•Œ๋Š” ๋‹ค์Œ ๋ณ€์ˆ˜๊ฐ€ ๊ฐ€์žฅ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค.

๋ณ€์ˆ˜ ์šฉ๋„ ๊ธฐ๋ณธ๊ฐ’
OMNIROUTE_WS_BRIDGE_SECRET WebSocket ๋ธŒ๋ฆฌ์ง€์˜ ๊ณต์œ  ๋น„๋ฐ€ ๊ฐ’์ž…๋‹ˆ๋‹ค. ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ํ•„์ˆ˜์ด๋ฉฐ, ๊ฐ•๋ ฅํ•œ ๋ฌด์ž‘์œ„ ๋ฌธ์ž์—ด๋กœ ์„ค์ •ํ•˜์„ธ์š”. ์„ค์ •๋˜์ง€ ์•Š์Œ(์ œ๊ณต ํ•„์ˆ˜)
REDIS_URL ์†๋„ ์ œํ•œ๊ธฐ/์บ์‹œ ๋ฐฑ์—”๋“œ์˜ ์—ฐ๊ฒฐ ๋ฌธ์ž์—ด redis://redis:6379
REDIS_PORT ๋ฒˆ๋“ค Redis ์ปจํ…Œ์ด๋„ˆ์˜ ํ˜ธ์ŠคํŠธ ์ธก ํฌํŠธ 6379
REDIS_BIND_HOST ๋ฒˆ๋“ค Redis ํฌํŠธ๊ฐ€ ๊ฒŒ์‹œ๋˜๋Š” ํ˜ธ์ŠคํŠธ ์ธํ„ฐํŽ˜์ด์Šค(AUTH๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋Š” ํ•œ ๋ฃจํ”„๋ฐฑ) 127.0.0.1
AUTO_UPDATE_HOST_REPO_DIR ์ž์ฒด ์—…๋ฐ์ดํŠธ ์›Œํฌํ”Œ๋กœ๋ฅผ ์œ„ํ•ด cli ํ”„๋กœํ•„์—์„œ /workspace/omniroute์— ๋งˆ์šดํŠธ๋˜๋Š” ํ˜ธ์ŠคํŠธ ๊ฒฝ๋กœ . (ํ˜„์žฌ ๋””๋ ‰ํ„ฐ๋ฆฌ)
OMNIROUTE_MEMORY_MB Docker ๋…๋ฆฝ ์‹คํ–‰ํ˜• ์„œ๋ฒ„์˜ ๋Ÿฐํƒ€์ž„ Node ํž™ ์ƒํ•œ์ด๋ฉฐ, ์œ„์˜ ์ด๋ฏธ์ง€ ๊ธฐ๋ณธ๊ฐ’์„ ์žฌ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋”ฉ ์—์ด์ „ํŠธ: 8192 ์ด์ƒ(๋Ÿฐํƒ€์ž„ RAM ์ฐธ์กฐ). 1024
DASHBOARD_PORT / API_PORT ๋Œ€์‹œ๋ณด๋“œ(20128) ๋ฐ API(20129)์˜ ๋…ธ์ถœ ํฌํŠธ๋ฅผ ์žฌ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. 20128 / 20129
APP_BIND_HOST docker-compose๊ฐ€ ๋Œ€์‹œ๋ณด๋“œ/API/๋ผ์ด๋ธŒ WS ํฌํŠธ๋ฅผ ๊ฒŒ์‹œํ•˜๋Š” ํ˜ธ์ŠคํŠธ ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. REQUIRE_API_KEY=false(๊ธฐ๋ณธ๊ฐ’)์ด๋ฉด 0.0.0.0์€ ์ต๋ช… /v1 ํ”„๋ก์‹œ๋ฅผ LAN์— ๋…ธ์ถœํ•˜๋ฏ€๋กœ, REQUIRE_API_KEY=true๋กœ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ ์•ž๋‹จ์— ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ๋ฅผ ๋‘” ๊ฒฝ์šฐ์—๋งŒ ๋ฒ”์œ„๋ฅผ ํ™•์žฅํ•˜์„ธ์š”. 127.0.0.1
CLIPROXY_BIND_HOST docker-compose๊ฐ€ cliproxyapi ์‚ฌ์ด๋“œ์นด๋ฅผ ๊ฒŒ์‹œํ•˜๋Š” ํ˜ธ์ŠคํŠธ ์ธํ„ฐํŽ˜์ด์Šค์ด๋ฉฐ, ํ•ด๋‹น ๋ฐ์ดํ„ฐ ๋ณผ๋ฅจ์—๋Š” ๊ณต๊ธ‰์ž ์ž๊ฒฉ ์ฆ๋ช…์ด ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. 127.0.0.1
OMNIROUTE_PLUGINS_DIR ๋Ÿฐํƒ€์ž„ ํ”Œ๋Ÿฌ๊ทธ์ธ ์Šค์บ๋„ˆ๊ฐ€ ์ฝ๊ณ  ์„ค์น˜ํ•˜๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ์ž…๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋ฐ”์ธ๋“œ ๋งˆ์šดํŠธ๋œ ๊ฒฝ์šฐ ์„ค์ •ํ•˜์„ธ์š”. ๊ธฐ๋ณธ๊ฐ’์€ HOME์„ ๋”ฐ๋ฅด์ง€๋งŒ ์ด๋ฏธ์ง€์—์„œ ์ด๋ฅผ ๋‚ด๋ณด๋‚ด์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ~/.omniroute/plugins
OMNIROUTE_BASE_PATH ์•ฑ์ด ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ ๋’ค์— ๊ฒŒ์‹œ๋  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” URL ํ•˜์œ„ ๊ฒฝ๋กœ(์˜ˆ: /omniroute) (๋น„์–ด ์žˆ์Œ = ๋ฃจํŠธ)
NEXT_PUBLIC_BASE_URL ํ•˜์œ„ ๊ฒฝ๋กœ๋ฅผ ํฌํ•จํ•œ ๊ณต๊ฐœ ๋ธŒ๋ผ์šฐ์ € ์›๋ณธ ์ฃผ์†Œ(์˜ˆ: https://host/omniroute) ์„ค์ •๋˜์ง€ ์•Š์Œ
PROD_DASHBOARD_PORT docker-compose.prod.yml์˜ ํ˜ธ์ŠคํŠธ ์ธก ๋Œ€์‹œ๋ณด๋“œ ํฌํŠธ 20130
CLIPROXYAPI_PORT cliproxyapi ์‚ฌ์ด๋“œ์นด์˜ ํ˜ธ์ŠคํŠธ ์ธก ํฌํŠธ 8317

ํ•˜์œ„ ๊ฒฝ๋กœ์˜ ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ (Traefik / nginx)

์„น์…˜ ์ œ๋ชฉ: โ€œํ•˜์œ„ ๊ฒฝ๋กœ์˜ ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ (Traefik / nginx)โ€

Next.js basePath๋Š” standalone ๋ฒˆ๋“ค์— ์ปดํŒŒ์ผ๋ฉ๋‹ˆ๋‹ค. OmniRoute๋Š” ์•ฑ ๋ฃจํŠธ์˜ ์„ผํ‹ฐ๋„ ํŒŒ์ผ์— ๋นŒ๋“œ ์‹œ ์ ์šฉ๋œ ๊ฐ’์„ ๊ธฐ๋กํ•˜๊ณ (npm run build ์‹คํ–‰ ์ค‘ ์ž‘์„ฑ๋˜๋ฉฐ scripts/docker/ensure-docker-base-path.mjs์—์„œ ์ฝ์Œ), ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์‹œ์ž‘๋  ๋•Œ ํ•ด๋‹น ๊ฐ’์„ OMNIROUTE_BASE_PATH์™€ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. ๊ฐ’์ด ์„œ๋กœ ๋‹ค๋ฅด๊ณ  ์ด๋ฏธ์ง€๊ฐ€ ๋„๋ฉ”์ธ ๋ฃจํŠธ์šฉ์œผ๋กœ ๋นŒ๋“œ๋œ ๊ฒฝ์šฐ, ์—”ํŠธ๋ฆฌํฌ์ธํŠธ๋Š” node dev/run-standalone.mjs๊ฐ€ ์‹คํ–‰๋˜๊ธฐ ์ „์— standalone ๋งค๋‹ˆํŽ˜์ŠคํŠธ, ํฌํ•จ๋œ basePath/assetPrefix ๋ฆฌํ„ฐ๋Ÿด(Next 16์€ assetPrefix๋งŒ ์‚ฌ์šฉํ•˜์—ฌ SSR ์• ์…‹ URL์„ ๋ Œ๋”๋งํ•˜๋ฏ€๋กœ ํŒจ์ฒ˜๊ฐ€ ํ•˜์œ„ ๊ฒฝ๋กœ๋ฅผ ํ•ด๋‹น ๊ฐ’์—๋„ ๋ฐ˜์˜ํ•จ), ๋นŒ๋“œ ์‹œ ํฌํ•จ๋œ /_next/static ์• ์…‹ URL(ํด๋ผ์ด์–ธํŠธ ์ฐธ์กฐ ๋งค๋‹ˆํŽ˜์ŠคํŠธ, ๋ฏธ๋””์–ด ๊ฐ€์ ธ์˜ค๊ธฐ, ์‚ฌ์ „ ๋ Œ๋”๋ง๋œ ์˜ค๋ฅ˜ ํŽ˜์ด์ง€) ๋ฐ ํด๋ผ์ด์–ธํŠธ process.env ์‹ฌ์„ ๋‹ค์‹œ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฏธ์ง€์™€ ๋Ÿฐํƒ€์ž„์˜ ์„ค์ •์ด ์ผ์น˜ํ•˜๋„๋ก .env์— ๋‘ ๋ณ€์ˆ˜๋ฅผ ๋ชจ๋‘ ์„ค์ •ํ•œ ํ›„ ๋‹ค์‹œ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค.

.env
OMNIROUTE_BASE_PATH=/omniroute
NEXT_PUBLIC_BASE_URL=https://myhostname.example.com/omniroute
ํ„ฐ๋ฏธ๋„ ์ฐฝ
docker compose --profile base up -d --build

docker-compose.yml์€ OMNIROUTE_BASE_PATH๋ฅผ Docker ๋นŒ๋“œ ์ธ์ˆ˜์™€ ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์ „ ๋นŒ๋“œ๋œ ๋ฃจํŠธ ์ด๋ฏธ์ง€ + ๋Ÿฐํƒ€์ž„ ํ•˜์œ„ ๊ฒฝ๋กœ

์„น์…˜ ์ œ๋ชฉ: โ€œ์‚ฌ์ „ ๋นŒ๋“œ๋œ ๋ฃจํŠธ ์ด๋ฏธ์ง€ + ๋Ÿฐํƒ€์ž„ ํ•˜์œ„ ๊ฒฝ๋กœโ€

๊ฒŒ์‹œ๋œ diegosouzapw/omniroute:* ์ด๋ฏธ์ง€๋Š” ๋„๋ฉ”์ธ ๋ฃจํŠธ์šฉ์œผ๋กœ ๋นŒ๋“œ๋ฉ๋‹ˆ๋‹ค. ๋Ÿฐํƒ€์ž„์— OMNIROUTE_BASE_PATH๋ฅผ ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ์œผ๋ฉฐ, ์ปจํ…Œ์ด๋„ˆ๋Š” ์‹œ์ž‘ ์‹œ ๋ฒˆ๋“ค์„ ํ•œ ๋ฒˆ ํŒจ์น˜ํ•ฉ๋‹ˆ๋‹ค. ์ด์— ๋Œ€์‘ํ•˜๋Š” ๊ณต๊ฐœ ์˜ค๋ฆฌ์ง„๋„ ํ•จ๊ป˜ ์„ค์ •ํ•˜์‹ญ์‹œ์˜ค.

services:
omniroute:
image: diegosouzapw/omniroute:latest
environment:
OMNIROUTE_BASE_PATH: /omniroute
NEXT_PUBLIC_BASE_URL: https://myhostname.example.com/omniroute

๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ๊ฐ€ ์™ธ๋ถ€ ๊ฒฝ๋กœ ์ „์ฒด๋ฅผ ์ „๋‹ฌํ•˜๋„๋ก ์„ค์ •ํ•˜์‹ญ์‹œ์˜ค(์ ‘๋‘์‚ฌ๋ฅผ ์ œ๊ฑฐํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค). Traefik์€ StripPrefix ์—†์ด PathPrefix(/omniroute)๋ฅผ ์ปจํ…Œ์ด๋„ˆ๋กœ ๋ผ์šฐํŒ…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด Next.js๊ฐ€ /omniroute/...๋ฅผ ์ˆ˜์‹ ํ•˜๊ณ  /omniroute/_next/...์—์„œ ์• ์…‹์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Docker ํ—ฌ์Šค ์ฒดํฌ๋Š” ํ™œ์„ฑ OMNIROUTE_BASE_PATH๊ฐ€ ์ ‘๋‘์‚ฌ๋กœ ์ถ”๊ฐ€๋œ ๊ฒฝ๋Ÿ‰ /healthz ์ˆ˜๋ช… ์ฃผ๊ธฐ ์—”๋“œํฌ์ธํŠธ๋ฅผ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค. /api/monitoring/health๋Š” ์‚ฌ์šฉ์ž/๋Œ€์‹œ๋ณด๋“œ ์ง„๋‹จ์šฉ์œผ๋กœ ๊ณ„์† ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ปจํ…Œ์ด๋„ˆ HEALTHCHECK๊ฐ€ ์ด ์—”๋“œํฌ์ธํŠธ๋ฅผ ๋‹ค์‹œ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜๋ ค๋ฉด(์˜ˆ: ์‹ฌ์ธต ์ƒํƒœ ๊ฒ€์‚ฌ๋ฅผ ๊ฐ•์ œํ•˜๋ ค๋Š” ๊ฒฝ์šฐ) OMNIROUTE_HEALTHCHECK_PATH=/api/monitoring/health๋ฅผ ์„ค์ •ํ•˜์‹ญ์‹œ์˜ค. ์ด ๊ฒฝ๋กœ๋Š” ์‹ฌ์ธต ๊ฒ€์‚ฌ(DB + ๋ชจ๋‹ˆํ„ฐ๋ง ์š”์•ฝ)์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •ํ•  ๊ฒฝ์šฐ Docker์˜ ๋‚ฎ์€ ๋นˆ๋„๋กœ ์‹คํ–‰๋˜๋Š” HEALTHCHECK์—๋Š” ์ ํ•ฉํ•˜์ง€๋งŒ, Kubernetes livenessProbe ์ฃผ๊ธฐ์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ(Kubernetes, Nomad ๋“ฑ)์˜ ๊ฒฝ์šฐ:

ํ”„๋กœ๋ธŒ ๊ถŒ์žฅ ์ง€์–‘
๋ผ์ด๋ธŒ๋‹ˆ์Šค HTTP GET /livez ๋˜๋Š” ๊ธฐ๋ณธ ํฌํŠธ(PORT, ๊ธฐ๋ณธ๊ฐ’ 20128)์˜ TCP ๋ผ์ด๋ธŒ๋‹ˆ์Šค์— /api/monitoring/health ์‚ฌ์šฉ
๋ ˆ๋””๋‹ˆ์Šค HTTP GET /healthz ์ด๋ฒคํŠธ ๋ฃจํ”„๊ฐ€ ๋ฐ”์œ ์ƒํƒœ๋ฅผ ํ”„๋กœ์„ธ์Šค ์ค‘๋‹จ์œผ๋กœ ๊ฐ„์ฃผํ•˜๋Š” ์งง์€ ํƒ€์ž„์•„์›ƒ
์‹ฌ์ธต / ๋ธ”๋ž™๋ฐ•์Šค /api/monitoring/health โ€”

/healthz๋Š” ํ”„๋กœ์„ธ์Šค ์ˆ˜๋ช… ์ฃผ๊ธฐ ์ƒํƒœ(ok / starting / stopping)๋ฅผ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. /livez๋Š” ํ”„๋กœ์„ธ์Šค์˜ ์ƒ์กด ์—ฌ๋ถ€๋งŒ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค(ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์‹คํ–‰๋  ์ˆ˜ ์žˆ์œผ๋ฉด ํ•ญ์ƒ 200์„ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ ๋ ˆ๋””๋‹ˆ์Šค ์ƒํƒœ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์Œ). ๋‘ ์—”๋“œํฌ์ธํŠธ ๋ชจ๋‘ ์š”์ฒญ ์ฒ˜๋ฆฌ์™€ ๋™์ผํ•œ Node ์ด๋ฒคํŠธ ๋ฃจํ”„์—์„œ ์‹คํ–‰๋˜๋ฏ€๋กœ CPU ์ง‘์•ฝ์ ์ธ ์นดํƒˆ๋กœ๊ทธ ๋˜๋Š” ์••์ถ• ์ž‘์—…์œผ๋กœ ์ธํ•ด ์‘๋‹ต์ด ์ง€์—ฐ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฆ‰, ๋ฐ”์จ โ‰  ์ค‘๋‹จ์ž…๋‹ˆ๋‹ค. HTTP ํ”„๋กœ๋ธŒ๊ฐ€ ์‹œ๊ฐ„ ์ดˆ๊ณผ๋˜๋ฉด TCP ๋ผ์ด๋ธŒ๋‹ˆ์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ „์ฒด ํ”„๋กœ๋ธŒ ์ง€์นจ: ๋ชจ๋‹ˆํ„ฐ๋ง ๊ฐ€์ด๋“œ โ€” Kubernetes ํ”„๋กœ๋ธŒ ๊ถŒ์žฅ ์‚ฌํ•ญ.

Caddy์˜ ์ž๋™ SSL ํ”„๋กœ๋น„์ €๋‹์„ ์‚ฌ์šฉํ•˜์—ฌ OmniRoute๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์™ธ๋ถ€์— ๊ณต๊ฐœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋„๋ฉ”์ธ์˜ DNS A ๋ ˆ์ฝ”๋“œ๊ฐ€ ์„œ๋ฒ„์˜ IP๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

services:
omniroute:
image: diegosouzapw/omniroute:latest
container_name: omniroute
restart: unless-stopped
volumes:
- omniroute-data:/app/data
environment:
- PORT=20128
# OAuth ์ฝœ๋ฐฑ, ๋Œ€์‹œ๋ณด๋“œ ๋งํฌ ๋ฐ ์ƒ์„ฑ๋œ ๊ณต๊ฐœ URL์„ ์œ„ํ•œ ๋ธŒ๋ผ์šฐ์ € ๋Œ€์ƒ ์˜ค๋ฆฌ์ง„์ž…๋‹ˆ๋‹ค.
- NEXT_PUBLIC_BASE_URL=https://your-domain.com
# ์˜ˆ์•ฝ ์ž‘์—…/์ž์ฒด ์š”์ฒญ์„ ์œ„ํ•œ ๋‚ด๋ถ€ ์„œ๋ฒ„ ๊ฐ„ URL์ž…๋‹ˆ๋‹ค.
- BASE_URL=http://omniroute:20128
- AUTH_COOKIE_SECURE=true
caddy:
image: caddy:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
command: caddy reverse-proxy --from https://your-domain.com --to http://omniroute:20128
volumes:
omniroute-data:

Caddy๋Š” ์—…์ŠคํŠธ๋ฆผ ์ปจํ…Œ์ด๋„ˆ์— ํ‘œ์ค€ ์ „๋‹ฌ ํ—ค๋”๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. OmniRoute๋Š” OAuth ์ฝœ๋ฐฑ ๋ฐ ์ƒ์„ฑ๋œ ๊ณต๊ฐœ ๋งํฌ์˜ ์ •์‹ ๊ณต๊ฐœ ์˜ค๋ฆฌ์ง„์œผ๋กœ NEXT_PUBLIC_BASE_URL์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ธ์ฆ๋œ ๋Œ€์‹œ๋ณด๋“œ ์“ฐ๊ธฐ ์ž‘์—…์—๋Š” ๋™์ผ ์ถœ์ฒ˜ ์š”์ฒญ๊ณผ ์„ธ์…˜์— ๋ฐ”์ธ๋”ฉ๋œ CSRF ๋ณดํ˜ธ๊ฐ€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๋ช…์‹œ์  ๊ตฌ์„ฑ ๋Œ€์‹  ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ์ „๋‹ฌ ํ—ค๋”์—์„œ ๊ณต๊ฐœ ์˜ค๋ฆฌ์ง„์„ ํŒŒ์ƒํ•˜๋„๋ก OmniRoute๋ฅผ ์˜๋„์ ์œผ๋กœ ์„ค์ •ํ•˜๋Š” ๊ณ ๊ธ‰ ๋ฐฐํฌ์—์„œ๋งŒ OMNIROUTE_TRUST_PROXY๋ฅผ ํ™œ์„ฑํ™”ํ•˜์„ธ์š”.

Docker ๋ฐฐํฌ์šฉ ๋Œ€์‹œ๋ณด๋“œ๋Š” Dashboard โ†’ Endpoints์—์„œ ํด๋ฆญ ํ•œ ๋ฒˆ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Cloudflare Quick Tunnel์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ฒ˜์Œ ํ™œ์„ฑํ™”ํ•˜๋ฉด ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ cloudflared๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ , ํ˜„์žฌ /v1 ์—”๋“œํฌ์ธํŠธ๋กœ ์ž„์‹œ ํ„ฐ๋„์„ ์‹œ์ž‘ํ•œ ๋‹ค์Œ, ์ƒ์„ฑ๋œ https://*.trycloudflare.com/v1 URL์„ ์ผ๋ฐ˜ ๊ณต๊ฐœ URL ๋ฐ”๋กœ ์•„๋ž˜์— ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

์—”๋“œํฌ์ธํŠธ ํ„ฐ๋„ ํŒจ๋„(Cloudflare, Tailscale, ngrok)์€ ํ™œ์„ฑ ํ„ฐ๋„ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  Settings โ†’ Appearance์—์„œ ํ‘œ์‹œํ•˜๊ฑฐ๋‚˜ ์ˆจ๊ธธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Quick Tunnel URL์€ ์ž„์‹œ์ด๋ฉฐ ๋‹ค์‹œ ์‹œ์ž‘ํ•  ๋•Œ๋งˆ๋‹ค ๋ณ€๊ฒฝ๋ฉ๋‹ˆ๋‹ค.
  • OmniRoute ๋˜๋Š” ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋‹ค์‹œ ์‹œ์ž‘ํ•œ ํ›„์—๋Š” Quick Tunnel์ด ์ž๋™์œผ๋กœ ๋ณต์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋Œ€์‹œ๋ณด๋“œ์—์„œ ๋‹ค์‹œ ํ™œ์„ฑํ™”ํ•˜์„ธ์š”.
  • ๊ด€๋ฆฌํ˜• ์„ค์น˜๋Š” ํ˜„์žฌ x64 / arm64 ๊ธฐ๋ฐ˜ Linux, macOS ๋ฐ Windows๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ด€๋ฆฌํ˜• Quick Tunnel์€ ์ œ์•ฝ์ด ์žˆ๋Š” ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์—์„œ ๊ณผ๋„ํ•œ QUIC UDP ๋ฒ„ํผ ๊ฒฝ๊ณ ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๊ธฐ๋ณธ์ ์œผ๋กœ HTTP/2 ์ „์†ก์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ „์†ก ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด CLOUDFLARED_PROTOCOL=quic ๋˜๋Š” auto๋กœ ์„ค์ •ํ•˜์„ธ์š”.
  • Docker ์ด๋ฏธ์ง€๋Š” ์‹œ์Šคํ…œ CA ๋ฃจํŠธ๋ฅผ ํฌํ•จํ•˜๊ณ  ์ด๋ฅผ ๊ด€๋ฆฌํ˜• cloudflared์— ์ „๋‹ฌํ•˜๋ฏ€๋กœ, ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ ํ„ฐ๋„์ด ๋ถ€ํŠธ์ŠคํŠธ๋žฉ๋  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” TLS ์‹ ๋ขฐ ์˜ค๋ฅ˜๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • OmniRoute๊ฐ€ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜๋Š” ๋Œ€์‹  ๊ธฐ์กด ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜๋ ค๋ฉด CLOUDFLARED_BIN=/absolute/path/to/cloudflared๋กœ ์„ค์ •ํ•˜์„ธ์š”.
์ด๋ฏธ์ง€ ํƒœ๊ทธ ํฌ๊ธฐ ์„ค๋ช…
diegosouzapw/omniroute latest ~250MB ๊ฐ€์žฅ ๋†’์€ ๊ฒŒ์‹œ๋œ ์•ˆ์ • SemVer(git main ์•„๋‹˜)
diegosouzapw/omniroute 3.8.0 ~250MB GitOps์—์„œ๋Š” ์ด ์œ ํ˜•์˜ ํƒœ๊ทธ๋ฅผ ๊ณ ์ •ํ•˜์—ฌ ์‚ฌ์šฉ

๋‹ค์ค‘ ํ”Œ๋žซํผ ๋งค๋‹ˆํŽ˜์ŠคํŠธ: linux/amd64 + linux/arm64 ๋„ค์ดํ‹ฐ๋ธŒ ์ง€์›(Apple Silicon, AWS Graviton, Raspberry Pi). Docker๋Š” ์ผ์น˜ํ•˜๋Š” ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ž๋™์œผ๋กœ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ARM ํ˜ธ์ŠคํŠธ์—์„œ AMD64 ์—๋ฎฌ๋ ˆ์ด์…˜์„ ๊ฐ•์ œํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ --platform linux/amd64๋ฅผ ์ „๋‹ฌํ•˜์„ธ์š”.

OmniRoute๋Š” ์•ˆ์ • ๋ฆด๋ฆฌ์Šค, ํ™œ์„ฑ ๋ฆด๋ฆฌ์Šค ๋ธŒ๋žœ์น˜ ํ…Œ์ŠคํŠธ, ๊ฐœ๋ฐœ ๋นŒ๋“œ๋ฅผ ์œ„ํ•œ ๋ณ„๋„์˜ Docker ์ฑ„๋„์„ ๊ฒŒ์‹œํ•ฉ๋‹ˆ๋‹ค.

์ฑ„๋„ ์†Œ์Šค ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ๊ถŒ์žฅ ์šฉ๋„
:&lt;version&gt; / :&lt;version&gt;-web ์„œ๋ช…๋˜๊ณ  ๋ฒ„์ „์ด ์ง€์ •๋œ ๋ฆด๋ฆฌ์Šค ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€ ์ •ํ™•ํ•œ ๋ฆด๋ฆฌ์Šค๋ฅผ ๊ณ ์ •ํ•˜๋Š” ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ
:latest / :latest-web ๊ฐ€์žฅ ๋†’์€ ๊ฒŒ์‹œ๋œ ์•ˆ์ • SemVer ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ์•ˆ์ • ํฌ์ธํ„ฐ SemVer ๊ฒŒ์‹œ ์ž‘์—… ์ดํ›„ ์•ˆ์ • ๋ฆด๋ฆฌ์Šค๋ฅผ ์ถ”์  โ€” main ๋˜๋Š” ๋ฆด๋ฆฌ์Šค๋˜์ง€ ์•Š์€ release/v* ์ปค๋ฐ‹์€ ์ถ”์ ํ•˜์ง€ ์•Š์Œ
:next / :next-web ํ˜„์žฌ ๊ธฐ๋ณธ release/v* ๋ธŒ๋žœ์น˜ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ์‹œํ—˜ํŒ ํฌ์ธํ„ฐ ํ™œ์„ฑ ๋ฆด๋ฆฌ์Šค ๋ธŒ๋žœ์น˜์— ๋ฐ˜์˜๋˜์—ˆ์ง€๋งŒ ์•„์ง ์•ˆ์ • ๋ฆด๋ฆฌ์Šค์—๋Š” ํฌํ•จ๋˜์ง€ ์•Š์€ ์ˆ˜์ • ์‚ฌํ•ญ ํ…Œ์ŠคํŠธ
:main / :main-web main ๋ธŒ๋žœ์น˜ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ๊ฐœ๋ฐœ ํฌ์ธํ„ฐ ๊ฐœ๋ฐœ ๋ฐ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ „์šฉ

์œ„์˜ ๋ชจ๋“  ์ฑ„๋„์€ -web ํƒœ๊ทธ(:latest-web, :&lt;version&gt;-web, :next-web, :main-web)๋„ ์ œ๊ณตํ•˜๋ฉฐ, ๋™์ผํ•œ ์ด๋ฏธ์ง€์— Playwright์™€ Chromium ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ถ”๊ฐ€๋œ runner-web ์Šคํ…Œ์ด์ง€์—์„œ ๋นŒ๋“œ๋ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜ ์ด๋ฏธ์ง€๋Š” Chromium ์—†์ด ์ œ๊ณต๋˜๋ฉฐ, gemini-web, claude-web, claude-turnstile์—๋Š” Chromium์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์˜ค๋ฅ˜๋Š” ์‹œ์ž‘ ์‹œ์ ์ด ์•„๋‹ˆ๋ผ ๋‚˜์ค‘์— ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ œ๊ณต์ž๋Š” ๋ชจ๋ธ ๋ชฉ๋ก์„ ํ‘œ์‹œํ•˜๊ณ  ๋Œ€์‹œ๋ณด๋“œ์—์„œ ์—ฐ๊ฒฐ๋œ ๊ฒƒ์œผ๋กœ ๋‚˜ํƒ€๋‚˜์ง€๋งŒ, ์ฒซ ๋ฒˆ์งธ ์š”์ฒญ์—์„œ๋งŒ ๋‹ค์Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

[500]: Failed to load external module playwright: Error: Cannot find module
'/app/node_modules/playwright/node_modules/playwright-core/browsers.json'

์ด๋Ÿฌํ•œ ์ œ๊ณต์ž๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ํ˜„์žฌ ์‚ฌ์šฉ ์ค‘์ธ ์ฑ„๋„์˜ -web ํƒœ๊ทธ๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๊ทธ ์™ธ์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. npm/CLI ์„ค์น˜(Docker ์ด๋ฏธ์ง€ ๋ฏธ์‚ฌ์šฉ)์—์„œ ์ด์— ํ•ด๋‹นํ•˜๋Š” ๋ˆ„๋ฝ ํ•ญ๋ชฉ์€ ๋ธŒ๋ผ์šฐ์ € ๋ฐ”์ด๋„ˆ๋ฆฌ์ž…๋‹ˆ๋‹ค. ํ˜ธ์ŠคํŠธ์—์„œ npx playwright install chromium์„ ์‹คํ–‰ํ•˜์„ธ์š”.

next ์ฑ„๋„์€ ํ˜„์žฌ ๊ธฐ๋ณธ release/v* ๋ธŒ๋žœ์น˜์— ํ‘ธ์‹œํ•  ๋•Œ๋งˆ๋‹ค ๋‹ค์‹œ ๋นŒ๋“œ๋˜๋ฉฐ AMD64์™€ ARM64์šฉ์œผ๋กœ ๋ชจ๋‘ ๊ฒŒ์‹œ๋ฉ๋‹ˆ๋‹ค. ์ด์ „ ์œ ์ง€๋ณด์ˆ˜ ๋ธŒ๋žœ์น˜๋Š” ์ด ์ฑ„๋„์„ ๋ฎ์–ด์“ธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด ์ฑ„๋„์€ ๋‹ค์Œ ์•ˆ์ • ํƒœ๊ทธ๊ฐ€ ์ƒ์„ฑ๋˜๊ธฐ ์ „์— ํ™œ์„ฑ ๋ฆด๋ฆฌ์Šค ๋ธŒ๋žœ์น˜์— ๋ณ‘ํ•ฉ๋œ ์ˆ˜์ • ์‚ฌํ•ญ์„ ํฌํ•จํ•˜๋Š” ๊ฐ€์ ธ์˜ค๊ธฐ ๊ฐ€๋Šฅํ•œ ์ด๋ฏธ์ง€๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

ํ„ฐ๋ฏธ๋„ ์ฐฝ
docker pull diegosouzapw/omniroute:next
docker pull diegosouzapw/omniroute:next-web

Docker Compose์—์„œ๋Š” ์„ ํƒํ•œ ํ”„๋กœํ•„์ด ์‚ฌ์šฉํ•˜๋Š” ์ด๋ฏธ์ง€ ํƒœ๊ทธ๋ฅผ ์žฌ์ •์˜ํ•œ ๋‹ค์Œ, ์„œ๋น„์Šค๋ฅผ ๊ฐ€์ ธ์™€ ๋‹ค์‹œ ์ƒ์„ฑํ•˜์„ธ์š”.

services:
omniroute:
image: diegosouzapw/omniroute:next
ํ„ฐ๋ฏธ๋„ ์ฐฝ
docker compose pull
docker compose up -d

next๋Š” ์œ ๋™์ ์ธ ์‹œํ—˜ํŒ ์ฑ„๋„์ž…๋‹ˆ๋‹ค. ํ™œ์„ฑ ๋ฆด๋ฆฌ์Šค ๋ธŒ๋žœ์น˜์— ํ‘ธ์‹œํ•  ๋•Œ๋งˆ๋‹ค ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ ํ”„๋กœ๋•์…˜ ์šฉ๋„๋กœ๋Š” ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํŠน์ • ๋นŒ๋“œ๋ฅผ ํ‰๊ฐ€ํ•˜๋Š” ๋™์•ˆ ์ด๋ฏธ์ง€ ๋‹ค์ด์ œ์ŠคํŠธ๋ฅผ ๊ณ ์ •ํ•˜์„ธ์š”.

ํ„ฐ๋ฏธ๋„ ์ฐฝ
docker pull diegosouzapw/omniroute:next
docker image inspect diegosouzapw/omniroute:next --format '{{index .RepoDigests 0}}'

ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์ „์— OmniRoute ๋ฐ์ดํ„ฐ ๋ณผ๋ฅจ ๋˜๋Š” ๋ฐ”์ธ๋“œ ๋งˆ์šดํŠธ๋œ ๋ฐ์ดํ„ฐ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋ฐฑ์—…ํ•˜์„ธ์š”. ๋กค๋ฐฑํ•˜๋ ค๋ฉด ์ด์ „์— ์‚ฌ์šฉํ•œ ์•ˆ์ • ๋ฒ„์ „ ๋˜๋Š” ๋‹ค์ด์ œ์ŠคํŠธ๋ฅผ ๋ณต์›ํ•˜๊ณ  ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋‹ค์‹œ ์ƒ์„ฑํ•˜์„ธ์š”.

ํ„ฐ๋ฏธ๋„ ์ฐฝ
docker pull diegosouzapw/omniroute:&lt;stable-version&gt;
docker compose up -d

๋ฆด๋ฆฌ์Šค ๋ธŒ๋žœ์น˜ ๋นŒ๋“œ๋Š” ์ ˆ๋Œ€๋กœ latest๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ ๊ฒฉํ•œ ์•ˆ์ • ์‹œ๋งจํ‹ฑ ๋ฒ„์ „๋งŒ ์•ˆ์ • ํฌ์ธํ„ฐ๋ฅผ ์Šน๊ฒฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. next ์ด๋ฏธ์ง€๋Š” ๋ฆด๋ฆฌ์Šค ์ด๋ฏธ์ง€ ๊ฒ€์‚ฌ์™€ ์ฐจ๋‹จ ๋ฐฉ์‹์˜ CRITICAL ์ทจ์•ฝ์  ๊ฒŒ์ดํŠธ๋ฅผ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

latest๋Š” git์˜ ์ตœ์‹  ์ƒํƒœ๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. main ๋˜๋Š” ํ™œ์„ฑ release/v* ๋ธŒ๋žœ์น˜์— ๋ณ‘ํ•ฉ๋œ ์ˆ˜์ • ์‚ฌํ•ญ์€ ์•ˆ์ • SemVer ์ด๋ฏธ์ง€๊ฐ€ ๊ฒŒ์‹œ๋˜๊ณ  ๊ฒŒ์‹œ ์ž‘์—…์ด :latest๋ฅผ ์Šน๊ฒฉํ•˜๊ธฐ ์ „๊นŒ์ง€ :latest์— ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(ํ•ด๋‹น SemVer์™€ ๋™์ผํ•œ ๋‹ค์ด์ œ์ŠคํŠธ). GitHub์— ์ˆ˜์ • ์‚ฌํ•ญ์ด ์ด๋ฏธ ํ‘œ์‹œ๋˜๋Š”๋ฐ๋„ latest๊ฐ€ ๊ทธ๋Œ€๋กœ์ธ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ธ๋‹ค๋ฉด, :next๋ฅผ ๊ฐ€์ ธ์™€ ๋ฆด๋ฆฌ์Šค ๋ธŒ๋žœ์น˜๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ฑฐ๋‚˜ SemVer ํƒœ๊ทธ๊ฐ€ ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ์„ธ์š”.

์›ํ•˜๋Š” ์‚ฌํ•ญ ์‚ฌ์šฉํ•  ํ•ญ๋ชฉ
๋ณ€๊ฒฝ๋˜์–ด์„œ๋Š” ์•ˆ ๋˜๋Š” GitOps / ํ”„๋กœ๋•์…˜ :X.Y.Z(๋˜๋Š” ์ด๋ฏธ์ง€ ๋‹ค์ด์ œ์ŠคํŠธ) ๊ณ ์ •
๊ฒŒ์‹œ๋œ ์•ˆ์ • ๋ฆด๋ฆฌ์Šค๋ฅผ ์ถ”์ ํ•˜๊ณ  ๋ฆด๋ฆฌ์Šค๋งˆ๋‹ค ์žฌ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์„ ํ—ˆ์šฉ :latest
๋ฆด๋ฆฌ์Šค๋˜์ง€ ์•Š์€ release/v* ์ปค๋ฐ‹ ํ…Œ์ŠคํŠธ :next(ํ”„๋กœ๋•์…˜์šฉ ์•„๋‹˜)
main ํ…Œ์ŠคํŠธ :main(ํ”„๋กœ๋•์…˜์šฉ ์•„๋‹˜)

๊ฐ€์šฉ์„ฑ: ๊ธฐ๋ณธ SQLite๋Š” ๋‹จ์ผ ๋ ˆํ”Œ๋ฆฌ์นด ๊ตฌ์„ฑ

์„น์…˜ ์ œ๋ชฉ: โ€œ๊ฐ€์šฉ์„ฑ: ๊ธฐ๋ณธ SQLite๋Š” ๋‹จ์ผ ๋ ˆํ”Œ๋ฆฌ์นด ๊ตฌ์„ฑโ€

๊ธฐ๋ณธ Docker / Kubernetes OmniRoute๋Š” ํ•˜๋‚˜์˜ Node ํ”„๋กœ์„ธ์Šค + ํ•˜๋‚˜์˜ SQLite writer๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. ์ด ํ† ํด๋กœ์ง€์—์„œ๋Š” ๊ณ ๊ฐ€์šฉ์„ฑ์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ œ์•ฝ ์‚ฌํ•ญ ๊ฒฐ๊ณผ
๋‹จ์ผ writer ๋™์ผํ•œ SQLite ํŒŒ์ผ์— ๋Œ€ํ•ด ์—ฌ๋Ÿฌ ๋ ˆํ”Œ๋ฆฌ์นด๋ฅผ ์‹คํ–‰ํ•˜์ง€ ๋งˆ์„ธ์š”. DB๊ฐ€ ์†์ƒ๋ฉ๋‹ˆ๋‹ค.
์žฌ์ƒ์„ฑ / ์žฌ์‹œ์ž‘ / HEALTHCHECK ์ข…๋ฃŒ ์ง„ํ–‰ ์ค‘์ธ SSE, ๋Œ€์‹œ๋ณด๋“œ ์„ธ์…˜ ๋ฐ ์ธ๋ฉ”๋ชจ๋ฆฌ ์ƒํƒœ๊ฐ€ ์™„์ „ํžˆ ์ค‘๋‹จ๋ฉ๋‹ˆ๋‹ค. ์—ฐ๊ฒฐ๋œ ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ์˜ ์—ฐ๊ฒฐ์ด ๋Š์–ด์ง‘๋‹ˆ๋‹ค. ์—”๋“œํฌ์ธํŠธ๊ฐ€ ๋น„์–ด ์žˆ๋Š” ๋™์•ˆ์˜ ์ƒˆ ์š”์ฒญ์€ OmniRoute JSON์ด ์•„๋‹Œ ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ์˜ **502 Bad Gateway: Unknown error**๋ฅผ ๋ฐ›์œผ๋ฏ€๋กœ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ด๋ฅผ ์ œ๊ณต์ž ์žฅ์• ์™€ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค(#11015).
/healthz์™€ ๋™์ผํ•œ ์ด๋ฒคํŠธ ๋ฃจํ”„ ์‚ฌ์šฉ๋Ÿ‰์ด ๋งŽ์€ catalog ๋˜๋Š” compression tick์œผ๋กœ ์ธํ•ด probe๊ฐ€ ์ง€์—ฐ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, timeout์ด ์งง์œผ๋ฉด ์œ ์ผํ•œ ๋ ˆํ”Œ๋ฆฌ์นด๊ฐ€ ์žฌ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.

Probe ๋งคํŠธ๋ฆญ์Šค(Kubernetes probe ๊ถŒ์žฅ ์‚ฌํ•ญ๋„ ์ฐธ์กฐ):

Probe ๋Œ€์ƒ ์‚ฌ์šฉํ•˜์ง€ ๋ง์•„์•ผ ํ•  ํ•ญ๋ชฉ
Liveness PORT(๊ธฐ๋ณธ๊ฐ’ 20128)์— ๋Œ€ํ•œ TCP ๋˜๋Š” ์™„ํ™”๋œ HTTP /healthz /api/monitoring/health
Readiness HTTP GET /healthz ์ด๋ฒคํŠธ ๋ฃจํ”„๊ฐ€ ์‚ฌ์šฉ ์ค‘์ธ ์ƒํƒœ๋ฅผ ์žฅ์• ๋กœ ์ทจ๊ธ‰ํ•˜๋Š” ์งง์€ timeout
์‹ฌ์ธต ์ ๊ฒ€ / ์‚ฌ์šฉ์ž /api/monitoring/health ์ž๋™ํ™”๋œ kubelet liveness

์—…๊ทธ๋ ˆ์ด๋“œ: ๋ชจ๋“  ์„ธ์…˜์˜ ์—ฐ๊ฒฐ์ด ๋Š์–ด์งˆ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€๋Šฅํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ๋ฅผ drainํ•˜์„ธ์š”. ๊ธฐ๋ณธ SQLite์—์„œ๋Š” rolling update๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. Compose์˜ restart: unless-stopped์™€ Docker HEALTHCHECK๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ์ปจํ…Œ์ด๋„ˆ๊ฐ€ Unhealthy ์ƒํƒœ์ผ ๋•Œ ์œ ์ผํ•œ ํ”„๋กœ์„ธ์Šค๋„ ๊ต์ฒด๋˜๋ฏ€๋กœ ์˜ํ–ฅ ๋ฒ”์œ„๊ฐ€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

๋‹จ์ผ ๋ ˆํ”Œ๋ฆฌ์นด์šฉ Kubernetes ์˜ˆ์‹œ(SQLite ํŒŒ์ผ ํ•˜๋‚˜์— ๋Œ€ํ•ด replicas๋ฅผ ๋Š˜๋ฆฌ์ง€ ๋งˆ์„ธ์š”. Recreate๊ฐ€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค):

spec:
replicas: 1
strategy:
type: Recreate
template:
spec:
terminationGracePeriodSeconds: 90
containers:
- name: omniroute
lifecycle:
preStop:
exec:
command: ["/bin/sleep", "15"]
readinessProbe:
httpGet:
path: /healthz
port: 20128
periodSeconds: 5
livenessProbe:
tcpSocket:
port: 20128
periodSeconds: 20

preStop sleep์„ ์‚ฌ์šฉํ•˜๋ฉด SIGTERM ์ „์— kube๊ฐ€ Service ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ƒˆ๋กœ์šด ํŠธ๋ž˜ํ”ฝ์ด ์ข…๋ฃŒ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค๋กœ ์ „๋‹ฌ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ง„ํ–‰ ์ค‘์ธ /v1/responses SSE๋Š” heavyweight admission lease๋ฅผ ํ†ตํ•ด SHUTDOWN_TIMEOUT_MS(๊ธฐ๋ณธ๊ฐ’ 30์ดˆ)๊นŒ์ง€ drain๋ฉ๋‹ˆ๋‹ค(#11015). ์—ฌ์ „ํžˆ ํ”„๋กœ์„ธ์Šค์— ๋„๋‹ฌํ•˜๋Š” ์ƒˆ ์š”์ฒญ์€ 503 + Retry-After: 5๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค. ๋Œ€์ฒด ํ”„๋กœ์„ธ์Šค๊ฐ€ Ready ์ƒํƒœ๊ฐ€ ๋  ๋•Œ๊นŒ์ง€ ๋ฐœ์ƒํ•˜๋Š” Recreate์˜ ๋นˆ ์—”๋“œํฌ์ธํŠธ ๊ตฌ๊ฐ„์€ ์—ฌ์ „ํžˆ ์™„์ „ํ•œ ์„œ๋น„์Šค ์ค‘๋‹จ์ž…๋‹ˆ๋‹ค. ์ด๋Š” probe ์„ค์ • ์˜ค๋ฅ˜๊ฐ€ ์•„๋‹ˆ๋ผ SQLite ํ† ํด๋กœ์ง€์˜ ํŠน์„ฑ์ž…๋‹ˆ๋‹ค.

์™ธ๋ถ€ Postgres / multi-writer HA๋Š” ๋ฌธ์„œํ™”๋œ ๊ธฐ๋ณธ ์ง€์› ๊ฒฝ๋กœ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. HA๊ฐ€ ํ•„์š”ํ•˜๋‹ค๋ฉด ๋‹จ์ผ ๋ ˆํ”Œ๋ฆฌ์นด๋ฅผ ์œ ์ง€ํ•˜๊ฑฐ๋‚˜ ํ”„๋กœ์ ํŠธ์—์„œ ๋ณ„๋„๋กœ ํ…Œ์ŠคํŠธํ•˜๊ณ  ๋ฌธ์„œํ™”ํ•œ ํ† ํด๋กœ์ง€๋ฅผ ์‚ฌ์šฉํ•˜์„ธ์š”. Postgres/MySQL ๊ด€๋ จ ์ž‘์—…์€ #8075์—์„œ ์ง„ํ–‰๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ธฐ๋Šฅ์ด ์ถœ์‹œ๋˜๊ธฐ ์ „๊นŒ์ง€ ๋Œ€๊ทœ๋ชจ /v1/responses ์šฉ๋Ÿ‰์„ ํ™•์žฅํ•˜๋Š” ์œ ์ผํ•˜๊ฒŒ ์ง€์›๋˜๋Š” ๋ฐฉ๋ฒ•์€ ํ•˜๋‚˜์˜ ๋ณผ๋ฅจ์—์„œ replicas > 1์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ N๊ฐœ์˜ ๋…๋ฆฝ ํ”„๋กœ์„ธ์Šค๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค(๋‹ค์Œ ์„น์…˜ ์ฐธ์กฐ).

์Šค์ผ€์ผ ์•„์›ƒ: N๊ฐœ์˜ ๋…๋ฆฝ ํ”„๋กœ์„ธ์Šค

์„น์…˜ ์ œ๋ชฉ: โ€œ์Šค์ผ€์ผ ์•„์›ƒ: N๊ฐœ์˜ ๋…๋ฆฝ ํ”„๋กœ์„ธ์Šคโ€

ํ•˜๋‚˜์˜ Node ํ”„๋กœ์„ธ์Šค๋Š” ํ•˜๋‚˜์˜ V8 ํž™์ž…๋‹ˆ๋‹ค. ์„œ๋กœ ๊ฒน์น˜๋Š” ์•ฝ 3โ€ฏMiB / ์•ฝ 750k-token ๊ทœ๋ชจ์˜ ์ฝ”๋”ฉ ์—์ด์ „ํŠธ POST /v1/responses ์š”์ฒญ ๋‘ ๊ฐœ(RTK + Caveman)๋Š” ์•ฝ 12โ€ฏGi์—์„œ ํ•ด๋‹น ํž™์„ ์ค‘๋‹จ์‹œํ‚ค๋ฉฐ(FATAL ERROR: Reached heap limit), 16โ€ฏGi cgroup์—์„œ OOM์„ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. #7849๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”. ์ด ์ธก์ •๊ฐ’์€ ๋ฉ”๋ชจ๋ฆฌ ์˜ˆ์‚ฐ์— ๋Œ€ํ•œ ๊ฒฝ๊ณ ์ด์ง€, ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๊ธด /v1/responses ์š”์ฒญ์„ ๋‘ ๊ฐœ๋กœ ์ œํ•œํ•˜๋Š” ์ œํ’ˆ์˜ ํ•˜๋“œ ์ตœ๋Œ€๊ฐ’์ด ์•„๋‹™๋‹ˆ๋‹ค. ๊ณ ๋ถ€ํ•˜ ์ฑ„ํŒ…์˜ ์ˆ˜๋ฝ ์—ฌ๋ถ€๋Š” ๋™์ผํ•œ V8/cgroup ์ƒํ•œ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž๋™ ์‚ฐ์ถœ๋˜๋Š” ์ˆ˜์‹  ๋ฐ”์ดํŠธ ์˜ˆ์‚ฐ(OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES, src/shared/middleware/admissionBudget.ts)์— ์˜ํ•ด ์ œ์–ด๋ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ํฌ๊ธฐ๊ฐ€ ์กฐ์ •๋œ ํ”„๋กœ์„ธ์Šค์—์„œ ์ด๋ฅผ ๋” ๋†’์€ ๊ฐ’์œผ๋กœ ์žฌ์ •์˜ํ•˜๊ฑฐ๋‚˜ ๋ ˆ๊ฑฐ์‹œ ์š”์ฒญ ์ˆ˜ ์ œํ•œ์ธ OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT๋ฅผ ์„ค์ •ํ•˜๋ฉด ์ค‘๋‹จ ๋ฌธ์ œ๊ฐ€ ๋‹ค์‹œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ž‘์€ ์ฑ„ํŒ…, /healthz, /v1/models, MCP๋Š” ์ด ์ œํ•œ์— ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‹จ์ผ ํ”„๋กœ์„ธ์Šค: ๋‘ ๊ฐœ๋ฅผ ์ดˆ๊ณผํ•˜๋Š” ๊ธด /v1/responses

์„น์…˜ ์ œ๋ชฉ: โ€œ๋‹จ์ผ ํ”„๋กœ์„ธ์Šค: ๋‘ ๊ฐœ๋ฅผ ์ดˆ๊ณผํ•˜๋Š” ๊ธด /v1/responsesโ€

์ •์ƒ ์ƒํƒœ์ธ ํ”„๋กœ์„ธ์Šค(ํž™์ด OMNIROUTE_CHAT_ADMISSION_HEAP_SHED_RATIO, ๊ธฐ๋ณธ๊ฐ’ 0.75 ๋ฏธ๋งŒ)๋Š” ํ”„๋กœ์„ธ์Šค ์ „์ฒด์˜ ์ฒ˜๋ฆฌ ์ค‘ ๋ฐ”์ดํŠธ ์˜ˆ์‚ฐ(OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES / #10110)์— ์—ฌ์œ ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ, ๋™์‹œ์— ๋‘ ๊ฐœ๋ฅผ ์ดˆ๊ณผํ•˜๋Š” ๊ธด POST /v1/responses ์š”์ฒญ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. OMNIROUTE_CHAT_LARGE_BODY_BYTES ์ด์ƒ(๊ธฐ๋ณธ๊ฐ’ 256โ€ฏKiB)์ธ ๋ณธ๋ฌธ์€ ๊ตฌ์กฐ๊ฐ€ ๋ณต์žกํ•œ ์š”์ฒญ๊ณผ ๋™์ผํ•œ ๊ณ ๋ถ€ํ•˜ ๋ฆฌ์Šค๋ฅผ ํš๋“ํ•˜๋ฉฐ, ๋™์ผํ•œ #10437 tryAcquireHealthyHeadroom ์šฐํšŒ ๊ฒฝ๋กœ(OMNIROUTE_CHAT_ADMISSION_HEALTHY_HEADROOM)๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋™์‹œ์— ์ˆ˜์‹ญ ๊ฐœ์˜ ๊ธด SSE ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ(์šด์˜์ž๋Š” ๋Œ€๊ฐœ 40โ€“50๊ฐœ๊ฐ€ ํ•„์š”ํ•จ)์€ ๋ฉ”๋ชจ๋ฆฌ ์˜ˆ์‚ฐ์˜ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ํž™ + ๊ธฐ๋ณธ/ํ—ค๋“œ๋ฃธ ์Šฌ๋กฏ + OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES์˜ ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•ด์•ผ ํ•˜๋ฉฐ, ์ œํ’ˆ์— ํ•˜๋“œ์ฝ”๋”ฉ๋œ โ€œ์ตœ๋Œ€ 2๊ฐœโ€ ์ œํ•œ์˜ ๋ฌธ์ œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์••๋ฐ•์„ ๋ฐ›๋Š” ํž™์€ ์žฌ์‹œ๋„ ๊ฐ€๋Šฅํ•œ 503์œผ๋กœ ๊ณ„์† ์š”์ฒญ์„ ์ฐจ๋‹จํ•˜๋ฏ€๋กœ #7849 ๋ฌธ์ œ๊ฐ€ ์žฌ๋ฐœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํž™ ์ˆ˜๋ฅผ ๋Š˜๋ฆฌ๋ ค๋ฉด(๋…๋ฆฝ์ ์ธ V8 old-space) ํ˜„์žฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•˜์„ธ์š”.

ํ•ด์•ผ ํ•  ๊ฒƒ ํ•˜์ง€ ๋ง์•„์•ผ ํ•  ๊ฒƒ
๊ฐ๊ฐ ์ž์ฒด DATA_DIR / ๋ณผ๋ฅจ์„ ์‚ฌ์šฉํ•˜๋Š” N๊ฐœ์˜ ์ปจํ…Œ์ด๋„ˆ/ํŒŒ๋“œ ์‹คํ–‰ ํ•˜๋‚˜์˜ SQLite ํŒŒ์ผ์„ ๋Œ€์ƒ์œผ๋กœ replicas > 1 ์„ค์ •
ํž™ / ์ฒ˜๋ฆฌ ์ค‘ ๋ฐ”์ดํŠธ ์˜ˆ์‚ฐ์„ ๊ธฐ์ค€์œผ๋กœ ๊ณ ๋ถ€ํ•˜ ์ฒ˜๋ฆฌ ์ค‘ ์š”์ฒญ ์ˆ˜ + ์ •์ƒ ์ƒํƒœ ํ—ค๋“œ๋ฃธ ํฌ๊ธฐ ์กฐ์ •. 1โ€“2๊ฐœ๋Š” ๋ณด์ˆ˜์ ์ธ #7849 ๊ธฐ๋ณธ๊ฐ’์ผ ๋ฟ, ์ œํ’ˆ์˜ ํ•˜๋“œ ์ตœ๋Œ€๊ฐ’์ด ์•„๋‹˜ ํ”„๋กœ์„ธ์Šค ํ•˜๋‚˜์— 8๋ฐฐ์˜ RAM๊ณผ ๋ฌด์ œํ•œ ์š”์ฒญ ์ˆ˜ ์ƒํ•œ ํ• ๋‹น
์„ ํƒ ์‚ฌํ•ญ: ๊ณต์œ  ํ• ๋‹น๋Ÿ‰ ์นด์šดํ„ฐ๋ฅผ ์œ„ํ•ด QUOTA_STORE_DRIVER=redis + QUOTA_STORE_REDIS_URL ์‚ฌ์šฉ Redis๋ฅผ ๊ณต์œ  SQLite๋กœ ์ทจ๊ธ‰ โ€” Redis๋Š” ๊ณต์œ  SQLite๊ฐ€ ์•„๋‹˜
๊ฐ ์ธ์Šคํ„ด์Šค์— ๊ณต๊ธ‰์ž ์‹œํฌ๋ฆฟ ๋ณต์ œ(๋˜๋Š” ๋ถ„๋ฆฌ๋œ ๋Œ€์‹œ๋ณด๋“œ ์ˆ˜์šฉ) ์ธ์Šคํ„ด์Šค ์ „์ฒด์— ๊ฑธ์ณ ํ•˜๋‚˜์˜ ๋Œ€์‹œ๋ณด๋“œ / ํ•˜๋‚˜์˜ ํ˜ธ์ถœ ๋กœ๊ทธ ๊ธฐ๋Œ€
์ž„์˜์˜ ๋กœ๋“œ ๋ฐธ๋Ÿฐ์„œ๋ฅผ ์•ž๋‹จ์— ๋ฐฐ์น˜. API ํ‚ค ๋˜๋Š” ์„ธ์…˜ ๊ธฐ์ค€ ์Šคํ‹ฐํ‚ค ๋ผ์šฐํŒ…์ด๋ฉด ์ถฉ๋ถ„ ๊ณต๊ธ‰์—…์ฒด๋ณ„ ํฌ๊ธฐ ์ธ์‹ ๋ฏธ๋“ค์›จ์–ด๊ฐ€ ํ•„์š”ํ•˜๋‹ค๊ณ  ๊ฐ„์ฃผ

ํ•˜๋“œ์›จ์–ด ์ธก๋ฉด์—์„œ ์ธ์Šคํ„ด์Šค๋‹น ๋™์‹œ์— ์‹คํ–‰๋˜๋Š” ๊ธด /v1/responses ์š”์ฒญ ์ˆ˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ์˜ˆ์‚ฐ์˜ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค(ํž™ + ์ฒ˜๋ฆฌ ์ค‘ ๋ฐ”์ดํŠธ / #10110). ๋…๋ฆฝ์ ์ธ DATA_DIR N๊ฐœ๋Š” ์—ฌ์ „ํžˆ ํž™ ์ˆ˜๋ฅผ ๋Š˜๋ฆฝ๋‹ˆ๋‹ค. ํ˜ธ์ŠคํŠธ RAM์€ โ€œN=8์ธ ํ•˜๋‚˜์˜ 16โ€ฏGi ํŒŒ๋“œโ€๊ฐ€ ์•„๋‹ˆ๋ผ N ร— cgroup์„ ๊ฐ๋‹นํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜์˜ SQLite ํŒŒ์ผ์—์„œ ์ ˆ๋Œ€๋กœ replicas > 1์„ ์‚ฌ์šฉํ•˜์ง€ ๋งˆ์„ธ์š”.

Compose ์˜ˆ์‹œ(ํž™ ๋‘ ๊ฐœ, ๋ณผ๋ฅจ ๋‘ ๊ฐœ โ€” deploy.replicas: 2๊ฐ€ ์•„๋‹˜):

services:
omniroute-a:
image: diegosouzapw/omniroute:3.8.49
environment:
DATA_DIR: /app/data
OMNIROUTE_MEMORY_MB: "12288"
QUOTA_STORE_DRIVER: redis
QUOTA_STORE_REDIS_URL: redis://redis:6379
volumes: [omniroute-a-data:/app/data]
ports: ["20128:20128"]
omniroute-b:
image: diegosouzapw/omniroute:3.8.49
environment:
DATA_DIR: /app/data
OMNIROUTE_MEMORY_MB: "12288"
QUOTA_STORE_DRIVER: redis
QUOTA_STORE_REDIS_URL: redis://redis:6379
volumes: [omniroute-b-data:/app/data]
ports: ["20138:20128"]
volumes:
omniroute-a-data:
omniroute-b-data:

ํ”„๋กœ์„ธ์Šค ๋‚ด ๋ฐ€๋„ ํ–ฅ์ƒ(HTTP isolate ์™ธ๋ถ€์—์„œ์˜ ์••์ถ•)์€ #11023์—์„œ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ๊ณต์œ  ์˜๊ตฌ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ•˜๋‚˜์˜ ๋…ผ๋ฆฌ์  ํด๋Ÿฌ์Šคํ„ฐ๋Š” #8075์—์„œ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

  • SQLite WAL ๋ชจ๋“œ: OmniRoute๊ฐ€ ์ตœ์‹  ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ storage.sqlite์— ์ฒดํฌํฌ์ธํŠธ๋กœ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ๋„๋ก docker stop์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฒˆ๋“ค๋กœ ์ œ๊ณต๋˜๋Š” Compose ํŒŒ์ผ์—๋Š” ์ด๋ฏธ 40์ดˆ์˜ ์ข…๋ฃŒ ์œ ์˜ˆ ๊ธฐ๊ฐ„์ด ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€๋ฅผ ์ง์ ‘ ์‹คํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ --stop-timeout 40์„ ์œ ์ง€ํ•˜์„ธ์š”.
  • DISABLE_SQLITE_AUTO_BACKUP: ์ •๊ธฐ/์“ฐ๊ธฐ ์ „ ๋ฐฑ์—…์„ ์™ธ๋ถ€์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ true๋กœ ์„ค์ •ํ•˜์„ธ์š”. ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์—๋Š” ์—ฌ์ „ํžˆ ๋ณ„๋„์˜ ๋‚ด๊ตฌ์„ฑ ์žˆ๋Š” ์•ˆ์ „ ์Šค๋ƒ…์ƒท๊ณผ ๋Œ€๊ทœ๋ชจ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ณดํ˜ธ ์žฅ์น˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ์ดํ„ฐ ์˜์†์„ฑ: ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋‹ค์‹œ ์‹œ์ž‘๋˜์–ด๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ํ‚ค ๋ฐ ๊ตฌ์„ฑ์„ ์œ ์ง€ํ•˜๋ ค๋ฉด ํ•ญ์ƒ /app/data์— ๋ณผ๋ฅจ์„ ๋งˆ์šดํŠธํ•˜์„ธ์š”.
  • ํฌํŠธ ๊ตฌ์„ฑ: ๊ธฐ๋ณธ ํฌํŠธ 20128์„ ๋ณ€๊ฒฝํ•˜๋ ค๋ฉด PORT ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์žฌ์ •์˜ํ•˜์„ธ์š”.

OmniRoute ์†Œ์Šค ์ฝ”๋“œ (a58000c7685f)

HagiCode

HagiCode๋Š” ๊ตฌ์กฐํ™”๋œ ์›Œํฌํ”Œ๋กœ, ๋‹ค์ค‘ ์—์ด์ „ํŠธ ์‹คํ–‰, Hero Dungeon ๋ทฐ๋ฅผ ๊ฐ–์ถ˜ ์—์ด์ „ํŠธ ์ฝ”๋”ฉ ์ž‘์—… ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค.

๋” ์Šค๋งˆํŠธํ•˜๊ณ  ๋น ๋ฅด๋ฉฐ ์ฆ๊ฑฐ์šด ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ๋กœ ์œ ์šฉํ•œ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ๋งŒ๋“œ์„ธ์š”.

HagiCode ๋ผ์ดํŠธ ํ…Œ๋งˆ ๋ฉ”์ธ ํ™”๋ฉด
  • Smart๊ตฌ์กฐํ™”๋œ ์›Œํฌํ”Œ๋กœ๋Š” ์˜๋„๋ฅผ ์•„์ด๋””์–ด๋ถ€ํ„ฐ ๋ฐฐํฌ๊นŒ์ง€ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ๊ฒฝ๋กœ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.
  • Efficient๋‹ค์ค‘ ์—์ด์ „ํŠธ ์›Œํฌํ”Œ๋กœ๋กœ ์กฐ์‚ฌ, ๊ตฌํ˜„, ๊ฒ€ํ† ๋ฅผ ๋ณ‘๋ ฌ๋กœ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  • FunHero Dungeon์€ ๊ธด ์ฝ”๋”ฉ ์„ธ์…˜์„ ์‹œ๊ฐ์ ์ด๊ณ  ํ˜‘์—…์ ์ธ ๊ฒฝํ—˜์œผ๋กœ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
HagiCode ๋ฐฉ๋ฌธ