502 lines
10 KiB
Markdown
502 lines
10 KiB
Markdown
# Docker Setup Documentation
|
|
|
|
## Overview
|
|
|
|
This document describes the production-ready Docker setup for the Feuerwehr Dashboard application, including multi-stage builds, security best practices, and deployment instructions.
|
|
|
|
## Architecture
|
|
|
|
The application consists of three Docker containers:
|
|
|
|
1. **PostgreSQL Database** - postgres:16-alpine
|
|
2. **Backend API** - Node.js application (TypeScript compiled to JavaScript)
|
|
3. **Frontend** - React SPA served by Nginx
|
|
|
|
All containers are connected via a Docker bridge network and orchestrated using Docker Compose.
|
|
|
|
## Files Structure
|
|
|
|
```
|
|
feuerwehr_dashboard/
|
|
├── docker-compose.yml # Production orchestration
|
|
├── docker-compose.dev.yml # Development orchestration
|
|
├── docker-test.sh # Docker build testing script
|
|
├── backend/
|
|
│ ├── Dockerfile # Multi-stage backend build
|
|
│ └── .dockerignore # Backend Docker ignore rules
|
|
└── frontend/
|
|
├── Dockerfile # Multi-stage frontend build
|
|
├── nginx.conf # Nginx configuration for SPA
|
|
└── .dockerignore # Frontend Docker ignore rules
|
|
```
|
|
|
|
## Backend Dockerfile
|
|
|
|
**Location:** `/backend/Dockerfile`
|
|
|
|
### Features
|
|
|
|
- **Multi-stage build** - Separate build and production stages
|
|
- **Build stage:**
|
|
- Based on node:20-alpine
|
|
- Installs all dependencies (including devDependencies)
|
|
- Compiles TypeScript to JavaScript
|
|
- Prunes devDependencies after build
|
|
- **Production stage:**
|
|
- Based on node:20-alpine
|
|
- Installs wget for health checks
|
|
- Creates non-root user (nodejs:1001)
|
|
- Copies only production node_modules
|
|
- Copies compiled JavaScript
|
|
- Copies database migrations to dist/
|
|
- Runs as non-root user
|
|
- Exposes port 3000
|
|
- Includes health check
|
|
|
|
### Build Command
|
|
|
|
```bash
|
|
cd backend
|
|
docker build -t feuerwehr-backend:latest .
|
|
```
|
|
|
|
### Image Size
|
|
|
|
Expected final image size: ~150-200 MB (alpine-based)
|
|
|
|
### Security Features
|
|
|
|
- Non-root user execution
|
|
- Minimal base image (Alpine Linux)
|
|
- Only production dependencies included
|
|
- No source code in final image
|
|
|
|
## Frontend Dockerfile
|
|
|
|
**Location:** `/frontend/Dockerfile`
|
|
|
|
### Features
|
|
|
|
- **Multi-stage build** - Build stage with Node.js + production stage with Nginx
|
|
- **Build stage:**
|
|
- Based on node:20-alpine
|
|
- Installs dependencies
|
|
- Runs Vite build
|
|
- Accepts build arguments for environment variables
|
|
- **Production stage:**
|
|
- Based on nginx:alpine
|
|
- Installs wget for health checks
|
|
- Copies custom nginx.conf
|
|
- Copies built static assets
|
|
- Configures non-root nginx user
|
|
- Exposes port 80
|
|
- Includes health check
|
|
|
|
### Build Command
|
|
|
|
```bash
|
|
cd frontend
|
|
docker build \
|
|
--build-arg VITE_API_URL=http://localhost:3000 \
|
|
--build-arg VITE_APP_NAME="Feuerwehr Dashboard" \
|
|
--build-arg VITE_APP_VERSION="1.0.0" \
|
|
-t feuerwehr-frontend:latest .
|
|
```
|
|
|
|
### Build Arguments
|
|
|
|
- `VITE_API_URL` - Backend API URL (default: http://localhost:3000)
|
|
- `VITE_APP_NAME` - Application name (default: "Feuerwehr Dashboard")
|
|
- `VITE_APP_VERSION` - Application version (default: "1.0.0")
|
|
|
|
### Image Size
|
|
|
|
Expected final image size: ~50-80 MB (alpine + static assets)
|
|
|
|
### Security Features
|
|
|
|
- Non-root nginx execution
|
|
- Minimal base image (Alpine Linux)
|
|
- Security headers configured
|
|
- No source code in final image
|
|
|
|
## Nginx Configuration
|
|
|
|
**Location:** `/frontend/nginx.conf`
|
|
|
|
### Features
|
|
|
|
1. **SPA Routing**
|
|
- All routes fall back to index.html
|
|
- Proper handling of client-side routing
|
|
|
|
2. **Performance**
|
|
- Gzip compression enabled
|
|
- Static asset caching (1 year)
|
|
- No caching for index.html
|
|
|
|
3. **Security Headers**
|
|
- X-Frame-Options: SAMEORIGIN
|
|
- X-Content-Type-Options: nosniff
|
|
- X-XSS-Protection: 1; mode=block
|
|
- Referrer-Policy: strict-origin-when-cross-origin
|
|
|
|
4. **Health Check**
|
|
- Endpoint: /health
|
|
- Returns 200 with "healthy" text
|
|
|
|
5. **Error Handling**
|
|
- 404 errors redirect to index.html
|
|
- Custom 50x error page
|
|
|
|
## Docker Compose
|
|
|
|
**Location:** `/docker-compose.yml`
|
|
|
|
### Services
|
|
|
|
#### PostgreSQL
|
|
|
|
```yaml
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
ports: 5432:5432
|
|
volumes: postgres_data_prod
|
|
health_check: pg_isready
|
|
restart: unless-stopped
|
|
```
|
|
|
|
#### Backend
|
|
|
|
```yaml
|
|
backend:
|
|
build: ./backend
|
|
ports: 3000:3000
|
|
depends_on: postgres (healthy)
|
|
health_check: wget localhost:3000/health
|
|
restart: unless-stopped
|
|
```
|
|
|
|
#### Frontend
|
|
|
|
```yaml
|
|
frontend:
|
|
build: ./frontend
|
|
build_args: VITE_API_URL
|
|
ports: 80:80
|
|
depends_on: backend (healthy)
|
|
health_check: wget localhost:80/health
|
|
restart: unless-stopped
|
|
```
|
|
|
|
### Environment Variables
|
|
|
|
Create a `.env` file based on `.env.example`:
|
|
|
|
**Required:**
|
|
- `POSTGRES_PASSWORD` - Database password
|
|
- `JWT_SECRET` - JWT signing secret
|
|
|
|
**Optional:**
|
|
- `POSTGRES_DB` - Database name (default: feuerwehr_prod)
|
|
- `POSTGRES_USER` - Database user (default: prod_user)
|
|
- `POSTGRES_PORT` - Database port (default: 5432)
|
|
- `BACKEND_PORT` - Backend port (default: 3000)
|
|
- `FRONTEND_PORT` - Frontend port (default: 80)
|
|
- `CORS_ORIGIN` - CORS origin (default: http://localhost:80)
|
|
- `VITE_API_URL` - Frontend API URL (default: http://localhost:3000)
|
|
|
|
### Networks
|
|
|
|
- `feuerwehr_network` - Bridge network connecting all services
|
|
|
|
### Volumes
|
|
|
|
- `postgres_data_prod` - Persistent PostgreSQL data
|
|
|
|
## Usage
|
|
|
|
### 1. Test Docker Builds
|
|
|
|
Run the test script to verify Docker builds work:
|
|
|
|
```bash
|
|
./docker-test.sh
|
|
```
|
|
|
|
This script will:
|
|
- Check Docker availability
|
|
- Build backend image
|
|
- Build frontend image
|
|
- Report success/failure
|
|
- Optionally cleanup test images
|
|
|
|
### 2. Configure Environment
|
|
|
|
Copy and configure environment file:
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
# Edit .env and set required variables
|
|
```
|
|
|
|
### 3. Build and Start Services
|
|
|
|
```bash
|
|
# Build and start all services
|
|
docker-compose up -d
|
|
|
|
# View logs
|
|
docker-compose logs -f
|
|
|
|
# Check service status
|
|
docker-compose ps
|
|
```
|
|
|
|
### 4. Access Application
|
|
|
|
- **Frontend:** http://localhost:80
|
|
- **Backend API:** http://localhost:3000
|
|
- **Database:** localhost:5432
|
|
|
|
### 5. Stop Services
|
|
|
|
```bash
|
|
# Stop all services
|
|
docker-compose down
|
|
|
|
# Stop and remove volumes
|
|
docker-compose down -v
|
|
```
|
|
|
|
## Health Checks
|
|
|
|
All services include health checks:
|
|
|
|
### PostgreSQL
|
|
- **Command:** `pg_isready -U $USER -d $DB`
|
|
- **Interval:** 10s
|
|
- **Retries:** 5
|
|
|
|
### Backend
|
|
- **Command:** `wget localhost:3000/health`
|
|
- **Interval:** 30s
|
|
- **Retries:** 3
|
|
- **Start Period:** 40s
|
|
|
|
### Frontend
|
|
- **Command:** `wget localhost:80/health`
|
|
- **Interval:** 30s
|
|
- **Retries:** 3
|
|
- **Start Period:** 30s
|
|
|
|
## Troubleshooting
|
|
|
|
### Backend Build Fails
|
|
|
|
1. Check TypeScript compilation:
|
|
```bash
|
|
cd backend
|
|
npm install
|
|
npm run build
|
|
```
|
|
|
|
2. Verify all source files exist
|
|
3. Check tsconfig.json configuration
|
|
|
|
### Frontend Build Fails
|
|
|
|
1. Check Vite build:
|
|
```bash
|
|
cd frontend
|
|
npm install
|
|
npm run build
|
|
```
|
|
|
|
2. Verify all dependencies are installed
|
|
3. Check build arguments are correct
|
|
|
|
### Container Won't Start
|
|
|
|
1. Check logs:
|
|
```bash
|
|
docker-compose logs [service-name]
|
|
```
|
|
|
|
2. Verify environment variables in .env
|
|
3. Check health check status:
|
|
```bash
|
|
docker-compose ps
|
|
```
|
|
|
|
### Database Connection Issues
|
|
|
|
1. Verify DATABASE_URL format:
|
|
```
|
|
postgresql://user:password@postgres:5432/database
|
|
```
|
|
|
|
2. Check postgres service is healthy:
|
|
```bash
|
|
docker-compose ps postgres
|
|
```
|
|
|
|
3. Verify network connectivity:
|
|
```bash
|
|
docker-compose exec backend ping postgres
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
### Production Checklist
|
|
|
|
- [ ] Set strong POSTGRES_PASSWORD
|
|
- [ ] Set strong JWT_SECRET (min 32 characters)
|
|
- [ ] Use HTTPS in production
|
|
- [ ] Configure proper CORS_ORIGIN
|
|
- [ ] Enable firewall rules
|
|
- [ ] Regular security updates
|
|
- [ ] Monitor container logs
|
|
- [ ] Backup database regularly
|
|
|
|
### Image Security
|
|
|
|
- Alpine Linux base (minimal attack surface)
|
|
- Non-root user execution
|
|
- No unnecessary packages
|
|
- Security headers enabled
|
|
- Health checks configured
|
|
|
|
## Maintenance
|
|
|
|
### Update Dependencies
|
|
|
|
```bash
|
|
# Rebuild images with latest dependencies
|
|
docker-compose build --no-cache
|
|
docker-compose up -d
|
|
```
|
|
|
|
### Backup Database
|
|
|
|
```bash
|
|
# Create backup
|
|
docker-compose exec postgres pg_dump -U $USER $DB > backup.sql
|
|
|
|
# Restore backup
|
|
docker-compose exec -T postgres psql -U $USER $DB < backup.sql
|
|
```
|
|
|
|
### View Logs
|
|
|
|
```bash
|
|
# All services
|
|
docker-compose logs -f
|
|
|
|
# Specific service
|
|
docker-compose logs -f backend
|
|
|
|
# Last 100 lines
|
|
docker-compose logs --tail=100 backend
|
|
```
|
|
|
|
### Restart Services
|
|
|
|
```bash
|
|
# Restart all
|
|
docker-compose restart
|
|
|
|
# Restart specific service
|
|
docker-compose restart backend
|
|
```
|
|
|
|
## Performance Optimization
|
|
|
|
### Build Cache
|
|
|
|
Docker uses layer caching. To optimize:
|
|
|
|
1. Copy package files first
|
|
2. Install dependencies
|
|
3. Copy source code last
|
|
|
|
This ensures dependency installation is cached.
|
|
|
|
### Image Size
|
|
|
|
Current image sizes:
|
|
- Backend: ~150-200 MB
|
|
- Frontend: ~50-80 MB
|
|
- Total: ~200-280 MB
|
|
|
|
### Resource Limits
|
|
|
|
Add resource limits in docker-compose.yml:
|
|
|
|
```yaml
|
|
services:
|
|
backend:
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: '1.0'
|
|
memory: 512M
|
|
```
|
|
|
|
## Monitoring
|
|
|
|
### Container Stats
|
|
|
|
```bash
|
|
docker stats
|
|
```
|
|
|
|
### Health Status
|
|
|
|
```bash
|
|
docker-compose ps
|
|
```
|
|
|
|
### Resource Usage
|
|
|
|
```bash
|
|
docker system df
|
|
```
|
|
|
|
## Production Deployment
|
|
|
|
### Using Docker Compose
|
|
|
|
```bash
|
|
# On production server
|
|
git clone <repository>
|
|
cd feuerwehr_dashboard
|
|
cp .env.example .env
|
|
# Configure .env with production values
|
|
docker-compose up -d
|
|
```
|
|
|
|
### Using Container Registry
|
|
|
|
```bash
|
|
# Build and tag images
|
|
docker build -t registry.example.com/feuerwehr-backend:v1.0.0 backend/
|
|
docker build -t registry.example.com/feuerwehr-frontend:v1.0.0 frontend/
|
|
|
|
# Push to registry
|
|
docker push registry.example.com/feuerwehr-backend:v1.0.0
|
|
docker push registry.example.com/feuerwehr-frontend:v1.0.0
|
|
|
|
# Pull and run on production
|
|
docker pull registry.example.com/feuerwehr-backend:v1.0.0
|
|
docker pull registry.example.com/feuerwehr-frontend:v1.0.0
|
|
docker-compose up -d
|
|
```
|
|
|
|
## Additional Resources
|
|
|
|
- [Docker Documentation](https://docs.docker.com/)
|
|
- [Docker Compose Documentation](https://docs.docker.com/compose/)
|
|
- [Nginx Documentation](https://nginx.org/en/docs/)
|
|
- [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices)
|