| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- #!/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 <domain> # e.g. maritime, finance, customs
- # ./deploy_domain.sh <domain> --rebuild # stop+remove existing, recreate
- #
- # Convention (everything is read from instance.yml — no other config needed):
- #
- # /home/67/knowledge/<domain>/
- # ├── 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: <template-name> # e.g. qaagent67lambda
- # knowledge:
- # baseDir: /home/67/knowledge/<domain>
- # demo:
- # title: ...
- # tagline: ...
- # placeholder: ...
- # domain: <domain>
- # port: <NNNN> # 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 <domain> [--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
|