version: "3.8" services: # ================================================================= # Databases # ================================================================= mariadb: image: mariadb:10.9 container_name: tk-mariadb restart: unless-stopped environment: - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} - MYSQL_DATABASE=${MYSQL_DATABASE:-hyungi} - MYSQL_USER=${MYSQL_USER:-hyungi_user} - MYSQL_PASSWORD=${MYSQL_PASSWORD} - TZ=Asia/Seoul volumes: - mariadb_data:/var/lib/mysql - ./scripts/migrate-users.sql:/docker-entrypoint-initdb.d/99-sso-users.sql ports: - "127.0.0.1:30306:3306" healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"] timeout: 20s retries: 10 networks: - tk-network redis: image: redis:6-alpine container_name: tk-redis restart: unless-stopped command: redis-server --maxmemory 200mb --maxmemory-policy allkeys-lru healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 10s timeout: 5s retries: 3 networks: - tk-network # ================================================================= # SSO Auth Service # ================================================================= sso-auth: build: context: ./sso-auth-service dockerfile: Dockerfile container_name: tk-sso-auth restart: unless-stopped ports: - "30050:3000" environment: - NODE_ENV=production - PORT=3000 - DB_HOST=mariadb - DB_PORT=3306 - DB_USER=${MYSQL_USER:-hyungi_user} - DB_PASSWORD=${MYSQL_PASSWORD} - DB_NAME=${MYSQL_DATABASE:-hyungi} - SSO_JWT_SECRET=${SSO_JWT_SECRET} - SSO_JWT_EXPIRES_IN=${SSO_JWT_EXPIRES_IN:-7d} - SSO_JWT_REFRESH_SECRET=${SSO_JWT_REFRESH_SECRET} - SSO_JWT_REFRESH_EXPIRES_IN=${SSO_JWT_REFRESH_EXPIRES_IN:-30d} - REDIS_HOST=redis - REDIS_PORT=6379 healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"] interval: 15s timeout: 5s retries: 3 start_period: 30s depends_on: mariadb: condition: service_healthy redis: condition: service_healthy networks: - tk-network # ================================================================= # System 1 - 공장관리 # ================================================================= system1-api: build: context: . dockerfile: system1-factory/api/Dockerfile container_name: tk-system1-api restart: unless-stopped ports: - "30005:3005" environment: - NODE_ENV=${SYSTEM1_NODE_ENV:-production} - PORT=3005 - DB_HOST=mariadb - DB_PORT=3306 - DB_USER=${MYSQL_USER:-hyungi_user} - DB_PASSWORD=${MYSQL_PASSWORD} - DB_NAME=${MYSQL_DATABASE:-hyungi} - DB_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} - JWT_SECRET=${SSO_JWT_SECRET} - JWT_EXPIRES_IN=${SSO_JWT_EXPIRES_IN:-7d} - JWT_REFRESH_SECRET=${SSO_JWT_REFRESH_SECRET} - JWT_REFRESH_EXPIRES_IN=${SSO_JWT_REFRESH_EXPIRES_IN:-30d} - REDIS_HOST=redis - REDIS_PORT=6379 - WEATHER_API_URL=${WEATHER_API_URL:-} - WEATHER_API_KEY=${WEATHER_API_KEY:-} - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY} healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:3005/api/health"] interval: 15s timeout: 5s retries: 3 start_period: 30s volumes: - system1_uploads:/usr/src/app/uploads - system1_logs:/usr/src/app/logs depends_on: mariadb: condition: service_healthy redis: condition: service_healthy networks: - tk-network system1-web: build: context: ./system1-factory/web dockerfile: Dockerfile container_name: tk-system1-web restart: unless-stopped ports: - "30080:80" volumes: - ./system1-factory/web:/usr/share/nginx/html:ro depends_on: system1-api: condition: service_healthy sso-auth: condition: service_healthy networks: - tk-network system1-fastapi: build: context: ./system1-factory/fastapi-bridge dockerfile: Dockerfile container_name: tk-system1-fastapi restart: unless-stopped ports: - "30008:8000" environment: - API_BASE_URL=http://system1-api:3005 healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"] interval: 15s timeout: 5s retries: 3 start_period: 30s depends_on: system1-api: condition: service_healthy networks: - tk-network # ================================================================= # System 2 - 신고 # ================================================================= system2-api: build: context: . dockerfile: system2-report/api/Dockerfile container_name: tk-system2-api restart: unless-stopped ports: - "30105:3005" environment: - NODE_ENV=${SYSTEM2_NODE_ENV:-production} - PORT=3005 - DB_HOST=mariadb - DB_PORT=3306 - DB_USER=${MYSQL_USER:-hyungi_user} - DB_PASSWORD=${MYSQL_PASSWORD} - DB_NAME=${MYSQL_DATABASE:-hyungi} - JWT_SECRET=${SSO_JWT_SECRET} - JWT_EXPIRES_IN=${SSO_JWT_EXPIRES_IN:-7d} - REDIS_HOST=redis - REDIS_PORT=6379 - M_PROJECT_API_URL=${M_PROJECT_API_URL:-http://system3-api:8000} - M_PROJECT_USERNAME=${M_PROJECT_USERNAME:-api_service} - M_PROJECT_PASSWORD=${M_PROJECT_PASSWORD:-} - M_PROJECT_DEFAULT_PROJECT_ID=${M_PROJECT_DEFAULT_PROJECT_ID:-1} - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY} healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:3005/api/health"] interval: 15s timeout: 5s retries: 3 start_period: 30s volumes: - system2_uploads:/usr/src/app/uploads - system2_logs:/usr/src/app/logs depends_on: mariadb: condition: service_healthy redis: condition: service_healthy networks: - tk-network system2-web: build: context: ./system2-report/web dockerfile: Dockerfile container_name: tk-system2-web restart: unless-stopped ports: - "30180:80" depends_on: system2-api: condition: service_healthy networks: - tk-network # ================================================================= # System 3 - 부적합관리 # ================================================================= system3-api: build: context: ./system3-nonconformance/api dockerfile: Dockerfile container_name: tk-system3-api restart: unless-stopped ports: - "30200:8000" environment: - DB_HOST=mariadb - DB_PORT=3306 - DB_USER=${MYSQL_USER:-hyungi_user} - DB_PASSWORD=${MYSQL_PASSWORD} - DB_NAME=${MYSQL_DATABASE:-hyungi} - SECRET_KEY=${SSO_JWT_SECRET} - ALGORITHM=HS256 - ACCESS_TOKEN_EXPIRE_MINUTES=10080 - ADMIN_USERNAME=${SYSTEM3_ADMIN_USERNAME:-hyungi} - TZ=Asia/Seoul - TKUSER_API_URL=http://tkuser-api:3000 healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/api/health')"] interval: 15s timeout: 5s retries: 3 start_period: 30s volumes: - system3_uploads:/app/uploads depends_on: mariadb: condition: service_healthy networks: - tk-network system3-web: build: context: ./system3-nonconformance/web dockerfile: Dockerfile container_name: tk-system3-web restart: unless-stopped ports: - "30280:80" volumes: - system3_uploads:/usr/share/nginx/html/uploads depends_on: system3-api: condition: service_healthy networks: - tk-network # ================================================================= # User Management (tkuser) # ================================================================= tkuser-api: build: context: . dockerfile: user-management/api/Dockerfile container_name: tk-tkuser-api restart: unless-stopped ports: - "30300:3000" environment: - NODE_ENV=production - PORT=3000 - DB_HOST=mariadb - DB_PORT=3306 - DB_USER=${MYSQL_USER:-hyungi_user} - DB_PASSWORD=${MYSQL_PASSWORD} - DB_NAME=${MYSQL_DATABASE:-hyungi} - SSO_JWT_SECRET=${SSO_JWT_SECRET} - VAPID_PUBLIC_KEY=${VAPID_PUBLIC_KEY} - VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY} - VAPID_SUBJECT=${VAPID_SUBJECT:-mailto:admin@technicalkorea.net} - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY} - NTFY_BASE_URL=${NTFY_BASE_URL:-http://ntfy:80} - NTFY_PUBLISH_TOKEN=${NTFY_PUBLISH_TOKEN} - NTFY_EXTERNAL_URL=${NTFY_EXTERNAL_URL:-https://ntfy.technicalkorea.net} - TKFB_BASE_URL=${TKFB_BASE_URL:-https://tkfb.technicalkorea.net} healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"] interval: 15s timeout: 5s retries: 3 start_period: 30s volumes: - system1_uploads:/usr/src/app/uploads depends_on: mariadb: condition: service_healthy networks: - tk-network tkuser-web: build: context: ./user-management/web dockerfile: Dockerfile container_name: tk-tkuser-web restart: unless-stopped ports: - "30380:80" depends_on: tkuser-api: condition: service_healthy networks: - tk-network # ================================================================= # Purchase Management (tkpurchase) # ================================================================= tkpurchase-api: build: context: . dockerfile: tkpurchase/api/Dockerfile container_name: tk-tkpurchase-api restart: unless-stopped ports: - "30400:3000" environment: - NODE_ENV=production - PORT=3000 - DB_HOST=mariadb - DB_PORT=3306 - DB_USER=${MYSQL_USER:-hyungi_user} - DB_PASSWORD=${MYSQL_PASSWORD} - DB_NAME=${MYSQL_DATABASE:-hyungi} - SSO_JWT_SECRET=${SSO_JWT_SECRET} - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY} healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"] interval: 15s timeout: 5s retries: 3 start_period: 30s depends_on: mariadb: condition: service_healthy networks: - tk-network tkpurchase-web: build: context: ./tkpurchase/web dockerfile: Dockerfile container_name: tk-tkpurchase-web restart: unless-stopped ports: - "30480:80" depends_on: tkpurchase-api: condition: service_healthy networks: - tk-network # ================================================================= # Safety Management (tksafety) # ================================================================= tksafety-api: build: context: . dockerfile: tksafety/api/Dockerfile container_name: tk-tksafety-api restart: unless-stopped ports: - "30500:3000" environment: - NODE_ENV=production - PORT=3000 - DB_HOST=mariadb - DB_PORT=3306 - DB_USER=${MYSQL_USER:-hyungi_user} - DB_PASSWORD=${MYSQL_PASSWORD} - DB_NAME=${MYSQL_DATABASE:-hyungi} - SSO_JWT_SECRET=${SSO_JWT_SECRET} - INTERNAL_SERVICE_KEY=${INTERNAL_SERVICE_KEY} healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"] interval: 15s timeout: 5s retries: 3 start_period: 30s volumes: - tksafety_uploads:/usr/src/app/uploads depends_on: mariadb: condition: service_healthy networks: - tk-network tksafety-web: build: context: ./tksafety/web dockerfile: Dockerfile container_name: tk-tksafety-web restart: unless-stopped ports: - "30580:80" volumes: - tksafety_uploads:/usr/share/nginx/html/uploads depends_on: tksafety-api: condition: service_healthy networks: - tk-network # ================================================================= # Support (tksupport) - 전사 행정지원 # ================================================================= tksupport-api: build: context: . dockerfile: tksupport/api/Dockerfile container_name: tk-tksupport-api restart: unless-stopped ports: - "30600:3000" environment: - NODE_ENV=production - PORT=3000 - DB_HOST=mariadb - DB_PORT=3306 - DB_USER=${MYSQL_USER:-hyungi_user} - DB_PASSWORD=${MYSQL_PASSWORD} - DB_NAME=${MYSQL_DATABASE:-hyungi} - SSO_JWT_SECRET=${SSO_JWT_SECRET} healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/health"] interval: 15s timeout: 5s retries: 3 start_period: 30s depends_on: mariadb: condition: service_healthy networks: - tk-network tksupport-web: build: context: ./tksupport/web dockerfile: Dockerfile container_name: tk-tksupport-web restart: unless-stopped ports: - "30680:80" depends_on: tksupport-api: condition: service_healthy networks: - tk-network # ================================================================= # TK-EG - BOM 자재관리 (tkeg) # ================================================================= tkeg-postgres: image: postgres:15-alpine container_name: tk-tkeg-postgres restart: unless-stopped environment: POSTGRES_DB: tk_bom POSTGRES_USER: tkbom_user POSTGRES_PASSWORD: ${TKEG_POSTGRES_PASSWORD} TZ: Asia/Seoul volumes: - tkeg_postgres_data:/var/lib/postgresql/data - ./tkeg/api/database/init:/docker-entrypoint-initdb.d healthcheck: test: ["CMD-SHELL", "pg_isready -U tkbom_user -d tk_bom"] interval: 30s timeout: 10s retries: 3 networks: - tk-network tkeg-api: build: context: ./tkeg/api dockerfile: Dockerfile container_name: tk-tkeg-api restart: unless-stopped ports: - "30700:8000" environment: - DATABASE_URL=postgresql://tkbom_user:${TKEG_POSTGRES_PASSWORD}@tkeg-postgres:5432/tk_bom - REDIS_URL=redis://redis:6379 - SECRET_KEY=${SSO_JWT_SECRET} - TKUSER_API_URL=http://tkuser-api:3000 - ENVIRONMENT=production - TZ=Asia/Seoul healthcheck: test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"] interval: 15s timeout: 5s retries: 3 start_period: 30s volumes: - tkeg_uploads:/app/uploads depends_on: tkeg-postgres: condition: service_healthy redis: condition: service_healthy networks: - tk-network tkeg-web: build: context: ./tkeg/web dockerfile: Dockerfile args: - VITE_API_URL=/api container_name: tk-tkeg-web restart: unless-stopped ports: - "30780:80" depends_on: tkeg-api: condition: service_healthy networks: - tk-network # ================================================================= # ntfy — 푸시 알림 서버 # ================================================================= ntfy: image: binwiederhier/ntfy container_name: tk-ntfy restart: unless-stopped command: serve ports: - "30750:80" environment: - TZ=Asia/Seoul volumes: - ./ntfy/etc:/etc/ntfy - ntfy_cache:/var/cache/ntfy networks: - tk-network # ================================================================= # AI Service — 맥미니로 이전됨 (~/docker/tk-ai-service/) # ================================================================= # ================================================================= # Gateway (로그인 + 대시보드 + 공유JS) # ================================================================= gateway: build: context: ./gateway dockerfile: Dockerfile container_name: tk-gateway restart: unless-stopped ports: - "30000:80" depends_on: sso-auth: condition: service_healthy system1-api: condition: service_healthy networks: - tk-network # ================================================================= # Tools # ================================================================= phpmyadmin: image: phpmyadmin/phpmyadmin:latest container_name: tk-phpmyadmin restart: unless-stopped ports: - "127.0.0.1:30880:80" environment: - PMA_HOST=mariadb - PMA_USER=${PMA_USER:-root} - PMA_PASSWORD=${MYSQL_ROOT_PASSWORD} - UPLOAD_LIMIT=${UPLOAD_LIMIT:-50M} depends_on: mariadb: condition: service_healthy networks: - tk-network # ================================================================= # Cloudflare Tunnel # ================================================================= cloudflared: image: cloudflare/cloudflared:latest container_name: tk-cloudflared restart: unless-stopped command: tunnel --no-autoupdate run environment: - TUNNEL_TOKEN=${CLOUDFLARE_TUNNEL_TOKEN} depends_on: - gateway - system1-web - system2-web - system3-web - tkpurchase-web - tksafety-web - tksupport-web - tkeg-web - ntfy networks: - tk-network volumes: mariadb_data: external: true name: tkfb-package_db_data system1_uploads: external: true name: tkfb_api_uploads system1_logs: system2_uploads: system2_logs: tksafety_uploads: system3_uploads: external: true name: tkqc-package_uploads tkeg_postgres_data: tkeg_uploads: ntfy_cache: networks: tk-network: driver: bridge name: tk-factory-network