Spaces:
Sleeping
Sleeping
|
Download docs/deployment.md from Sid-WC121/visual-notebook: direct link, hf CLI and curl.
- Browser
- Download file 10.9 kB
-
https://huggingface.co/spaces/Sid-WC121/visual-notebook/resolve/main/docs/deployment.md
- Command line
-
hf download hf://spaces/Sid-WC121/visual-notebook/docs/deployment.md
-
curl -L -o deployment.md https://huggingface.co/spaces/Sid-WC121/visual-notebook/resolve/main/docs/deployment.md
10.9 kB
| # Deployment Guide | |
| This document covers all deployment options for the Visual Notebook, including Docker builds, Docker Compose for development, CI/CD pipelines, Hugging Face Spaces deployment, environment configuration, and production considerations. | |
| ## Docker Build Options | |
| The project provides three Dockerfile configurations for different deployment scenarios: | |
| ### Combined Build (Dockerfile) | |
| The combined Dockerfile builds both the frontend and backend into a single image. This is the recommended approach for most deployments, including HF Spaces. | |
| **Build stages**: | |
| 1. **frontend-build** (Node.js 20): Installs npm dependencies and builds the React application with `npm run build`. The output is written to `frontend/dist/`. | |
| 2. **Runtime** (Python 3.11): Installs the backend using `uv pip install --system -e .`, copies the built frontend from the previous stage into `/app/static`, and configures environment variables. | |
| ```dockerfile | |
| # Stage 1: Build frontend | |
| FROM node:20-slim AS frontend-build | |
| WORKDIR /app/frontend | |
| COPY frontend/package.json frontend/package-lock.json* ./ | |
| RUN npm install | |
| COPY frontend/ ./ | |
| RUN npm run build | |
| # Stage 2: Runtime | |
| FROM python:3.11-slim | |
| WORKDIR /app | |
| RUN pip install uv | |
| COPY backend/pyproject.toml backend/ | |
| COPY backend/visual_notebook/ backend/visual_notebook/ | |
| RUN cd backend && uv pip install --system -e . | |
| COPY datasets/ /app/datasets/ | |
| COPY --from=frontend-build /app/frontend/dist /app/static | |
| ENV DATA_DIR=/app/datasets | |
| ENV CHECKPOINT_DIR=/app/checkpoints | |
| ENV CORS_ORIGINS=* | |
| ENV HOST=0.0.0.0 | |
| ENV PORT=7860 | |
| EXPOSE 7860 | |
| CMD ["uvicorn", "visual_notebook.main:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "1"] | |
| ``` | |
| **Build and run**: | |
| ```bash | |
| docker build -t visual-notebook . | |
| docker run -p 7860:7860 -v $(pwd)/datasets:/app/datasets visual-notebook | |
| ``` | |
| ### Standalone Backend (Dockerfile.backend) | |
| The backend-only Dockerfile skips the frontend build entirely. It is intended for deployments where the frontend is served separately (e.g., by a CDN or Nginx). | |
| ```dockerfile | |
| FROM python:3.11-slim | |
| WORKDIR /app | |
| RUN pip install uv | |
| COPY backend/pyproject.toml backend/ | |
| COPY backend/visual_notebook/ backend/visual_notebook/ | |
| COPY datasets/ /app/datasets/ | |
| RUN cd backend && uv pip install --system -e . | |
| ENV DATA_DIR=/app/datasets | |
| ENV CHECKPOINT_DIR=/app/checkpoints | |
| ENV CORS_ORIGINS=* | |
| ENV HOST=0.0.0.0 | |
| ENV PORT=7860 | |
| EXPOSE 7860 | |
| CMD ["uvicorn", "visual_notebook.main:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "1"] | |
| ``` | |
| ### Frontend-Only (frontend/Dockerfile) | |
| The frontend-only Dockerfile builds the React application and serves it with Nginx: | |
| ```dockerfile | |
| FROM node:20-slim AS build | |
| WORKDIR /app | |
| COPY package.json package-lock.json* ./ | |
| RUN npm install | |
| COPY . . | |
| RUN npm run build | |
| FROM nginx:alpine | |
| COPY --from=build /app/dist /usr/share/nginx/html | |
| COPY nginx.conf /etc/nginx/conf.d/default.conf | |
| EXPOSE 80 | |
| CMD ["nginx", "-g", "daemon off;"] | |
| ``` | |
| The Nginx configuration (`frontend/nginx.conf`) is set up to proxy API requests to the backend and serve static files for all other routes. | |
| ## Docker Compose for Development | |
| The `docker-compose.yml` defines two services for local development: | |
| ```yaml | |
| services: | |
| backend: | |
| build: | |
| context: . | |
| dockerfile: Dockerfile.backend | |
| ports: | |
| - "8000:7860" | |
| volumes: | |
| - ./datasets:/app/datasets | |
| - ./checkpoints:/app/checkpoints | |
| environment: | |
| - DATA_DIR=/app/datasets | |
| - CHECKPOINT_DIR=/app/checkpoints | |
| - CORS_ORIGINS=http://localhost:5173,http://localhost:3000 | |
| frontend: | |
| build: | |
| context: ./frontend | |
| dockerfile: Dockerfile | |
| ports: | |
| - "5173:80" | |
| environment: | |
| - VITE_API_URL=http://localhost:8000 | |
| depends_on: | |
| - backend | |
| ``` | |
| **Usage**: | |
| ```bash | |
| # Start both services | |
| docker compose up --build | |
| # Backend available at http://localhost:8000 | |
| # Frontend available at http://localhost:5173 | |
| ``` | |
| The volume mounts ensure that uploaded datasets and checkpoints persist across container restarts. | |
| ## GitHub Actions CI/CD | |
| The recommended GitHub Actions workflow follows a test-build-deploy pipeline: | |
| ### Workflow Stages | |
| 1. **Test**: Run pytest against the backend with `uv run pytest`. | |
| 2. **Build**: Build the Docker image using the combined Dockerfile. | |
| 3. **Deploy**: Push the image to a container registry and deploy. | |
| ### Example Workflow | |
| ```yaml | |
| name: CI/CD | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| jobs: | |
| test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: astral-sh/setup-uv@v4 | |
| with: | |
| version: "latest" | |
| - name: Install dependencies | |
| run: uv sync | |
| - name: Run tests | |
| run: uv run pytest backend/visual_notebook/tests/ -v | |
| build: | |
| needs: test | |
| runs-on: ubuntu-latest | |
| if: github.ref == 'refs/heads/main' | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and push | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| push: true | |
| tags: ghcr.io/${{ github.repository }}:latest | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| ``` | |
| ### UV Caching | |
| The `setup-uv` action caches the uv installation and downloaded packages. For Docker builds, the `cache-from` and `cache-to` options use GitHub Actions cache to persist Docker layers between builds, significantly reducing build times for subsequent pushes. | |
| ## GitLab CI/CD Pipeline | |
| For GitLab deployments, the following pipeline structure is recommended: | |
| ```yaml | |
| stages: | |
| - test | |
| - build | |
| - deploy | |
| test: | |
| stage: test | |
| image: python:3.11-slim | |
| before_script: | |
| - pip install uv | |
| - uv sync | |
| script: | |
| - uv run pytest backend/visual_notebook/tests/ -v | |
| build: | |
| stage: build | |
| image: docker:24 | |
| services: | |
| - docker:24-dind | |
| script: | |
| - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . | |
| - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA | |
| only: | |
| - main | |
| deploy: | |
| stage: deploy | |
| image: alpine:latest | |
| script: | |
| - apk add --no-cache curl | |
| - curl -X POST "$DEPLOY_WEBHOOK_URL" | |
| only: | |
| - main | |
| ``` | |
| ## Hugging Face Spaces Deployment | |
| The combined Dockerfile is designed to be compatible with HF Spaces requirements: | |
| - **Port 7860**: HF Spaces expects the application to listen on port 7860. The Dockerfile `EXPOSE 7860` and the uvicorn command use this port. | |
| - **Static file serving**: The backend serves the frontend from `/app/static`, which is populated during the Docker build. | |
| - **Environment variables**: HF Spaces allows setting secrets and environment variables through the Space settings UI. | |
| ### Deployment Steps | |
| 1. Create a new Docker Space on Hugging Face. | |
| 2. Clone the Space repository. | |
| 3. Copy the project files (or add the Space as a git remote). | |
| 4. Push to the Space repository. HF Spaces will build and deploy automatically. | |
| ### Space Configuration | |
| Set the following secrets in the Space settings: | |
| | Variable | Value | | |
| |----------|-------| | |
| | `DATA_DIR` | `/app/datasets` | | |
| | `CHECKPOINT_DIR` | `/app/checkpoints` | | |
| | `CORS_ORIGINS` | `*` (or your specific domain) | | |
| | `MAX_UPLOAD_SIZE` | `524288000` (500 MB) | | |
| ### Persistent Storage | |
| HF Spaces offers persistent storage for paid tiers. To enable it, mount a persistent volume at `/app/datasets` and `/app/checkpoints`. Without persistent storage, uploaded files are lost when the Space restarts. | |
| ## Environment Variable Configuration | |
| All configuration is managed through environment variables, read by the `Settings` dataclass in `config.py`: | |
| | Variable | Default | Description | | |
| |----------|---------|-------------| | |
| | `DATA_DIR` | `datasets` | Directory for uploaded CSV files | | |
| | `CHECKPOINT_DIR` | `checkpoints` | Directory for session checkpoints | | |
| | `MAX_UPLOAD_SIZE` | `524288000` (500 MB) | Maximum CSV file size in bytes | | |
| | `PREVIEW_ROWS` | `50` | Number of rows returned by default in `head()` | | |
| | `SAMPLE_ROWS` | `10000` | Number of rows for Polars CSV sampling | | |
| | `CORS_ORIGINS` | `http://localhost:5173,http://localhost:3000` | Comma-separated allowed origins | | |
| | `HOST` | `0.0.0.0` | Server bind address | | |
| | `PORT` | `8000` | Server bind port (7860 in Docker) | | |
| ## Production Considerations | |
| ### Session Storage | |
| The current `SessionStore` is in-memory, meaning all session data is lost when the server restarts. For production deployments, consider replacing it with a persistent store: | |
| - **Redis**: Store session metadata in Redis with TTL-based expiration. DuckDB tables would need to be exported to Parquet and reloaded on session restore. | |
| - **Database-backed**: Use a lightweight database (SQLite, PostgreSQL) to store session state and checkpoint references. | |
| - **File-based**: Serialize session metadata to JSON files in the checkpoint directory. | |
| ### Static File Serving | |
| In production, the backend serves static files from `/app/static`. For high-traffic deployments, consider: | |
| - Using a CDN in front of the application for static asset caching. | |
| - Serving static files directly from Nginx rather than through FastAPI's `StaticFiles` middleware. | |
| - Enabling gzip compression for JS and CSS assets. | |
| ### Health Checks | |
| The `/health` endpoint returns a JSON response with status and version. Configure your load balancer or orchestrator to use this endpoint for health monitoring: | |
| ```json | |
| {"status": "ok", "version": "0.1.0"} | |
| ``` | |
| For more comprehensive health checks, consider extending the endpoint to verify DuckDB connectivity and disk space availability. | |
| ### Resource Limits | |
| DuckDB operates entirely in memory. For datasets that exceed available RAM: | |
| - Set `max_memory` in the DuckDB connection to enable disk spilling: `duckdb.connect(":memory:", config={"memory_limit": "2GB"})`. | |
| - Use `PREVIEW_ROWS` and `SAMPLE_ROWS` to limit data sent to the frontend. | |
| - Monitor memory usage and set container memory limits appropriately. | |
| ### Scaling Considerations | |
| The current architecture is single-process and single-session-per-container. To scale horizontally: | |
| - **Session affinity**: Use cookie-based sticky sessions to route each user to the same backend instance. | |
| - **Shared storage**: Use a networked filesystem (EFS, S3) for `DATA_DIR` so all instances can access uploaded files. | |
| - **Connection pooling**: If moving to a shared DuckDB instance, implement connection pooling to manage concurrent access. | |
| - **Load balancing**: Place multiple backend instances behind a load balancer with health check routing. | |
| For most use cases, a single instance with sufficient RAM (4-8 GB) handles dozens of concurrent users comfortably, given that CSV analysis is typically an interactive, low-concurrency workload. | |