#!/usr/bin/env bash # ════════════════════════════════════════════════════════════ # deploy_domain.sh — One-click deploy a new QA domain instance. # # Canonical location: /home/67/agentexample/qaagent67lambda/scripts/ # # Usage: # ./deploy_domain.sh # e.g. maritime, finance, customs # ./deploy_domain.sh --rebuild # stop+remove existing, recreate # # Convention (everything is read from instance.yml — no other config needed): # # /home/67/knowledge// # ├── instance.yml ← required, must have a `demo:` section # ├── rag_index.json ← required (BM25 index) # ├── rag_vectors_v2.npy ← required (vector index) # ├── search_unified.py ← required (search module) # └── config.py ← required (path resolver) # # instance.yml MUST contain: # agent: # e.g. qaagent67lambda # knowledge: # baseDir: /home/67/knowledge/ # demo: # title: ... # tagline: ... # placeholder: ... # domain: # port: # unique per instance # bind: 127.0.0.1 # optional (default 0.0.0.0); use 127.0.0.1 + nginx in front # examples: [{q,label}, ...] # ════════════════════════════════════════════════════════════ set -euo pipefail DOMAIN="${1:-}" REBUILD="${2:-}" if [ -z "$DOMAIN" ]; then echo "usage: $0 [--rebuild]" >&2 exit 1 fi KNOW_DIR="/home/67/knowledge/$DOMAIN" INSTANCE_YML="$KNOW_DIR/instance.yml" APP_PY="/home/67/agentexample/qaagent67lambda/scripts/app.py" IMAGE="lambdagentpaas:latest" CONTAINER="${DOMAIN}-qa" # ── 1. Pre-flight checks ───────────────────────────────────── echo "[1/6] Validating $KNOW_DIR ..." [ -d "$KNOW_DIR" ] || { echo "ERROR: $KNOW_DIR does not exist"; exit 2; } [ -f "$INSTANCE_YML" ] || { echo "ERROR: $INSTANCE_YML missing"; exit 2; } [ -f "$KNOW_DIR/rag_index.json" ] || { echo "ERROR: rag_index.json missing"; exit 2; } [ -f "$KNOW_DIR/search_unified.py" ] || { echo "ERROR: search_unified.py missing"; exit 2; } [ -f "$KNOW_DIR/config.py" ] || { echo "ERROR: config.py missing"; exit 2; } [ -f "$APP_PY" ] || { echo "ERROR: $APP_PY missing"; exit 2; } # ── 2. Read demo.port + demo.bind + agent template from instance.yml ──── echo "[2/6] Parsing $INSTANCE_YML ..." read AGENT_TEMPLATE PORT BIND << EOF $(python3 -c " import yaml with open('$INSTANCE_YML') as f: cfg = yaml.safe_load(f) agent = cfg.get('agent') or 'qaagent67lambda' demo = cfg.get('demo') or {} port = demo.get('port') if not port: raise SystemExit('ERROR: instance.yml has no demo.port') bind = demo.get('bind') or '0.0.0.0' print(agent, port, bind) ") EOF AGENT_CFG="/home/67/agentexample/$AGENT_TEMPLATE/agent-config.yml" [ -f "$AGENT_CFG" ] || { echo "ERROR: agent template config $AGENT_CFG missing"; exit 2; } echo " agent template: $AGENT_TEMPLATE" echo " port: $PORT" echo " bind: $BIND" echo " agent-config: $AGENT_CFG" # ── 3. Port conflict check ────────────────────────────────── echo "[3/6] Checking port $PORT availability ..." if ss -tlnp 2>/dev/null | grep -q ":$PORT "; then OWNER=$(ss -tlnp 2>/dev/null | grep ":$PORT " | head -1) if [ "$REBUILD" != "--rebuild" ] && ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then echo "ERROR: port $PORT in use by another process: $OWNER" echo " (and no existing $CONTAINER container to replace)" exit 3 fi fi # ── 4. Stop + remove existing container if present ────────── if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then echo "[4/6] Removing existing $CONTAINER ..." docker stop "$CONTAINER" >/dev/null 2>&1 || true docker rm "$CONTAINER" >/dev/null else echo "[4/6] No existing $CONTAINER — fresh deploy" fi # ── 5. Run new container ──────────────────────────────────── echo "[5/6] Starting $CONTAINER on $BIND:$PORT ..." docker run -d \ --name "$CONTAINER" \ --network host \ --restart unless-stopped \ -v /home/67:/home/67 \ -e "PYTHONPATH=$KNOW_DIR:/app" \ -e "AGENT_CONFIG=$AGENT_CFG" \ -e "INSTANCE_CONFIG=$INSTANCE_YML" \ -e "PORT=$PORT" \ -e "BIND_HOST=$BIND" \ -e "OLLAMA_EMBED_URL=http://127.0.0.1:11435/api/embed" \ -e "PYTHONUNBUFFERED=1" \ "$IMAGE" \ bash -c "pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple -q 2>&1 | tail -1 && python $APP_PY" \ >/dev/null # ── 6. Health check ───────────────────────────────────────── echo "[6/6] Waiting for http://127.0.0.1:$PORT/ to become ready ..." for i in $(seq 1 30); do code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 3 "http://127.0.0.1:$PORT/" 2>/dev/null || echo "000") if [ "$code" = "200" ]; then echo echo "✅ $CONTAINER ready" if [ "$BIND" = "127.0.0.1" ]; then echo " Internal: http://127.0.0.1:$PORT/ (loopback only — set up nginx for external access)" else echo " URL: http://127.0.0.1:$PORT/ (公网: http://qa.lambdagent.cn:$PORT/)" fi echo " Logs: docker logs -f $CONTAINER" echo " Knowledge: $KNOW_DIR" echo " Instance: $INSTANCE_YML" exit 0 fi sleep 4 done echo "⚠️ Container started but not responding on port $PORT after 120s. Check: docker logs $CONTAINER" exit 4