Skip to main content

blurd-deploying

Deploying blurd with Docker Compose or Kubernetes. Read before changing the Dockerfile, docker-compose.yml, deploy/nginx.conf or deploy/helm/. Covers the three compose profiles and what each is proven to do, the Helm chart's guardrails and why each exists, and the container gotchas (non-root, cgroup sizing, model fetching) that only appear in a real deployment.

معلومات المصدر

المستودع
javimosch/blurd
آخر نشاط في المصدر
٦ أكتوبر ٢٠٢٦ في ١٦:٤٩
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٠
التفرعات
٠

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
blurd-deploying
description
Deploying blurd with Docker Compose or Kubernetes. Read before changing the Dockerfile, docker-compose.yml, deploy/nginx.conf or deploy/helm/. Covers the three compose profiles and what each is proven to do, the Helm chart's guardrails and why each exists, and the container gotchas (non-root, cgroup sizing, model fetching) that only appear in a real deployment.
# Deploying blurd `docs/deployment.md` is the operator-facing page: the decision table, the exact commands for each shape, and the failure modes. **Keep it current** — it is the one place a human looks, and the deployment facts were previously spread over five files, which is exactly how the README came to carry a stale memory model and a "blurd does not use Postgres yet" line months after it did. This skill is the part an agent needs that does not belong in operator docs. Two supported shapes, both exercised end to end: ``` local one instance, SQLite, blobs on disk OR in S3 distributed N replicas, Postgres OR Mongo, LB in front, blobs in object storage OR on one shared (RWX) volume ``` All four storage/scale combinations are supported and measured. The only one that fails is several replicas with a volume EACH -- see below. ## Docker Compose ```bash docker compose up -d # 1 replica, S3 blobs, SQLite metadata docker compose --profile pg up -d # + Postgres docker compose --profile mongo up -d # + MongoDB ``` Three replicas behind nginx, on either shared backend: ```bash BLURD_DB_BACKEND=postgres \ BLURD_DB_DSN=postgresql://blurd:blurd-dev-secret@postgres:5432/blurd \ docker compose --profile pg --profile scale up -d --scale blurd=3 # the load balancer is the front door on :8780 ``` **Proven, not assumed** — all of the following were run: | shape | result | |---|---| | default profile (S3 + SQLite) | 113/113 conformance | | 3 replicas on Postgres, through the LB | 113/113; 20/20 images completed with a replica SIGKILLed mid-flight | | 3 replicas on Mongo, through the LB | 113/113; same kill test; clean SIGTERM drain | | 512Mi non-root container, 3 workers | 113/113, no OOM | | 1 instance, local blobs, no S3 at all | 113/113 | | 3 replicas + Postgres + **local blobs on one shared volume** | 113/113 through the LB | | 2 replicas + Postgres + local blobs, **a volume each** | blob 404s on the replica that did not process the image | ### Compose gotchas - **The host port is a RANGE (`8770-8779:8770`)**, because `--scale blurd=3` cannot bind one fixed port three times. The first replica does not reliably land on 8770 — check `docker port blurd-blurd-1`. A `curl localhost:8770` that returns nothing is usually this, not a broken container. - **`curl` exits 0 on a 502.** A readiness loop written as `curl -s -o /dev/null … && break` breaks immediately while nginx is still returning 502 from before the replicas were listening. Compare the status code instead. - **nginx must re-resolve per request.** `upstream { server blurd:8770; }` resolves once at startup and pins to a replica that may later die. The config uses `resolver 127.0.0.11 valid=5s` and a variable `proxy_pass`; do not "simplify" it back. - **`client_max_body_size` must match `MAX_BODY`** (64 MB in `src/server.py`), or large uploads are rejected by the proxy with a 413 blurd never sees. ## Kubernetes (Helm) `deploy/helm/blurd/`. Read its README for the production invocation. The chart's main value is **what it refuses to render**. Every refusal encodes a failure that is silent at runtime or presents as something else entirely: - `sqlite` + more than one replica, or + autoscaling — SQLite is a single-writer file; two writers corrupt it silently. - `storage.backend=local` + more than one replica **without a shared volume** — blobs written by one pod are invisible to the others. Measured: the metadata read returns 200 (it is in the shared database) while the blob returns 404 on whichever replica did not process that image. Intermittent failures on a fraction of reads, which is worse to diagnose than an outage. A **shared** volume is a legitimate configuration and the chart accepts it (`persistence.accessMode=ReadWriteMany`, or `persistence.shared=true` for an existingClaim it cannot inspect). Three replicas on one shared volume pass 113/113. The chart cannot inspect a claim's access mode, so it makes the operator declare it rather than assuming — guessing wrong puts the silent failure back. - **no `resources.limits`** — see below; this is the big one. - `terminationGracePeriodSeconds <= drainSeconds` — a pod SIGKILLed mid-drain loses the uploads queued in its memory. - a dashboard with no password, a backend with no DSN, no source of models. `bash tests/helm_guardrails.sh` asserts all of them still fire, and that every valid combination still renders (30 checks). Run it after touching the chart: a guardrail that stops firing is a promise the chart has quietly stopped keeping. ### Container gotchas — each cost real debugging 1. **No cgroup limits means blurd sizes itself from the NODE.** `resources.py` reads `memory.max` and `cpu.max`; with no limit the cgroup reports "max" and the fallback is the node's figures. A pod on a 64 GB node starts as many workers as the node could feed and is OOM-killed against a limit it never saw. The chart refuses to render without limits for exactly this reason. **The memory limit is the sizing knob** — leave `workers` empty. 2. **The image runs as uid 65532 and chowns its home in the Dockerfile**, rather than relying on the pod's `fsGroup`. Not every CSI driver applies fsGroup, and a chart that works on some clusters is worse than one that fails on all. 3. **Anything that creates the home must mount the home.** `models pull` initialises the whole home — it creates `blobs/` alongside `models/` — so an init container that mounts only the models volume writes into the image's directory and fails with `EACCES` on every single deploy. The init container mounts both. 4. **Fetch models in an init container, not at startup.** A pod that cannot get its models otherwise starts healthy, passes its probes, and fails *every* submission — the worst way to find out. 5. **Readiness is the drain signal.** On SIGTERM blurd answers 503 on `/v1/health`, so the readiness probe withdraws the endpoint before it stops accepting. Keep the readiness probe twitchy and the liveness probe tolerant: a busy replica is not a broken one, and restarting it mid-job discards queued uploads for nothing. 6. **Roll pods when config changes.** blurd reads its configuration once at startup, so a `helm upgrade` that only edits the ConfigMap would leave every replica on the old settings. The Deployment carries a `checksum/config` annotation for this. ### Known gap The chart has **not been applied to a live cluster** — none was reachable from the development environment. It lints, renders valid manifests for every supported combination (schema-validated against the Kubernetes 1.29 API), and the pod shape passes conformance under Docker. Scheduling, CSI behaviour, ingress and HPA behaviour remain unverified in situ. Say so rather than implying otherwise. ## Configuration reaches the container by ENVIRONMENT only A container cannot run `blurd config set` before it starts, and a running daemon reads its configuration once. **Anything a deployment must set needs an entry in `config.ENV_OVERRIDES`** — adding a setting without one makes it unreachable in every deployment shape that matters.
عرض على GitHub