feat(deploy): RTGS 3센터 컨테이너 배포 산출물

- Dockerfile.backend: 7서비스 공용 이미지(멀티스테이지 gradle 빌드, SERVICE env 선택, curl 포함)
- docker-compose.yml: PG(3DB)+Kafka+순번기+DC1/DC2/DC3 각6서비스+프론트, 컨테이너 격리로 포트오프셋 불필요, depends_on 헬스 순서
- Dockerfile.frontend+nginx.conf: 콘솔+DT 정적빌드→nginx 서빙+API 리버스프록시(X-RTGS-DC 헤더/dcN 경로 센터 라우팅)
- init/init-3centers.sql, .dockerignore, README(Coder 런북 + Coolify 어댑테이션/정책 경고)
- vite.config: 배포 빌드에 dt.html 엔트리 추가(콘솔+DT 한번에)

빌드/기동 검증은 Coder(podman)에서 — 로컬 PC는 Docker 미지원. 프론트 build·compose 구조는 로컬 정적 검증 완료.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
rtgs
2026-07-16 18:14:03 +09:00
parent 427f361783
commit 0c29884780
8 changed files with 357 additions and 0 deletions

185
deploy/docker-compose.yml Normal file
View File

@@ -0,0 +1,185 @@
name: rtgs
# RTGS 3센터 A-A-A 풀스택 (컨테이너). 로컬 native(run-*.cmd)와 별개 — Coder/podman 등 컨테이너 호스트용.
# 공유 인프라(PostgreSQL 3DB + Kafka) + 단일 순번기 + DC1/DC2/DC3 각 6서비스 + 프론트(콘솔·DT).
# 컨테이너는 네트워크 격리라 포트 오프셋 불필요(전부 기본 포트, service name으로 구분).
# ---------- 공용 백엔드(서비스 공통) ----------
x-backend-env: &backend-env
POSTGRES_HOST: postgres
POSTGRES_PORT: "5432"
POSTGRES_USER: rtgs
POSTGRES_PASSWORD: rtgs
KAFKA_BOOTSTRAP: kafka:9092
CENTER_COUNT: "3"
GUCCI_JWT_SECRET: rtgs-gucci-dev-secret-key-please-change-0123456789
x-backend: &backend
image: rtgs-backend:latest
build:
context: ..
dockerfile: deploy/Dockerfile.backend
restart: unless-stopped
x-health: &svc-health
healthcheck:
test: ["CMD-SHELL", "curl -fs http://localhost:$${SERVER_PORT}/actuator/health || exit 1"]
interval: 10s
timeout: 4s
retries: 30
start_period: 40s
services:
# ================= 인프라 =================
postgres:
image: postgres:16
container_name: rtgs-postgres
environment:
POSTGRES_DB: rtgs
POSTGRES_USER: rtgs
POSTGRES_PASSWORD: rtgs
command: ["postgres", "-c", "max_connections=300"]
volumes:
- pgdata:/var/lib/postgresql/data
- ./init/init-3centers.sql:/docker-entrypoint-initdb.d/01-init-3centers.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U rtgs -d rtgs"]
interval: 5s
timeout: 3s
retries: 30
kafka:
image: apache/kafka:3.8.1
container_name: rtgs-kafka
environment:
KAFKA_NODE_ID: "1"
KAFKA_PROCESS_ROLES: "broker,controller"
KAFKA_CONTROLLER_QUORUM_VOTERS: "1@kafka:9093"
KAFKA_LISTENERS: "PLAINTEXT://:9092,CONTROLLER://:9093"
KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://kafka:9092"
KAFKA_CONTROLLER_LISTENER_NAMES: "CONTROLLER"
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT"
KAFKA_INTER_BROKER_LISTENER_NAME: "PLAINTEXT"
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: "1"
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: "1"
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: "1"
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: "0"
KAFKA_NUM_PARTITIONS: "1" # 저널 전역순서 = 단일 파티션
KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
healthcheck:
test: ["CMD-SHELL", "/opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 --list || exit 1"]
interval: 10s
timeout: 5s
retries: 30
start_period: 20s
# ================= 순번기(전역 단일, DC1) =================
sequencer:
<<: [*backend, *svc-health]
container_name: rtgs-sequencer
environment: {<<: *backend-env, SERVICE: sequencer, SERVER_PORT: "8090", CENTER_ID: DC1, POSTGRES_DB: rtgs_dc1}
depends_on:
kafka: {condition: service_healthy}
dc1-louisvuitton: {condition: service_healthy}
# ================= DC1 =================
dc1-louisvuitton:
<<: [*backend, *svc-health]
container_name: rtgs-dc1-louisvuitton
environment: {<<: *backend-env, SERVICE: louisvuitton, SERVER_PORT: "8099", CENTER_ID: DC1, POSTGRES_DB: rtgs_dc1}
depends_on: {postgres: {condition: service_healthy}}
ports: ["8099:8099"]
dc1-dior:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: dior, SERVER_PORT: "8092", CENTER_ID: DC1, POSTGRES_DB: rtgs_dc1}
depends_on: {dc1-louisvuitton: {condition: service_healthy}, kafka: {condition: service_healthy}}
dc1-hermes:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: hermes, SERVER_PORT: "8093", CENTER_ID: DC1, POSTGRES_DB: rtgs_dc1}
depends_on: {dc1-louisvuitton: {condition: service_healthy}, kafka: {condition: service_healthy}}
dc1-prada:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: prada, SERVER_PORT: "8094", CENTER_ID: DC1, POSTGRES_DB: rtgs_dc1}
depends_on: {dc1-louisvuitton: {condition: service_healthy}, kafka: {condition: service_healthy}}
dc1-chanel:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: chanel, SERVER_PORT: "8091", CENTER_ID: DC1, POSTGRES_DB: rtgs_dc1}
depends_on: {dc1-louisvuitton: {condition: service_healthy}, kafka: {condition: service_healthy}}
dc1-gucci:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: gucci, SERVER_PORT: "8095", CENTER_ID: DC1, POSTGRES_DB: rtgs_dc1, CHANEL_URL: "http://dc1-chanel:8091"}
depends_on: {dc1-chanel: {condition: service_healthy}}
ports: ["8095:8095"]
# ================= DC2 =================
dc2-louisvuitton:
<<: [*backend, *svc-health]
container_name: rtgs-dc2-louisvuitton
environment: {<<: *backend-env, SERVICE: louisvuitton, SERVER_PORT: "8099", CENTER_ID: DC2, POSTGRES_DB: rtgs_dc2, RTGS_CONTROL_ENABLED: "false"}
depends_on: {postgres: {condition: service_healthy}}
ports: ["8199:8099"]
dc2-dior:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: dior, SERVER_PORT: "8092", CENTER_ID: DC2, POSTGRES_DB: rtgs_dc2}
depends_on: {dc2-louisvuitton: {condition: service_healthy}, kafka: {condition: service_healthy}}
dc2-hermes:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: hermes, SERVER_PORT: "8093", CENTER_ID: DC2, POSTGRES_DB: rtgs_dc2}
depends_on: {dc2-louisvuitton: {condition: service_healthy}, kafka: {condition: service_healthy}}
dc2-prada:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: prada, SERVER_PORT: "8094", CENTER_ID: DC2, POSTGRES_DB: rtgs_dc2}
depends_on: {dc2-louisvuitton: {condition: service_healthy}, kafka: {condition: service_healthy}}
dc2-chanel:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: chanel, SERVER_PORT: "8091", CENTER_ID: DC2, POSTGRES_DB: rtgs_dc2}
depends_on: {dc2-louisvuitton: {condition: service_healthy}, kafka: {condition: service_healthy}}
dc2-gucci:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: gucci, SERVER_PORT: "8095", CENTER_ID: DC2, POSTGRES_DB: rtgs_dc2, CHANEL_URL: "http://dc2-chanel:8091"}
depends_on: {dc2-chanel: {condition: service_healthy}}
ports: ["8195:8095"]
# ================= DC3 =================
dc3-louisvuitton:
<<: [*backend, *svc-health]
container_name: rtgs-dc3-louisvuitton
environment: {<<: *backend-env, SERVICE: louisvuitton, SERVER_PORT: "8099", CENTER_ID: DC3, POSTGRES_DB: rtgs_dc3, RTGS_CONTROL_ENABLED: "false"}
depends_on: {postgres: {condition: service_healthy}}
ports: ["8299:8099"]
dc3-dior:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: dior, SERVER_PORT: "8092", CENTER_ID: DC3, POSTGRES_DB: rtgs_dc3}
depends_on: {dc3-louisvuitton: {condition: service_healthy}, kafka: {condition: service_healthy}}
dc3-hermes:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: hermes, SERVER_PORT: "8093", CENTER_ID: DC3, POSTGRES_DB: rtgs_dc3}
depends_on: {dc3-louisvuitton: {condition: service_healthy}, kafka: {condition: service_healthy}}
dc3-prada:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: prada, SERVER_PORT: "8094", CENTER_ID: DC3, POSTGRES_DB: rtgs_dc3}
depends_on: {dc3-louisvuitton: {condition: service_healthy}, kafka: {condition: service_healthy}}
dc3-chanel:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: chanel, SERVER_PORT: "8091", CENTER_ID: DC3, POSTGRES_DB: rtgs_dc3}
depends_on: {dc3-louisvuitton: {condition: service_healthy}, kafka: {condition: service_healthy}}
dc3-gucci:
<<: [*backend, *svc-health]
environment: {<<: *backend-env, SERVICE: gucci, SERVER_PORT: "8095", CENTER_ID: DC3, POSTGRES_DB: rtgs_dc3, CHANEL_URL: "http://dc3-chanel:8091"}
depends_on: {dc3-chanel: {condition: service_healthy}}
ports: ["8295:8095"]
# ================= 프론트(콘솔 + DT) =================
frontend:
image: rtgs-frontend:latest
build:
context: ..
dockerfile: deploy/Dockerfile.frontend
container_name: rtgs-frontend
restart: unless-stopped
ports: ["8080:80"]
depends_on:
dc1-gucci: {condition: service_healthy}
volumes:
pgdata: