From c605b82626aa6e0ba92ea3b628014650d8e1898d Mon Sep 17 00:00:00 2001 From: mrangjw <157506327+mrangjw@users.noreply.github.com> Date: Mon, 16 Mar 2026 17:44:01 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20#4=20Docker=20=ED=86=B5=ED=95=A9=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD=20=EA=B5=AC=EC=84=B1=20-=20Dockerfile=20+=20?= =?UTF-8?q?docker-compose=20=EC=88=98=EC=A0=95=20+=20=ED=94=84=EB=A1=A0?= =?UTF-8?q?=ED=8A=B8=EC=97=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 전체 서비스가 docker compose up -d로 기동되도록 통합 환경을 구성합니다. ## Dockerfile - docker/Dockerfile.spring: Gradle 8.12 + JDK 21 멀티스테이지 빌드 - docker/Dockerfile.web: Node 20 빌드 + Nginx SPA 서빙 - docker/nginx.conf: SPA fallback + API 프록시 → Gateway(8080) ## docker-compose.yml 주요 수정 - JDBC → R2DBC URL로 변경 (모든 서비스가 WebFlux/R2DBC 기반) - JWT_SECRET 공유: gateway ↔ auth-service 동일 시크릿 - EVENT_SERVICE_URL: reservation → event 서비스 간 WebClient 통신 - Gateway 라우팅: 환경변수 기반으로 5개 서비스 라우팅 구성 - version 키 제거 (Compose V2 표준) - Web 프론트엔드 서비스 추가 (Nginx, 포트 3000) ## 서비스 구성 (8개) | Service | Port | 역할 | |---------|------|------| | web | 3000 | Vue 3 프론트엔드 (Nginx) | | gateway | 8080 | API Gateway + JWT 인증 | | auth | 8081 | 인증/인가 | | user | 8082 | 유저/테넌트 | | event | 8083 | 공연/좌석 | | reservation | 8084 | 예매/대기열 | | payment | 8085 | 결제 | | infra | - | PostgreSQL, Redis, Kafka | --- docker-compose.yml | 164 +++++++++++++++++++++++++++------------ docker/Dockerfile.spring | 22 ++++++ docker/Dockerfile.web | 42 ++++++++++ docker/nginx.conf | 40 ++++++++++ 4 files changed, 217 insertions(+), 51 deletions(-) create mode 100644 docker/Dockerfile.spring create mode 100644 docker/Dockerfile.web create mode 100644 docker/nginx.conf diff --git a/docker-compose.yml b/docker-compose.yml index 5d6be53..4863f4c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,10 +1,8 @@ -version: "3.9" - # ============================================================================= # OpenTraum — Full Local Development Environment # ============================================================================= # Usage: docker compose up -d -# All microservices + infrastructure for full integration testing +# All microservices + frontend + infrastructure for full integration testing # ============================================================================= services: @@ -103,6 +101,24 @@ services: networks: - opentraum-net + # --------------------------------------------------------------------------- + # Frontend (Vue 3 + Nginx) + # --------------------------------------------------------------------------- + web: + image: opentraum/web:latest + container_name: opentraum-web + restart: unless-stopped + build: + context: ../OpenTraum-Web + dockerfile: ../OpenTraum-Infra/docker/Dockerfile.web + ports: + - "3000:3000" + depends_on: + gateway: + condition: service_healthy + networks: + - opentraum-net + # --------------------------------------------------------------------------- # Microservices # --------------------------------------------------------------------------- @@ -112,19 +128,33 @@ services: restart: unless-stopped build: context: ../OpenTraum-gateway - dockerfile: Dockerfile + dockerfile: ../OpenTraum-Infra/docker/Dockerfile.spring ports: - "8080:8080" environment: - SPRING_PROFILES_ACTIVE: local - SPRING_CLOUD_GATEWAY_ROUTES: "" - AUTH_SERVICE_URL: http://auth-service:8081 - USER_SERVICE_URL: http://user-service:8082 - EVENT_SERVICE_URL: http://event-service:8083 - RESERVATION_SERVICE_URL: http://reservation-service:8084 - PAYMENT_SERVICE_URL: http://payment-service:8085 + SERVER_PORT: 8080 + SPRING_PROFILES_ACTIVE: prod + # Redis SPRING_DATA_REDIS_HOST: redis SPRING_DATA_REDIS_PORT: 6379 + # JWT (auth-service와 동일) + JWT_SECRET: opentraum-default-jwt-secret-key-must-be-at-least-256-bits-long-for-hs256 + # Gateway 라우팅 (환경변수 기반) + SPRING_CLOUD_GATEWAY_ROUTES_0_ID: auth-service + SPRING_CLOUD_GATEWAY_ROUTES_0_URI: http://auth-service:8081 + SPRING_CLOUD_GATEWAY_ROUTES_0_PREDICATES_0: Path=/api/v1/auth/** + SPRING_CLOUD_GATEWAY_ROUTES_1_ID: user-service + SPRING_CLOUD_GATEWAY_ROUTES_1_URI: http://user-service:8082 + SPRING_CLOUD_GATEWAY_ROUTES_1_PREDICATES_0: Path=/api/v1/users/**,/api/v1/tenants/** + SPRING_CLOUD_GATEWAY_ROUTES_2_ID: event-service + SPRING_CLOUD_GATEWAY_ROUTES_2_URI: http://event-service:8083 + SPRING_CLOUD_GATEWAY_ROUTES_2_PREDICATES_0: Path=/api/v1/concerts/**,/api/v1/schedules/**,/api/v1/admin/seats/** + SPRING_CLOUD_GATEWAY_ROUTES_3_ID: reservation-service + SPRING_CLOUD_GATEWAY_ROUTES_3_URI: http://reservation-service:8084 + SPRING_CLOUD_GATEWAY_ROUTES_3_PREDICATES_0: Path=/api/v1/reservations/**,/api/v1/queue/**,/api/v1/live/**,/api/v1/lottery/** + SPRING_CLOUD_GATEWAY_ROUTES_4_ID: payment-service + SPRING_CLOUD_GATEWAY_ROUTES_4_URI: http://payment-service:8085 + SPRING_CLOUD_GATEWAY_ROUTES_4_PREDICATES_0: Path=/api/v1/payment/** depends_on: redis: condition: service_healthy @@ -143,17 +173,27 @@ services: restart: unless-stopped build: context: ../OpenTraum-auth-service - dockerfile: Dockerfile + dockerfile: ../OpenTraum-Infra/docker/Dockerfile.spring ports: - "8081:8081" environment: - SPRING_PROFILES_ACTIVE: local - SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/opentraum_auth - SPRING_DATASOURCE_USERNAME: opentraum - SPRING_DATASOURCE_PASSWORD: opentraum - SPRING_DATA_REDIS_HOST: redis - SPRING_DATA_REDIS_PORT: 6379 - SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:29092 + SERVER_PORT: 8081 + # R2DBC (WebFlux 기반 — JDBC 아님) + SPRING_R2DBC_URL: r2dbc:postgresql://postgres:5432/opentraum_auth + SPRING_R2DBC_USERNAME: opentraum + SPRING_R2DBC_PASSWORD: opentraum + DB_HOST: postgres + DB_PORT: 5432 + DB_NAME: opentraum_auth + DB_USERNAME: opentraum + DB_PASSWORD: opentraum + # Redis + REDIS_HOST: redis + REDIS_PORT: 6379 + # JWT + JWT_SECRET: opentraum-default-jwt-secret-key-must-be-at-least-256-bits-long-for-hs256 + # Kafka + KAFKA_BOOTSTRAP_SERVERS: kafka:29092 depends_on: postgres: condition: service_healthy @@ -176,17 +216,22 @@ services: restart: unless-stopped build: context: ../OpenTraum-user-service - dockerfile: Dockerfile + dockerfile: ../OpenTraum-Infra/docker/Dockerfile.spring ports: - "8082:8082" environment: - SPRING_PROFILES_ACTIVE: local - SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/opentraum_user - SPRING_DATASOURCE_USERNAME: opentraum - SPRING_DATASOURCE_PASSWORD: opentraum - SPRING_DATA_REDIS_HOST: redis - SPRING_DATA_REDIS_PORT: 6379 - SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:29092 + SERVER_PORT: 8082 + SPRING_R2DBC_URL: r2dbc:postgresql://postgres:5432/opentraum_user + SPRING_R2DBC_USERNAME: opentraum + SPRING_R2DBC_PASSWORD: opentraum + DB_HOST: postgres + DB_PORT: 5432 + DB_NAME: opentraum_user + DB_USERNAME: opentraum + DB_PASSWORD: opentraum + REDIS_HOST: redis + REDIS_PORT: 6379 + KAFKA_BOOTSTRAP_SERVERS: kafka:29092 depends_on: postgres: condition: service_healthy @@ -209,17 +254,22 @@ services: restart: unless-stopped build: context: ../OpenTraum-event-service - dockerfile: Dockerfile + dockerfile: ../OpenTraum-Infra/docker/Dockerfile.spring ports: - "8083:8083" environment: - SPRING_PROFILES_ACTIVE: local - SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/opentraum_event - SPRING_DATASOURCE_USERNAME: opentraum - SPRING_DATASOURCE_PASSWORD: opentraum - SPRING_DATA_REDIS_HOST: redis - SPRING_DATA_REDIS_PORT: 6379 - SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:29092 + SERVER_PORT: 8083 + SPRING_R2DBC_URL: r2dbc:postgresql://postgres:5432/opentraum_event + SPRING_R2DBC_USERNAME: opentraum + SPRING_R2DBC_PASSWORD: opentraum + DB_HOST: postgres + DB_PORT: 5432 + DB_NAME: opentraum_event + DB_USERNAME: opentraum + DB_PASSWORD: opentraum + REDIS_HOST: redis + REDIS_PORT: 6379 + KAFKA_BOOTSTRAP_SERVERS: kafka:29092 depends_on: postgres: condition: service_healthy @@ -242,17 +292,24 @@ services: restart: unless-stopped build: context: ../OpenTraum-reservation-service - dockerfile: Dockerfile + dockerfile: ../OpenTraum-Infra/docker/Dockerfile.spring ports: - "8084:8084" environment: - SPRING_PROFILES_ACTIVE: local - SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/opentraum_reservation - SPRING_DATASOURCE_USERNAME: opentraum - SPRING_DATASOURCE_PASSWORD: opentraum - SPRING_DATA_REDIS_HOST: redis - SPRING_DATA_REDIS_PORT: 6379 - SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:29092 + SERVER_PORT: 8084 + SPRING_R2DBC_URL: r2dbc:postgresql://postgres:5432/opentraum_reservation + SPRING_R2DBC_USERNAME: opentraum + SPRING_R2DBC_PASSWORD: opentraum + DB_HOST: postgres + DB_PORT: 5432 + DB_NAME: opentraum_reservation + DB_USERNAME: opentraum + DB_PASSWORD: opentraum + REDIS_HOST: redis + REDIS_PORT: 6379 + KAFKA_BOOTSTRAP_SERVERS: kafka:29092 + # 서비스 간 통신 + EVENT_SERVICE_URL: http://event-service:8083 depends_on: postgres: condition: service_healthy @@ -275,17 +332,22 @@ services: restart: unless-stopped build: context: ../OpenTraum-payment-service - dockerfile: Dockerfile + dockerfile: ../OpenTraum-Infra/docker/Dockerfile.spring ports: - "8085:8085" environment: - SPRING_PROFILES_ACTIVE: local - SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/opentraum_payment - SPRING_DATASOURCE_USERNAME: opentraum - SPRING_DATASOURCE_PASSWORD: opentraum - SPRING_DATA_REDIS_HOST: redis - SPRING_DATA_REDIS_PORT: 6379 - SPRING_KAFKA_BOOTSTRAP_SERVERS: kafka:29092 + SERVER_PORT: 8085 + SPRING_R2DBC_URL: r2dbc:postgresql://postgres:5432/opentraum_payment + SPRING_R2DBC_USERNAME: opentraum + SPRING_R2DBC_PASSWORD: opentraum + DB_HOST: postgres + DB_PORT: 5432 + DB_NAME: opentraum_payment + DB_USERNAME: opentraum + DB_PASSWORD: opentraum + REDIS_HOST: redis + REDIS_PORT: 6379 + KAFKA_BOOTSTRAP_SERVERS: kafka:29092 depends_on: postgres: condition: service_healthy diff --git a/docker/Dockerfile.spring b/docker/Dockerfile.spring new file mode 100644 index 0000000..77e0676 --- /dev/null +++ b/docker/Dockerfile.spring @@ -0,0 +1,22 @@ +# ============================================================================= +# OpenTraum — Spring Boot Microservice Dockerfile +# ============================================================================= +# Multi-stage build: Gradle build → JRE runtime +# Usage: docker compose에서 각 서비스의 build context와 함께 사용 +# ============================================================================= + +FROM gradle:8.12-jdk21 AS builder +WORKDIR /app +COPY . . +RUN gradle bootJar --no-daemon -x test + +FROM eclipse-temurin:21-jre +WORKDIR /app + +COPY --from=builder /app/build/libs/*.jar app.jar + +# Spring Boot Actuator health endpoint +HEALTHCHECK --interval=15s --timeout=5s --retries=5 --start-period=60s \ + CMD curl -f http://localhost:${SERVER_PORT:-8080}/actuator/health || exit 1 + +ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/docker/Dockerfile.web b/docker/Dockerfile.web new file mode 100644 index 0000000..e73b8b1 --- /dev/null +++ b/docker/Dockerfile.web @@ -0,0 +1,42 @@ +# ============================================================================= +# OpenTraum — Vue 3 Frontend Dockerfile +# ============================================================================= +# Multi-stage build: Node build → Nginx serve +# ============================================================================= + +FROM node:20-alpine AS builder +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . +RUN npm run build + +FROM nginx:alpine + +COPY --from=builder /app/dist /usr/share/nginx/html + +# nginx.conf는 docker-compose volume mount 또는 별도 COPY로 제공 +# 기본 설정: SPA fallback + API 프록시 +RUN echo 'server { \n\ + listen 3000; \n\ + server_name localhost; \n\ + root /usr/share/nginx/html; \n\ + index index.html; \n\ + location / { try_files $uri $uri/ /index.html; } \n\ + location /api/ { \n\ + proxy_pass http://gateway:8080; \n\ + proxy_set_header Host $host; \n\ + proxy_set_header X-Real-IP $remote_addr; \n\ + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; \n\ + proxy_http_version 1.1; \n\ + proxy_read_timeout 300s; \n\ + } \n\ + gzip on; \n\ + gzip_types text/plain text/css application/json application/javascript text/xml; \n\ +}' > /etc/nginx/conf.d/default.conf + +EXPOSE 3000 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 0000000..f861f29 --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,40 @@ +server { + listen 3000; + server_name localhost; + + root /usr/share/nginx/html; + index index.html; + + # Vue Router history mode — SPA fallback + location / { + try_files $uri $uri/ /index.html; + } + + # API 프록시 → Gateway + location /api/ { + proxy_pass http://gateway:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + # WebSocket (SSE/대기열 폴링용) + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # 긴 폴링 타임아웃 + proxy_read_timeout 300s; + } + + # 정적 파일 캐싱 + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # gzip + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript image/svg+xml; + gzip_min_length 1000; +}