diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..d4b399b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,19 @@ +.git +.github +__pycache__ +*.pyc +*.pyo +*.pyd +.Python +*.egg-info +.venv +venv +.env +.pytest_cache +.mypy_cache +.ruff_cache +.coverage +dist +build +*.log +terminals diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..e398ab5 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,47 @@ +name: Build and publish Docker image + +on: + push: + branches: ["main"] + tags: ["v*"] + workflow_dispatch: {} + +permissions: + contents: read + packages: write + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository_owner }}/light-object-detect + tags: | + type=raw,value=latest,enable={{is_default_branch}} + type=ref,event=branch + type=ref,event=tag + type=sha + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ac2c5a9 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +FROM python:3.11-slim + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PIP_DISABLE_PIP_VERSION_CHECK=1 \ + PIP_NO_CACHE_DIR=1 + +WORKDIR /app + +# Minimal runtime libs for opencv-python on slim images +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + libgl1 \ + libglib2.0-0 \ + && rm -rf /var/lib/apt/lists/* + +# Install Python deps (keeps this image self-contained for Unraid) +COPY Pipfile Pipfile.lock ./ +RUN python -m pip install --upgrade pip \ + && python -m pip install \ + fastapi \ + uvicorn \ + python-multipart \ + pydantic \ + pydantic-settings \ + pillow \ + exceptiongroup \ + numpy \ + tensorflow \ + onnxruntime==1.23.2 \ + opencv-python \ + scipy \ + shapely + +COPY . . + +EXPOSE 8000 + +# .env is optional; Unraid can mount it to /app/.env +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/README.md b/README.md index 97ef1a9..2c8583b 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,51 @@ A lightweight Python API for object detection with pluggable backends. This API 3. Access the API documentation at http://localhost:9001/docs +## Docker (z.B. Unraid / lightNVR) + +### Build + +```bash +docker build -t light-object-detect:local . +``` + +### Run + +Option A: ohne `.env` (Defaults aus `config.py`): + +```bash +docker run --rm -p 8000:8000 --name light-object-detect light-object-detect:local +``` + +Option B: mit `.env` (empfohlen, z.B. Backend/Model-Pfade): + +```bash +docker run --rm -p 8000:8000 --name light-object-detect \ + -v "$(pwd)/.env:/app/.env:ro" \ + light-object-detect:local +``` + +PowerShell: + +```powershell +docker run --rm -p 8000:8000 --name light-object-detect ` + -v "${PWD}\.env:/app/.env:ro" ` + light-object-detect:local +``` + +- **Healthcheck**: `GET /health` +- **Swagger UI**: `GET /docs` + +### lightNVR Integration + +In lightNVR als API-URL typischerweise: + +- `http://:8000/api/v1/detect` + ## API Endpoints - `GET /` - Root endpoint with API information +- `GET /health` - Health check endpoint (useful for Docker/Unraid) - `GET /api/v1/backends` - List available detection backends - `POST /api/v1/detect` - Detect objects in an uploaded image diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6b575cd --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +services: + light-object-detect: + build: . + container_name: light-object-detect + ports: + - "8000:8000" + # Optional: provide config via a bind-mounted .env file + # volumes: + # - ./docker.env:/app/.env:ro + restart: unless-stopped diff --git a/main.py b/main.py index 5630fc1..1057f26 100644 --- a/main.py +++ b/main.py @@ -63,5 +63,10 @@ async def root(): "available_backends": settings.AVAILABLE_BACKENDS, } +@app.get("/health") +async def health(): + """Health check endpoint for container orchestrators.""" + return {"status": "ok"} + if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=9001, reload=True)