add docker compose

This commit is contained in:
sirin.ph 2025-11-20 17:13:55 +07:00
parent 6f9defe29a
commit c62af10b02
7 changed files with 558 additions and 0 deletions

55
.dockerignore Normal file
View File

@ -0,0 +1,55 @@
# Dependencies
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Build output
dist
build
.vite
# Environment files
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# IDE and editor files
.vscode
.idea
*.swp
*.swo
*~
.DS_Store
# Git
.git
.gitignore
.gitattributes
# Docker
Dockerfile*
docker-compose*.yml
.dockerignore
# Documentation
README.md
STYLE_GUIDE.md
*.md
# Testing
coverage
.nyc_output
*.test.ts
*.test.tsx
*.spec.ts
*.spec.tsx
# Misc
.cache
*.log
tmp
temp

6
.env.example Normal file
View File

@ -0,0 +1,6 @@
# Gemini API Key
# Get your API key from: https://aistudio.google.com/app/apikey
GEMINI_API_KEY=your_gemini_api_key_here
# Node Environment (development | production)
NODE_ENV=development

353
DOCKER_README.md Normal file
View File

@ -0,0 +1,353 @@
# Docker Deployment Guide
This guide explains how to deploy the Pradit application using Docker and Docker Compose.
## Prerequisites
- Docker Engine 20.10+
- Docker Compose 2.0+
- Gemini API Key (get from https://aistudio.google.com/app/apikey)
## Quick Start
### 1. Setup Environment Variables
Copy the example environment file and add your Gemini API key:
```bash
cp .env.example .env
```
Edit `.env` and set your `GEMINI_API_KEY`:
```
GEMINI_API_KEY=your_actual_api_key_here
```
### 2. Run Development Mode
Development mode includes hot reload for rapid iteration:
```bash
docker-compose --profile dev up
```
Access the app at: http://localhost:3000
### 3. Run Production Mode
Production mode uses optimized build with nginx:
```bash
docker-compose --profile prod up -d
```
Access the app at: http://localhost:80
## Docker Compose Profiles
This project uses Docker Compose profiles to separate dev and prod environments.
### Development Profile (`dev`)
- **Port**: 3000
- **Hot Reload**: Enabled
- **Volume Mounts**: Source code mounted for live changes
- **Server**: Vite dev server
- **Optimizations**: None (fast rebuild)
**Commands**:
```bash
# Start dev server
docker-compose --profile dev up
# Start in background
docker-compose --profile dev up -d
# View logs
docker-compose --profile dev logs -f
# Stop
docker-compose --profile dev down
```
### Production Profile (`prod`)
- **Port**: 80
- **Hot Reload**: Disabled
- **Build**: Optimized static build
- **Server**: Nginx with compression and caching
- **Health Check**: Enabled on /health endpoint
**Commands**:
```bash
# Build and start production
docker-compose --profile prod up -d
# Rebuild after code changes
docker-compose --profile prod up -d --build
# View logs
docker-compose --profile prod logs -f
# Stop
docker-compose --profile prod down
```
## Manual Docker Commands
### Build Images
```bash
# Development image
docker build -f Dockerfile.dev -t pradit:dev .
# Production image
docker build -f Dockerfile -t pradit:prod .
```
### Run Containers
```bash
# Development
docker run -p 3000:3000 -v $(pwd):/app -v /app/node_modules \
-e GEMINI_API_KEY=your_key pradit:dev
# Production
docker run -p 80:80 pradit:prod
```
## Architecture
### Development Container
- **Base**: node:20-alpine
- **Package Manager**: pnpm
- **Volumes**: Source code mounted for hot reload
- **Port**: 3000
- **Command**: `pnpm run dev`
### Production Container
Multi-stage build for minimal image size:
1. **Builder Stage**:
- Installs dependencies with pnpm
- Builds optimized production bundle
- Uses node:20-alpine
2. **Runtime Stage**:
- Serves static files with nginx:alpine
- Configured for SPA routing
- Gzip compression enabled
- Security headers added
- Health check endpoint at `/health`
## Configuration Files
### Dockerfile
Production multi-stage build configuration.
### Dockerfile.dev
Development container with hot reload support.
### docker-compose.yml
Orchestration for both dev and prod profiles.
### nginx.conf
Nginx configuration for production:
- SPA routing (serves index.html for all routes)
- Gzip compression
- Static asset caching (1 year)
- Security headers
- Health check endpoint
### .dockerignore
Excludes unnecessary files from Docker build context:
- node_modules
- Build artifacts
- Environment files
- IDE configurations
- Documentation
## Environment Variables
| Variable | Required | Description |
|----------|----------|-------------|
| `GEMINI_API_KEY` | Yes | API key for Gemini AI |
| `NODE_ENV` | No | Environment mode (development/production) |
**Security Note**: Never commit `.env` file. Use `.env.example` as template.
## Port Configuration
| Service | Port | Description |
|---------|------|-------------|
| Development | 3000 | Vite dev server |
| Production | 80 | Nginx web server |
To use different ports, edit `docker-compose.yml`:
```yaml
ports:
- "8080:80" # Host:Container
```
## Troubleshooting
### Port Already in Use
```bash
# Find process using port 3000
netstat -ano | findstr :3000 # Windows
lsof -i :3000 # Linux/Mac
# Change port in docker-compose.yml
ports:
- "3001:3000"
```
### API Key Not Working
Ensure `.env` file exists and contains valid key:
```bash
cat .env # Linux/Mac
type .env # Windows
```
Restart containers after changing `.env`:
```bash
docker-compose --profile dev down
docker-compose --profile dev up
```
### Container Won't Start
Check logs for errors:
```bash
docker-compose --profile dev logs
docker-compose --profile prod logs
```
Remove containers and rebuild:
```bash
docker-compose down -v
docker-compose --profile prod up -d --build
```
### Volume Permission Issues (Linux)
If encountering permission issues with mounted volumes:
```bash
# Add user ID to docker-compose.yml
user: "${UID}:${GID}"
```
### Build Cache Issues
Clear Docker build cache:
```bash
docker builder prune -a
docker-compose build --no-cache
```
## Health Checks
Production container includes health check:
```bash
# Check container health
docker ps
# Manual health check
curl http://localhost/health
```
## Logs and Debugging
```bash
# Follow logs
docker-compose logs -f
# Specific service logs
docker-compose --profile dev logs pradit-dev
docker-compose --profile prod logs pradit-prod
# Enter running container
docker exec -it pradit-dev sh
docker exec -it pradit-prod sh
```
## Cleaning Up
```bash
# Stop and remove containers
docker-compose down
# Remove containers and volumes
docker-compose down -v
# Remove images
docker rmi pradit:dev pradit:prod
# Full cleanup (all unused Docker resources)
docker system prune -a
```
## Production Deployment
For cloud deployment (AWS, GCP, Azure, etc.):
1. Build and tag image:
```bash
docker build -t your-registry/pradit:latest .
```
2. Push to registry:
```bash
docker push your-registry/pradit:latest
```
3. Deploy to orchestration platform (Kubernetes, ECS, etc.)
## Performance Optimization
### Production Build
- Tree shaking removes unused code
- Minification reduces bundle size
- Gzip compression (nginx)
- Static asset caching (1 year)
- Multi-stage build minimizes image size
### Development Build
- Fast rebuild with hot module replacement
- Source maps for debugging
- No minification for faster builds
## Security Best Practices
1. **Never commit API keys** - Use environment variables
2. **Use multi-stage builds** - Minimize attack surface
3. **Run as non-root** - Production nginx runs as nginx user
4. **Keep images updated** - Regularly update base images
5. **Scan for vulnerabilities** - Use `docker scan pradit:prod`
6. **Use secrets management** - For production, use Docker secrets or cloud provider solutions
## Additional Resources
- [Docker Documentation](https://docs.docker.com/)
- [Docker Compose Documentation](https://docs.docker.com/compose/)
- [Vite Documentation](https://vitejs.dev/)
- [Nginx Documentation](https://nginx.org/en/docs/)
## Support
For issues specific to Docker setup, check:
1. Docker daemon is running
2. Environment variables are set correctly
3. Ports are not already in use
4. Sufficient disk space for images and containers
For application issues, refer to main [README.md](README.md).

35
Dockerfile Normal file
View File

@ -0,0 +1,35 @@
# Build stage
FROM node:20-alpine AS builder
# Set working directory
WORKDIR /app
# Copy package files
COPY package.json pnpm-lock.yaml ./
# Install pnpm
RUN npm install -g pnpm
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy source code
COPY . .
# Build the application
RUN pnpm run build
# Production stage
FROM nginx:alpine
# Copy built assets from builder stage
COPY --from=builder /app/dist /usr/share/nginx/html
# Copy nginx configuration
COPY nginx.conf /etc/nginx/conf.d/default.conf
# Expose port 80
EXPOSE 80
# Start nginx
CMD ["nginx", "-g", "daemon off;"]

23
Dockerfile.dev Normal file
View File

@ -0,0 +1,23 @@
# Development Dockerfile
FROM node:20-alpine
# Set working directory
WORKDIR /app
# Install pnpm globally
RUN npm install -g pnpm
# Copy package files
COPY package.json pnpm-lock.yaml ./
# Install dependencies
RUN pnpm install --frozen-lockfile
# Copy source code
COPY . .
# Expose development port
EXPOSE 3000
# Start development server with hot reload
CMD ["pnpm", "run", "dev"]

51
docker-compose.yml Normal file
View File

@ -0,0 +1,51 @@
version: '3.8'
services:
# Development service
pradit-dev:
build:
context: .
dockerfile: Dockerfile.dev
container_name: pradit-dev
profiles:
- dev
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
- GEMINI_API_KEY=${GEMINI_API_KEY}
command: pnpm run dev
networks:
- pradit-network
restart: unless-stopped
# Production service
pradit-prod:
build:
context: .
dockerfile: Dockerfile
args:
- GEMINI_API_KEY=${GEMINI_API_KEY}
container_name: pradit-prod
profiles:
- prod
ports:
- "80:80"
environment:
- NODE_ENV=production
networks:
- pradit-network
restart: unless-stopped
healthcheck:
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
networks:
pradit-network:
driver: bridge

35
nginx.conf Normal file
View File

@ -0,0 +1,35 @@
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/javascript application/json;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# SPA routing - serve index.html for all routes
location / {
try_files $uri $uri/ /index.html;
}
# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}