From e3b9ec1071dba29df41f4a3d9c267f81d1e7f222 Mon Sep 17 00:00:00 2001 From: zephyrdark Date: Tue, 3 Feb 2026 12:27:34 +0900 Subject: [PATCH] feat: update Docker configuration - Backend Dockerfile: Python 3.12, non-root user, healthcheck - Frontend Dockerfile: Multi-stage build, production stage - docker-compose.yml: env_file, healthchecks, restart policies - docker-compose.prod.yml: Production config with nginx - .env.example: Updated with all variables Co-Authored-By: Claude Opus 4.5 --- .env.example | 14 ++++++-- backend/Dockerfile | 12 ++++++- docker-compose.prod.yml | 77 +++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 28 ++++++++++----- frontend/Dockerfile | 44 +++++++++++++++++++---- 5 files changed, 156 insertions(+), 19 deletions(-) create mode 100644 docker-compose.prod.yml diff --git a/.env.example b/.env.example index dd12aa5..645a13b 100644 --- a/.env.example +++ b/.env.example @@ -1,13 +1,21 @@ +# Galaxy-PO Environment Variables +# Copy this file to .env and fill in the values + # Database +DB_USER=galaxy DB_PASSWORD=your_secure_password_here +DB_NAME=galaxy_po # JWT Authentication -JWT_SECRET=your_jwt_secret_key_here +JWT_SECRET=your_jwt_secret_key_here_at_least_32_characters -# Korea Investment & Securities OpenAPI +# Korea Investment & Securities OpenAPI (optional) KIS_APP_KEY=your_kis_app_key KIS_APP_SECRET=your_kis_app_secret KIS_ACCOUNT_NO=your_account_number -# DART OpenAPI (Financial Statements) +# DART OpenAPI (Financial Statements, optional) DART_API_KEY=your_dart_api_key + +# Production only +API_URL=https://your-domain.com diff --git a/backend/Dockerfile b/backend/Dockerfile index f287691..f378d39 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,5 @@ -FROM python:3.14-slim +# Backend Dockerfile for Galaxy-PO +FROM python:3.12-slim WORKDIR /app @@ -15,8 +16,17 @@ RUN pip install --no-cache-dir -r requirements.txt # Copy application code COPY . . +# Create non-root user for security +RUN adduser --disabled-password --gecos '' appuser && \ + chown -R appuser:appuser /app +USER appuser + # Expose port EXPOSE 8000 +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')" || exit 1 + # Run the application CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..288819d --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,77 @@ +# Production Docker Compose +# Usage: docker-compose -f docker-compose.prod.yml up -d + +services: + postgres: + image: postgres:15-alpine + container_name: galaxy-po-db + environment: + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASSWORD} + POSTGRES_DB: ${DB_NAME} + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"] + interval: 5s + timeout: 5s + retries: 5 + restart: always + networks: + - galaxy-net + + backend: + build: + context: ./backend + dockerfile: Dockerfile + container_name: galaxy-po-backend + env_file: + - .env.prod + environment: + DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/${DB_NAME} + PYTHONPATH: /app + depends_on: + postgres: + condition: service_healthy + restart: always + networks: + - galaxy-net + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + target: production + container_name: galaxy-po-frontend + environment: + NEXT_PUBLIC_API_URL: ${API_URL} + depends_on: + backend: + condition: service_healthy + restart: always + networks: + - galaxy-net + + nginx: + image: nginx:alpine + container_name: galaxy-po-nginx + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + - ./certs:/etc/nginx/certs:ro + depends_on: + - frontend + - backend + restart: always + networks: + - galaxy-net + +volumes: + postgres_data: + driver: local + +networks: + galaxy-net: + driver: bridge diff --git a/docker-compose.yml b/docker-compose.yml index 0f16d1f..7c20339 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,55 +3,67 @@ services: image: postgres:15-alpine container_name: galaxy-po-db environment: - POSTGRES_USER: galaxy + POSTGRES_USER: ${DB_USER:-galaxy} POSTGRES_PASSWORD: ${DB_PASSWORD:-devpassword} - POSTGRES_DB: galaxy_po + POSTGRES_DB: ${DB_NAME:-galaxy_po} volumes: - postgres_data:/var/lib/postgresql/data ports: - "5432:5432" healthcheck: - test: ["CMD-SHELL", "pg_isready -U galaxy -d galaxy_po"] + test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-galaxy} -d ${DB_NAME:-galaxy_po}"] interval: 5s timeout: 5s retries: 5 + restart: unless-stopped backend: build: context: ./backend dockerfile: Dockerfile container_name: galaxy-po-backend + env_file: + - .env environment: - DATABASE_URL: postgresql://galaxy:${DB_PASSWORD:-devpassword}@postgres:5432/galaxy_po + DATABASE_URL: postgresql://${DB_USER:-galaxy}:${DB_PASSWORD:-devpassword}@postgres:5432/${DB_NAME:-galaxy_po} JWT_SECRET: ${JWT_SECRET:-dev-jwt-secret-change-in-production} KIS_APP_KEY: ${KIS_APP_KEY:-} KIS_APP_SECRET: ${KIS_APP_SECRET:-} KIS_ACCOUNT_NO: ${KIS_ACCOUNT_NO:-} DART_API_KEY: ${DART_API_KEY:-} + PYTHONPATH: /app ports: - "8000:8000" depends_on: postgres: condition: service_healthy - volumes: - - ./backend:/app - command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"] + interval: 30s + timeout: 10s + start_period: 10s + retries: 3 + restart: unless-stopped frontend: build: context: ./frontend dockerfile: Dockerfile + target: development container_name: galaxy-po-frontend environment: NEXT_PUBLIC_API_URL: http://localhost:8000 ports: - "3000:3000" depends_on: - - backend + backend: + condition: service_healthy volumes: - ./frontend:/app - /app/node_modules - /app/.next + restart: unless-stopped volumes: postgres_data: + driver: local diff --git a/frontend/Dockerfile b/frontend/Dockerfile index ded2fd2..9dfd961 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -1,16 +1,46 @@ -FROM node:24-alpine +# Frontend Dockerfile for Galaxy-PO +FROM node:22-alpine AS base WORKDIR /app -# Install dependencies +# Install dependencies only when needed +FROM base AS deps COPY package*.json ./ RUN npm ci -# Copy application code +# Development stage +FROM base AS development +COPY --from=deps /app/node_modules ./node_modules COPY . . - -# Expose port EXPOSE 3000 - -# Development command CMD ["npm", "run", "dev"] + +# Build stage +FROM base AS builder +COPY --from=deps /app/node_modules ./node_modules +COPY . . +RUN npm run build + +# Production stage +FROM base AS production +ENV NODE_ENV=production + +# Create non-root user +RUN addgroup --system --gid 1001 nodejs && \ + adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 +ENV PORT=3000 +ENV HOSTNAME="0.0.0.0" + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:3000/ || exit 1 + +CMD ["node", "server.js"]