chore: consolidate to single Docker container
Replace two-container setup (separate backend + nginx frontend) with a single image that runs both via supervisord: - New Dockerfile: Node stage builds React, Python+nginx stage is the runtime - supervisord.conf: manages uvicorn (api_server.py) + nginx as sibling procs - nginx.conf: proxy_pass updated to localhost:8000 (same container) - docker-compose.yml: simplified to one service on port 80 Deploy: docker-compose up -d # or docker build -t flight-radar . && docker run -p 80:80 flight-radar Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
54
flight-comparator/Dockerfile
Normal file
54
flight-comparator/Dockerfile
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# ── Stage 1: Build React frontend ─────────────────────────────────────────
|
||||||
|
FROM node:20-alpine AS frontend-builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
COPY frontend/package*.json ./
|
||||||
|
RUN npm ci
|
||||||
|
COPY frontend/ .
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
# ── Stage 2: Single runtime image ─────────────────────────────────────────
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
ENV PYTHONUNBUFFERED=1 \
|
||||||
|
PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PIP_NO_CACHE_DIR=1
|
||||||
|
|
||||||
|
# Install nginx, supervisor, and gcc (for some pip packages)
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
nginx \
|
||||||
|
supervisor \
|
||||||
|
gcc \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Python dependencies
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Backend source
|
||||||
|
COPY api_server.py airports.py cache.py ./
|
||||||
|
COPY database/ ./database/
|
||||||
|
|
||||||
|
# Frontend build output
|
||||||
|
COPY --from=frontend-builder /app/dist /usr/share/nginx/html
|
||||||
|
|
||||||
|
# Config files
|
||||||
|
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY supervisord.conf /etc/supervisor/conf.d/app.conf
|
||||||
|
|
||||||
|
# Remove the default nginx site
|
||||||
|
RUN rm -f /etc/nginx/sites-enabled/default
|
||||||
|
|
||||||
|
# Pre-fetch airport data and initialise the database at build time
|
||||||
|
RUN mkdir -p data && \
|
||||||
|
python -c "from airports import download_and_build_airport_data; download_and_build_airport_data()" && \
|
||||||
|
python database/init_db.py
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
||||||
|
CMD wget -q --spider http://localhost/ || exit 1
|
||||||
|
|
||||||
|
CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/conf.d/app.conf"]
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
# Backend Dockerfile for Flight Radar API
|
|
||||||
FROM python:3.11-slim
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Set environment variables
|
|
||||||
ENV PYTHONUNBUFFERED=1 \
|
|
||||||
PYTHONDONTWRITEBYTECODE=1 \
|
|
||||||
PIP_NO_CACHE_DIR=1 \
|
|
||||||
PIP_DISABLE_PIP_VERSION_CHECK=1
|
|
||||||
|
|
||||||
# Install system dependencies
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
gcc \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Copy requirements file
|
|
||||||
COPY requirements.txt .
|
|
||||||
|
|
||||||
# Install Python dependencies
|
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
|
||||||
|
|
||||||
# Copy application code
|
|
||||||
COPY api_server.py .
|
|
||||||
COPY airports.py .
|
|
||||||
COPY cache.py .
|
|
||||||
COPY database/ ./database/
|
|
||||||
|
|
||||||
# Create necessary directories
|
|
||||||
RUN mkdir -p data
|
|
||||||
|
|
||||||
# Download airport data on build
|
|
||||||
RUN python -c "from airports import download_and_build_airport_data; download_and_build_airport_data()"
|
|
||||||
|
|
||||||
# Initialize database
|
|
||||||
RUN python database/init_db.py
|
|
||||||
|
|
||||||
# Expose port
|
|
||||||
EXPOSE 8000
|
|
||||||
|
|
||||||
# Health check
|
|
||||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
||||||
CMD python -c "import requests; requests.get('http://localhost:8000/health').raise_for_status()"
|
|
||||||
|
|
||||||
# Run the application
|
|
||||||
CMD ["python", "api_server.py"]
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
# Frontend Dockerfile for Flight Radar UI
|
|
||||||
# Stage 1: Build React application
|
|
||||||
FROM node:20-alpine AS builder
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Copy package files
|
|
||||||
COPY frontend/package*.json ./
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
RUN npm ci
|
|
||||||
|
|
||||||
# Copy source code
|
|
||||||
COPY frontend/ .
|
|
||||||
|
|
||||||
# Build production app
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
# Stage 2: Serve with nginx
|
|
||||||
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
|
|
||||||
EXPOSE 80
|
|
||||||
|
|
||||||
# Health check
|
|
||||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
||||||
CMD wget --quiet --tries=1 --spider http://localhost/ || exit 1
|
|
||||||
|
|
||||||
# Start nginx
|
|
||||||
CMD ["nginx", "-g", "daemon off;"]
|
|
||||||
@@ -1,54 +1,15 @@
|
|||||||
services:
|
services:
|
||||||
# Backend API Server
|
app:
|
||||||
backend:
|
build: .
|
||||||
build:
|
container_name: flight-radar
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile.backend
|
|
||||||
container_name: flight-radar-backend
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "8000:8000"
|
|
||||||
environment:
|
|
||||||
- PORT=8000
|
|
||||||
- DATABASE_PATH=/app/data/cache.db
|
|
||||||
- ALLOWED_ORIGINS=http://localhost,http://localhost:80,http://frontend
|
|
||||||
volumes:
|
|
||||||
- backend-data:/app/data
|
|
||||||
- ./cache.db:/app/cache.db:rw
|
|
||||||
networks:
|
|
||||||
- flight-radar-network
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "python", "-c", "import requests; requests.get('http://localhost:8000/health').raise_for_status()"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 3s
|
|
||||||
retries: 3
|
|
||||||
start_period: 10s
|
|
||||||
|
|
||||||
# Frontend UI
|
|
||||||
frontend:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile.frontend
|
|
||||||
container_name: flight-radar-frontend
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "80:80"
|
- "80:80"
|
||||||
depends_on:
|
environment:
|
||||||
backend:
|
- DATABASE_PATH=/app/data/cache.db
|
||||||
condition: service_healthy
|
volumes:
|
||||||
networks:
|
- flight-radar-data:/app/data
|
||||||
- flight-radar-network
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 3s
|
|
||||||
retries: 3
|
|
||||||
start_period: 5s
|
|
||||||
|
|
||||||
networks:
|
|
||||||
flight-radar-network:
|
|
||||||
driver: bridge
|
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
backend-data:
|
flight-radar-data:
|
||||||
driver: local
|
driver: local
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ server {
|
|||||||
|
|
||||||
# API proxy
|
# API proxy
|
||||||
location /api/ {
|
location /api/ {
|
||||||
proxy_pass http://backend:8000;
|
proxy_pass http://localhost:8000;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection 'upgrade';
|
proxy_set_header Connection 'upgrade';
|
||||||
@@ -30,7 +30,7 @@ server {
|
|||||||
|
|
||||||
# Health check endpoint proxy
|
# Health check endpoint proxy
|
||||||
location /health {
|
location /health {
|
||||||
proxy_pass http://backend:8000;
|
proxy_pass http://localhost:8000;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
}
|
}
|
||||||
|
|||||||
24
flight-comparator/supervisord.conf
Normal file
24
flight-comparator/supervisord.conf
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[supervisord]
|
||||||
|
nodaemon=true
|
||||||
|
logfile=/dev/null
|
||||||
|
logfile_maxbytes=0
|
||||||
|
pidfile=/tmp/supervisord.pid
|
||||||
|
|
||||||
|
[program:api]
|
||||||
|
command=python /app/api_server.py
|
||||||
|
directory=/app
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
|
|
||||||
|
[program:nginx]
|
||||||
|
command=nginx -g "daemon off;"
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
stdout_logfile=/dev/stdout
|
||||||
|
stdout_logfile_maxbytes=0
|
||||||
|
stderr_logfile=/dev/stderr
|
||||||
|
stderr_logfile_maxbytes=0
|
||||||
Reference in New Issue
Block a user