diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fcebe89 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,76 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +.venv/ + +# Testing +.pytest_cache/ +.coverage +htmlcov/ +.tox/ +coverage.xml +*.cover + +# Development tools +.mypy_cache/ +.ruff_cache/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Git +.git/ +.gitignore + +# Documentation +*.md +!README.md + +# Docker +Dockerfile* +docker-compose*.yml +.dockerignore + +# Data files (may contain sensitive information) +*.ndjson +*.ldjson +*.json + +# Reports +*-report.json +bandit-report.json +safety-report.json + +# Screenshots +*.png +*.jpg +*.jpeg +*.gif + +# Logs +*.log + +# Temporary files +*.tmp +*.temp \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..002130c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,68 @@ +# Two-stage Dockerfile for EmbeddingBuddy +# Stage 1: Builder +FROM python:3.11-slim as builder + +# Install system dependencies for building Python packages +RUN apt-get update && apt-get install -y \ + build-essential \ + gcc \ + g++ \ + && rm -rf /var/lib/apt/lists/* + +# Install uv for dependency management +RUN pip install uv + +# Set working directory +WORKDIR /app + +# Copy dependency files +COPY pyproject.toml uv.lock ./ + +# Copy source code (needed for editable install) +COPY src/ src/ +COPY main.py . +COPY assets/ assets/ + +# Create virtual environment and install dependencies +RUN uv venv .venv +RUN uv sync --frozen + +# Stage 2: Runtime +FROM python:3.11-slim as runtime + +# Install runtime dependencies for compiled packages +RUN apt-get update && apt-get install -y \ + libgomp1 \ + && rm -rf /var/lib/apt/lists/* + +# Set working directory +WORKDIR /app + +# Copy virtual environment from builder stage +COPY --from=builder /app/.venv /app/.venv + +# Copy application files from builder stage +COPY --from=builder /app/src /app/src +COPY --from=builder /app/main.py /app/main.py +COPY --from=builder /app/assets /app/assets + +# Make sure the virtual environment is in PATH +ENV PATH="/app/.venv/bin:$PATH" + +# Set Python path +ENV PYTHONPATH="/app/src:$PYTHONPATH" + +# Environment variables for production +ENV EMBEDDINGBUDDY_HOST=0.0.0.0 +ENV EMBEDDINGBUDDY_PORT=8050 +ENV EMBEDDINGBUDDY_DEBUG=False + +# Expose port +EXPOSE 8050 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ + CMD python -c "import requests; requests.get('http://localhost:8050/', timeout=5)" || exit 1 + +# Run application +CMD ["python", "main.py"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2a2bee8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,69 @@ +services: + opensearch: + image: opensearchproject/opensearch:2.13.0 + container_name: embeddingbuddy-opensearch + profiles: + - opensearch + environment: + - cluster.name=embeddingbuddy-cluster + - node.name=embeddingbuddy-node + - discovery.type=single-node + - bootstrap.memory_lock=true + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" + - "DISABLE_INSTALL_DEMO_CONFIG=true" + - "DISABLE_SECURITY_PLUGIN=true" + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + volumes: + - opensearch-data:/usr/share/opensearch/data + ports: + - "9200:9200" + - "9600:9600" + networks: + - embeddingbuddy + healthcheck: + test: ["CMD-SHELL", "curl -f http://localhost:9200/_cluster/health || exit 1"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 60s + + embeddingbuddy: + build: . + container_name: embeddingbuddy-app + environment: + - EMBEDDINGBUDDY_HOST=0.0.0.0 + - EMBEDDINGBUDDY_PORT=8050 + - EMBEDDINGBUDDY_DEBUG=false + - OPENSEARCH_HOST=opensearch + - OPENSEARCH_PORT=9200 + - OPENSEARCH_SCHEME=http + - OPENSEARCH_VERIFY_CERTS=false + ports: + - "8050:8050" + networks: + - embeddingbuddy + depends_on: + opensearch: + condition: service_healthy + required: false + healthcheck: + test: ["CMD-SHELL", "python -c 'import requests; requests.get(\"http://localhost:8050/\", timeout=5)'"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + restart: unless-stopped + +volumes: + opensearch-data: + driver: local + +networks: + embeddingbuddy: + driver: bridge \ No newline at end of file