From f64817814aa2c16b8810b8c1650490a14d96ff77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Boschi?= Date: Fri, 6 Feb 2026 15:00:24 +0100 Subject: [PATCH] feat: slim docker distro (#314) * feat: slim docker distro * feat: slim docker distro * push --- .github/workflows/release.yml | 25 +++- .github/workflows/test.yml | 43 +++++- README.md | 2 +- docker/README.md | 135 ++++++++++++++++++ .../test-image.sh | 64 ++++++--- docker/test-slim-local.sh | 51 +++++++ hindsight-docs/docs/developer/installation.md | 75 ++++++++++ 7 files changed, 370 insertions(+), 25 deletions(-) create mode 100644 docker/README.md rename scripts/docker-smoke-test.sh => docker/test-image.sh (60%) create mode 100755 docker/test-slim-local.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 23e23722..14ee64c4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -340,6 +340,7 @@ jobs: retention-days: 1 release-docker-images: + name: Release Docker (${{ matrix.image_name }}${{ matrix.tag_suffix }}) runs-on: ubuntu-latest permissions: contents: read @@ -349,10 +350,28 @@ jobs: include: - target: api-only image_name: hindsight-api + tag_suffix: "" + build_args: "" + - target: api-only + image_name: hindsight-api + tag_suffix: "-slim" + build_args: | + INCLUDE_LOCAL_MODELS=false + PRELOAD_ML_MODELS=false - target: cp-only image_name: hindsight-control-plane + tag_suffix: "" + build_args: "" - target: standalone image_name: hindsight + tag_suffix: "" + build_args: "" + - target: standalone + image_name: hindsight + tag_suffix: "-slim" + build_args: | + INCLUDE_LOCAL_MODELS=false + PRELOAD_ML_MODELS=false steps: - uses: actions/checkout@v4 @@ -390,6 +409,9 @@ jobs: uses: docker/metadata-action@v5 with: images: ghcr.io/${{ github.repository_owner }}/${{ matrix.image_name }} + flavor: | + latest=auto + suffix=${{ matrix.tag_suffix }} tags: | type=semver,pattern={{version}},value=${{ steps.get_version.outputs.VERSION }} type=semver,pattern={{major}}.{{minor}},value=${{ steps.get_version.outputs.VERSION }} @@ -415,7 +437,7 @@ jobs: # - name: Smoke test - verify container starts # env: # GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} - # run: ./scripts/docker-smoke-test.sh "${{ matrix.image_name }}:test" "${{ matrix.target }}" + # run: ./docker/test-image.sh "${{ matrix.image_name }}:test" "${{ matrix.target }}" # Build multi-platform and push to release tags - name: Build and push release images @@ -424,6 +446,7 @@ jobs: context: . file: docker/standalone/Dockerfile target: ${{ matrix.target }} + build-args: ${{ matrix.build_args }} push: true platforms: linux/amd64,linux/arm64 tags: ${{ steps.meta.outputs.tags }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c9a339dc..4fb01123 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -277,16 +277,35 @@ jobs: run: helm lint helm/hindsight build-docker-images: + name: Build Docker (${{ matrix.name }}) runs-on: ubuntu-latest strategy: matrix: include: - target: api-only name: api + variant: full + build_args: "" + - target: api-only + name: api-slim + variant: slim + build_args: | + INCLUDE_LOCAL_MODELS=false + PRELOAD_ML_MODELS=false - target: cp-only name: control-plane + variant: full + build_args: "" - target: standalone name: standalone + variant: full + build_args: "" + - target: standalone + name: standalone-slim + variant: slim + build_args: | + INCLUDE_LOCAL_MODELS=false + PRELOAD_ML_MODELS=false steps: - uses: actions/checkout@v4 @@ -305,20 +324,30 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Build ${{ matrix.name }} image + - name: Build ${{ matrix.name }} image (${{ matrix.variant }}) uses: docker/build-push-action@v6 with: context: . file: docker/standalone/Dockerfile target: ${{ matrix.target }} + build-args: ${{ matrix.build_args }} push: false - load: false + load: ${{ matrix.variant == 'slim' }} + tags: hindsight-${{ matrix.name }}:test + cache-from: type=gha,scope=${{ matrix.name }} + cache-to: type=gha,mode=max,scope=${{ matrix.name }} - # TODO: Re-enable smoke test when disk space issue is resolved - # - name: Smoke test - verify container starts - # env: - # GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} - # run: ./scripts/docker-smoke-test.sh "hindsight-${{ matrix.name }}:test" "${{ matrix.target }}" + # Only test slim variants to save disk space (they're much smaller) + # Slim variants require external embedding providers + - name: Smoke test - verify container starts + if: matrix.variant == 'slim' + env: + GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} + HINDSIGHT_API_EMBEDDINGS_PROVIDER: openai + HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + HINDSIGHT_API_RERANKER_PROVIDER: cohere + HINDSIGHT_API_COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }} + run: ./docker/test-image.sh "hindsight-${{ matrix.name }}:test" "${{ matrix.target }}" test-api: runs-on: ubuntu-latest diff --git a/README.md b/README.md index a370f21a..313aad33 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \ You can modify the LLM provider by setting `HINDSIGHT_API_LLM_PROVIDER`. Valid options are `openai`, `anthropic`, `gemini`, `groq`, `ollama`, and `lmstudio`. The documentation provides more details on [supported models](https://hindsight.vectorize.io/developer/models). -API: http://localhost:8888 +API: http://localhost:8888 UI: http://localhost:9999 Install client: diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000..f0ce84d1 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,135 @@ +# Docker Testing + +Scripts for testing Hindsight Docker images locally and in CI. + +## Scripts + +### `test-image.sh` + +General-purpose Docker image test script. Starts a container and verifies it becomes healthy. + +**Usage:** +```bash +./docker/test-image.sh [target] +``` + +**Arguments:** +- `image` - Docker image to test (e.g., `hindsight:test`, `ghcr.io/vectorize-io/hindsight:latest`) +- `target` - Optional: `cp-only` for control plane, `api-only` for API, or `standalone` (default) + +**Environment Variables:** +- `GROQ_API_KEY` - Required for API/standalone images +- `HINDSIGHT_API_LLM_PROVIDER` - LLM provider (default: `groq`) +- `HINDSIGHT_API_LLM_MODEL` - LLM model (default: `llama-3.3-70b-versatile`) +- `HINDSIGHT_API_EMBEDDINGS_PROVIDER` - Embeddings provider (for slim images) +- `HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY` - OpenAI API key for embeddings +- `HINDSIGHT_API_RERANKER_PROVIDER` - Reranker provider (for slim images) +- `HINDSIGHT_API_COHERE_API_KEY` - Cohere API key for reranking +- `SMOKE_TEST_TIMEOUT` - Timeout in seconds (default: 120) + +**Examples:** + +Test a full image (with local ML models): +```bash +export GROQ_API_KEY=gsk_xxx +./docker/test-image.sh hindsight:test +``` + +Test a slim image (with external providers): +```bash +export GROQ_API_KEY=gsk_xxx +export HINDSIGHT_API_EMBEDDINGS_PROVIDER=openai +export HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY=sk-xxx +export HINDSIGHT_API_RERANKER_PROVIDER=cohere +export HINDSIGHT_API_COHERE_API_KEY=xxx +./docker/test-image.sh hindsight-slim:test +``` + +### `test-slim-local.sh` + +Convenience wrapper for testing slim images locally. Automatically configures external providers. + +**Usage:** +```bash +# Set API keys +export GROQ_API_KEY=gsk_xxx +export OPENAI_API_KEY=sk-xxx +export COHERE_API_KEY=xxx + +# Run test +./docker/test-slim-local.sh [image] +``` + +**Or inline:** +```bash +GROQ_API_KEY=gsk_xxx \ +OPENAI_API_KEY=sk-xxx \ +COHERE_API_KEY=xxx \ +./docker/test-slim-local.sh hindsight-slim:test +``` + +This script: +- ✅ Validates API keys are set +- ✅ Configures OpenAI embeddings automatically +- ✅ Configures Cohere reranking automatically +- ✅ Calls `test-image.sh` with the right configuration + +## Building and Testing Locally + +### Build a slim image + +```bash +docker build \ + --build-arg INCLUDE_LOCAL_MODELS=false \ + --build-arg PRELOAD_ML_MODELS=false \ + --target standalone \ + -t hindsight-slim:test \ + -f docker/standalone/Dockerfile \ + . +``` + +### Test the slim image + +```bash +# With API keys +export GROQ_API_KEY=gsk_xxx +export OPENAI_API_KEY=sk-xxx +export COHERE_API_KEY=xxx + +# Run test +./docker/test-slim-local.sh hindsight-slim:test +``` + +## Expected Output + +**Successful test:** +``` +Starting smoke test for: hindsight-slim:test + Target: standalone + Health endpoint: http://localhost:8888/health + Timeout: 120s + +Starting container... +Waiting for health endpoint at http://localhost:8888/health... + Still waiting... (10s) + Still waiting... (20s) + +Container is healthy after 25s + +=== Health Response === +{ + "status": "healthy", + "database": "connected" +} + +Smoke test PASSED +``` + +## CI Integration + +These scripts are used in CI to validate Docker images on every PR: + +- `.github/workflows/test.yml` - Runs `test-image.sh` for slim variants with OpenAI/Cohere +- `.github/workflows/release.yml` - Can optionally run smoke tests during release + +See the workflows for the exact configuration. diff --git a/scripts/docker-smoke-test.sh b/docker/test-image.sh similarity index 60% rename from scripts/docker-smoke-test.sh rename to docker/test-image.sh index 58b51306..10cc55f6 100755 --- a/scripts/docker-smoke-test.sh +++ b/docker/test-image.sh @@ -6,28 +6,40 @@ # Can be run locally or in CI pipelines. # # Usage: -# ./scripts/docker-smoke-test.sh [target] +# ./docker/test-image.sh [target] # # Arguments: # image - Docker image to test (e.g., hindsight-api:test, ghcr.io/vectorize-io/hindsight:latest) # target - Optional: 'cp-only' for control plane, otherwise assumes API image (default: api) # # Environment variables: -# GROQ_API_KEY - Required for API/standalone images (LLM verification) -# HINDSIGHT_API_LLM_PROVIDER - LLM provider (default: groq) -# HINDSIGHT_API_LLM_MODEL - LLM model (default: llama-3.3-70b-versatile) -# SMOKE_TEST_TIMEOUT - Timeout in seconds (default: 120) -# SMOKE_TEST_CONTAINER_NAME - Container name (default: hindsight-smoke-test) +# GROQ_API_KEY - Required for API/standalone images (LLM verification) +# HINDSIGHT_API_LLM_PROVIDER - LLM provider (default: groq) +# HINDSIGHT_API_LLM_MODEL - LLM model (default: llama-3.3-70b-versatile) +# HINDSIGHT_API_EMBEDDINGS_PROVIDER - Embeddings provider (optional, for slim images: openai, cohere, tei) +# HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY - OpenAI API key for embeddings (optional) +# HINDSIGHT_API_RERANKER_PROVIDER - Reranker provider (optional, for slim images: cohere, tei) +# HINDSIGHT_API_COHERE_API_KEY - Cohere API key for reranking (optional) +# SMOKE_TEST_TIMEOUT - Timeout in seconds (default: 120) +# SMOKE_TEST_CONTAINER_NAME - Container name (default: hindsight-smoke-test) # # Examples: -# # Test a locally built image -# ./scripts/docker-smoke-test.sh hindsight-api:test +# # Test a locally built full image +# ./docker/test-image.sh hindsight-api:test # # # Test a released image -# ./scripts/docker-smoke-test.sh ghcr.io/vectorize-io/hindsight:latest +# ./docker/test-image.sh ghcr.io/vectorize-io/hindsight:latest # # # Test control plane image -# ./scripts/docker-smoke-test.sh hindsight-control-plane:test cp-only +# ./docker/test-image.sh hindsight-control-plane:test cp-only +# +# # Test slim image with external providers +# export GROQ_API_KEY=gsk_xxx +# export HINDSIGHT_API_EMBEDDINGS_PROVIDER=openai +# export HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY=sk-xxx +# export HINDSIGHT_API_RERANKER_PROVIDER=cohere +# export HINDSIGHT_API_COHERE_API_KEY=xxx +# ./docker/test-image.sh hindsight-slim:test # # Exit codes: # 0 - Success (container healthy) @@ -108,12 +120,32 @@ if [ "$TARGET" = "cp-only" ]; then -p "${HEALTH_PORT}:${HEALTH_PORT}" \ "$IMAGE" else - docker run -d --name "$CONTAINER_NAME" \ - -e HINDSIGHT_API_LLM_PROVIDER="$LLM_PROVIDER" \ - -e HINDSIGHT_API_LLM_API_KEY="${GROQ_API_KEY}" \ - -e HINDSIGHT_API_LLM_MODEL="$LLM_MODEL" \ - -p "${HEALTH_PORT}:${HEALTH_PORT}" \ - "$IMAGE" + # Build docker run command with required and optional env vars + DOCKER_CMD="docker run -d --name $CONTAINER_NAME" + DOCKER_CMD="$DOCKER_CMD -e HINDSIGHT_API_LLM_PROVIDER=$LLM_PROVIDER" + DOCKER_CMD="$DOCKER_CMD -e HINDSIGHT_API_LLM_API_KEY=${GROQ_API_KEY}" + DOCKER_CMD="$DOCKER_CMD -e HINDSIGHT_API_LLM_MODEL=$LLM_MODEL" + + # Add optional embeddings provider config + if [ -n "${HINDSIGHT_API_EMBEDDINGS_PROVIDER:-}" ]; then + DOCKER_CMD="$DOCKER_CMD -e HINDSIGHT_API_EMBEDDINGS_PROVIDER=${HINDSIGHT_API_EMBEDDINGS_PROVIDER}" + fi + if [ -n "${HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY:-}" ]; then + DOCKER_CMD="$DOCKER_CMD -e HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY=${HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY}" + fi + + # Add optional reranker provider config + if [ -n "${HINDSIGHT_API_RERANKER_PROVIDER:-}" ]; then + DOCKER_CMD="$DOCKER_CMD -e HINDSIGHT_API_RERANKER_PROVIDER=${HINDSIGHT_API_RERANKER_PROVIDER}" + fi + if [ -n "${HINDSIGHT_API_COHERE_API_KEY:-}" ]; then + DOCKER_CMD="$DOCKER_CMD -e HINDSIGHT_API_COHERE_API_KEY=${HINDSIGHT_API_COHERE_API_KEY}" + fi + + DOCKER_CMD="$DOCKER_CMD -p ${HEALTH_PORT}:${HEALTH_PORT}" + DOCKER_CMD="$DOCKER_CMD $IMAGE" + + eval $DOCKER_CMD fi # Wait for health endpoint diff --git a/docker/test-slim-local.sh b/docker/test-slim-local.sh new file mode 100755 index 00000000..08dabbb1 --- /dev/null +++ b/docker/test-slim-local.sh @@ -0,0 +1,51 @@ +#!/bin/bash +# +# Local Test Script for Slim Docker Images +# +# This script makes it easy to test slim images locally with external providers. +# It expects API keys to be set in environment variables. +# +# Usage: +# export GROQ_API_KEY=gsk_xxx +# export OPENAI_API_KEY=sk-xxx +# export COHERE_API_KEY=xxx +# ./docker/test-slim-local.sh +# +# Or inline: +# GROQ_API_KEY=gsk_xxx OPENAI_API_KEY=sk_xxx COHERE_API_KEY=xxx ./docker/test-slim-local.sh +# + +set -euo pipefail + +# Check for required API keys +if [ -z "${GROQ_API_KEY:-}" ]; then + echo "❌ Error: GROQ_API_KEY environment variable is required" + echo "Set it with: export GROQ_API_KEY=gsk_xxx" + exit 1 +fi + +if [ -z "${OPENAI_API_KEY:-}" ]; then + echo "❌ Error: OPENAI_API_KEY environment variable is required" + echo "Set it with: export OPENAI_API_KEY=sk-xxx" + exit 1 +fi + +if [ -z "${COHERE_API_KEY:-}" ]; then + echo "❌ Error: COHERE_API_KEY environment variable is required" + echo "Set it with: export COHERE_API_KEY=xxx" + exit 1 +fi + +# Configuration +IMAGE="${1:-hindsight-slim:test}" +echo "Testing image: $IMAGE" +echo "" + +# Set up external providers +export HINDSIGHT_API_EMBEDDINGS_PROVIDER=openai +export HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY=$OPENAI_API_KEY +export HINDSIGHT_API_RERANKER_PROVIDER=cohere +export HINDSIGHT_API_COHERE_API_KEY=$COHERE_API_KEY + +# Run the test +exec "$(dirname "$0")/test-image.sh" "$IMAGE" standalone diff --git a/hindsight-docs/docs/developer/installation.md b/hindsight-docs/docs/developer/installation.md index eea36344..983f8972 100644 --- a/hindsight-docs/docs/developer/installation.md +++ b/hindsight-docs/docs/developer/installation.md @@ -50,6 +50,81 @@ docker run --rm -it --pull always -p 8888:8888 -p 9999:9999 \ - **API Server**: http://localhost:8888 - **Control Plane** (Web UI): http://localhost:9999 +### Docker Image Variants + +Hindsight provides two image variants with different size/capability tradeoffs: + +| Variant | Size (AMD64) | Size (ARM64) | Use Case | +|---------|--------------|--------------|----------| +| **Full** (`latest`) | ~9 GB | ~3.7 GB | Includes local ML models (embeddings, reranking) | +| **Slim** (`slim`) | ~500 MB | ~500 MB | Requires external embedding/reranking providers | + +**Full image** (default): +```bash +docker run --rm -it -p 8888:8888 \ + -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \ + ghcr.io/vectorize-io/hindsight:latest +``` +- ✅ Works out of the box with local ML models +- ✅ No additional services needed +- ❌ Larger image size (AMD64 includes CUDA libraries for GPU support) + +**Slim image**: +```bash +docker run --rm -it -p 8888:8888 \ + -e HINDSIGHT_API_LLM_API_KEY=$OPENAI_API_KEY \ + -e HINDSIGHT_API_EMBEDDINGS_PROVIDER=openai \ + -e HINDSIGHT_API_RERANKER_PROVIDER=cohere \ + -e HINDSIGHT_API_COHERE_API_KEY=$COHERE_API_KEY \ + ghcr.io/vectorize-io/hindsight:slim +``` +- ✅ Dramatically smaller image (~95% reduction on AMD64) +- ✅ Faster pull/deploy times +- ✅ Lower memory footprint +- ❌ Requires external embedding/reranking services (OpenAI, Cohere, TEI) + +**When to use slim:** +- Cloud deployments where image size matters +- Using managed embedding services (OpenAI, Cohere) +- Running on Text Embeddings Inference (TEI) infrastructure +- Kubernetes environments with fast pull requirements + +:::warning Slim Image Requires External Providers +If you run the slim image **without** setting external embedding providers, you'll see this error: + +``` +ImportError: sentence-transformers is required for LocalSTEmbeddings. +Install it with: pip install sentence-transformers +``` + +**Fix:** Always set embedding and reranking providers when using slim images: +```bash +-e HINDSIGHT_API_EMBEDDINGS_PROVIDER=openai +-e HINDSIGHT_API_EMBEDDINGS_OPENAI_API_KEY=sk-xxx +-e HINDSIGHT_API_RERANKER_PROVIDER=cohere +-e HINDSIGHT_API_COHERE_API_KEY=xxx +``` +::: + +See [Configuration](./configuration#embeddings-and-reranking) for all embedding provider options. + +### Available Tags + +```bash +# Standalone (API + Control Plane) +ghcr.io/vectorize-io/hindsight:latest # Full, latest release +ghcr.io/vectorize-io/hindsight:slim # Slim, latest release +ghcr.io/vectorize-io/hindsight:0.4.9 # Full, specific version +ghcr.io/vectorize-io/hindsight:0.4.9-slim # Slim, specific version + +# API only +ghcr.io/vectorize-io/hindsight-api:latest +ghcr.io/vectorize-io/hindsight-api:slim + +# Control Plane only +ghcr.io/vectorize-io/hindsight-control-plane:latest +``` + --- ## Helm / Kubernetes