# ๐Ÿณ Docker Guide โ€” OmniRoute (ํ•œ๊ตญ์–ด) ๐ŸŒ **Languages:** ๐Ÿ‡บ๐Ÿ‡ธ [English](../../../../guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ช๐Ÿ‡น [am](../../../am/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ธ๐Ÿ‡ฆ [ar](../../../ar/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฆ๐Ÿ‡ฟ [az](../../../az/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ง๐Ÿ‡ฌ [bg](../../../bg/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ง๐Ÿ‡ฉ [bn](../../../bn/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ง๐Ÿ‡ฆ [bs](../../../bs/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡จ๐Ÿ‡ฟ [cs](../../../cs/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฉ๐Ÿ‡ฐ [da](../../../da/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฉ๐Ÿ‡ช [de](../../../de/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฌ๐Ÿ‡ท [el](../../../el/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ช๐Ÿ‡ธ [es](../../../es/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ช๐Ÿ‡ช [et](../../../et/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ท [fa](../../../fa/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ซ๐Ÿ‡ฎ [fi](../../../fi/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ซ๐Ÿ‡ท [fr](../../../fr/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ช [ga](../../../ga/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [gu](../../../gu/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ณ๐Ÿ‡ฌ [ha](../../../ha/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ฑ [he](../../../he/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [hi](../../../hi/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ญ๐Ÿ‡ท [hr](../../../hr/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ญ๐Ÿ‡บ [hu](../../../hu/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฆ๐Ÿ‡ฒ [hy](../../../hy/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ฉ [id](../../../id/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ณ๐Ÿ‡ฌ [ig](../../../ig/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡น [it](../../../it/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฏ๐Ÿ‡ต [ja](../../../ja/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฌ๐Ÿ‡ช [ka](../../../ka/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฐ๐Ÿ‡ญ [km](../../../km/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [kn](../../../kn/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฑ๐Ÿ‡น [lt](../../../lt/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฑ๐Ÿ‡ป [lv](../../../lv/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [ml](../../../ml/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [mr](../../../mr/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฒ๐Ÿ‡พ [ms](../../../ms/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฒ๐Ÿ‡น [mt](../../../mt/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฒ๐Ÿ‡ฒ [my](../../../my/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ณ๐Ÿ‡ต [ne](../../../ne/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ณ๐Ÿ‡ฑ [nl](../../../nl/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ณ๐Ÿ‡ด [no](../../../no/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [or](../../../or/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [pa](../../../pa/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ต๐Ÿ‡ญ [phi](../../../phi/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ต๐Ÿ‡ฑ [pl](../../../pl/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ต๐Ÿ‡น [pt](../../../pt/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ง๐Ÿ‡ท [pt-BR](../../../pt-BR/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ท๐Ÿ‡ด [ro](../../../ro/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ท๐Ÿ‡บ [ru](../../../ru/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฑ๐Ÿ‡ฐ [si](../../../si/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ธ๐Ÿ‡ฐ [sk](../../../sk/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ธ๐Ÿ‡ฎ [sl](../../../sl/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ท๐Ÿ‡ธ [sr](../../../sr/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ธ๐Ÿ‡ช [sv](../../../sv/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฐ๐Ÿ‡ช [sw](../../../sw/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [ta](../../../ta/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [te](../../../te/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡น๐Ÿ‡ญ [th](../../../th/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡น๐Ÿ‡ท [tr](../../../tr/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡บ๐Ÿ‡ฆ [uk-UA](../../../uk-UA/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ต๐Ÿ‡ฐ [ur](../../../ur/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡บ๐Ÿ‡ฟ [uz](../../../uz/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ป๐Ÿ‡ณ [vi](../../../vi/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡ณ๐Ÿ‡ฌ [yo](../../../yo/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡จ๐Ÿ‡ณ [zh-CN](../../../zh-CN/docs/guides/DOCKER_GUIDE.md) ยท ๐Ÿ‡น๐Ÿ‡ผ [zh-TW](../../../zh-TW/docs/guides/DOCKER_GUIDE.md) --- > ์ „์ฒด Docker ๋ฐฐํฌ ์ฐธ์กฐ ๋ฌธ์„œ์ž…๋‹ˆ๋‹ค. ๋น ๋ฅด๊ฒŒ ์‹œ์ž‘ํ•˜๋ ค๋ฉด [README์˜ Docker ์„น์…˜](../README.md#-docker)์„ ์ฐธ์กฐํ•˜์„ธ์š”. ## ๋ชฉ์ฐจ - [๋น ๋ฅธ ์‹คํ–‰](#quick-run) - [ํ™˜๊ฒฝ ํŒŒ์ผ ์‚ฌ์šฉ](#with-environment-file) - [Docker Compose](#docker-compose) - [์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ”„๋กœํ•„](#available-profiles) - [OmniRoute๊ฐ€ Docker์—์„œ ์‹คํ–‰๋  ๋•Œ ํ˜ธ์ŠคํŠธ CLI ๋„๊ตฌ ๊ตฌ์„ฑ](#configuring-host-cli-tools-when-omniroute-runs-in-docker) - [Redis ์‚ฌ์ด๋“œ์นด](#redis-sidecar) - [ํ”„๋กœ๋•์…˜ Compose](#production-compose) - [Dockerfile ์Šคํ…Œ์ด์ง€](#dockerfile-stages) - [์ค‘์š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜](#critical-environment-variables) - [Caddy๋ฅผ ์‚ฌ์šฉํ•˜๋Š” Docker Compose (HTTPS)](#docker-compose-with-caddy-https-auto-tls) - [Cloudflare Quick Tunnel](#cloudflare-quick-tunnel) - [์ด๋ฏธ์ง€ ํƒœ๊ทธ](#image-tags) - [๊ฐ€์šฉ์„ฑ: ๊ธฐ๋ณธ SQLite๋Š” ๋‹จ์ผ ๋ณต์ œ๋ณธ](#availability-default-sqlite-is-single-replica) - [์ค‘์š” ์ฐธ๊ณ  ์‚ฌํ•ญ](#important-notes) --- ## ๋น ๋ฅธ ์‹คํ–‰ > **๋ช…๋ น์–ด ํ•˜๋‚˜๋กœ ์…€ํ”„ ํ˜ธ์ŠคํŒ…ํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?** > [์…€ํ”„ ํ˜ธ์ŠคํŒ… ๊ฐ€์ด๋“œ](../getting-started/SELF_HOST_GUIDE.md)๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š” โ€” > `docker compose -f docker-compose.selfhost.yml up -d`(๊ฒŒ์‹œ๋œ ์ด๋ฏธ์ง€ + > Redis, ๋ฃจํ”„๋ฐฑ ์ „์šฉ, ํ”„๋กœํ•„ ์„ ํƒ ์—†์Œ). ์•„๋ž˜์˜ ๋น ๋ฅธ ์‹คํ–‰์€ ์ด๋ฏธ ๋‹ค๋ฅธ ๊ณณ์—์„œ > Redis๋ฅผ ์‹คํ–‰ ์ค‘์ธ ์‚ฌ์šฉ์ž๋ฅผ ์œ„ํ•œ ๋‹จ์ผ ์ปจํ…Œ์ด๋„ˆ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ```bash docker run -d \ --name omniroute \ --restart unless-stopped \ --stop-timeout 40 \ -p 20128:20128 \ -v omniroute-data:/app/data \ diegosouzapw/omniroute:latest ``` ## ํ™˜๊ฒฝ ํŒŒ์ผ ์‚ฌ์šฉ ```bash # ๋จผ์ € .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 ``` ## Docker Compose ```bash # ๊ธฐ๋ณธ ํ”„๋กœํ•„(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](https://github.com/router-for-me/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 setup-codex`, `setup-claude`, `config set ` ๋ฐ ๋Œ€์‹œ๋ณด๋“œ์˜ **๊ตฌ์„ฑ ์ €์žฅ** ๋ฒ„ํŠผ์€ ๋ชจ๋‘ `~/.codex/*.config.toml`๊ณผ ๊ฐ™์€ ํŒŒ์ผ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒฝ๋กœ๋Š” CLI๊ฐ€ ์‹ค์ œ๋กœ ์‹คํ–‰๋˜๋Š” ๋จธ์‹ ์—์„œ๋งŒ ์˜๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ปจํ…Œ์ด๋„ˆ ๋‚ด๋ถ€์—์„œ ์‹คํ–‰ํ•˜๋ฉด ์ปจํ…Œ์ด๋„ˆ ์ž์ฒด์˜ ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ(`/home/node` โ€” ์ด๋ฏธ์ง€๋Š” `USER node`๋กœ ์‹คํ–‰๋จ)์— ํŒŒ์ผ์ด ์ž‘์„ฑ๋˜๋ฏ€๋กœ, ํ˜ธ์ŠคํŠธ CLI๊ฐ€ ์ด๋ฅผ ์ฝ์„ ์ˆ˜ ์—†์œผ๋ฉฐ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์žฌ์ƒ์„ฑ๋˜๋Š” ์ฆ‰์‹œ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค. OmniRoute๋Š” ์ด๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ์„ฑ๊ณต ๊ฒฐ๊ณผ๋ฅผ ๋ณด๊ณ ํ•˜๋Š” ๋Œ€์‹  ์•ˆ๋‚ด์™€ ํ•จ๊ป˜ ์“ฐ๊ธฐ๋ฅผ ๊ฑฐ๋ถ€ํ•ฉ๋‹ˆ๋‹ค. CLI๋Š” `2`๋กœ ์ข…๋ฃŒ๋˜๊ณ  API๋Š” `containerEphemeralTarget: true`์™€ ํ•จ๊ป˜ `422`๋กœ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค. ### ๊ถŒ์žฅ: CLI๋Š” ํ˜ธ์ŠคํŠธ์—์„œ, OmniRoute๋Š” Docker์—์„œ ์‹คํ–‰ ์ปจํ…Œ์ด๋„ˆ๋Š” API๋ฅผ ์ œ๊ณตํ•˜๊ณ , CLI๋Š” ํ˜ธ์ŠคํŠธ ๋„๊ตฌ๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ```bash 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` ํ”„๋กœํ•„) ์ปจํ…Œ์ด๋„ˆ ์ž์ฒด์—์„œ ํ˜ธ์ŠคํŠธ ๊ตฌ์„ฑ์„ ์ž‘์„ฑํ•˜๋„๋ก ํ•˜๋ ค๋ฉด ํ•ด๋‹น ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋งˆ์šดํŠธํ•˜๊ณ  `CLI_CONFIG_HOME`์ด ๋งˆ์šดํŠธ ๋ฃจํŠธ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋„๋ก ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. `host` ํ”„๋กœํ•„์—๋Š” ์ด๋ฏธ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ```yaml 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` ํ”„๋กœํ•„)์—๋Š” ์“ฐ๊ธฐ๊ฐ€ ์˜๋„๋œ ๋™์ž‘์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  `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`๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”. ## Redis ์‚ฌ์ด๋“œ์นด 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์œผ๋กœ ์กฐ์ •ํ•˜์„ธ์š”. ```bash docker compose up -d --scale redis=0 ``` ## ํ”„๋กœ๋•์…˜ Compose ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ํ•จ๊ป˜ ์‹คํ–‰๋˜๋Š” ๊ฒฉ๋ฆฌ๋œ ํ”„๋กœ๋•์…˜ ์Šค๋ƒ…์ƒท์—๋Š” `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` | ์‚ฌ์šฉ ๋ฐฉ๋ฒ•: ```bash # ํ”„๋กœ๋•์…˜ ์Šคํƒ ๋นŒ๋“œ ๋ฐ ์‹œ์ž‘ 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(`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` ์ฐธ๊ณ  ์‚ฌํ•ญ ์ฐธ์กฐ). | ํŠน์ • ๋Œ€์ƒ์„ ์ˆ˜๋™์œผ๋กœ ๋นŒ๋“œํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์‹คํ–‰ํ•˜์„ธ์š”. ```bash 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` ๋„์ค‘์— ๋‹จ์ˆœํžˆ ์ค‘์ง€๋˜๋ฏ€๋กœ ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ์ด ์•„๋‹ˆ๋ผ ๋ฉˆ์ถ˜ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ž…๋‹ˆ๋‹ค. ๋นŒ๋“œ ํ˜ธ์ŠคํŠธ์˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ์ œํ•œ๋œ ๊ฒฝ์šฐ ๋ฒˆ๋“ค๋Ÿฌ๋ฅผ ์ „ํ™˜ํ•˜์„ธ์š”. ```bash 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=`๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. - Node๋Š” ๋ฐ˜๋ณต๋œ `--max-old-space-size` ๊ฐ’ ์ค‘ ๋งˆ์ง€๋ง‰ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, `OMNIROUTE_MEMORY_MB`๋ฅผ ์„ค์ •ํ•˜๋ฉด ์‹ค์งˆ์ ์ธ Docker ํž™ ์ œํ•œ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - ์ด๋ฏธ์ง€์—์„œ ์ด ๊ฐ’์„ ํ•ญ์ƒ ์„ค์ •ํ•˜๋ฏ€๋กœ Docker์—์„œ๋Š” ๋Ÿฐ์ฒ˜ ์ž์ฒด์˜ RAM ๊ธฐ๋ฐ˜ ๋ณด์ • ํด๋ฐฑ์ด ์ ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์›Œํฌ๋กœ๋“œ์— ๋งž๊ฒŒ ๋ช…์‹œ์ ์œผ๋กœ ๋Š˜๋ฆฌ์‹ญ์‹œ์˜ค(์•„๋ž˜ ํ‘œ ์ฐธ์กฐ). ์ฝ”๋”ฉ ์—์ด์ „ํŠธ์˜ `/v1/responses`์—๋Š” `2048`๋„ ์—ฌ์ „ํžˆ ๋„ˆ๋ฌด ์ž‘์Šต๋‹ˆ๋‹ค. ### ์ฝ”๋”ฉ ์—์ด์ „ํŠธ์˜ ๋Ÿฐํƒ€์ž„ RAM Docker ๊ธฐ๋ณธ๊ฐ’์ธ 1โ€ฏGiB๋Š” ๋Œ€์‹œ๋ณด๋“œ/๊ฐ€๋ฒผ์šด ์ฑ„ํŒ…์„ ์œ„ํ•œ ์ตœ์ € ๊ธฐ์ค€์ด์ง€, ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์— ์ ํ•ฉํ•œ ํฌ๊ธฐ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ธด `POST /v1/responses` ๋ณธ๋ฌธ(์ˆ˜๋ฐฑ ๊ฐœ์˜ ๋ฉ”์‹œ์ง€์™€ ์ˆ˜์‹ญ ๊ฐœ์˜ ๋„๊ตฌ)์€ ์••์ถ• ์ค‘์— ์—ฌ๋Ÿฌ ์ธ๋ฉ”๋ชจ๋ฆฌ ๊ทธ๋ž˜ํ”„๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋กœ ๊ฒน์ณ ์‹คํ–‰๋œ ์•ฝ 3โ€ฏMiB / ์•ฝ 750k ํ† ํฐ ์š”์ฒญ ๋‘ ๊ฑด์€ **12โ€ฏGiB** old-space์—์„œ V8์„ ์ค‘๋‹จ์‹œ์ผฐ์œผ๋ฉฐ(`FATAL ERROR: Reached heap limit`), 16โ€ฏGiB cgroup OOM๋„ ๋ฐœ์ƒ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. [#7849](https://github.com/diegosouzapw/OmniRoute/issues/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`๋ฅผ ์„ค์ •ํ•˜๋ฏ€๋กœ ๊ณต์‹ ์ด๋ฏธ์ง€์—์„œ๋Š” ์ด ๋ณด์ •์ด ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ```bash 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](../reference/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](#runtime-ram-for-coding-agents) ์ฐธ์กฐ). | `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) 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` ์‹ฌ์„ ๋‹ค์‹œ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค. ### Compose ๋นŒ๋“œ(๊ถŒ์žฅ) ์ด๋ฏธ์ง€์™€ ๋Ÿฐํƒ€์ž„์˜ ์„ค์ •์ด ์ผ์น˜ํ•˜๋„๋ก `.env`์— ๋‘ ๋ณ€์ˆ˜๋ฅผ ๋ชจ๋‘ ์„ค์ •ํ•œ ํ›„ ๋‹ค์‹œ ๋นŒ๋“œํ•ฉ๋‹ˆ๋‹ค. ```bash # .env OMNIROUTE_BASE_PATH=/omniroute NEXT_PUBLIC_BASE_URL=https://myhostname.example.com/omniroute ``` ```bash docker compose --profile base up -d --build ``` `docker-compose.yml`์€ `OMNIROUTE_BASE_PATH`๋ฅผ Docker ๋นŒ๋“œ ์ธ์ˆ˜์™€ ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ### ์‚ฌ์ „ ๋นŒ๋“œ๋œ ๋ฃจํŠธ ์ด๋ฏธ์ง€ + ๋Ÿฐํƒ€์ž„ ํ•˜์œ„ ๊ฒฝ๋กœ ๊ฒŒ์‹œ๋œ `diegosouzapw/omniroute:*` ์ด๋ฏธ์ง€๋Š” ๋„๋ฉ”์ธ ๋ฃจํŠธ์šฉ์œผ๋กœ ๋นŒ๋“œ๋ฉ๋‹ˆ๋‹ค. ๋Ÿฐํƒ€์ž„์— `OMNIROUTE_BASE_PATH`๋ฅผ ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ์œผ๋ฉฐ, ์ปจํ…Œ์ด๋„ˆ๋Š” ์‹œ์ž‘ ์‹œ ๋ฒˆ๋“ค์„ ํ•œ ๋ฒˆ ํŒจ์น˜ํ•ฉ๋‹ˆ๋‹ค. ์ด์— ๋Œ€์‘ํ•˜๋Š” ๊ณต๊ฐœ ์˜ค๋ฆฌ์ง„๋„ ํ•จ๊ป˜ ์„ค์ •ํ•˜์‹ญ์‹œ์˜ค. ```yaml 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 ํ”„๋กœ๋ธŒ ๊ถŒ์žฅ ์‚ฌํ•ญ](../ops/MONITORING_GUIDE.md#kubernetes-probe-recommendations). ## Caddy๋ฅผ ์‚ฌ์šฉํ•œ Docker Compose (HTTPS ์ž๋™ TLS) Caddy์˜ ์ž๋™ SSL ํ”„๋กœ๋น„์ €๋‹์„ ์‚ฌ์šฉํ•˜์—ฌ OmniRoute๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์™ธ๋ถ€์— ๊ณต๊ฐœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋„๋ฉ”์ธ์˜ DNS A ๋ ˆ์ฝ”๋“œ๊ฐ€ ์„œ๋ฒ„์˜ IP๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”. ```yaml 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`๋ฅผ ํ™œ์„ฑํ™”ํ•˜์„ธ์š”. ## Cloudflare Quick Tunnel 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 ์ฑ„๋„์„ ๊ฒŒ์‹œํ•ฉ๋‹ˆ๋‹ค. | ์ฑ„๋„ | ์†Œ์Šค | ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ | ๊ถŒ์žฅ ์šฉ๋„ | | ------------------------------- | -------------------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------- | | `:` / `:-web` | ์„œ๋ช…๋˜๊ณ  ๋ฒ„์ „์ด ์ง€์ •๋œ ๋ฆด๋ฆฌ์Šค | ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€ | ์ •ํ™•ํ•œ ๋ฆด๋ฆฌ์Šค๋ฅผ ๊ณ ์ •ํ•˜๋Š” ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ | | `:latest` / `:latest-web` | ๊ฐ€์žฅ ๋†’์€ **๊ฒŒ์‹œ๋œ** ์•ˆ์ • SemVer | ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ์•ˆ์ • ํฌ์ธํ„ฐ | SemVer ๊ฒŒ์‹œ ์ž‘์—… **์ดํ›„** ์•ˆ์ • ๋ฆด๋ฆฌ์Šค๋ฅผ ์ถ”์  โ€” `main` ๋˜๋Š” ๋ฆด๋ฆฌ์Šค๋˜์ง€ ์•Š์€ `release/v*` ์ปค๋ฐ‹์€ ์ถ”์ ํ•˜์ง€ **์•Š์Œ** | | `:next` / `:next-web` | ํ˜„์žฌ ๊ธฐ๋ณธ `release/v*` ๋ธŒ๋žœ์น˜ | ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ์‹œํ—˜ํŒ ํฌ์ธํ„ฐ | ํ™œ์„ฑ ๋ฆด๋ฆฌ์Šค ๋ธŒ๋žœ์น˜์— ๋ฐ˜์˜๋˜์—ˆ์ง€๋งŒ ์•„์ง ์•ˆ์ • ๋ฆด๋ฆฌ์Šค์—๋Š” ํฌํ•จ๋˜์ง€ ์•Š์€ ์ˆ˜์ • ์‚ฌํ•ญ ํ…Œ์ŠคํŠธ | | `:main` / `:main-web` | `main` ๋ธŒ๋žœ์น˜ | ๋ณ€๊ฒฝ ๊ฐ€๋Šฅํ•œ ๊ฐœ๋ฐœ ํฌ์ธํ„ฐ | ๊ฐœ๋ฐœ ๋ฐ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ „์šฉ | #### ์›น ์„ธ์…˜ ์ œ๊ณต์ž: `-web` ์ด๋ฏธ์ง€ ์œ„์˜ ๋ชจ๋“  ์ฑ„๋„์€ `-web` ํƒœ๊ทธ(`:latest-web`, `:-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์šฉ์œผ๋กœ ๋ชจ๋‘ ๊ฒŒ์‹œ๋ฉ๋‹ˆ๋‹ค. ์ด์ „ ์œ ์ง€๋ณด์ˆ˜ ๋ธŒ๋žœ์น˜๋Š” ์ด ์ฑ„๋„์„ ๋ฎ์–ด์“ธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด ์ฑ„๋„์€ ๋‹ค์Œ ์•ˆ์ • ํƒœ๊ทธ๊ฐ€ ์ƒ์„ฑ๋˜๊ธฐ ์ „์— ํ™œ์„ฑ ๋ฆด๋ฆฌ์Šค ๋ธŒ๋žœ์น˜์— ๋ณ‘ํ•ฉ๋œ ์ˆ˜์ • ์‚ฌํ•ญ์„ ํฌํ•จํ•˜๋Š” ๊ฐ€์ ธ์˜ค๊ธฐ ๊ฐ€๋Šฅํ•œ ์ด๋ฏธ์ง€๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ```bash docker pull diegosouzapw/omniroute:next docker pull diegosouzapw/omniroute:next-web ``` Docker Compose์—์„œ๋Š” ์„ ํƒํ•œ ํ”„๋กœํ•„์ด ์‚ฌ์šฉํ•˜๋Š” ์ด๋ฏธ์ง€ ํƒœ๊ทธ๋ฅผ ์žฌ์ •์˜ํ•œ ๋‹ค์Œ, ์„œ๋น„์Šค๋ฅผ ๊ฐ€์ ธ์™€ ๋‹ค์‹œ ์ƒ์„ฑํ•˜์„ธ์š”. ```yaml services: omniroute: image: diegosouzapw/omniroute:next ``` ```bash docker compose pull docker compose up -d ``` #### ์•ˆ์ „ ๋ฐ ๋กค๋ฐฑ `next`๋Š” ์œ ๋™์ ์ธ ์‹œํ—˜ํŒ ์ฑ„๋„์ž…๋‹ˆ๋‹ค. ํ™œ์„ฑ ๋ฆด๋ฆฌ์Šค ๋ธŒ๋žœ์น˜์— ํ‘ธ์‹œํ•  ๋•Œ๋งˆ๋‹ค ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ **ํ”„๋กœ๋•์…˜ ์šฉ๋„๋กœ๋Š” ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค**. ํŠน์ • ๋นŒ๋“œ๋ฅผ ํ‰๊ฐ€ํ•˜๋Š” ๋™์•ˆ ์ด๋ฏธ์ง€ ๋‹ค์ด์ œ์ŠคํŠธ๋ฅผ ๊ณ ์ •ํ•˜์„ธ์š”. ```bash docker pull diegosouzapw/omniroute:next docker image inspect diegosouzapw/omniroute:next --format '{{index .RepoDigests 0}}' ``` ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์ „์— OmniRoute ๋ฐ์ดํ„ฐ ๋ณผ๋ฅจ ๋˜๋Š” ๋ฐ”์ธ๋“œ ๋งˆ์šดํŠธ๋œ ๋ฐ์ดํ„ฐ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋ฐฑ์—…ํ•˜์„ธ์š”. ๋กค๋ฐฑํ•˜๋ ค๋ฉด ์ด์ „์— ์‚ฌ์šฉํ•œ ์•ˆ์ • ๋ฒ„์ „ ๋˜๋Š” ๋‹ค์ด์ œ์ŠคํŠธ๋ฅผ ๋ณต์›ํ•˜๊ณ  ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋‹ค์‹œ ์ƒ์„ฑํ•˜์„ธ์š”. ```bash docker pull diegosouzapw/omniroute: 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๋Š” ๋‹จ์ผ ๋ ˆํ”Œ๋ฆฌ์นด ๊ตฌ์„ฑ ๊ธฐ๋ณธ 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 ๊ถŒ์žฅ ์‚ฌํ•ญ](../ops/MONITORING_GUIDE.md#kubernetes-probe-recommendations)๋„ ์ฐธ์กฐ): | 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๊ฐ€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค): ```yaml 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](https://github.com/diegosouzapw/OmniRoute/issues/8075)์—์„œ ์ง„ํ–‰๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๊ธฐ๋Šฅ์ด ์ถœ์‹œ๋˜๊ธฐ ์ „๊นŒ์ง€ **๋Œ€๊ทœ๋ชจ** `/v1/responses` ์šฉ๋Ÿ‰์„ ํ™•์žฅํ•˜๋Š” ์œ ์ผํ•˜๊ฒŒ ์ง€์›๋˜๋Š” ๋ฐฉ๋ฒ•์€ ํ•˜๋‚˜์˜ ๋ณผ๋ฅจ์—์„œ `replicas > 1`์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ 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](https://github.com/diegosouzapw/OmniRoute/issues/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` **์ •์ƒ ์ƒํƒœ์ธ** ํ”„๋กœ์„ธ์Šค(ํž™์ด `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](https://github.com/diegosouzapw/OmniRoute/pull/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`๊ฐ€ ์•„๋‹˜): ```yaml 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](https://github.com/diegosouzapw/OmniRoute/issues/11023)์—์„œ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ๊ณต์œ  ์˜๊ตฌ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํ•˜๋‚˜์˜ ๋…ผ๋ฆฌ์  ํด๋Ÿฌ์Šคํ„ฐ๋Š” [#8075](https://github.com/diegosouzapw/OmniRoute/issues/8075)์—์„œ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ## ์ค‘์š” ์ฐธ๊ณ  ์‚ฌํ•ญ - **SQLite WAL ๋ชจ๋“œ:** OmniRoute๊ฐ€ ์ตœ์‹  ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ `storage.sqlite`์— ์ฒดํฌํฌ์ธํŠธ๋กœ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ๋„๋ก `docker stop`์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฒˆ๋“ค๋กœ ์ œ๊ณต๋˜๋Š” Compose ํŒŒ์ผ์—๋Š” ์ด๋ฏธ 40์ดˆ์˜ ์ข…๋ฃŒ ์œ ์˜ˆ ๊ธฐ๊ฐ„์ด ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€๋ฅผ ์ง์ ‘ ์‹คํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ `--stop-timeout 40`์„ ์œ ์ง€ํ•˜์„ธ์š”. - **`DISABLE_SQLITE_AUTO_BACKUP`:** ์ •๊ธฐ/์“ฐ๊ธฐ ์ „ ๋ฐฑ์—…์„ ์™ธ๋ถ€์—์„œ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ `true`๋กœ ์„ค์ •ํ•˜์„ธ์š”. ๊ธฐ์กด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜์—๋Š” ์—ฌ์ „ํžˆ ๋ณ„๋„์˜ ๋‚ด๊ตฌ์„ฑ ์žˆ๋Š” ์•ˆ์ „ ์Šค๋ƒ…์ƒท๊ณผ ๋Œ€๊ทœ๋ชจ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋ณดํ˜ธ ์žฅ์น˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. - **๋ฐ์ดํ„ฐ ์˜์†์„ฑ:** ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋‹ค์‹œ ์‹œ์ž‘๋˜์–ด๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค, ํ‚ค ๋ฐ ๊ตฌ์„ฑ์„ ์œ ์ง€ํ•˜๋ ค๋ฉด ํ•ญ์ƒ `/app/data`์— ๋ณผ๋ฅจ์„ ๋งˆ์šดํŠธํ•˜์„ธ์š”. - **ํฌํŠธ ๊ตฌ์„ฑ:** ๊ธฐ๋ณธ ํฌํŠธ `20128`์„ ๋ณ€๊ฒฝํ•˜๋ ค๋ฉด `PORT` ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์žฌ์ •์˜ํ•˜์„ธ์š”. ## ํ•จ๊ป˜ ๋ณด๊ธฐ - [VM ๋ฐฐํฌ ๊ฐ€์ด๋“œ](../ops/VM_DEPLOYMENT_GUIDE.md) โ€” VM + nginx + Cloudflare ์„ค์ • - [Fly.io ๋ฐฐํฌ ๊ฐ€์ด๋“œ](../ops/FLY_IO_DEPLOYMENT_GUIDE.md) โ€” Fly.io์— ๋ฐฐํฌ - [ํ™˜๊ฒฝ ๊ตฌ์„ฑ](../reference/ENVIRONMENT.md) โ€” ์ „์ฒด `.env` ์ฐธ์กฐ