|
| 1 | +######################################################################## |
| 2 | +# Stage 1: Frontend build |
| 3 | +######################################################################## |
| 4 | +FROM node:slim AS frontend-builder |
| 5 | +WORKDIR /app/frontend/nextjs |
| 6 | + |
| 7 | +# Copy package files and install dependencies |
| 8 | +COPY frontend/nextjs/package.json frontend/nextjs/package-lock.json* ./ |
| 9 | +RUN npm install --legacy-peer-deps |
| 10 | + |
| 11 | +# Copy the rest of the frontend application and build it |
| 12 | +COPY frontend/nextjs/ ./ |
| 13 | +RUN npm run build |
| 14 | + |
| 15 | +######################################################################## |
| 16 | +# Stage 2: Browser and backend build tools installation |
| 17 | +######################################################################## |
| 18 | +FROM python:3.13.3-slim-bookworm AS install-browser |
| 19 | + |
| 20 | +# Install Chromium, Chromedriver, Firefox, Geckodriver, and build tools in one layer |
| 21 | +RUN echo 'Acquire::Retries "3";' > /etc/apt/apt.conf.d/80-retries \ |
| 22 | + && echo 'Acquire::http::Timeout "60";' >> /etc/apt/apt.conf.d/80-retries \ |
| 23 | + && echo 'Acquire::https::Timeout "60";' >> /etc/apt/apt.conf.d/80-retries \ |
| 24 | + && echo 'Acquire::ftp::Timeout "60";' >> /etc/apt/apt.conf.d/80-retries \ |
| 25 | + && apt-get update \ |
| 26 | + && apt-get install -y gnupg wget ca-certificates --no-install-recommends \ |
| 27 | + && ARCH=$(dpkg --print-architecture) \ |
| 28 | + && if [ "$ARCH" = "arm64" ]; then \ |
| 29 | + apt-get install -y chromium chromium-driver \ |
| 30 | + && chromium --version && chromedriver --version; \ |
| 31 | + else \ |
| 32 | + wget -qO - https://dl.google.com/linux/linux_signing_key.pub | apt-key add - \ |
| 33 | + && echo "deb [arch=${ARCH}] http://dl.google.com/linux/chrome/deb/ stable main" \ |
| 34 | + > /etc/apt/sources.list.d/google-chrome.list \ |
| 35 | + && apt-get update \ |
| 36 | + && apt-get install -y google-chrome-stable; \ |
| 37 | + fi \ |
| 38 | + && apt-get install -y --no-install-recommends firefox-esr build-essential \ |
| 39 | + && GECKO_ARCH=$(case ${ARCH} in amd64) echo "linux64" ;; arm64) echo "linux-aarch64" ;; *) echo "linux64" ;; esac) \ |
| 40 | + && wget https://github.com/mozilla/geckodriver/releases/download/v0.36.0/geckodriver-v0.36.0-${GECKO_ARCH}.tar.gz \ |
| 41 | + && tar -xvzf geckodriver-v0.36.0-${GECKO_ARCH}.tar.gz \ |
| 42 | + && chmod +x geckodriver \ |
| 43 | + && mv geckodriver /usr/local/bin/ \ |
| 44 | + && rm geckodriver-v0.36.0-${GECKO_ARCH}.tar.gz \ |
| 45 | + && rm -rf /var/lib/apt/lists/* |
| 46 | + |
| 47 | +######################################################################## |
| 48 | +# Stage 3: Python dependencies installation |
| 49 | +######################################################################## |
| 50 | +FROM install-browser AS backend-builder |
| 51 | +WORKDIR /usr/src/app |
| 52 | + |
| 53 | +ENV PIP_ROOT_USER_ACTION=ignore |
| 54 | + |
| 55 | +COPY ./requirements.txt ./requirements.txt |
| 56 | +COPY ./multi_agents/requirements.txt ./multi_agents/requirements.txt |
| 57 | + |
| 58 | +# Install Python packages with retry logic and timeout configuration |
| 59 | +RUN pip config set global.timeout 60 && \ |
| 60 | + pip config set global.retries 3 && \ |
| 61 | + pip install --upgrade pip && \ |
| 62 | + pip install --no-cache-dir -r requirements.txt --upgrade --prefer-binary && \ |
| 63 | + pip install --no-cache-dir -r multi_agents/requirements.txt --upgrade --prefer-binary |
| 64 | + |
| 65 | +######################################################################## |
| 66 | +# Stage 4: Final image with backend, frontend |
| 67 | +######################################################################## |
| 68 | +FROM backend-builder AS final |
| 69 | + |
| 70 | +WORKDIR /usr/src/app |
| 71 | + |
| 72 | +# Install Node.js and supervisord with retry logic |
| 73 | +RUN apt-get update && \ |
| 74 | + apt-get install -y curl supervisor nginx && \ |
| 75 | + curl -fsSL --retry 3 --retry-delay 10 https://deb.nodesource.com/setup_20.x | bash - && \ |
| 76 | + apt-get install -y nodejs && \ |
| 77 | + rm -rf /var/lib/apt/lists/* |
| 78 | + |
| 79 | +# Set backend server configuration |
| 80 | +ARG HOST=0.0.0.0 |
| 81 | +ENV HOST=${HOST} |
| 82 | + |
| 83 | +ARG PORT=8000 |
| 84 | +ENV PORT=${PORT} |
| 85 | +EXPOSE ${PORT} |
| 86 | + |
| 87 | +ARG NEXT_PORT=3000 |
| 88 | +ENV NEXT_PORT=${NEXT_PORT} |
| 89 | +EXPOSE ${NEXT_PORT} |
| 90 | + |
| 91 | +# Internal Next.js port (not exposed) |
| 92 | +ARG NEXT_INTERNAL_PORT=3001 |
| 93 | +ENV NEXT_INTERNAL_PORT=${NEXT_INTERNAL_PORT} |
| 94 | + |
| 95 | +# Copy application files |
| 96 | +COPY ./ ./ |
| 97 | + |
| 98 | +# Copy built frontend from the frontend-builder stage |
| 99 | +COPY --from=frontend-builder /app/frontend/nextjs/.next ./frontend/nextjs/.next |
| 100 | +COPY --from=frontend-builder /app/frontend/nextjs/node_modules ./frontend/nextjs/node_modules |
| 101 | +COPY --from=frontend-builder /app/frontend/nextjs/public ./frontend/nextjs/public |
| 102 | +COPY --from=frontend-builder /app/frontend/nextjs/package.json ./frontend/nextjs/package.json |
| 103 | +# Ensure next.config.mjs and other necessary files are present |
| 104 | +COPY --from=frontend-builder /app/frontend/nextjs/next.config.mjs ./frontend/nextjs/next.config.mjs |
| 105 | + |
| 106 | +# Create nginx configuration |
| 107 | +RUN echo 'events {' > /etc/nginx/nginx.conf && \ |
| 108 | + echo ' worker_connections 1024;' >> /etc/nginx/nginx.conf && \ |
| 109 | + echo '}' >> /etc/nginx/nginx.conf && \ |
| 110 | + echo '' >> /etc/nginx/nginx.conf && \ |
| 111 | + echo 'http {' >> /etc/nginx/nginx.conf && \ |
| 112 | + echo ' include /etc/nginx/mime.types;' >> /etc/nginx/nginx.conf && \ |
| 113 | + echo ' default_type application/octet-stream;' >> /etc/nginx/nginx.conf && \ |
| 114 | + echo '' >> /etc/nginx/nginx.conf && \ |
| 115 | + echo ' # Logging' >> /etc/nginx/nginx.conf && \ |
| 116 | + echo ' access_log /var/log/nginx/access.log;' >> /etc/nginx/nginx.conf && \ |
| 117 | + echo ' error_log /var/log/nginx/error.log;' >> /etc/nginx/nginx.conf && \ |
| 118 | + echo '' >> /etc/nginx/nginx.conf && \ |
| 119 | + echo ' # Gzip compression' >> /etc/nginx/nginx.conf && \ |
| 120 | + echo ' gzip on;' >> /etc/nginx/nginx.conf && \ |
| 121 | + echo ' gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;' >> /etc/nginx/nginx.conf && \ |
| 122 | + echo '' >> /etc/nginx/nginx.conf && \ |
| 123 | + echo ' # WebSocket support' >> /etc/nginx/nginx.conf && \ |
| 124 | + echo ' map $http_upgrade $connection_upgrade {' >> /etc/nginx/nginx.conf && \ |
| 125 | + echo ' default upgrade;' >> /etc/nginx/nginx.conf && \ |
| 126 | + echo ' '"'"''"'"' close;' >> /etc/nginx/nginx.conf && \ |
| 127 | + echo ' }' >> /etc/nginx/nginx.conf && \ |
| 128 | + echo '' >> /etc/nginx/nginx.conf && \ |
| 129 | + echo ' server {' >> /etc/nginx/nginx.conf && \ |
| 130 | + echo ' listen 3000;' >> /etc/nginx/nginx.conf && \ |
| 131 | + echo ' server_name _;' >> /etc/nginx/nginx.conf && \ |
| 132 | + echo '' >> /etc/nginx/nginx.conf && \ |
| 133 | + echo ' # Proxy backend routes to FastAPI server' >> /etc/nginx/nginx.conf && \ |
| 134 | + echo ' location /outputs {' >> /etc/nginx/nginx.conf && \ |
| 135 | + echo ' proxy_pass http://127.0.0.1:8000;' >> /etc/nginx/nginx.conf && \ |
| 136 | + echo ' proxy_set_header Host $host;' >> /etc/nginx/nginx.conf && \ |
| 137 | + echo ' proxy_set_header X-Real-IP $remote_addr;' >> /etc/nginx/nginx.conf && \ |
| 138 | + echo ' proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;' >> /etc/nginx/nginx.conf && \ |
| 139 | + echo ' proxy_set_header X-Forwarded-Proto $scheme;' >> /etc/nginx/nginx.conf && \ |
| 140 | + echo ' }' >> /etc/nginx/nginx.conf && \ |
| 141 | + echo '' >> /etc/nginx/nginx.conf && \ |
| 142 | + echo ' location /reports {' >> /etc/nginx/nginx.conf && \ |
| 143 | + echo ' proxy_pass http://127.0.0.1:8000;' >> /etc/nginx/nginx.conf && \ |
| 144 | + echo ' proxy_set_header Host $host;' >> /etc/nginx/nginx.conf && \ |
| 145 | + echo ' proxy_set_header X-Real-IP $remote_addr;' >> /etc/nginx/nginx.conf && \ |
| 146 | + echo ' proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;' >> /etc/nginx/nginx.conf && \ |
| 147 | + echo ' proxy_set_header X-Forwarded-Proto $scheme;' >> /etc/nginx/nginx.conf && \ |
| 148 | + echo ' }' >> /etc/nginx/nginx.conf && \ |
| 149 | + echo '' >> /etc/nginx/nginx.conf && \ |
| 150 | + echo ' location /ws {' >> /etc/nginx/nginx.conf && \ |
| 151 | + echo ' proxy_pass http://127.0.0.1:8000;' >> /etc/nginx/nginx.conf && \ |
| 152 | + echo ' proxy_http_version 1.1;' >> /etc/nginx/nginx.conf && \ |
| 153 | + echo ' proxy_set_header Upgrade $http_upgrade;' >> /etc/nginx/nginx.conf && \ |
| 154 | + echo ' proxy_set_header Connection $connection_upgrade;' >> /etc/nginx/nginx.conf && \ |
| 155 | + echo ' proxy_set_header Host $host;' >> /etc/nginx/nginx.conf && \ |
| 156 | + echo ' proxy_set_header X-Real-IP $remote_addr;' >> /etc/nginx/nginx.conf && \ |
| 157 | + echo ' proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;' >> /etc/nginx/nginx.conf && \ |
| 158 | + echo ' proxy_set_header X-Forwarded-Proto $scheme;' >> /etc/nginx/nginx.conf && \ |
| 159 | + echo ' }' >> /etc/nginx/nginx.conf && \ |
| 160 | + echo '' >> /etc/nginx/nginx.conf && \ |
| 161 | + echo ' # Proxy all other requests to Next.js' >> /etc/nginx/nginx.conf && \ |
| 162 | + echo ' location / {' >> /etc/nginx/nginx.conf && \ |
| 163 | + echo ' proxy_pass http://127.0.0.1:3001;' >> /etc/nginx/nginx.conf && \ |
| 164 | + echo ' proxy_set_header Host $host;' >> /etc/nginx/nginx.conf && \ |
| 165 | + echo ' proxy_set_header X-Real-IP $remote_addr;' >> /etc/nginx/nginx.conf && \ |
| 166 | + echo ' proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;' >> /etc/nginx/nginx.conf && \ |
| 167 | + echo ' proxy_set_header X-Forwarded-Proto $scheme;' >> /etc/nginx/nginx.conf && \ |
| 168 | + echo ' }' >> /etc/nginx/nginx.conf && \ |
| 169 | + echo ' }' >> /etc/nginx/nginx.conf && \ |
| 170 | + echo '}' >> /etc/nginx/nginx.conf |
| 171 | + |
| 172 | +# Create supervisord configuration |
| 173 | +# stdout/stderr_maxbytes prevents log file rotation and ensures continuous output |
| 174 | +RUN echo '[supervisord]' > /etc/supervisor/conf.d/supervisord.conf && \ |
| 175 | + echo 'nodaemon=true' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 176 | + echo 'user=root' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 177 | + echo 'logfile=/dev/stdout' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 178 | + echo 'logfile_maxbytes=0' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 179 | + echo '' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 180 | + echo '[program:backend]' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 181 | + echo 'command=uvicorn main:app --host %(ENV_HOST)s --port %(ENV_PORT)s' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 182 | + echo 'directory=/usr/src/app' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 183 | + echo 'autostart=true' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 184 | + echo 'autorestart=true' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 185 | + echo 'stdout_logfile=/dev/stdout' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 186 | + echo 'stdout_logfile_maxbytes=0' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 187 | + echo 'stderr_logfile=/dev/stderr' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 188 | + echo 'stderr_logfile_maxbytes=0' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 189 | + echo '' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 190 | + echo '[program:frontend]' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 191 | + echo 'command=npm run start -- -p %(ENV_NEXT_INTERNAL_PORT)s' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 192 | + echo 'directory=/usr/src/app/frontend/nextjs' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 193 | + echo 'autostart=true' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 194 | + echo 'autorestart=true' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 195 | + echo 'stdout_logfile=/dev/stdout' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 196 | + echo 'stdout_logfile_maxbytes=0' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 197 | + echo 'stderr_logfile=/dev/stderr' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 198 | + echo 'stderr_logfile_maxbytes=0' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 199 | + echo '' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 200 | + echo '[program:nginx]' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 201 | + echo 'command=nginx -g "daemon off;"' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 202 | + echo 'autostart=true' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 203 | + echo 'autorestart=true' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 204 | + echo 'stdout_logfile=/dev/stdout' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 205 | + echo 'stdout_logfile_maxbytes=0' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 206 | + echo 'stderr_logfile=/dev/stderr' >> /etc/supervisor/conf.d/supervisord.conf && \ |
| 207 | + echo 'stderr_logfile_maxbytes=0' >> /etc/supervisor/conf.d/supervisord.conf |
| 208 | + |
| 209 | +# Start supervisord to manage both services |
| 210 | +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] |
0 commit comments