Files
project-base/docker-compose.yml
T
ttipo e4262b2816
CI / Backend (lint · test · e2e · build) (push) Has been cancelled
CI / Frontend (lint · type-check · test) (push) Has been cancelled
CI / Flutter (analyze · test) (push) Has been cancelled
CI / 에러 코드 SSOT 동기화 검사 (push) Has been cancelled
refactor(docker): Redis 를 기본 비활성으로 전환 (Compose profile)
Redis 컨테이너가 항상 떠 있었지만 애플리케이션은 사용하지 않았다.
쓰지 않는 컨테이너는 메모리를 차지하고 "이게 왜 떠 있지" 하는 혼란을 준다.

- redis 서비스를 profiles: ["redis"] 로 숨겨 기본 기동에서 제외
- backend 의 redis 의존성을 required: false 로 두어 없어도 정상 기동
- 루트 .env 의 COMPOSE_PROFILES 한 줄로 켤 수 있게 함
  (데이터 볼륨과 REDIS_* 는 유지되어 켜면 이전 상태가 그대로 이어진다)

profile 미지정 시 서비스 목록에서 제외되는 것, redis 없이 앱이 정상 동작하는 것,
COMPOSE_PROFILES=redis 로 다시 기동되는 것을 모두 확인했다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 18:48:02 +09:00

161 lines
4.5 KiB
YAML

services:
# ----------------------------------------
# 1. Frontend (Nginx 정적 호스팅)
# ----------------------------------------
frontend:
container_name: ${PROJECT_NAME}-frontend
build:
context: ./${FRONTEND_DIR}
dockerfile: Dockerfile
args:
- BUILD_MODE=${BUILD_MODE}
- VITE_API_BASE_URL=${BACKEND_API_URL}
expose:
- "8080"
environment:
- NODE_ENV=${APP_ENV}
restart: unless-stopped
networks:
- app-network
- proxy
depends_on:
- backend
healthcheck:
# localhost 는 컨테이너 안에서 ::1 로 먼저 해석되는데 nginx 는 IPv4 만 바인딩하므로 127.0.0.1 을 명시한다
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8080/ || exit 1"]
interval: 30s
timeout: 5s
retries: 5
start_period: 10s
deploy:
resources:
limits:
cpus: '0.5'
memory: 512m
# ----------------------------------------
# 2. Backend (HTTP API & WebSocket 통합)
# ----------------------------------------
backend:
container_name: ${PROJECT_NAME}-backend
build:
context: ./${BACKEND_DIR}
dockerfile: Dockerfile
expose:
- "${BACKEND_PORT}"
- "${WEBSOCKET_PORT}"
# PID 1 을 tini 로 두어 SIGTERM 이 애플리케이션까지 확실히 전달되게 한다 (graceful shutdown)
init: true
# 처리 중인 요청을 마칠 시간 — 초과하면 SIGKILL (기본 10초)
stop_grace_period: 30s
env_file:
- ./${BACKEND_DIR}/.env.${APP_ENV}
environment:
- NODE_ENV=${APP_ENV}
- PORT=${BACKEND_PORT}
- WEBSOCKET_PORT=${WEBSOCKET_PORT}
- CORS_ORIGIN=${CORS_ORIGIN}
- THROTTLE_TTL_MS=${THROTTLE_TTL_MS}
- THROTTLE_LIMIT=${THROTTLE_LIMIT}
- DB_HOST=db
- DB_PORT=5432
- DB_USER=${DB_USER}
- DB_PASSWORD=${DB_PASSWORD}
- DB_NAME=${DB_NAME}
- DB_SYNCHRONIZE=${DB_SYNCHRONIZE}
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
restart: unless-stopped
networks:
- app-network
- proxy
depends_on:
db:
condition: service_healthy
# redis 는 profile 로 비활성화될 수 있으므로 선택적 의존으로 둔다
redis:
condition: service_healthy
required: false
healthcheck:
# localhost 는 컨테이너 안에서 ::1 로 먼저 해석되는데 Node 는 0.0.0.0(IPv4) 바인딩이므로 127.0.0.1 을 명시한다
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:${BACKEND_PORT}/ || exit 1"]
interval: 30s
timeout: 5s
retries: 5
start_period: 30s
deploy:
resources:
limits:
cpus: '1.0'
memory: 1g
# ----------------------------------------
# 3. Database (PostgreSQL) — 내부 네트워크 전용
# ----------------------------------------
db:
container_name: ${PROJECT_NAME}-db
image: ${DB_IMAGE}
expose:
- "5432"
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
volumes:
- ${DB_DATA_DIR}:/var/lib/postgresql/data
restart: always
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
deploy:
resources:
limits:
cpus: '0.5'
memory: 512m
labels:
- "com.centurylinklabs.watchtower.enable=true"
# ----------------------------------------
# 4. Redis — 내부 네트워크 전용 · 기본 비활성
#
# 기본적으로 기동하지 않는다. 캐시/세션 공유가 실제로 필요해질 때 켠다.
# 루트 .env.{APP_ENV} 에서 COMPOSE_PROFILES=redis
# 또는 일회성으로 docker compose --profile redis up -d
# ----------------------------------------
redis:
profiles: ["redis"]
container_name: ${PROJECT_NAME}-redis
image: ${REDIS_IMAGE}
command: redis-server --requirepass ${REDIS_PASSWORD}
expose:
- "6379"
volumes:
- ${REDIS_DATA_DIR}:/data
restart: always
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "redis-cli -a ${REDIS_PASSWORD} ping | grep PONG"]
interval: 10s
timeout: 5s
retries: 5
start_period: 5s
deploy:
resources:
limits:
cpus: '0.25'
memory: 256m
labels:
- "com.centurylinklabs.watchtower.enable=true"
networks:
app-network:
driver: bridge
proxy:
external: true