diff --git a/.env.default b/.env.default index ada21b356..94cbf7921 100644 --- a/.env.default +++ b/.env.default @@ -25,7 +25,11 @@ COMPOSE_PROFILES=gpu #RABBITMQ_DEFAULT_VHOST=default #GIRDER_WORKER_BROKER=amqp://guest:guest@rabbit/default #GIRDER_WORKER_BACKEND=rpc://guest:guest@localhost/ +# Server-side: stamped into jobs at schedule time (worker.api_url setting) #GIRDER_SETTING_WORKER_API_URL=http://girder:8080/api/v1 +# Worker-side: per-worker override when this process cannot reach the stamped URL +# (e.g. remote workers: http://WEB_HOST:8010/api/v1). Leave unset on single-node. +#GIRDER_WORKER_API_URL= #GIRDER_NOTIFICATION_REDIS_URL=redis://redis:6379 # Docker image tag for kitware/viame-web (workers use cpu/gpu tags in compose) @@ -66,7 +70,7 @@ COMPOSE_PROFILES=gpu #ACME_EMAIL=changeme@domain.com #ACME_CA_SERVER=https://acme-v02.api.letsencrypt.org/directory #LOG_LEVEL=ERROR -# Split or standalone worker: remote Girder API (replaces legacy WORKER_API_URL) -#GIRDER_SETTING_WORKER_API_URL=https://viame.kitware.com/api/v1 +# Split / remote worker: override stamped API URL on the worker process +#GIRDER_WORKER_API_URL=https://viame.kitware.com/api/v1 #SOCK_PATH=/var/run/docker.sock WATCHTOWER_API_TOKEN="customtokenstring" diff --git a/docker-compose.yml b/docker-compose.yml index d4ee3cedc..a7312ff68 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -77,6 +77,7 @@ services: - "GIRDER_MONGO_URI=mongodb://mongo:27017/girder" - "GIRDER_NOTIFICATION_REDIS_URL=redis://redis:6379" - "C_FORCE_ROOT=1" + - "GIRDER_WORKER_API_URL=${GIRDER_WORKER_API_URL:-}" # Memcache is used for caching girder-large-images, should auto configure with it being present memcached: @@ -144,6 +145,7 @@ services: - "GIRDER_WORKER_BROKER=${GIRDER_WORKER_BROKER:-amqp://guest:guest@rabbit/default}" - "GIRDER_WORKER_BACKEND=${GIRDER_WORKER_BACKEND:-rpc://guest:guest@localhost/}" - "GIRDER_SETTING_WORKER_API_URL=${GIRDER_SETTING_WORKER_API_URL:-http://girder:8080/api/v1}" + - "GIRDER_WORKER_API_URL=${GIRDER_WORKER_API_URL:-}" girder_worker_pipelines: # Merge base-worker object with this config @@ -173,6 +175,7 @@ services: - "CELERY_BROKER_URL=${CELERY_BROKER_URL:-amqp://guest:guest@rabbit/default}" - "GIRDER_WORKER_BACKEND=${GIRDER_WORKER_BACKEND:-rpc://guest:guest@localhost/}" - "GIRDER_SETTING_WORKER_API_URL=${GIRDER_SETTING_WORKER_API_URL:-http://girder:8080/api/v1}" + - "GIRDER_WORKER_API_URL=${GIRDER_WORKER_API_URL:-}" - "GIRDER_MONGO_URI=mongodb://mongo:27017/girder" - "GIRDER_NOTIFICATION_REDIS_URL=redis://redis:6379" healthcheck: @@ -209,6 +212,7 @@ services: - "CELERY_BROKER_URL=${CELERY_BROKER_URL:-amqp://guest:guest@rabbit/default}" - "GIRDER_WORKER_BACKEND=${GIRDER_WORKER_BACKEND:-rpc://guest:guest@localhost/}" - "GIRDER_SETTING_WORKER_API_URL=${GIRDER_SETTING_WORKER_API_URL:-http://girder:8080/api/v1}" + - "GIRDER_WORKER_API_URL=${GIRDER_WORKER_API_URL:-}" healthcheck: test: ["CMD", "nvidia-smi"] interval: 15m diff --git a/docs/Deployment-Docker-Compose.md b/docs/Deployment-Docker-Compose.md index 382dd9e8b..18b330ee2 100644 --- a/docs/Deployment-Docker-Compose.md +++ b/docs/Deployment-Docker-Compose.md @@ -119,32 +119,63 @@ It's possible to split your web server and task runner between multiple nodes. * Make two cloud VM instances, one with NVIDIA drivers and container toolkit, and one without. This is still a special case of scenario 1 from the [Provisioning Guide](Deployment-Provision.md) * Clone the dive repository on both VMs. The `.env` files are **not** identical — the web VM keeps the internal Compose service names, while the worker VM points at the web VM's IP or hostname (referred to below as `WEB_HOST`). +### Worker API URL settings + +Two related variables control how workers call back to Girder. They apply at different layers: + +| Variable | Where to set it | Role | +|----------|-----------------|------| +| `GIRDER_SETTING_WORKER_API_URL` | **Girder web server** process | Sets the Girder system setting `worker.api_url`. That value is **stamped into every job** when it is scheduled (Celery headers / `jobInfoSpec`). | +| `GIRDER_WORKER_API_URL` | **Worker** process only | Optional **per-worker override**. When set, the worker uses this URL for API callbacks instead of the URL stamped at schedule time (including job status/log updates). | + +!!! tip "When to set `GIRDER_WORKER_API_URL`" + + Use it when a worker cannot reach the API URL stamped by the server — typically remote or external workers that cannot resolve Docker-internal names like `girder:8080`. + + Typical mixed setup: + + * Web VM + `localworker` on the Compose network keep the default stamped URL `http://girder:8080/api/v1`. + * Remote worker VMs set `GIRDER_WORKER_API_URL=http://WEB_HOST:8010/api/v1` so they reach Girder through the host-published Traefik port. + + Leave `GIRDER_WORKER_API_URL` **unset** on single-node stacks and on any worker that can already reach the stamped URL. + !!! warning - `GIRDER_SETTING_WORKER_API_URL` is applied by the **web server** as the `worker.api_url` Girder system setting and is then stamped into every job that is dispatched to workers. It must be set on the **web VM**. Setting it only on the worker VM has no effect on the callback URL that workers actually use. + `GIRDER_SETTING_WORKER_API_URL` only affects the **web server** process that schedules jobs. Setting it on a worker container does **not** change the callback URL that worker uses at runtime — use `GIRDER_WORKER_API_URL` for that. + + Port `8080` is Girder's in-container port and is **not** published on the host. Traefik publishes the API on host port `8010` (base `docker-compose.yml`) or on `80`/`443` when you also use `docker-compose.prod.yml`. Remote workers should use the published port — never `8080` unless you publish it yourself. ### Web VM `.env` -The web VM runs RabbitMQ, Redis, and Mongo as Compose services, so it keeps the internal service names — with one exception: `GIRDER_SETTING_WORKER_API_URL` must be an address the **remote worker** can reach, not the internal `girder:8080`. +The web VM runs RabbitMQ, Redis, and Mongo as Compose services and should keep internal service names. For a mixed local + remote worker deployment, leave the server stamping the internal URL (the Compose default): ``` -GIRDER_SETTING_WORKER_API_URL=http://WEB_HOST:8010/api/v1 +# Optional — this is already the Compose default for a single-node / mixed stack +#GIRDER_SETTING_WORKER_API_URL=http://girder:8080/api/v1 ``` -!!! note +`localworker` on the same Compose network can reach `girder:8080` and does not need `GIRDER_WORKER_API_URL`. - Port `8080` is Girder's in-container port and is **not** published on the host. Traefik publishes the API on host port `8010` (base `docker-compose.yml`) or on `80`/`443` when you also use `docker-compose.prod.yml`. Use the published port here — never `8080`. +!!! note - This only applies when workers run on a **separate** host. For a single-node (all-in-one) stack, leave `GIRDER_SETTING_WORKER_API_URL` unset — the default `http://girder:8080/api/v1` is correct because the worker resolves `girder` and reaches port `8080` over the internal Compose network. + If **every** worker (including `localworker`) can reach a single public API URL — for example via host hairpin to `WEB_HOST:8010` — you may instead set `GIRDER_SETTING_WORKER_API_URL=http://WEB_HOST:8010/api/v1` on the web VM and omit `GIRDER_WORKER_API_URL`. Prefer `GIRDER_WORKER_API_URL` on remote workers when local and remote workers need different reachable URLs. ### Worker VM `.env` On the worker VM, uncomment and set these to point at `WEB_HOST`: * `GIRDER_WORKER_BROKER` — RabbitMQ URL on the web VM (e.g. `amqp://guest:guest@WEB_HOST/default`) -* `GIRDER_SETTING_WORKER_API_URL` — Girder API URL on the web VM (e.g. `http://WEB_HOST:8010/api/v1`; match the web VM's value) +* `GIRDER_WORKER_API_URL` — Girder API URL reachable from this worker (e.g. `http://WEB_HOST:8010/api/v1`) * `GIRDER_NOTIFICATION_REDIS_URL` — Redis URL on the web VM (e.g. `redis://WEB_HOST:6379`); required so workers can publish job/UI status notifications +Example: + +``` +GIRDER_WORKER_BROKER=amqp://guest:guest@WEB_HOST/default +GIRDER_WORKER_API_URL=http://WEB_HOST:8010/api/v1 +GIRDER_NOTIFICATION_REDIS_URL=redis://WEB_HOST:6379 +``` + ### Required connectivity The worker VM must be able to reach the web VM on these ports (port `8080` does **not** need to be open between VMs): @@ -169,13 +200,24 @@ In this split setup, `localworker` on the web server handles the `local` queue, ### Verify the configuration -After the stack is up, confirm the web server is handing out the correct callback URL (not the internal `girder:8080` default). From the web VM's Swagger page or via curl: +After the stack is up: + +1. Confirm what the web server stamps into jobs (Swagger or curl on the web VM): ``` GET /api/v1/system/setting?key=worker.api_url ``` -This must return your `http://WEB_HOST:8010/api/v1` value. If it still shows `http://girder:8080/api/v1`, the web VM's `GIRDER_SETTING_WORKER_API_URL` was not applied — jobs will appear to dispatch (and `GET /worker/status` will look healthy, since that only reflects broker connectivity) but will stall because remote workers cannot reach `girder:8080`. +For the recommended mixed setup this is `http://girder:8080/api/v1`. That value is correct for `localworker`; remote workers rely on `GIRDER_WORKER_API_URL` instead. + +2. On each remote worker container, confirm the override is present: + +```bash +docker compose exec girder_worker_default printenv GIRDER_WORKER_API_URL +# -> http://WEB_HOST:8010/api/v1 +``` + +If remote workers stall while `GET /worker/status` looks healthy (broker only), they usually cannot reach the stamped URL and `GIRDER_WORKER_API_URL` is missing or wrong. ## Addon management @@ -233,7 +275,7 @@ This image contains both the backend and client. | GIRDER_ADMIN_PASS | `letmein` | admin password | | GIRDER_WORKER_BROKER | `amqp://guest:guest@rabbit/default` | RabbitMQ connection string (Celery broker) | | GIRDER_WORKER_BACKEND | `rpc://guest:guest@localhost/` | Celery result backend (RPC) | -| GIRDER_SETTING_WORKER_API_URL | `http://girder:8080/api/v1` | Girder REST API URL used by workers. Default is correct for a single-node stack; override with the web host's published URL (e.g. `http://WEB_HOST:8010/api/v1`) only for [split deployments](#splitting-services). | +| GIRDER_SETTING_WORKER_API_URL | `http://girder:8080/api/v1` | Girder system setting `worker.api_url` stamped into jobs at schedule time. Default is correct for single-node and for `localworker` on the Compose network. See [Worker API URL settings](#worker-api-url-settings). | | GIRDER_NOTIFICATION_REDIS_URL | `redis://redis:6379` | Redis URL for notification fan-out | | GIRDER_STATIC_ROOT_DIR | `/opt/dive/clients/girder` | Built web client static files (set in image/Compose) | @@ -260,7 +302,8 @@ This image contains a celery worker to run VIAME pipelines and transcoding jobs. | WORKER_CONCURRENCY | `# of CPU cores` | max concurrnet jobs. **Lower this if you run training** | | WORKER_GPU_UUID | null | leave empty to use all GPUs. Specify UUID to use specific device | | GIRDER_WORKER_BROKER | `amqp://guest:guest@rabbit/default` | RabbitMQ connection string. Ignored in standalone mode. | -| GIRDER_SETTING_WORKER_API_URL | `http://girder:8080/api/v1` | Girder API URL (split/multi-node deployments). Ignored in standalone mode when using `DIVE_API_URL`. | +| GIRDER_WORKER_API_URL | unset | Optional per-worker override of the Girder API base URL used for callbacks. Set on remote/external workers that cannot reach the URL stamped by the server (e.g. `http://WEB_HOST:8010/api/v1`). See [Worker API URL settings](#worker-api-url-settings). Ignored in standalone mode when using `DIVE_API_URL`. | +| GIRDER_SETTING_WORKER_API_URL | `http://girder:8080/api/v1` | Only meaningful on the **web server** (stamped at schedule time). Setting it on a worker process does not override callback URLs at runtime — use `GIRDER_WORKER_API_URL` instead. | | GIRDER_NOTIFICATION_REDIS_URL | `redis://redis:6379` | Redis for notifications when running workers in Compose | | KWIVER_DEFAULT_LOG_LEVEL | `warn` | Log level for VIAME pipeline jobs (env name unchanged; used by the Kwiver logging stack) | | DIVE_USERNAME | null | Username to start private queue processor. Providing this enables standalone mode. | diff --git a/docs/Deployment-Girder-5-Upgrade.md b/docs/Deployment-Girder-5-Upgrade.md index d588a13cd..2e6dcd24f 100644 --- a/docs/Deployment-Girder-5-Upgrade.md +++ b/docs/Deployment-Girder-5-Upgrade.md @@ -21,12 +21,15 @@ Update your `.env` (and any external orchestration) to use Girder 5 names: | Girder 4 / legacy name | Girder 5 name | Purpose | |------------------------|---------------|---------| | `CELERY_BROKER_URL` | `GIRDER_WORKER_BROKER` | RabbitMQ URL for Celery (message broker). | -| `WORKER_API_URL` | `GIRDER_SETTING_WORKER_API_URL` | Girder REST API URL workers call (e.g. `http://girder:8080/api/v1`). | +| `WORKER_API_URL` | `GIRDER_SETTING_WORKER_API_URL` | Server-side Girder setting `worker.api_url` stamped into jobs at schedule time (e.g. `http://girder:8080/api/v1`). | +| *(not used)* | `GIRDER_WORKER_API_URL` | Optional **worker-side** override of the stamped API URL for callbacks. Set on remote workers that cannot reach the server-stamped URL. | | *(not used)* | `GIRDER_WORKER_BACKEND` | Celery result backend (Compose default: `rpc://guest:guest@localhost/`). | | *(not used)* | `GIRDER_NOTIFICATION_REDIS_URL` | Redis URL for Girder notification fan-out (e.g. `redis://redis:6379`). | | *(not used)* | `GIRDER_STATIC_ROOT_DIR` | Path to built web client inside the Girder container (set in Compose). | -`GIRDER_WORKER_BACKEND` is **not** a replacement for `WORKER_API_URL`. Do not point it at `http://…/api/v1`; use `GIRDER_SETTING_WORKER_API_URL` for that. +`GIRDER_WORKER_BACKEND` is **not** a replacement for `WORKER_API_URL`. Do not point it at `http://…/api/v1`. + +Use `GIRDER_SETTING_WORKER_API_URL` on the **web server** (what gets stamped into jobs). Use `GIRDER_WORKER_API_URL` on **worker** processes when they need a different reachable API base than the stamped value. Details: [Worker API URL settings](Deployment-Docker-Compose.md#worker-api-url-settings). ## Recommended upgrade steps @@ -35,7 +38,7 @@ Update your `.env` (and any external orchestration) to use Girder 5 names: 3. **Set image tag** if needed (for example `TAG=latest` in `.env`), then pull images. 4. **Add Redis** — use the `redis` service from `docker-compose.yml` or an external Redis instance and set `GIRDER_NOTIFICATION_REDIS_URL` on `girder` and all worker services. 5. **Rename variables** in `.env` per the table above. -6. **Multi-node deployments:** set `GIRDER_WORKER_BROKER` to your RabbitMQ URL and `GIRDER_SETTING_WORKER_API_URL` to your Girder API URL on every worker host; workers must reach Redis used by the web tier if they rely on notification-related features configured in Compose. +6. **Multi-node deployments:** set `GIRDER_WORKER_BROKER` to your RabbitMQ URL and `GIRDER_WORKER_API_URL` to a Girder API URL reachable from that worker (for example `http://WEB_HOST:8010/api/v1`). Keep `GIRDER_SETTING_WORKER_API_URL` on the web VM at the value appropriate for `localworker` (often the Compose default `http://girder:8080/api/v1`). Workers must reach Redis used by the web tier if they rely on notification-related features configured in Compose. 7. **Rebuild or pull** images, then restart the stack: ```bash diff --git a/test-girder-worker-api-url/Dockerfile.girder b/test-girder-worker-api-url/Dockerfile.girder new file mode 100644 index 000000000..ac18e7c70 --- /dev/null +++ b/test-girder-worker-api-url/Dockerfile.girder @@ -0,0 +1,75 @@ +# Test build of kitware/viame-web with Girder from girder-worker-api-override. +# Based on docker/girder.Dockerfile; installs Girder packages from the test branch. + +# ======================== +# == CLIENT BUILD STAGE == +# ======================== +FROM node:24.15.0 AS client-builder +WORKDIR /app + +COPY client/package.json client/package-lock.json /app/ +RUN npm install +COPY .git/ /app/.git/ +COPY client/ /app/ +RUN npm run build:web + +FROM node:24.15.0 AS girder-client-builder +WORKDIR /app +RUN apt-get update && apt-get install -y git +ARG GIRDER_GIT_REPO=https://github.com/girder/girder.git +ARG GIRDER_GIT_REF=girder-worker-api-override +ARG CACHEBUST=1 +RUN git clone --depth 1 --branch "${GIRDER_GIT_REF}" "${GIRDER_GIT_REPO}" girder-src \ + && mv girder-src girder +RUN cd girder/girder/web && npm install --include=optional && npx vite build --base=/girder/ + +# ======================== +# == SERVER BUILD STAGE == +# ======================== +FROM ghcr.io/astral-sh/uv:python3.11-bookworm-slim AS server-builder +SHELL ["/bin/bash", "-c"] + +WORKDIR /opt/dive/src +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + curl git build-essential libssl-dev libffi-dev python3-libtiff \ + libgdal-dev python3-dev cargo nodejs npm && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy +ENV VIRTUAL_ENV="/opt/dive/local/venv" +ENV UV_PROJECT_ENVIRONMENT=/opt/dive/local/venv +ENV PATH="/opt/dive/local/venv/bin:$PATH" +RUN uv venv /opt/dive/local/venv + +COPY server/pyproject.toml server/uv.lock /opt/dive/src/ +RUN uv sync --frozen --no-install-project --no-dev --extra large-image +COPY server/ /opt/dive/src/ +RUN uv sync --frozen --no-dev --extra large-image + +# Override locked girder-worker with the test branch (adds GIRDER_WORKER_API_URL). +ARG GIRDER_GIT_REPO=https://github.com/girder/girder.git +ARG GIRDER_GIT_REF=girder-worker-api-override +COPY test-girder-worker-api-url/install_girder_branch.sh /tmp/install_girder_branch.sh +RUN chmod +x /tmp/install_girder_branch.sh \ + && GIRDER_GIT_REPO="${GIRDER_GIT_REPO}" GIRDER_GIT_REF="${GIRDER_GIT_REF}" \ + /tmp/install_girder_branch.sh + +# ================= +# == DIST SERVER == +# ================= +FROM ghcr.io/astral-sh/uv:python3.11-bookworm-slim AS server + +ENV GIT_PYTHON_REFRESH="quiet" +ENV VIRTUAL_ENV="/opt/dive/local/venv" +ENV UV_PROJECT_ENVIRONMENT=/opt/dive/local/venv +ENV PATH="/opt/dive/local/venv/bin:/usr/local/bin:$PATH" + +COPY --from=server-builder /opt/dive/local/venv /opt/dive/local/venv +COPY --from=server-builder /opt/dive/src /opt/dive/src +COPY --from=client-builder /app/dist/ /opt/dive/clients/dive +COPY --from=girder-client-builder /app/girder/girder/web/dist/ /opt/dive/clients/girder +COPY docker/entrypoint_server.sh docker/server_setup.py / +RUN export GIRDER_STATIC_ROOT_DIR=/opt/dive/clients/girder +ENTRYPOINT [ "/entrypoint_server.sh" ] diff --git a/test-girder-worker-api-url/Dockerfile.worker b/test-girder-worker-api-url/Dockerfile.worker new file mode 100644 index 000000000..cef6eddaf --- /dev/null +++ b/test-girder-worker-api-url/Dockerfile.worker @@ -0,0 +1,50 @@ +# Test build of kitware/viame-worker:cpu with Girder from girder-worker-api-override. +# Based on docker/girder_worker.Dockerfile; installs Girder packages from the test branch. + +FROM python:3.11-bookworm AS worker + +RUN apt-get update && \ + apt-get install -qy tini ffmpeg git && \ + apt-get clean && rm -rf /var/lib/apt/lists/* + +RUN ln -sf /usr/bin/tini /tini +RUN ln -fs /usr/bin/python3.11 /usr/bin/python +WORKDIR /opt/dive/src + +COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv +ENV VIRTUAL_ENV="/opt/dive/local/venv" +ENV UV_PROJECT_ENVIRONMENT=/opt/dive/local/venv +ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy +RUN uv venv /opt/dive/local/venv +ENV PATH="/opt/dive/local/venv/bin:/usr/local/bin:$PATH" + +COPY server/pyproject.toml server/uv.lock /opt/dive/src/ +RUN uv sync --frozen --no-install-project --no-dev + +COPY server/ /opt/dive/src/ + +RUN useradd --create-home --uid 1099 --shell=/bin/bash dive +RUN install -g dive -o dive -d /tmp/addons +RUN chown -R dive /opt/dive/local/ + +USER dive +RUN uv sync --frozen --no-dev + +# Override locked girder-worker with the test branch (adds GIRDER_WORKER_API_URL). +ARG GIRDER_GIT_REPO=https://github.com/girder/girder.git +ARG GIRDER_GIT_REF=girder-worker-api-override +USER root +COPY test-girder-worker-api-url/install_girder_branch.sh /tmp/install_girder_branch.sh +RUN chmod +x /tmp/install_girder_branch.sh \ + && chown dive:dive /tmp/install_girder_branch.sh +USER dive +RUN GIRDER_GIT_REPO="${GIRDER_GIT_REPO}" GIRDER_GIT_REF="${GIRDER_GIT_REF}" \ + /tmp/install_girder_branch.sh + +RUN install -d /opt/dive/local/ffmpeg && \ + ln -sf /usr/bin/ffmpeg /opt/dive/local/ffmpeg/ffmpeg && \ + ln -sf /usr/bin/ffprobe /opt/dive/local/ffmpeg/ffprobe +COPY --chown=dive:dive docker/entrypoint_worker.sh / + +ENTRYPOINT ["/tini", "--"] +CMD ["/entrypoint_worker.sh"] diff --git a/test-girder-worker-api-url/README.md b/test-girder-worker-api-url/README.md new file mode 100644 index 000000000..bf3818c27 --- /dev/null +++ b/test-girder-worker-api-url/README.md @@ -0,0 +1,73 @@ +# Test harness for `GIRDER_WORKER_API_URL` (Girder branch `girder-worker-api-override`) + +This folder reproduces the split-worker scenario that motivated the env var: + +1. **Girder** stamps jobs with the internal URL `http://girder:8080/api/v1`. +2. **`localworker`** (same Compose network) sets `GIRDER_WORKER_API_URL=http://girder:8080/api/v1`. +3. **CPU worker** (separate Compose project, no shared network) sets + `GIRDER_WORKER_API_URL=http://host.docker.internal:8010/api/v1` so callbacks reach + Girder through the host-published Traefik port. + +Both images reinstall **only** `girder-worker` from branch `girder-worker-api-override` +(override with `GIRDER_GIT_REF` / `GIRDER_GIT_REPO`). Other Girder packages stay on PyPI +so plugin `web_client/dist` assets remain available. + +## Quick start + +From the repository root: + +```bash +# 1. Main stack + localworker (internal API URL) +docker compose -f test-girder-worker-api-url/docker-compose.yml up -d --build + +# 2. Separate CPU worker (host.docker.internal override) +docker compose -f test-girder-worker-api-url/docker-compose.cpu-worker.yml up -d --build +``` + +UI / API: http://localhost:8010 +RabbitMQ management: http://localhost:15672 (guest/guest) + +## What to verify + +```bash +# Confirm the branch install exposes the helper +docker compose -f test-girder-worker-api-url/docker-compose.yml exec localworker \ + python -c "from girder_worker.utils import GIRDER_WORKER_API_URL_ENV; print(GIRDER_WORKER_API_URL_ENV)" + +docker compose -f test-girder-worker-api-url/docker-compose.cpu-worker.yml exec girder_worker_default \ + python -c "import os; from girder_worker.utils import resolve_girder_api_url; print(os.environ.get('GIRDER_WORKER_API_URL')); print(resolve_girder_api_url('http://girder:8080/api/v1'))" +``` + +The CPU worker check should print: + +``` +http://host.docker.internal:8010/api/v1 +http://host.docker.internal:8010/api/v1 +``` + +Server-stamped setting (for contrast): + +```bash +curl -s "http://localhost:8010/api/v1/system/setting?key=worker.api_url" +# -> "http://girder:8080/api/v1" +``` + +Dispatch a `local` queue job (handled by `localworker`) and a `celery` queue job +(handled by the external CPU worker). The external worker should succeed only +because `GIRDER_WORKER_API_URL` rewrites the stamped `girder:8080` callback URL. + +## Tear down + +```bash +docker compose -f test-girder-worker-api-url/docker-compose.cpu-worker.yml down +docker compose -f test-girder-worker-api-url/docker-compose.yml down +``` + +## Files + +| File | Role | +|------|------| +| `docker-compose.yml` | Girder stack + `localworker` with `girder:8080` override | +| `docker-compose.cpu-worker.yml` | Isolated CPU worker with `host.docker.internal` override | +| `Dockerfile.girder` / `Dockerfile.worker` | Builds that install Girder from the test branch | +| `install_girder_branch.sh` | Shared `uv pip install` of `girder-worker` from the test branch | diff --git a/test-girder-worker-api-url/docker-compose.cpu-worker.yml b/test-girder-worker-api-url/docker-compose.cpu-worker.yml new file mode 100644 index 000000000..65fd31ef0 --- /dev/null +++ b/test-girder-worker-api-url/docker-compose.cpu-worker.yml @@ -0,0 +1,35 @@ +## Standalone CPU worker for testing GIRDER_WORKER_API_URL +## +## Runs outside the main Compose network. Jobs are stamped with +## http://girder:8080/api/v1 (unreachable here), so this worker overrides +## callbacks via host.docker.internal (host-published Traefik port 8010). +## +## Prerequisites: main stack already running +## docker compose -f test-girder-worker-api-url/docker-compose.yml up -d --build +## +## Start this worker: +## docker compose -f test-girder-worker-api-url/docker-compose.cpu-worker.yml up -d --build + +name: dive-gw-api-cpu-worker + +services: + girder_worker_default: + build: + context: .. + dockerfile: test-girder-worker-api-url/Dockerfile.worker + args: + GIRDER_GIT_REPO: ${GIRDER_GIT_REPO:-https://github.com/girder/girder.git} + GIRDER_GIT_REF: ${GIRDER_GIT_REF:-girder-worker-api-override} + image: kitware/viame-worker:gw-api-test-cpu + ipc: host + extra_hosts: + - "host.docker.internal:host-gateway" + environment: + - "WORKER_WATCHING_QUEUES=celery" + - "WORKER_CONCURRENCY=${DEFAULT_WORKER_CONCURRENCY:-2}" + - "GIRDER_WORKER_BROKER=amqp://guest:guest@host.docker.internal:5672/default" + - "GIRDER_WORKER_BACKEND=rpc://guest:guest@localhost/" + - "GIRDER_NOTIFICATION_REDIS_URL=redis://host.docker.internal:6379" + # Override stamped girder:8080 URL with a host-reachable API. + - "GIRDER_WORKER_API_URL=http://host.docker.internal:8010/api/v1" + - "C_FORCE_ROOT=1" diff --git a/test-girder-worker-api-url/docker-compose.yml b/test-girder-worker-api-url/docker-compose.yml new file mode 100644 index 000000000..37ef37944 --- /dev/null +++ b/test-girder-worker-api-url/docker-compose.yml @@ -0,0 +1,124 @@ +## Main stack for testing GIRDER_WORKER_API_URL +## +## Girder stamps jobs with http://girder:8080/api/v1 (internal Docker DNS). +## localworker overrides callbacks with the same internal URL via GIRDER_WORKER_API_URL. +## +## Run from repo root: +## docker compose -f test-girder-worker-api-url/docker-compose.yml up -d --build +## +## Then start the separate CPU worker: +## docker compose -f test-girder-worker-api-url/docker-compose.cpu-worker.yml up -d --build + +name: dive-gw-api-test + +services: + traefik: + image: traefik:v3.7.4 + container_name: dive-gw-api-traefik + command: > + --providers.docker=true + --providers.docker.exposedByDefault=false + --entrypoints.web.address=:80 + volumes: + - "${SOCK_PATH:-/var/run/docker.sock}:/var/run/docker.sock" + ports: + - "8010:80" + + rabbit: + image: rabbitmq:4.2-management + ports: + - "5672:5672" + - "15672:15672" + environment: + - RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER:-guest} + - RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS:-guest} + - RABBITMQ_DEFAULT_VHOST=${RABBITMQ_DEFAULT_VHOST:-default} + - "RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS=-rabbit consumer_timeout 432000000" + + mongo: + image: mongo:5.0 + volumes: + - mongo_db:/data/db + + redis: + image: redis:latest + command: ["redis-server", "--maxmemory", "8192MB", "--maxmemory-policy", "volatile-lru"] + ports: + - "6379:6379" + volumes: + - redis_data:/data + + memcached: + image: memcached + command: -m 1024 --max-item-size 8M + restart: unless-stopped + + girder: + build: + context: .. + dockerfile: test-girder-worker-api-url/Dockerfile.girder + args: + GIRDER_GIT_REPO: ${GIRDER_GIT_REPO:-https://github.com/girder/girder.git} + GIRDER_GIT_REF: ${GIRDER_GIT_REF:-girder-worker-api-override} + image: kitware/viame-web:gw-api-test + init: true + command: ["--mode", "production"] + depends_on: + - mongo + - traefik + - memcached + - rabbit + - redis + volumes: + - girder_assetstore:/home/assetstore + - type: bind + source: ${PUBLIC_DATA_PATH:-../docker/girder_data} + target: /data/public + read_only: true + environment: + - "GIRDER_MONGO_URI=mongodb://mongo:27017/girder" + - "GIRDER_ADMIN_USER=${GIRDER_ADMIN_USER:-admin}" + - "GIRDER_ADMIN_PASS=${GIRDER_ADMIN_PASS:-letmein}" + - "GIRDER_WORKER_BROKER=${GIRDER_WORKER_BROKER:-amqp://guest:guest@rabbit/default}" + - "GIRDER_WORKER_BACKEND=${GIRDER_WORKER_BACKEND:-rpc://guest:guest@localhost/}" + # Stamp jobs with the internal URL so remote workers must use GIRDER_WORKER_API_URL. + - "GIRDER_SETTING_WORKER_API_URL=http://girder:8080/api/v1" + - "GIRDER_STATIC_ROOT_DIR=/opt/dive/clients/girder" + - "GIRDER_NOTIFICATION_REDIS_URL=redis://redis:6379" + - "LARGE_IMAGE_CACHE_BACKEND=memcached" + - "LARGE_IMAGE_CACHE_MEMCACHED_URL=memcached" + labels: + - "traefik.enable=true" + - "traefik.http.services.girder-svc.loadbalancer.server.port=8080" + - "traefik.http.routers.girder-rtr.entrypoints=web" + - "traefik.http.routers.girder-rtr.rule=HostRegexp(`.+`)" + + # Same Docker network as Girder — uses internal API URL override. + localworker: + build: + context: .. + dockerfile: test-girder-worker-api-url/Dockerfile.girder + args: + GIRDER_GIT_REPO: ${GIRDER_GIT_REPO:-https://github.com/girder/girder.git} + GIRDER_GIT_REF: ${GIRDER_GIT_REF:-girder-worker-api-override} + image: kitware/viame-web:gw-api-test + depends_on: + - girder + - rabbit + - redis + - mongo + entrypoint: celery -A girder_worker.app worker -Q local + environment: + - "WORKER_WATCHING_QUEUES=local" + - "GIRDER_WORKER_BROKER=amqp://guest:guest@rabbit/default" + - "GIRDER_WORKER_BACKEND=rpc://guest:guest@localhost/" + - "GIRDER_MONGO_URI=mongodb://mongo:27017/girder" + - "GIRDER_NOTIFICATION_REDIS_URL=redis://redis:6379" + - "C_FORCE_ROOT=1" + # Per-worker override reachable on the Compose network. + - "GIRDER_WORKER_API_URL=http://girder:8080/api/v1" + +volumes: + mongo_db: + girder_assetstore: + redis_data: diff --git a/test-girder-worker-api-url/install_girder_branch.sh b/test-girder-worker-api-url/install_girder_branch.sh new file mode 100755 index 000000000..c5aade574 --- /dev/null +++ b/test-girder-worker-api-url/install_girder_branch.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# Reinstall girder-worker from a specific git branch/ref. +# The GIRDER_WORKER_API_URL feature lives only in the worker package; leave +# girder / girder-jobs / girder-plugin-worker on PyPI so their prebuilt +# web_client/dist assets remain intact. +set -e + +GIRDER_GIT_REPO="${GIRDER_GIT_REPO:-https://github.com/girder/girder.git}" +GIRDER_GIT_REF="${GIRDER_GIT_REF:-girder-worker-api-override}" +BASE="git+${GIRDER_GIT_REPO}@${GIRDER_GIT_REF}" + +echo "Installing girder-worker from ${GIRDER_GIT_REPO}@${GIRDER_GIT_REF}" + +uv pip install --reinstall \ + "girder-worker @ ${BASE}#subdirectory=worker" + +python -c "from girder_worker.utils import GIRDER_WORKER_API_URL_ENV; print(f'OK: {GIRDER_WORKER_API_URL_ENV} available')"