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.
147 lines
5.1 KiB
Bash
Executable File
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}}'
|