Spaces:
Paused
Paused
File size: 7,023 Bytes
8c1b9fe 656439d 8c1b9fe | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | #!/usr/bin/env bash
# Bring up the Auralynq stack (rootless Podman, no sudo). See ADR-0012/0013/0014.
#
# Networking: this host's CNI generates conflists with cniVersion 1.0.0, but the
# installed `firewall` plugin only supports up to 0.4.0 β so the network silently
# loses container DNS. The default `podman` network also ships without the
# `dnsname` plugin. We fix BOTH without sudo by rewriting the relevant conflist(s)
# under ~/.config/cni/net.d to cniVersion 0.4.0 + ensuring the dnsname plugin is
# present. With DNS working, services reach peers by container_name
# (auralynq-qdrant / auralynq-api / auralynq-web) β see ADR-0014.
set -euo pipefail
cd "$(dirname "$0")/.."
COMPOSE="$(./scripts/check_container_runtime.sh)"
CF="compose.yml"
CNI_DIR="${HOME}/.config/cni/net.d"
# Patch a CNI conflist in place: pin cniVersion 0.4.0 and append dnsname if absent.
patch_conflist() {
local f="$1"
[ -f "$f" ] || return 0
python3 - "$f" <<'PY'
import json, sys
p = sys.argv[1]
d = json.load(open(p))
changed = False
if d.get("cniVersion") != "0.4.0":
d["cniVersion"] = "0.4.0"; changed = True
types = [pl.get("type") for pl in d.get("plugins", [])]
if "dnsname" not in types:
d.setdefault("plugins", []).append(
{"type": "dnsname", "domainName": "dns.podman", "capabilities": {"aliases": True}})
changed = True
if changed:
json.dump(d, open(p, "w"), indent=2)
print(f" patched {p} -> 0.4.0 + dnsname")
PY
}
echo "β ensuring rootless CNI networks have DNS (no sudo)β¦"
# The default podman network (used by podman-compose) + any project network.
shopt -s nullglob
for f in "${CNI_DIR}"/87-podman.conflist "${CNI_DIR}"/*podman*.conflist "${CNI_DIR}"/auralynq*.conflist; do
patch_conflist "$f"
done
shopt -u nullglob
# Prefer an exported env var (e.g. from scripts/run_local.sh) over the .env file,
# matching podman-compose's own precedence so the printed URL is accurate.
bind_internal="${AURALYNQ_BIND_INTERNAL:-$(grep -E '^AURALYNQ_BIND_INTERNAL=' .env 2>/dev/null | cut -d= -f2)}"; bind_internal="${bind_internal:-127.0.0.1}"
https_port="${AURALYNQ_HTTPS_PORT:-$(grep -E '^AURALYNQ_HTTPS_PORT=' .env 2>/dev/null | cut -d= -f2)}"; https_port="${https_port:-8443}"
# ββ Host Ollama reachability (rootless) βββββββββββββββββββββββββββββββββββββββ
# Containers on the rootless compose bridge cannot reach host loopback services,
# and Podman auto-injects host.containers.internal pointing at the bridge gateway
# (10.88.0.1) β which is NOT the host in rootless mode. If the user runs Ollama
# on the host, route the containers to the host's real LAN IP instead (Ollama
# binds 0.0.0.0, so the bridge can reach it there). Without this the API silently
# degrades to the extractive LLM fallback even when a local GPU model is present.
existing_base="${AURALYNQ_LLM__BASE_URL:-$(grep -E '^AURALYNQ_LLM__BASE_URL=' .env 2>/dev/null | cut -d= -f2- || true)}"
if [ -z "${existing_base}" ]; then
host_ip="$(ip -4 route get 1.1.1.1 2>/dev/null | grep -oP 'src \K\S+' | head -1 || true)"
if [ -n "${host_ip}" ] && curl -fsS -m 2 "http://${host_ip}:11434/api/tags" >/dev/null 2>&1; then
export AURALYNQ_LLM__BASE_URL="http://${host_ip}:11434"
echo "β host Ollama reachable at ${host_ip}:11434 β routing containers there (local model, not extractive)."
else
echo "β host Ollama not detected β API will use its configured/auto LLM (extractive fallback if none)."
fi
fi
# Same reasoning for a host-side vLLM server: inside the container `localhost`
# is the container, so a vLLM on the host is only reachable via the LAN IP.
existing_vllm="${AURALYNQ_LLM__VLLM_BASE_URL:-$(grep -E '^AURALYNQ_LLM__VLLM_BASE_URL=' .env 2>/dev/null | cut -d= -f2- || true)}"
if [ -z "${existing_vllm}" ]; then
vllm_port="${AURALYNQ_VLLM_PORT:-8001}"
vllm_host_ip="$(ip -4 route get 1.1.1.1 2>/dev/null | grep -oP 'src \K\S+' | head -1 || true)"
if [ -n "${vllm_host_ip}" ]; then
export AURALYNQ_LLM__VLLM_BASE_URL="http://${vllm_host_ip}:${vllm_port}/v1"
if curl -fsS -m 2 "http://${vllm_host_ip}:${vllm_port}/v1/models" >/dev/null 2>&1; then
echo "β host vLLM reachable at ${vllm_host_ip}:${vllm_port} β routing containers there."
fi
fi
fi
# ββ Optional NVIDIA GPU visibility for hardware detection βββββββββββββββββββββ
# The API's ModelFit page reports the host's real GPUs. In a rootless container
# without the nvidia-container-toolkit those GPUs are invisible, so the report
# would wrongly say "CPU only". nvidia-smi only needs its binary + libnvidia-ml
# + the /dev/nvidia* device nodes to enumerate GPUs (no CUDA runtime, no toolkit).
# When they exist on the host we generate a gitignored compose override that
# read-only-mounts them into the api container. This is detection-only; inference
# still runs on host Ollama. compose.gpu.yml is regenerated each start.
GPU_OVERRIDE="compose.gpu.yml"
gpu_args=()
rm -f "$GPU_OVERRIDE"
smi_path="$(command -v nvidia-smi 2>/dev/null || true)"
if [ -n "$smi_path" ] && [ -e /dev/nvidiactl ]; then
libml="$(ldconfig -p 2>/dev/null | grep -m1 'libnvidia-ml.so.1' | awk '{print $NF}')"
libml="${libml:-/usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1}"
if [ -e "$libml" ]; then
{
echo "# Auto-generated by scripts/stack_up.sh β host NVIDIA GPU visibility for"
echo "# ModelFit hardware detection (nvidia-smi only). Do not edit; regenerated."
echo "services:"
echo " api:"
echo " devices:"
for d in /dev/nvidiactl /dev/nvidia-uvm /dev/nvidia-uvm-tools /dev/nvidia0 /dev/nvidia1 /dev/nvidia2 /dev/nvidia3 /dev/nvidia4 /dev/nvidia5 /dev/nvidia6 /dev/nvidia7; do
[ -e "$d" ] && echo " - \"$d\""
done
echo " volumes:"
echo " - \"$smi_path:/usr/bin/nvidia-smi:ro\""
echo " - \"$libml:/usr/lib/x86_64-linux-gnu/libnvidia-ml.so.1:ro\""
} > "$GPU_OVERRIDE"
gpu_args=(-f "$GPU_OVERRIDE")
echo "β NVIDIA GPU(s) detected β exposing nvidia-smi to the API for accurate hardware detection."
fi
fi
echo "β starting stackβ¦"
$COMPOSE -f "$CF" "${gpu_args[@]}" up -d
# The compose `up` above creates the project network at cniVersion 1.0.0, which
# the older `firewall` CNI plugin rejects (noisy validation warnings on every
# later podman command). Patch it down to 0.4.0 + dnsname now that it exists so
# subsequent `podman ps/logs/exec` and the next `up` run clean.
shopt -s nullglob
for f in "${CNI_DIR}"/*_default.conflist "${CNI_DIR}"/auralynq*.conflist; do
patch_conflist "$f"
done
shopt -u nullglob
echo "β stack up (services resolve peers by container_name via dnsname):"
echo " HTTPS : https://<SERVER_IP>:${https_port} (public β Caddy TLS proxy)"
echo " web/api : internal (${bind_internal} loopback), fronted by Caddy"
echo " Qdrant : internal (${bind_internal} loopback)"
echo " Phoenix : internal (${bind_internal} loopback)"
|