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; +}