From 6dd32b7ddea2c4876ebe3812b6a18a2e37f83ba7 Mon Sep 17 00:00:00 2001 From: Philip Meier Date: Mon, 22 Jan 2024 09:57:39 +0100 Subject: [PATCH] dockerize (#283) --- .dockerignore | 6 + .github/workflows/docker.yml | 108 +++++++++ Dockerfile | 30 +++ environment-dev.yml | 2 + ragna-docker.toml | 22 ++ requirements-docker.lock | 410 +++++++++++++++++++++++++++++++++++ 6 files changed, 578 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker.yml create mode 100644 Dockerfile create mode 100644 ragna-docker.toml create mode 100644 requirements-docker.lock diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..ad337859 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +ragna/_version.py + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 00000000..92af065c --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,108 @@ +name: docker + +on: + pull_request: + paths: + - ".github/workflows/docker.yml" + - ".dockerignore" + - "Dockerfile" + - "environment-dev.yml" + - "pyproject.toml" + - "ragna-docker.toml" + - "requirements-docker.lock" + push: + branches: + - release/* + workflow_dispatch: + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +env: + REGISTRY: quay.io + PROJECT: quansight/ragna + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + shell: bash -el {0} + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup environment + uses: ./.github/actions/setup-env + with: + optional-dependencies: "false" + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Set up Docker Build Cache + id: cache + run: | + CACHE_DIR="${HOME}/.cache/docker" + mkdir --parents "${CACHE_DIR}" + echo "cache-dir=${CACHE_DIR}" | tee --append $GITHUB_OUTPUT + + - name: Restore docker cache + uses: actions/cache@v3 + with: + path: ${{ steps.cache.outputs.cache-dir }} + key: docker-${{ hashFiles('Dockerfile','requirements-docker.lock') }} + restore-keys: docker- + + - name: Setup metadata + id: metadata + run: | + IMAGE_NAME="${{ env.REGISTRY }}/${{ env.PROJECT }}" + echo "image-name=${IMAGE_NAME}" | tee --append $GITHUB_OUTPUT + + VERSION=$(python -m setuptools_scm) + echo "version=${VERSION}" | tee --append $GITHUB_OUTPUT + + TAG=$(echo "${VERSION}" | sed 's/+/-/g') + echo "tag=${TAG}" | tee --append $GITHUB_OUTPUT + + TAGGED_IMAGE_NAME="${IMAGE_NAME}:${TAG}" + echo "tagged-image-name=${TAGGED_IMAGE_NAME}" | tee --append $GITHUB_OUTPUT + + - name: Login to Quay.io + if: ${{ github.event_name == 'workflow_dispatch' }} + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.DOCKER_REGISTRY_USERNAME }} + password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} + + - name: Build and push on workflow dispatch + id: build + uses: docker/build-push-action@v5 + with: + cache-from: type=local,src=${{ steps.cache.outputs.cache-dir }} + cache-to: type=local,dest=${{ steps.cache.outputs.cache-dir }} + tags: ${{ steps.metadata.outputs.tagged-image-name }} + build-args: | + SETUPTOOLS_SCM_PRETEND_VERSION_FOR_RAGNA=${{ steps.metadata.outputs.version }} + # Unfortunately, there currently seems to be no way to build a multiplatform + # image and access a single one for the host platform afterwards. Thus, we + # only build a multiplatform image when we also want to push to the registry. + # https://github.com/docker/buildx/issues/166 + # https://github.com/moby/buildkit/issues/1555 + # prettier-ignore + platforms: + ${{ github.event_name == 'workflow_dispatch' && 'linux/amd64,linux/arm64' || 'linux/amd64' }} + load: ${{ github.event_name != 'workflow_dispatch' }} + push: ${{ github.event_name == 'workflow_dispatch' }} + + - name: Smoke test + run: docker run ${{ steps.metadata.outputs.tagged-image-name }} --version diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..b3b6a332 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,30 @@ +FROM python:3.11 + +WORKDIR /opt/ragna + +COPY requirements-docker.lock . +RUN pip install --progress-bar=off --no-deps --no-cache --requirement requirements-docker.lock + +# Pre-download the default embedding model +RUN python -c "from chromadb.utils.embedding_functions import ONNXMiniLM_L6_V2; ONNXMiniLM_L6_V2()._download_model_if_not_exists()" + +COPY ragna ./ragna +COPY pyproject.toml . +# Since we don't copy the .git folder, but still use setuptools-scm as build-backend +# we need to make two manual changes: +# 1. With setuptools-scm all files that are tracked by git are automatically included in +# the built wheel. Since we have corresponding .dockerignore file to our .gitignore, +# the ragna folder only includes files that we are tracking. Thus, we just include +# everything manually. +# 2. We need to pass the version expliclitly as +# --build-arg SETUPTOOLS_SCM_PRETEND_VERSION_FOR_RAGNA=..., +# since setuptools-scm cannot infer the version +RUN echo '[tool.setuptools.package-data]\n"*" = ["*"]' >> pyproject.toml +ARG SETUPTOOLS_SCM_PRETEND_VERSION_FOR_RAGNA +RUN pip install --progress-bar=off --no-deps . + +WORKDIR /var/ragna +COPY ragna-docker.toml ragna.toml + +ENTRYPOINT ["ragna"] +CMD ["ui"] diff --git a/environment-dev.yml b/environment-dev.yml index b6d7230f..9eb18ea0 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -13,6 +13,8 @@ dependencies: - pre-commit - types-aiofiles - sqlalchemy-stubs + - setuptools-scm + - pip-tools # documentation - mkdocs - mkdocs-material diff --git a/ragna-docker.toml b/ragna-docker.toml new file mode 100644 index 00000000..6e328fc2 --- /dev/null +++ b/ragna-docker.toml @@ -0,0 +1,22 @@ +local_cache_root = "/var/ragna" +document = "ragna.core.LocalDocument" +authentication = "ragna.deploy.RagnaDemoAuthentication" + +[components] +source_storages = [ + "ragna.source_storages.Chroma", + "ragna.source_storages.RagnaDemoSourceStorage", + "ragna.source_storages.LanceDB" +] +assistants = [ + "ragna.assistants.RagnaDemoAssistant" +] + +[api] +url = "http://0.0.0.0:31476" +origins = ["http://localhost:31477"] +database_url = "sqlite:////var/ragna/ragna.db" + +[ui] +url = "http://0.0.0.0:31477" +origins = ["http://localhost:31477"] diff --git a/requirements-docker.lock b/requirements-docker.lock new file mode 100644 index 00000000..3b5dfd2e --- /dev/null +++ b/requirements-docker.lock @@ -0,0 +1,410 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# pip-compile --extra=all --output-file=requirements-docker.lock --strip-extras pyproject.toml +# +aiofiles==23.2.1 + # via Ragna (pyproject.toml) +annotated-types==0.6.0 + # via pydantic +anyio==4.2.0 + # via + # httpx + # sse-starlette + # starlette + # watchfiles +asgiref==3.7.2 + # via opentelemetry-instrumentation-asgi +attrs==23.2.0 + # via lancedb +backoff==2.2.1 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc + # posthog +bcrypt==4.1.2 + # via chromadb +bleach==6.1.0 + # via panel +bokeh==3.3.3 + # via panel +build==1.0.3 + # via chromadb +cachetools==5.3.2 + # via + # google-auth + # lancedb +certifi==2023.11.17 + # via + # httpcore + # httpx + # kubernetes + # pulsar-client + # requests +charset-normalizer==3.3.2 + # via requests +chroma-hnswlib==0.7.3 + # via chromadb +chromadb==0.4.22 + # via Ragna (pyproject.toml) +click==8.1.7 + # via + # lancedb + # typer + # uvicorn +coloredlogs==15.0.1 + # via onnxruntime +contourpy==1.2.0 + # via bokeh +decorator==5.1.1 + # via retry +deprecated==1.2.14 + # via + # opentelemetry-api + # opentelemetry-exporter-otlp-proto-grpc +deprecation==2.1.0 + # via lancedb +emoji==2.9.0 + # via Ragna (pyproject.toml) +fastapi==0.109.0 + # via + # Ragna (pyproject.toml) + # chromadb + # sse-starlette +filelock==3.13.1 + # via huggingface-hub +flatbuffers==23.5.26 + # via onnxruntime +fsspec==2023.12.2 + # via huggingface-hub +google-auth==2.26.2 + # via kubernetes +googleapis-common-protos==1.62.0 + # via opentelemetry-exporter-otlp-proto-grpc +greenlet==3.0.3 + # via sqlalchemy +grpcio==1.60.0 + # via + # chromadb + # opentelemetry-exporter-otlp-proto-grpc +h11==0.14.0 + # via + # httpcore + # uvicorn +httpcore==1.0.2 + # via httpx +httptools==0.6.1 + # via uvicorn +httpx==0.26.0 + # via Ragna (pyproject.toml) +httpx-sse==0.4.0 + # via Ragna (pyproject.toml) +huggingface-hub==0.20.2 + # via tokenizers +humanfriendly==10.0 + # via coloredlogs +idna==3.6 + # via + # anyio + # httpx + # requests +importlib-metadata==6.11.0 + # via opentelemetry-api +importlib-resources==6.1.1 + # via chromadb +jinja2==3.1.3 + # via bokeh +kubernetes==29.0.0 + # via chromadb +lancedb==0.4.4 + # via Ragna (pyproject.toml) +linkify-it-py==2.0.2 + # via panel +markdown==3.5.2 + # via panel +markdown-it-py==3.0.0 + # via + # mdit-py-plugins + # panel + # rich +markupsafe==2.1.3 + # via jinja2 +mdit-py-plugins==0.4.0 + # via panel +mdurl==0.1.2 + # via markdown-it-py +mmh3==4.1.0 + # via chromadb +monotonic==1.6 + # via posthog +mpmath==1.3.0 + # via sympy +numpy==1.26.3 + # via + # bokeh + # chroma-hnswlib + # chromadb + # contourpy + # onnxruntime + # pandas + # pyarrow + # pylance +oauthlib==3.2.2 + # via + # kubernetes + # requests-oauthlib +onnxruntime==1.16.3 + # via chromadb +opentelemetry-api==1.22.0 + # via + # chromadb + # opentelemetry-exporter-otlp-proto-grpc + # opentelemetry-instrumentation + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-fastapi + # opentelemetry-sdk +opentelemetry-exporter-otlp-proto-common==1.22.0 + # via opentelemetry-exporter-otlp-proto-grpc +opentelemetry-exporter-otlp-proto-grpc==1.22.0 + # via chromadb +opentelemetry-instrumentation==0.43b0 + # via + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-fastapi +opentelemetry-instrumentation-asgi==0.43b0 + # via opentelemetry-instrumentation-fastapi +opentelemetry-instrumentation-fastapi==0.43b0 + # via chromadb +opentelemetry-proto==1.22.0 + # via + # opentelemetry-exporter-otlp-proto-common + # opentelemetry-exporter-otlp-proto-grpc +opentelemetry-sdk==1.22.0 + # via + # chromadb + # opentelemetry-exporter-otlp-proto-grpc +opentelemetry-semantic-conventions==0.43b0 + # via + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-fastapi + # opentelemetry-sdk +opentelemetry-util-http==0.43b0 + # via + # opentelemetry-instrumentation-asgi + # opentelemetry-instrumentation-fastapi +overrides==7.4.0 + # via + # chromadb + # lancedb +packaging==23.2 + # via + # Ragna (pyproject.toml) + # bokeh + # build + # deprecation + # huggingface-hub + # onnxruntime +pandas==2.1.4 + # via + # bokeh + # panel +panel==1.3.6 + # via Ragna (pyproject.toml) +param==2.0.1 + # via + # panel + # pyviz-comms +pillow==10.2.0 + # via bokeh +posthog==3.3.1 + # via chromadb +prompt-toolkit==3.0.36 + # via questionary +protobuf==4.25.2 + # via + # googleapis-common-protos + # onnxruntime + # opentelemetry-proto +pulsar-client==3.4.0 + # via chromadb +py==1.11.0 + # via retry +pyarrow==14.0.2 + # via + # Ragna (pyproject.toml) + # pylance +pyasn1==0.5.1 + # via + # pyasn1-modules + # rsa +pyasn1-modules==0.3.0 + # via google-auth +pydantic==2.5.3 + # via + # Ragna (pyproject.toml) + # chromadb + # fastapi + # lancedb + # pydantic-settings +pydantic-core==2.14.6 + # via + # Ragna (pyproject.toml) + # pydantic +pydantic-settings==2.1.0 + # via Ragna (pyproject.toml) +pygments==2.17.2 + # via rich +pyjwt==2.8.0 + # via Ragna (pyproject.toml) +pylance==0.9.6 + # via lancedb +pymupdf==1.23.15 + # via Ragna (pyproject.toml) +pymupdfb==1.23.9 + # via pymupdf +pypika==0.48.9 + # via chromadb +pyproject-hooks==1.0.0 + # via build +python-dateutil==2.8.2 + # via + # kubernetes + # pandas + # posthog +python-dotenv==1.0.0 + # via + # pydantic-settings + # uvicorn +python-multipart==0.0.6 + # via Ragna (pyproject.toml) +pytz==2023.3.post1 + # via pandas +pyviz-comms==3.0.1 + # via panel +pyyaml==6.0.1 + # via + # bokeh + # chromadb + # huggingface-hub + # kubernetes + # lancedb + # uvicorn +questionary==2.0.1 + # via Ragna (pyproject.toml) +ratelimiter==1.2.0.post0 + # via lancedb +redis==5.0.1 + # via Ragna (pyproject.toml) +regex==2023.12.25 + # via tiktoken +requests==2.31.0 + # via + # chromadb + # huggingface-hub + # kubernetes + # lancedb + # panel + # posthog + # requests-oauthlib + # tiktoken +requests-oauthlib==1.3.1 + # via kubernetes +retry==0.9.2 + # via lancedb +rich==13.7.0 + # via Ragna (pyproject.toml) +rsa==4.9 + # via google-auth +semver==3.0.2 + # via lancedb +six==1.16.0 + # via + # bleach + # kubernetes + # posthog + # python-dateutil +sniffio==1.3.0 + # via + # anyio + # httpx +sqlalchemy==2.0.25 + # via Ragna (pyproject.toml) +sse-starlette==1.8.2 + # via Ragna (pyproject.toml) +starlette==0.35.1 + # via + # Ragna (pyproject.toml) + # fastapi + # sse-starlette +sympy==1.12 + # via onnxruntime +tenacity==8.2.3 + # via chromadb +tiktoken==0.5.2 + # via Ragna (pyproject.toml) +tokenizers==0.15.0 + # via chromadb +tomlkit==0.12.3 + # via Ragna (pyproject.toml) +tornado==6.4 + # via bokeh +tqdm==4.66.1 + # via + # chromadb + # huggingface-hub + # lancedb + # panel +typer==0.9.0 + # via + # Ragna (pyproject.toml) + # chromadb +typing-extensions==4.9.0 + # via + # chromadb + # fastapi + # huggingface-hub + # opentelemetry-sdk + # panel + # pydantic + # pydantic-core + # sqlalchemy + # typer +tzdata==2023.4 + # via pandas +uc-micro-py==1.0.2 + # via linkify-it-py +urllib3==2.1.0 + # via + # kubernetes + # requests +uvicorn==0.26.0 + # via + # Ragna (pyproject.toml) + # chromadb + # sse-starlette +uvloop==0.19.0 + # via uvicorn +watchfiles==0.21.0 + # via uvicorn +wcwidth==0.2.13 + # via prompt-toolkit +webencodings==0.5.1 + # via bleach +websocket-client==1.7.0 + # via kubernetes +websockets==12.0 + # via uvicorn +wrapt==1.16.0 + # via + # deprecated + # opentelemetry-instrumentation +xyzservices==2023.10.1 + # via + # bokeh + # panel +zipp==3.17.0 + # via importlib-metadata + +# The following packages are considered to be unsafe in a requirements file: +# setuptools