Skip to content

Commit 55ace1c

Browse files
authored
Merge branch 'docker-uv' into chunking
2 parents 4fc4c56 + f8fc3fd commit 55ace1c

File tree

8 files changed

+199
-58
lines changed

8 files changed

+199
-58
lines changed

.dockerignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,9 @@ build/
5757

5858
# Model cache and downloads
5959
model_cache/
60-
downloads/
60+
downloads/
61+
62+
*.pyc
63+
*.pyo
64+
*.pyd
65+
.env

Dockerfile

Lines changed: 62 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,44 @@
1-
# First, build the application and install dependencies
21
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder
2+
ARG CPU_ONLY=false
33

44
WORKDIR /app
55

6-
# Download models in builder stage
6+
# Install build dependencies
77
RUN apt-get update && \
8-
apt-get install -y libgl1 libglib2.0-0 && \
9-
apt-get clean
8+
apt-get install -y --no-install-recommends libgl1 libglib2.0-0 && \
9+
rm -rf /var/lib/apt/lists/*
1010

1111
# Copy only dependency files and create a dummy README
1212
COPY pyproject.toml uv.lock ./
1313
# Create a dummy README.md file to satisfy package requirements
1414
RUN echo "# Placeholder README" > README.md
1515

16-
# Create venv and install project for model downloads
17-
RUN python -m venv /app/.venv && \
18-
. /app/.venv/bin/activate && \
19-
uv pip install -e .
16+
# Install dependencies but not the project itself
17+
RUN --mount=type=cache,target=/root/.cache/uv \
18+
uv sync --frozen --no-install-project
19+
20+
# Copy the rest of the project
21+
COPY . .
2022

21-
# Set up cache directories and download models
22-
ENV HF_HOME=/app/.cache/huggingface \
23-
TORCH_HOME=/app/.cache/torch
23+
# Better GPU detection: Check both architecture and if NVIDIA is available
24+
RUN ARCH=$(uname -m) && \
25+
if [ "$CPU_ONLY" = "true" ] || [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ] || ! command -v nvidia-smi >/dev/null 2>&1; then \
26+
USE_GPU=false; \
27+
else \
28+
USE_GPU=true; \
29+
fi && \
30+
echo "Detected GPU availability: $USE_GPU" && \
31+
# For PyTorch installation with architecture detection
32+
uv pip uninstall -y torch torchvision torchaudio || true && \
33+
if [ "$USE_GPU" = "false" ]; then \
34+
# For CPU or ARM architectures or no NVIDIA
35+
echo "Installing PyTorch for CPU" && \
36+
uv pip install --no-cache-dir torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu; \
37+
else \
38+
# For x86_64 with GPU support
39+
echo "Installing PyTorch with CUDA support" && \
40+
uv pip install --no-cache-dir torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121; \
41+
fi
2442

2543
# Download models
2644
RUN . /app/.venv/bin/activate && \
@@ -29,16 +47,29 @@ RUN . /app/.venv/bin/activate && \
2947
python -c 'import easyocr; reader = easyocr.Reader(["fr", "de", "es", "en", "it", "pt"], gpu=True); print("EasyOCR models downloaded successfully")' && \
3048
python -c 'from chonkie import SDPMChunker; chunker = SDPMChunker(embedding_model="minishlab/potion-base-8M"); print("Chonkie models downloaded successfully")'
3149

32-
# Final stage with CUDA support
33-
FROM python:3.12-slim-bookworm AS runtime
50+
# Download models for the pipeline
51+
RUN uv run python -c "from docling.pipeline.standard_pdf_pipeline import StandardPdfPipeline; artifacts_path = StandardPdfPipeline.download_models_hf(force=True)"
3452

35-
ARG CPU_ONLY=false
53+
# Pre-download EasyOCR models with better GPU detection
54+
RUN ARCH=$(uname -m) && \
55+
if [ "$CPU_ONLY" = "true" ] || [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ] || ! command -v nvidia-smi >/dev/null 2>&1; then \
56+
echo "Downloading EasyOCR models for CPU" && \
57+
uv run python -c "import easyocr; reader = easyocr.Reader(['fr', 'de', 'es', 'en', 'it', 'pt'], gpu=False); print('EasyOCR CPU models downloaded successfully')"; \
58+
else \
59+
echo "Downloading EasyOCR models with GPU support" && \
60+
uv run python -c "import easyocr; reader = easyocr.Reader(['fr', 'de', 'es', 'en', 'it', 'pt'], gpu=True); print('EasyOCR GPU models downloaded successfully')"; \
61+
fi
62+
63+
RUN uv run python -c 'from chonkie import SDPMChunker; chunker = SDPMChunker(embedding_model="minishlab/potion-base-8M"); print("Chonkie models downloaded successfully")'
64+
65+
# Production stage
66+
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
3667
WORKDIR /app
3768

3869
# Install runtime dependencies
3970
RUN apt-get update && \
40-
apt-get install -y redis-server libgl1 libglib2.0-0 && \
41-
apt-get clean
71+
apt-get install -y --no-install-recommends redis-server libgl1 libglib2.0-0 curl && \
72+
rm -rf /var/lib/apt/lists/*
4273

4374
# Copy model cache from builder - this rarely changes
4475
COPY --from=builder --chown=app:app /app/.cache /app/.cache/
@@ -57,31 +88,24 @@ COPY --chown=app:app main.py ./
5788
ENV PYTHONPATH=/app \
5889
HF_HOME=/app/.cache/huggingface \
5990
TORCH_HOME=/app/.cache/torch \
60-
OMP_NUM_THREADS=4
91+
PYTHONPATH=/app \
92+
OMP_NUM_THREADS=4 \
93+
UV_COMPILE_BYTECODE=1
6194

62-
# Create app user
63-
RUN useradd -m app && \
64-
chown -R app:app /app /tmp && \
65-
python -m venv /app/.venv && \
66-
chown -R app:app /app/.venv
95+
# Create a non-root user
96+
RUN useradd --create-home app && \
97+
mkdir -p /app && \
98+
chown -R app:app /app /tmp
6799

68-
USER app
100+
# Copy the virtual environment from the builder stage
101+
COPY --from=builder --chown=app:app /app/.venv /app/.venv
102+
ENV PATH="/app/.venv/bin:$PATH"
69103

70-
# Install dependencies and project
71-
RUN . /app/.venv/bin/activate && \
72-
cd /app && \
73-
pip install -e .
104+
# Copy necessary files for the application
105+
COPY --chown=app:app . .
74106

75-
# Install PyTorch with CUDA support
76-
RUN . /app/.venv/bin/activate && \
77-
if [ "$CPU_ONLY" = "true" ]; then \
78-
pip install --no-cache-dir torch torchvision --extra-index-url https://download.pytorch.org/whl/cpu; \
79-
else \
80-
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121; \
81-
fi
82-
83-
ENV PATH="/app/.venv/bin:$PATH"
107+
# Switch to non-root user
108+
USER app
84109

85110
EXPOSE 8080
86-
87-
CMD ["python", "-m", "uvicorn", "--port", "8080", "--host", "0.0.0.0", "main:app"]
111+
CMD ["uvicorn", "main:app", "--port", "8080", "--host", "0.0.0.0"]

Makefile

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ help:
2020
@echo "Docker:"
2121
@echo " docker-build-cpu - Build Docker image (CPU version)"
2222
@echo " docker-build-gpu - Build Docker image (GPU version)"
23+
@echo " docker-start - Auto-detect system and start appropriate container (CPU/GPU)"
2324
@echo " docker-start-cpu - Start services in CPU mode"
2425
@echo " docker-start-gpu - Start services in GPU mode"
2526
@echo " docker-stop - Stop all Docker services"
@@ -79,6 +80,17 @@ docker-start-cpu:
7980
docker-start-gpu:
8081
$(DOCKER_GPU_COMPOSE) up --build --scale celery_worker=3
8182

83+
# Auto-detect architecture and start appropriate container
84+
docker-start:
85+
@echo "Auto-detecting system architecture..."
86+
@if [ "$(shell uname -m)" = "arm64" ] || [ "$(shell uname -m)" = "aarch64" ] || ! command -v nvidia-smi >/dev/null 2>&1; then \
87+
echo "ARM architecture or NVIDIA drivers not detected. Using CPU mode."; \
88+
$(MAKE) docker-start-cpu; \
89+
else \
90+
echo "NVIDIA GPU detected. Using GPU mode."; \
91+
$(MAKE) docker-start-gpu; \
92+
fi
93+
8294
docker-stop:
8395
$(DOCKER_CPU_COMPOSE) down
8496
$(DOCKER_GPU_COMPOSE) down

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ The project includes a Makefile for convenient management of Docker operations:
154154
```bash
155155
# Build and run in CPU mode with 1 worker
156156
make docker-build-cpu
157-
make docker-run-cpu
157+
make docker-start-cpu
158158

159159
# Or build and run with multiple workers
160160
make docker-run-cpu WORKER_COUNT=3
@@ -164,7 +164,7 @@ make docker-run-cpu WORKER_COUNT=3
164164
```bash
165165
# Build and run in GPU mode with 1 worker
166166
make docker-build-gpu
167-
make docker-run-gpu
167+
make docker-start-gpu
168168

169169
# Or build and run with multiple workers
170170
make docker-run-gpu WORKER_COUNT=3

detect_gpu.sh

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/bin/bash
2+
3+
# Script to detect GPU and select the appropriate Docker Compose file
4+
5+
# Check if nvidia-smi exists and can be executed
6+
if command -v nvidia-smi >/dev/null 2>&1; then
7+
# Try to run nvidia-smi to check if drivers are loaded correctly
8+
if nvidia-smi >/dev/null 2>&1; then
9+
echo "NVIDIA GPU detected with working drivers."
10+
GPU_AVAILABLE=true
11+
12+
# Check CUDA version
13+
CUDA_VERSION=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader | cut -d'.' -f1)
14+
echo "CUDA compatible driver version: $CUDA_VERSION"
15+
16+
# Check if the detected CUDA version is compatible with our requirements (CUDA 11+)
17+
if [ -n "$CUDA_VERSION" ] && [ "$CUDA_VERSION" -ge 11 ]; then
18+
echo "Using GPU configuration (CUDA $CUDA_VERSION detected)"
19+
COMPOSE_FILE="docker-compose.gpu.yml"
20+
DOCKER_BUILDKIT=1
21+
DOCKER_BUILD_ARGS="--build-arg CPU_ONLY=false"
22+
# Pass GPU capabilities to docker build
23+
export DOCKER_BUILDKIT=1
24+
export DOCKER_DEFAULT_PLATFORM=linux/amd64
25+
export DOCKER_CLI_EXPERIMENTAL=enabled
26+
else
27+
echo "NVIDIA GPU detected but CUDA version ($CUDA_VERSION) is too old. Minimum required: 11"
28+
echo "Falling back to CPU configuration."
29+
GPU_AVAILABLE=false
30+
COMPOSE_FILE="docker-compose.cpu.yml"
31+
DOCKER_BUILD_ARGS="--build-arg CPU_ONLY=true"
32+
fi
33+
else
34+
echo "NVIDIA GPU software detected but drivers may not be properly installed."
35+
GPU_AVAILABLE=false
36+
COMPOSE_FILE="docker-compose.cpu.yml"
37+
DOCKER_BUILD_ARGS="--build-arg CPU_ONLY=true"
38+
fi
39+
else
40+
echo "No NVIDIA GPU detected. Using CPU configuration."
41+
GPU_AVAILABLE=false
42+
COMPOSE_FILE="docker-compose.cpu.yml"
43+
DOCKER_BUILD_ARGS="--build-arg CPU_ONLY=true"
44+
fi
45+
46+
# Check architecture
47+
ARCH=$(uname -m)
48+
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
49+
echo "ARM architecture detected. Forcing CPU mode regardless of GPU availability."
50+
GPU_AVAILABLE=false
51+
COMPOSE_FILE="docker-compose.cpu.yml"
52+
DOCKER_BUILD_ARGS="--build-arg CPU_ONLY=true"
53+
fi
54+
55+
# Export for other scripts to use
56+
export GPU_AVAILABLE
57+
export COMPOSE_FILE
58+
export DOCKER_BUILD_ARGS
59+
60+
echo "Selected configuration: $COMPOSE_FILE"
61+
echo "Build arguments: $DOCKER_BUILD_ARGS"
62+
echo "GPU_AVAILABLE=$GPU_AVAILABLE"
63+
64+
# If this script is being sourced, don't execute docker-compose
65+
if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
66+
return 0
67+
fi
68+
69+
# If passed arguments, run docker-compose with them
70+
if [ $# -gt 0 ]; then
71+
echo "Running: docker-compose -f $COMPOSE_FILE $@"
72+
docker-compose -f $COMPOSE_FILE $@
73+
else
74+
echo "Usage: $0 [docker-compose commands]"
75+
echo "or source this script to export the variables"
76+
fi

docker-compose.cpu.yml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@ services:
44
context: .
55
args:
66
CPU_ONLY: "true"
7-
target: runtime
87
image: converter-cpu-image
9-
command: /app/.venv/bin/python -m celery -A worker.celery_config worker --pool=solo -n worker_primary --loglevel=info
8+
command: uv run celery -A worker.celery_config worker --pool=solo -n worker_primary --loglevel=info
109
volumes:
1110
- ./worker:/app/worker
1211
environment:
1312
- REDIS_HOST=${REDIS_HOST}
1413
- ENV=production
1514
restart: on-failure
1615
healthcheck:
17-
test: [ "CMD", "/app/.venv/bin/celery", "-A", "worker.celery_config", "inspect", "ping", "-d", "celery@worker_primary" ]
16+
test: [ "CMD", "uv", "run", "celery", "-A", "worker.celery_config", "inspect", "ping", "-d", "celery@worker_primary" ]
1817
interval: 30s
1918
timeout: 10s
2019
retries: 3
@@ -28,11 +27,10 @@ services:
2827
context: .
2928
args:
3029
CPU_ONLY: "true"
31-
target: runtime
3230
cache_from:
3331
- converter-cpu-image
3432
image: converter-cpu-image
35-
command: /app/.venv/bin/python -m uvicorn main:app --port 8080 --host 0.0.0.0 --workers 4 --proxy-headers
33+
command: uv run uvicorn main:app --port 8080 --host 0.0.0.0 --workers 4 --proxy-headers
3634
environment:
3735
- REDIS_HOST=${REDIS_HOST}
3836
- ENV=production
@@ -71,11 +69,10 @@ services:
7169
context: .
7270
args:
7371
CPU_ONLY: "true"
74-
target: runtime
7572
cache_from:
7673
- converter-cpu-image
7774
image: converter-cpu-image
78-
command: /app/.venv/bin/python -m celery -A worker.celery_config flower --port=5555
75+
command: uv run celery -A worker.celery_config flower --port=5555
7976
ports:
8077
- "5556:5555"
8178
volumes:

0 commit comments

Comments
 (0)