version: "3.9" # Full local development stack # Start everything: docker-compose up -d # View logs: docker-compose logs -f inference-api # Tear down: docker-compose down -v services: # ── Postgres ──────────────────────────────────────────────────────────────── postgres: image: postgres:15-alpine environment: POSTGRES_DB: flights POSTGRES_USER: admin POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-localpassword} ports: ["5432:5432"] volumes: [postgres_data:/var/lib/postgresql/data] healthcheck: test: ["CMD-SHELL", "pg_isready -U admin -d flights"] interval: 10s timeout: 5s retries: 5 # ── MLflow tracking server ─────────────────────────────────────────────────── mlflow: image: python:3.11-slim depends_on: postgres: { condition: service_healthy } command: > sh -c "pip install -q mlflow psycopg2-binary boto3 && mlflow server --host 0.0.0.0 --port 5000 --backend-store-uri postgresql://admin:${POSTGRES_PASSWORD:-localpassword}@postgres:5432/flights --default-artifact-root /mlruns" ports: ["5000:5000"] volumes: [mlruns:/mlruns] environment: MLFLOW_TRACKING_URI: http://localhost:5000 # ── Airflow (single-container quickstart) ──────────────────────────────────── airflow: image: apache/airflow:2.9.1-python3.11 depends_on: postgres: { condition: service_healthy } environment: AIRFLOW__CORE__EXECUTOR: LocalExecutor AIRFLOW__DATABASE__SQL_ALCHEMY_CONN: postgresql+psycopg2://admin:${POSTGRES_PASSWORD:-localpassword}@postgres:5432/flights AIRFLOW__CORE__FERNET_KEY: ${AIRFLOW_FERNET_KEY:-} AIRFLOW__WEBSERVER__SECRET_KEY: ${AIRFLOW_SECRET_KEY:-changeme} AIRFLOW__CORE__LOAD_EXAMPLES: "false" MLFLOW_TRACKING_URI: http://mlflow:5000 ports: ["8080:8080"] volumes: - ./dags:/opt/airflow/dags - ./etl:/opt/airflow/etl - ./ml:/opt/airflow/ml - ./mlops:/opt/airflow/mlops - ./data:/opt/airflow/data command: > bash -c "airflow db init && airflow users create --username admin --password admin --firstname Admin --lastname User --role Admin --email admin@example.com && airflow webserver & airflow scheduler" # ── Inference API ──────────────────────────────────────────────────────────── inference-api: build: . depends_on: postgres: { condition: service_healthy } ports: ["8000:8000"] environment: DATABASE_URL: postgresql://admin:${POSTGRES_PASSWORD:-localpassword}@postgres:5432/flights MLFLOW_TRACKING_URI: http://mlflow:5000 MODEL_VERSION: local volumes: - ./models:/app/models # mount trained models - ./data:/app/data healthcheck: test: ["CMD-SHELL", "curl -f http://localhost:8000/health || exit 1"] interval: 30s timeout: 10s retries: 3 # ── Prometheus ─────────────────────────────────────────────────────────────── prometheus: image: prom/prometheus:v2.51.0 ports: ["9090:9090"] volumes: - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro - prometheus_data:/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' - '--storage.tsdb.retention.time=15d' # ── Grafana ────────────────────────────────────────────────────────────────── grafana: image: grafana/grafana:10.4.0 depends_on: [prometheus] ports: ["3000:3000"] environment: GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_PASSWORD:-admin} volumes: - grafana_data:/var/lib/grafana volumes: postgres_data: mlruns: prometheus_data: grafana_data: