Jelajahi Sumber

Implement multi-stage Docker build with dev and prod modes

- Updated Dockerfile with 6 stages: base, dependencies, development, builder, prod-dependencies, production
- Added build step in builder stage that compiles all applications
- Production stage runs on built code with only prod dependencies
- Added start scripts to main package.json for production mode
- Updated docker-compose.yml with separate app-dev and app services
- Dev mode mounts source code for live editing
- Prod mode runs optimized built code with minimal dependencies
- Both modes support CLI as default entrypoint with server override option
Timothy Pomeroy 1 bulan lalu
induk
melakukan
c602da3791
3 mengubah file dengan 98 tambahan dan 16 penghapusan
  1. 70 11
      Dockerfile
  2. 25 5
      docker-compose.yml
  3. 3 0
      package.json

+ 70 - 11
Dockerfile

@@ -1,4 +1,5 @@
-FROM node:20
+# Base stage with system dependencies
+FROM node:20 AS base
 
 # Install system dependencies including HandBrakeCLI, SQLite, and FFmpeg
 RUN apt-get update && apt-get install -y \
@@ -12,31 +13,89 @@ RUN npm install -g pnpm
 
 WORKDIR /app
 
-# Copy package files
+# Copy package files for dependency installation
 COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json ./
-
-# Copy app package files
 COPY apps/web/package.json apps/web/
 COPY apps/service/package.json apps/service/
-COPY apps/docs/package.json apps/docs/
 COPY apps/cli/package.json apps/cli/
-
-# Copy packages
 COPY packages/eslint-config/package.json packages/eslint-config/
 COPY packages/typescript-config/package.json packages/typescript-config/
 COPY packages/ui/package.json packages/ui/
 
-# Install dependencies
+# Dependencies stage
+FROM base AS dependencies
+
+# Install all dependencies (including dev dependencies for building)
 RUN pnpm install --frozen-lockfile
 
-# Copy source code
+# Development stage
+FROM dependencies AS development
+
+# Copy all source code
 COPY . .
 
 # Expose ports
 EXPOSE 3000 3001 3002
 
-# Set the CLI as the default entrypoint
+# Set the CLI as the default entrypoint for development
 ENTRYPOINT ["pnpm", "run", "cli"]
+CMD []
+
+# Builder stage - builds the application
+FROM dependencies AS builder
+
+# Copy all source code
+COPY . .
+
+# Build all applications
+RUN pnpm run build
+
+# Production dependencies stage
+FROM base AS prod-dependencies
 
-# Default command (can be overridden)
+# Install only production dependencies
+RUN pnpm install --frozen-lockfile --prod
+
+# Production stage
+FROM node:20-slim AS production
+
+# Install runtime system dependencies
+RUN apt-get update && apt-get install -y \
+    handbrake-cli \
+    sqlite3 \
+    ffmpeg \
+    && rm -rf /var/lib/apt/lists/*
+
+# Install pnpm
+RUN npm install -g pnpm
+
+WORKDIR /app
+
+# Copy package files
+COPY package.json pnpm-lock.yaml pnpm-workspace.yaml turbo.json ./
+COPY apps/web/package.json apps/web/
+COPY apps/service/package.json apps/service/
+COPY apps/cli/package.json apps/cli/
+COPY packages/eslint-config/package.json packages/eslint-config/
+COPY packages/typescript-config/package.json packages/typescript-config/
+COPY packages/ui/package.json packages/ui/
+
+# Copy production dependencies
+COPY --from=prod-dependencies /app/node_modules ./node_modules
+
+# Copy built applications
+COPY --from=builder /app/apps/web/.next ./apps/web/.next
+COPY --from=builder /app/apps/web/public ./apps/web/public
+COPY --from=builder /app/apps/service/dist ./apps/service/dist
+COPY --from=builder /app/apps/cli/dist ./apps/cli/dist
+
+# Copy necessary source files for Next.js
+COPY apps/web/next.config.ts apps/web/
+COPY apps/web/next.config.js apps/web/
+
+# Expose ports
+EXPOSE 3000 3001 3002
+
+# Set the CLI as the default entrypoint for production
+ENTRYPOINT ["pnpm", "run", "cli"]
 CMD []

+ 25 - 5
docker-compose.yml

@@ -1,18 +1,38 @@
 version: "3.8"
 
 services:
-  app:
-    build: .
+  # Development mode
+  app-dev:
+    build:
+      context: .
+      target: development
     ports:
       - "3000:3000" # Web app
       - "3001:3001" # Service app
     volumes:
       - .:/app
       - /app/node_modules
-      - ./data:/app/data  # Persist database files
+      - ./data:/app/data # Persist database files
     environment:
       - NODE_ENV=development
     # CLI is now the default entrypoint, can be overridden with command
     # command: pnpm dev  # Uncomment to run dev servers instead of CLI
-    stdin_open: true  # Keep stdin open for interactive CLI
-    tty: true         # Allocate a pseudo-TTY for interactive CLI
+    stdin_open: true # Keep stdin open for interactive CLI
+    tty: true # Allocate a pseudo-TTY for interactive CLI
+
+  # Production mode
+  app:
+    build:
+      context: .
+      target: production
+    ports:
+      - "3000:3000" # Web app
+      - "3001:3001" # Service app
+    volumes:
+      - ./data:/app/data # Persist database files only
+    environment:
+      - NODE_ENV=production
+    # CLI is now the default entrypoint, can be overridden with command
+    # command: pnpm start  # Uncomment to run production servers instead of CLI
+    stdin_open: true # Keep stdin open for interactive CLI
+    tty: true # Allocate a pseudo-TTY for interactive CLI

+ 3 - 0
package.json

@@ -4,12 +4,15 @@
   "scripts": {
     "build": "turbo run build",
     "dev": "turbo run dev",
+    "start": "turbo run start",
     "lint": "turbo run lint",
     "format": "prettier --write \"**/*.{ts,tsx,md}\"",
     "check-types": "turbo run check-types",
     "service": "pnpm --filter ./apps/service run start:dev -- --port=3001",
+    "service:prod": "pnpm --filter ./apps/service run start:prod",
     "cli": "pnpm --filter ./apps/cli run start",
     "web": "pnpm --filter ./apps/web run dev --port 3000",
+    "web:prod": "pnpm --filter ./apps/web run start",
     "test:e2e": "pnpm --filter ./apps/web run test:e2e",
     "test:e2e:full": "concurrently \"pnpm run service\" \"pnpm run web\" --names \"service,web\" --prefix name --success first"
   },