Spaces:
Sleeping
Sleeping
Commit
·
62e82b2
1
Parent(s):
d0d37b9
Fix Docker container startup conflicts and improve error diagnostics
Browse files- .dockerignore +30 -0
- Dockerfile +34 -0
- app.py +9 -5
- docker-entrypoint.sh +92 -0
.dockerignore
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Python artifacts
|
| 2 |
+
__pycache__/
|
| 3 |
+
*.py[cod]
|
| 4 |
+
*$py.class
|
| 5 |
+
|
| 6 |
+
# Virtual environments
|
| 7 |
+
.venv/
|
| 8 |
+
venv/
|
| 9 |
+
env/
|
| 10 |
+
|
| 11 |
+
# OS / IDE
|
| 12 |
+
.DS_Store
|
| 13 |
+
Thumbs.db
|
| 14 |
+
.idea/
|
| 15 |
+
.vscode/
|
| 16 |
+
|
| 17 |
+
# Git
|
| 18 |
+
.git/
|
| 19 |
+
.gitignore
|
| 20 |
+
|
| 21 |
+
# Local data and caches
|
| 22 |
+
data/
|
| 23 |
+
logs/
|
| 24 |
+
|
| 25 |
+
# Node / misc large dirs (if ever created)
|
| 26 |
+
node_modules/
|
| 27 |
+
|
| 28 |
+
# Docker build outputs
|
| 29 |
+
*.log
|
| 30 |
+
|
Dockerfile
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.10-slim
|
| 2 |
+
|
| 3 |
+
ENV PYTHONDONTWRITEBYTECODE=1 \
|
| 4 |
+
PYTHONUNBUFFERED=1
|
| 5 |
+
|
| 6 |
+
WORKDIR /app
|
| 7 |
+
|
| 8 |
+
# Install system dependencies
|
| 9 |
+
RUN apt-get update && \
|
| 10 |
+
apt-get install -y --no-install-recommends \
|
| 11 |
+
build-essential \
|
| 12 |
+
curl \
|
| 13 |
+
git && \
|
| 14 |
+
rm -rf /var/lib/apt/lists/*
|
| 15 |
+
|
| 16 |
+
# Install Python dependencies
|
| 17 |
+
COPY requirements.txt /tmp/requirements.txt
|
| 18 |
+
RUN pip install --upgrade pip && \
|
| 19 |
+
pip install --no-cache-dir -r /tmp/requirements.txt
|
| 20 |
+
|
| 21 |
+
# Copy project files
|
| 22 |
+
COPY . .
|
| 23 |
+
|
| 24 |
+
# Ensure entrypoint script is executable
|
| 25 |
+
RUN chmod +x docker-entrypoint.sh
|
| 26 |
+
|
| 27 |
+
EXPOSE 7860 8000 8900
|
| 28 |
+
|
| 29 |
+
ENV API_PORT=8000 \
|
| 30 |
+
MCP_PORT=8900 \
|
| 31 |
+
GRADIO_PORT=7860
|
| 32 |
+
|
| 33 |
+
ENTRYPOINT ["./docker-entrypoint.sh"]
|
| 34 |
+
|
app.py
CHANGED
|
@@ -2732,13 +2732,17 @@ if __name__ == "__main__":
|
|
| 2732 |
import time
|
| 2733 |
import requests
|
| 2734 |
|
| 2735 |
-
#
|
| 2736 |
-
# HF Spaces sets SPACE_ID
|
|
|
|
| 2737 |
is_hf_space = os.getenv("SPACE_ID") is not None
|
| 2738 |
-
|
| 2739 |
|
| 2740 |
-
#
|
| 2741 |
-
if is_hf_space
|
|
|
|
|
|
|
|
|
|
| 2742 |
def start_mcp_server():
|
| 2743 |
"""Start MCP server in a background process."""
|
| 2744 |
try:
|
|
|
|
| 2732 |
import time
|
| 2733 |
import requests
|
| 2734 |
|
| 2735 |
+
# Detect environment
|
| 2736 |
+
# - HF Spaces sets SPACE_ID
|
| 2737 |
+
# - Docker entrypoint script manages services, so don't auto-start here
|
| 2738 |
is_hf_space = os.getenv("SPACE_ID") is not None
|
| 2739 |
+
is_docker = os.path.exists("/.dockerenv") or os.getenv("DOCKER_CONTAINER") == "1"
|
| 2740 |
|
| 2741 |
+
# For Hugging Face Spaces or Docker, bind to 0.0.0.0; for local dev, use 127.0.0.1
|
| 2742 |
+
server_name = "0.0.0.0" if (is_hf_space or is_docker) else "127.0.0.1"
|
| 2743 |
+
|
| 2744 |
+
# Start backend services if running in HF Spaces (but NOT in Docker - entrypoint handles that)
|
| 2745 |
+
if is_hf_space and not is_docker:
|
| 2746 |
def start_mcp_server():
|
| 2747 |
"""Start MCP server in a background process."""
|
| 2748 |
try:
|
docker-entrypoint.sh
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
|
| 4 |
+
log() {
|
| 5 |
+
echo "[IntegraChat][$(date -Iseconds)] $*"
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
API_PORT="${API_PORT:-8000}"
|
| 9 |
+
MCP_PORT="${MCP_PORT:-8900}"
|
| 10 |
+
GRADIO_PORT="${GRADIO_PORT:-7860}"
|
| 11 |
+
|
| 12 |
+
export API_PORT MCP_PORT GRADIO_PORT
|
| 13 |
+
|
| 14 |
+
LOG_DIR="/app/logs"
|
| 15 |
+
mkdir -p "${LOG_DIR}"
|
| 16 |
+
|
| 17 |
+
log "Container starting. Python: $(python --version 2>&1)"
|
| 18 |
+
log "API_PORT=${API_PORT}, MCP_PORT=${MCP_PORT}, GRADIO_PORT=${GRADIO_PORT}"
|
| 19 |
+
|
| 20 |
+
cleanup() {
|
| 21 |
+
log "Received termination signal. Stopping services..."
|
| 22 |
+
# Kill child processes
|
| 23 |
+
kill "${MCP_PID}" "${API_PID}" "${GRADIO_PID}" "${TAIL_PID}" 2>/dev/null || true
|
| 24 |
+
wait || true
|
| 25 |
+
log "All services stopped. Exiting."
|
| 26 |
+
}
|
| 27 |
+
trap cleanup SIGTERM SIGINT
|
| 28 |
+
|
| 29 |
+
# Start MCP server
|
| 30 |
+
log "Starting MCP server..."
|
| 31 |
+
python -m backend.mcp_server.server > "${LOG_DIR}/mcp.log" 2>&1 &
|
| 32 |
+
MCP_PID=$!
|
| 33 |
+
|
| 34 |
+
# Give MCP a moment
|
| 35 |
+
sleep 2
|
| 36 |
+
|
| 37 |
+
# Start FastAPI backend
|
| 38 |
+
log "Starting FastAPI backend..."
|
| 39 |
+
uvicorn backend.api.main:app --host 0.0.0.0 --port "${API_PORT}" > "${LOG_DIR}/fastapi.log" 2>&1 &
|
| 40 |
+
API_PID=$!
|
| 41 |
+
|
| 42 |
+
# Check if FastAPI process started
|
| 43 |
+
sleep 2
|
| 44 |
+
if ! kill -0 "${API_PID}" 2>/dev/null; then
|
| 45 |
+
log "ERROR: FastAPI process died immediately. Last 20 lines of log:"
|
| 46 |
+
tail -n 20 "${LOG_DIR}/fastapi.log" || true
|
| 47 |
+
exit 1
|
| 48 |
+
fi
|
| 49 |
+
|
| 50 |
+
# Wait for FastAPI to become healthy
|
| 51 |
+
log "Waiting for FastAPI health endpoint..."
|
| 52 |
+
for attempt in {1..60}; do
|
| 53 |
+
if curl -fsS "http://127.0.0.1:${API_PORT}/health" >/dev/null 2>&1; then
|
| 54 |
+
log "FastAPI backend is healthy."
|
| 55 |
+
break
|
| 56 |
+
fi
|
| 57 |
+
# Check if process is still running
|
| 58 |
+
if ! kill -0 "${API_PID}" 2>/dev/null; then
|
| 59 |
+
log "ERROR: FastAPI process died. Last 20 lines of log:"
|
| 60 |
+
tail -n 20 "${LOG_DIR}/fastapi.log" || true
|
| 61 |
+
exit 1
|
| 62 |
+
fi
|
| 63 |
+
sleep 1
|
| 64 |
+
if [ $((attempt % 10)) -eq 0 ]; then
|
| 65 |
+
log "FastAPI not ready yet (attempt ${attempt}/60). Last 5 lines of log:"
|
| 66 |
+
tail -n 5 "${LOG_DIR}/fastapi.log" || true
|
| 67 |
+
else
|
| 68 |
+
log "FastAPI not ready yet (attempt ${attempt}/60)..."
|
| 69 |
+
fi
|
| 70 |
+
done
|
| 71 |
+
|
| 72 |
+
# Final check
|
| 73 |
+
if ! curl -fsS "http://127.0.0.1:${API_PORT}/health" >/dev/null 2>&1; then
|
| 74 |
+
log "WARNING: FastAPI health check failed after 60 attempts. Last 30 lines of log:"
|
| 75 |
+
tail -n 30 "${LOG_DIR}/fastapi.log" || true
|
| 76 |
+
log "Continuing anyway..."
|
| 77 |
+
fi
|
| 78 |
+
|
| 79 |
+
# Start Gradio UI (foreground)
|
| 80 |
+
log "Starting Gradio UI on port ${GRADIO_PORT}..."
|
| 81 |
+
# Set DOCKER_CONTAINER flag so app.py knows not to auto-start services
|
| 82 |
+
export DOCKER_CONTAINER=1
|
| 83 |
+
python app.py > "${LOG_DIR}/gradio.log" 2>&1 &
|
| 84 |
+
GRADIO_PID=$!
|
| 85 |
+
|
| 86 |
+
log "All services launched. Streaming logs (Ctrl+C to stop)..."
|
| 87 |
+
tail -n 50 -F "${LOG_DIR}/"*.log &
|
| 88 |
+
TAIL_PID=$!
|
| 89 |
+
|
| 90 |
+
wait "${GRADIO_PID}"
|
| 91 |
+
cleanup
|
| 92 |
+
|