Files
stack/deploy.sh
kert 666195e3fd fix(llm): rebuild pgvector without AVX-512 so HNSW builds on Zen2 (closes #580)
Upstream Bitnami vector.so was built -march=native on an AVX-512 host;
vector_norm's EVEX AVX-512 (vextractf64x2/vcvtusi2sd) SIGILLs the Ryzen
3950X during hnsw/ivfflat index builds, crashing the shared postgres.
infra/images/postgresql.Dockerfile rebuilds pgvector -march=x86-64-v3
FROM the mirror; compose + deploy.sh build fhirworx/postgresql:pgvector.
HNSW verified on the 15852-vector pilot (~2s, index scan). Flip
[llm].build_ann_index default to true.
2026-07-17 16:09:13 -04:00

147 lines
5.1 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# ── Load environment ────────────────────────────────────────────────
cd "$(dirname "$0")"
set -a; source .env; set +a
PREFIX="${IMAGE_PREFIX:-fhirworx}"
SHA="${COMMIT_SHA:-latest}"
HEAVY="${HEAVY_TAG:-latest}"
# ── Helpers ─────────────────────────────────────────────────────────
log() { printf '\033[1;34m▸ %s\033[0m\n' "$*"; }
ok() { printf '\033[1;32m✓ %s\033[0m\n' "$*"; }
warn() { printf '\033[1;33m⚠ %s\033[0m\n' "$*"; }
die() { printf '\033[1;31m✗ %s\033[0m\n' "$*" >&2; exit 1; }
up() {
log "Starting: $*"
docker compose up -d --no-deps "$@"
}
wait_healthy() {
local svc="$1" timeout="${2:-60}" elapsed=0
log "Waiting for $svc to be healthy (${timeout}s timeout)…"
while [ "$elapsed" -lt "$timeout" ]; do
local state
state=$(docker inspect --format='{{.State.Health.Status}}' "$svc" 2>/dev/null || echo "missing")
case "$state" in
healthy) ok "$svc healthy"; return 0 ;;
unhealthy) die "$svc is unhealthy" ;;
esac
sleep 2
elapsed=$((elapsed + 2))
done
die "$svc did not become healthy within ${timeout}s"
}
wait_running() {
local svc="$1" timeout="${2:-30}" elapsed=0
log "Waiting for $svc to be running (${timeout}s timeout)…"
while [ "$elapsed" -lt "$timeout" ]; do
local state
state=$(docker inspect --format='{{.State.Status}}' "$svc" 2>/dev/null || echo "missing")
if [ "$state" = "running" ]; then
ok "$svc running"
return 0
fi
sleep 2
elapsed=$((elapsed + 2))
done
die "$svc did not start within ${timeout}s"
}
image_exists() {
docker image inspect "$1" &>/dev/null
}
# ── Buildable services: compose service → image name ────────────────
# Services with build: sections that can be built locally if missing
declare -A BUILDABLE=(
[mc]="${PREFIX}/mc:${SHA}"
[notebooks]="${PREFIX}/notebooks:${HEAVY}"
[zotero]="${PREFIX}/zotero:${HEAVY}"
[docs]="${PREFIX}/docs:${SHA}"
[api]="${PREFIX}/api:${SHA}"
# Patched pgvector (AVX-512-free) built FROM the postgresql:latest mirror (#580).
[postgres]="${PREFIX}/postgresql:pgvector"
)
# ── Pre-flight: check local images ─────────────────────────────────
BUILD_NEEDED=()
for svc in "${!BUILDABLE[@]}"; do
img="${BUILDABLE[$svc]}"
if image_exists "$img"; then
ok "Local: $img"
else
warn "Missing: $img — will build"
BUILD_NEEDED+=("$svc")
fi
done
# Also check non-buildable fhirworx images
for img in "${PREFIX}/rustfs:latest" "${PREFIX}/postgresql:latest"; do
if image_exists "$img"; then
ok "Local: $img"
else
die "Missing base image: $img — build or pull it manually"
fi
done
# ── Build missing images ────────────────────────────────────────────
if [ "${#BUILD_NEEDED[@]}" -gt 0 ]; then
log "Building ${#BUILD_NEEDED[@]} image(s)…"
for svc in "${BUILD_NEEDED[@]}"; do
log "Building $svc${BUILDABLE[$svc]}"
docker compose build "$svc" || die "Failed to build $svc"
ok "Built ${BUILDABLE[$svc]}"
done
fi
# ── Tier 0: Infrastructure (public images) ─────────────────────────
log "Tier 0: Infrastructure"
up coredns traefik rustfs postgres
wait_running rustfs
wait_running postgres
# ── Tier 1: Gitea (registry + git host) ─────────────────────────────
log "Tier 1: Registry"
up gitea
wait_running gitea
log "Waiting for Gitea HTTP…"
for i in $(seq 1 30); do
if curl -sf http://localhost:3000/api/v1/version &>/dev/null; then
ok "Gitea API ready"
break
fi
[ "$i" -eq 30 ] && die "Gitea API did not respond within 60s"
sleep 2
done
# ── Tier 2: Application services ───────────────────────────────────
log "Tier 2: Application services"
up mc docs api notebooks zotero
# ── Tier 3: CI (depends on Gitea) ──────────────────────────────────
log "Tier 3: CI"
up act-runner
# ── Tier 4: Observability ──────────────────────────────────────────
log "Tier 4: Observability"
up tempo loki prometheus
up promtail otel-collector grafana
# ── Tier 5: Remaining services ─────────────────────────────────────
log "Tier 5: Remaining"
up webdav nessie polaris dashboard cloudflared
wait_running nessie
up trino
# ── Done ────────────────────────────────────────────────────────────
ok "Stack deployed"
docker compose ps --format 'table {{.Name}}\t{{.Status}}'