Skip to content

Commit 76fbe29

Browse files
Bingxi ZhaoBingxi Zhao
authored andcommitted
add docker
1 parent d480969 commit 76fbe29

File tree

7 files changed

+295
-237
lines changed

7 files changed

+295
-237
lines changed

Dockerfile

Lines changed: 125 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
# This Dockerfile builds a production-ready image for DeepTutor
55
# containing both the FastAPI backend and Next.js frontend
66
#
7-
# Build: docker build -t deeptutor .
8-
# Run: docker run -p 8001:8001 -p 3782:3782 --env-file .env deeptutor
7+
# Build: docker compose build
8+
# Run: docker compose up -d
9+
#
10+
# Prerequisites:
11+
# 1. Copy .env.example to .env and configure your API keys
12+
# 2. Optionally customize config/main.yaml
913
# ============================================
1014

1115
# ============================================
@@ -15,6 +19,9 @@ FROM node:20-alpine AS frontend-builder
1519

1620
WORKDIR /app/web
1721

22+
# Accept build argument for backend port
23+
ARG BACKEND_PORT=8001
24+
1825
# Copy package files first for better caching
1926
COPY web/package.json web/package-lock.json* ./
2027

@@ -24,11 +31,12 @@ RUN npm ci --legacy-peer-deps
2431
# Copy frontend source code
2532
COPY web/ ./
2633

27-
# Build the Next.js application
28-
# Create a placeholder .env.local for build (will be overridden at runtime)
29-
RUN echo "NEXT_PUBLIC_API_BASE=http://localhost:8001" > .env.local
34+
# Create .env.local with placeholder that will be replaced at runtime
35+
# Use a unique placeholder that can be safely replaced
36+
RUN echo "NEXT_PUBLIC_API_BASE=__NEXT_PUBLIC_API_BASE_PLACEHOLDER__" > .env.local
3037

31-
# Build Next.js for production
38+
# Build Next.js for production with standalone output
39+
# This allows runtime environment variable injection
3240
RUN npm run build
3341

3442
# ============================================
@@ -46,10 +54,16 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
4654
WORKDIR /app
4755

4856
# Install system dependencies
57+
# Note: libgl1 and libglib2.0-0 are required for OpenCV (used by mineru)
4958
RUN apt-get update && apt-get install -y --no-install-recommends \
5059
curl \
5160
git \
5261
build-essential \
62+
libgl1 \
63+
libglib2.0-0 \
64+
libsm6 \
65+
libxext6 \
66+
libxrender1 \
5367
&& rm -rf /var/lib/apt/lists/*
5468

5569
# Copy requirements and install Python dependencies
@@ -78,12 +92,18 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
7892

7993
WORKDIR /app
8094

81-
# Install Node.js 20.x (LTS) and npm
95+
# Install Node.js 20.x (LTS) and required tools
96+
# Note: libgl1 and libglib2.0-0 are required for OpenCV (used by mineru)
8297
RUN apt-get update && apt-get install -y --no-install-recommends \
8398
curl \
8499
ca-certificates \
85100
gnupg \
86101
supervisor \
102+
libgl1 \
103+
libglib2.0-0 \
104+
libsm6 \
105+
libxext6 \
106+
libxrender1 \
87107
&& mkdir -p /etc/apt/keyrings \
88108
&& curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg \
89109
&& echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
@@ -103,18 +123,14 @@ COPY --from=frontend-builder /app/web/package.json ./web/package.json
103123
COPY --from=frontend-builder /app/web/next.config.js ./web/next.config.js
104124
COPY --from=frontend-builder /app/web/node_modules ./web/node_modules
105125

106-
# Create a startup script for the frontend
107-
RUN echo '#!/bin/bash\ncd /app/web && exec node node_modules/next/dist/bin/next start -H 0.0.0.0 -p ${FRONTEND_PORT:-3782}' > /app/start-frontend.sh && \
108-
chmod +x /app/start-frontend.sh
109-
110126
# Copy application source code
111127
COPY src/ ./src/
112128
COPY config/ ./config/
113129
COPY scripts/ ./scripts/
114130
COPY pyproject.toml ./
115131
COPY requirements.txt ./
116132

117-
# Create necessary directories
133+
# Create necessary directories (these will be overwritten by volume mounts)
118134
RUN mkdir -p \
119135
data/user/solve \
120136
data/user/question \
@@ -130,22 +146,25 @@ RUN mkdir -p \
130146
data/knowledge_bases
131147

132148
# Create supervisord configuration for running both services
149+
# Log output goes to stdout/stderr so docker logs can capture them
133150
RUN mkdir -p /etc/supervisor/conf.d
134151

135152
COPY <<EOF /etc/supervisor/conf.d/deeptutor.conf
136153
[supervisord]
137154
nodaemon=true
138-
logfile=/var/log/supervisor/supervisord.log
155+
logfile=/dev/null
156+
logfile_maxbytes=0
139157
pidfile=/var/run/supervisord.pid
140-
childlogdir=/var/log/supervisor
141158

142159
[program:backend]
143-
command=python -m uvicorn src.api.main:app --host 0.0.0.0 --port %(ENV_BACKEND_PORT)s
160+
command=/bin/bash /app/start-backend.sh
144161
directory=/app
145162
autostart=true
146163
autorestart=true
147-
stderr_logfile=/var/log/supervisor/backend.err.log
148-
stdout_logfile=/var/log/supervisor/backend.out.log
164+
stdout_logfile=/dev/fd/1
165+
stdout_logfile_maxbytes=0
166+
stderr_logfile=/dev/fd/2
167+
stderr_logfile_maxbytes=0
149168
environment=PYTHONPATH="/app",PYTHONUNBUFFERED="1"
150169

151170
[program:frontend]
@@ -154,13 +173,63 @@ directory=/app/web
154173
autostart=true
155174
autorestart=true
156175
startsecs=5
157-
stderr_logfile=/var/log/supervisor/frontend.err.log
158-
stdout_logfile=/var/log/supervisor/frontend.out.log
176+
stdout_logfile=/dev/fd/1
177+
stdout_logfile_maxbytes=0
178+
stderr_logfile=/dev/fd/2
179+
stderr_logfile_maxbytes=0
159180
environment=NODE_ENV="production"
160181
EOF
161182

162-
# Create log directories
163-
RUN mkdir -p /var/log/supervisor
183+
# Create backend startup script
184+
COPY <<'EOF' /app/start-backend.sh
185+
#!/bin/bash
186+
set -e
187+
188+
BACKEND_PORT=${BACKEND_PORT:-8001}
189+
190+
echo "[Backend] 🚀 Starting FastAPI backend on port ${BACKEND_PORT}..."
191+
192+
# Run uvicorn directly - the application's logging system already handles:
193+
# 1. Console output (visible in docker logs)
194+
# 2. File logging to data/user/logs/ai_tutor_*.log
195+
exec python -m uvicorn src.api.main:app --host 0.0.0.0 --port ${BACKEND_PORT}
196+
EOF
197+
198+
RUN chmod +x /app/start-backend.sh
199+
200+
# Create frontend startup script
201+
# This script handles runtime environment variable injection for Next.js
202+
COPY <<'EOF' /app/start-frontend.sh
203+
#!/bin/bash
204+
set -e
205+
206+
# Get the backend port (default to 8001)
207+
BACKEND_PORT=${BACKEND_PORT:-8001}
208+
FRONTEND_PORT=${FRONTEND_PORT:-3782}
209+
210+
# Determine the API base URL
211+
if [ -n "$NEXT_PUBLIC_API_BASE_EXTERNAL" ]; then
212+
API_BASE="$NEXT_PUBLIC_API_BASE_EXTERNAL"
213+
else
214+
API_BASE="http://localhost:${BACKEND_PORT}"
215+
fi
216+
217+
echo "[Frontend] 🚀 Starting Next.js frontend on port ${FRONTEND_PORT}..."
218+
echo "[Frontend] 📌 API base URL: ${API_BASE}"
219+
220+
# Replace placeholder in built Next.js files
221+
# This is necessary because NEXT_PUBLIC_* vars are inlined at build time
222+
find /app/web/.next -type f \( -name "*.js" -o -name "*.json" \) -exec \
223+
sed -i "s|__NEXT_PUBLIC_API_BASE_PLACEHOLDER__|${API_BASE}|g" {} \; 2>/dev/null || true
224+
225+
# Also update .env.local for any runtime reads
226+
echo "NEXT_PUBLIC_API_BASE=${API_BASE}" > /app/web/.env.local
227+
228+
# Start Next.js
229+
cd /app/web && exec node node_modules/next/dist/bin/next start -H 0.0.0.0 -p ${FRONTEND_PORT}
230+
EOF
231+
232+
RUN chmod +x /app/start-frontend.sh
164233

165234
# Create entrypoint script
166235
COPY <<'EOF' /app/entrypoint.sh
@@ -175,24 +244,38 @@ echo "============================================"
175244
export BACKEND_PORT=${BACKEND_PORT:-8001}
176245
export FRONTEND_PORT=${FRONTEND_PORT:-3782}
177246

178-
# Update Next.js environment file with actual backend URL
179-
echo "NEXT_PUBLIC_API_BASE=http://localhost:${BACKEND_PORT}" > /app/web/.env.local
180-
181-
# If NEXT_PUBLIC_API_BASE is provided externally, use it
182-
if [ -n "$NEXT_PUBLIC_API_BASE_EXTERNAL" ]; then
183-
echo "NEXT_PUBLIC_API_BASE=${NEXT_PUBLIC_API_BASE_EXTERNAL}" > /app/web/.env.local
184-
fi
185-
186247
echo "📌 Backend Port: ${BACKEND_PORT}"
187248
echo "📌 Frontend Port: ${FRONTEND_PORT}"
188-
echo "============================================"
189249

190250
# Check for required environment variables
191251
if [ -z "$LLM_BINDING_API_KEY" ]; then
192252
echo "⚠️ Warning: LLM_BINDING_API_KEY not set"
193-
echo " Please provide LLM configuration via environment variables"
253+
echo " Please provide LLM configuration via environment variables or .env file"
254+
fi
255+
256+
if [ -z "$LLM_MODEL" ]; then
257+
echo "⚠️ Warning: LLM_MODEL not set"
258+
echo " Please configure LLM_MODEL in your .env file"
259+
fi
260+
261+
# Initialize user data directories if empty
262+
echo "📁 Checking data directories..."
263+
if [ ! -f "/app/data/user/user_history.json" ]; then
264+
echo " Initializing user data directories..."
265+
python -c "
266+
from pathlib import Path
267+
from src.core.setup import init_user_directories
268+
init_user_directories(Path('/app'))
269+
" 2>/dev/null || echo " ⚠️ Directory initialization skipped (will be created on first use)"
194270
fi
195271

272+
echo "============================================"
273+
echo "📦 Configuration loaded from:"
274+
echo " - Environment variables (.env file)"
275+
echo " - config/main.yaml"
276+
echo " - config/agents.yaml"
277+
echo "============================================"
278+
196279
# Start supervisord
197280
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/deeptutor.conf
198281
EOF
@@ -227,20 +310,23 @@ RUN pip install --no-cache-dir \
227310
ruff
228311

229312
# Override supervisord config for development (with reload)
313+
# Log output goes to stdout/stderr so docker logs can capture them
230314
COPY <<EOF /etc/supervisor/conf.d/deeptutor.conf
231315
[supervisord]
232316
nodaemon=true
233-
logfile=/var/log/supervisor/supervisord.log
317+
logfile=/dev/null
318+
logfile_maxbytes=0
234319
pidfile=/var/run/supervisord.pid
235-
childlogdir=/var/log/supervisor
236320

237321
[program:backend]
238322
command=python -m uvicorn src.api.main:app --host 0.0.0.0 --port %(ENV_BACKEND_PORT)s --reload
239323
directory=/app
240324
autostart=true
241325
autorestart=true
242-
stderr_logfile=/var/log/supervisor/backend.err.log
243-
stdout_logfile=/var/log/supervisor/backend.out.log
326+
stdout_logfile=/dev/fd/1
327+
stdout_logfile_maxbytes=0
328+
stderr_logfile=/dev/fd/2
329+
stderr_logfile_maxbytes=0
244330
environment=PYTHONPATH="/app",PYTHONUNBUFFERED="1"
245331

246332
[program:frontend]
@@ -249,8 +335,10 @@ directory=/app/web
249335
autostart=true
250336
autorestart=true
251337
startsecs=5
252-
stderr_logfile=/var/log/supervisor/frontend.err.log
253-
stdout_logfile=/var/log/supervisor/frontend.out.log
338+
stdout_logfile=/dev/fd/1
339+
stdout_logfile_maxbytes=0
340+
stderr_logfile=/dev/fd/2
341+
stderr_logfile_maxbytes=0
254342
environment=NODE_ENV="development"
255343
EOF
256344

0 commit comments

Comments
 (0)