deploy_domain.sh 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. #!/usr/bin/env bash
  2. # ════════════════════════════════════════════════════════════
  3. # deploy_domain.sh — One-click deploy a new QA domain instance.
  4. #
  5. # Canonical location: /home/67/agentexample/qaagent67lambda/scripts/
  6. #
  7. # Usage:
  8. # ./deploy_domain.sh <domain> # e.g. maritime, finance, customs
  9. # ./deploy_domain.sh <domain> --rebuild # stop+remove existing, recreate
  10. #
  11. # Convention (everything is read from instance.yml — no other config needed):
  12. #
  13. # /home/67/knowledge/<domain>/
  14. # ├── instance.yml ← required, must have a `demo:` section
  15. # ├── rag_index.json ← required (BM25 index)
  16. # ├── rag_vectors_v2.npy ← required (vector index)
  17. # ├── search_unified.py ← required (search module)
  18. # └── config.py ← required (path resolver)
  19. #
  20. # instance.yml MUST contain:
  21. # agent: <template-name> # e.g. qaagent67lambda
  22. # knowledge:
  23. # baseDir: /home/67/knowledge/<domain>
  24. # demo:
  25. # title: ...
  26. # tagline: ...
  27. # placeholder: ...
  28. # domain: <domain>
  29. # port: <NNNN> # unique per instance
  30. # bind: 127.0.0.1 # optional (default 0.0.0.0); use 127.0.0.1 + nginx in front
  31. # examples: [{q,label}, ...]
  32. # ════════════════════════════════════════════════════════════
  33. set -euo pipefail
  34. DOMAIN="${1:-}"
  35. REBUILD="${2:-}"
  36. if [ -z "$DOMAIN" ]; then
  37. echo "usage: $0 <domain> [--rebuild]" >&2
  38. exit 1
  39. fi
  40. KNOW_DIR="/home/67/knowledge/$DOMAIN"
  41. INSTANCE_YML="$KNOW_DIR/instance.yml"
  42. APP_PY="/home/67/agentexample/qaagent67lambda/scripts/app.py"
  43. IMAGE="lambdagentpaas:latest"
  44. CONTAINER="${DOMAIN}-qa"
  45. # ── 1. Pre-flight checks ─────────────────────────────────────
  46. echo "[1/6] Validating $KNOW_DIR ..."
  47. [ -d "$KNOW_DIR" ] || { echo "ERROR: $KNOW_DIR does not exist"; exit 2; }
  48. [ -f "$INSTANCE_YML" ] || { echo "ERROR: $INSTANCE_YML missing"; exit 2; }
  49. [ -f "$KNOW_DIR/rag_index.json" ] || { echo "ERROR: rag_index.json missing"; exit 2; }
  50. [ -f "$KNOW_DIR/search_unified.py" ] || { echo "ERROR: search_unified.py missing"; exit 2; }
  51. [ -f "$KNOW_DIR/config.py" ] || { echo "ERROR: config.py missing"; exit 2; }
  52. [ -f "$APP_PY" ] || { echo "ERROR: $APP_PY missing"; exit 2; }
  53. # ── 2. Read demo.port + demo.bind + agent template from instance.yml ────
  54. echo "[2/6] Parsing $INSTANCE_YML ..."
  55. read AGENT_TEMPLATE PORT BIND << EOF
  56. $(python3 -c "
  57. import yaml
  58. with open('$INSTANCE_YML') as f:
  59. cfg = yaml.safe_load(f)
  60. agent = cfg.get('agent') or 'qaagent67lambda'
  61. demo = cfg.get('demo') or {}
  62. port = demo.get('port')
  63. if not port:
  64. raise SystemExit('ERROR: instance.yml has no demo.port')
  65. bind = demo.get('bind') or '0.0.0.0'
  66. print(agent, port, bind)
  67. ")
  68. EOF
  69. AGENT_CFG="/home/67/agentexample/$AGENT_TEMPLATE/agent-config.yml"
  70. [ -f "$AGENT_CFG" ] || { echo "ERROR: agent template config $AGENT_CFG missing"; exit 2; }
  71. echo " agent template: $AGENT_TEMPLATE"
  72. echo " port: $PORT"
  73. echo " bind: $BIND"
  74. echo " agent-config: $AGENT_CFG"
  75. # ── 3. Port conflict check ──────────────────────────────────
  76. echo "[3/6] Checking port $PORT availability ..."
  77. if ss -tlnp 2>/dev/null | grep -q ":$PORT "; then
  78. OWNER=$(ss -tlnp 2>/dev/null | grep ":$PORT " | head -1)
  79. if [ "$REBUILD" != "--rebuild" ] && ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then
  80. echo "ERROR: port $PORT in use by another process: $OWNER"
  81. echo " (and no existing $CONTAINER container to replace)"
  82. exit 3
  83. fi
  84. fi
  85. # ── 4. Stop + remove existing container if present ──────────
  86. if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER}$"; then
  87. echo "[4/6] Removing existing $CONTAINER ..."
  88. docker stop "$CONTAINER" >/dev/null 2>&1 || true
  89. docker rm "$CONTAINER" >/dev/null
  90. else
  91. echo "[4/6] No existing $CONTAINER — fresh deploy"
  92. fi
  93. # ── 5. Run new container ────────────────────────────────────
  94. echo "[5/6] Starting $CONTAINER on $BIND:$PORT ..."
  95. docker run -d \
  96. --name "$CONTAINER" \
  97. --network host \
  98. --restart unless-stopped \
  99. -v /home/67:/home/67 \
  100. -e "PYTHONPATH=$KNOW_DIR:/app" \
  101. -e "AGENT_CONFIG=$AGENT_CFG" \
  102. -e "INSTANCE_CONFIG=$INSTANCE_YML" \
  103. -e "PORT=$PORT" \
  104. -e "BIND_HOST=$BIND" \
  105. -e "OLLAMA_EMBED_URL=http://127.0.0.1:11435/api/embed" \
  106. -e "PYTHONUNBUFFERED=1" \
  107. "$IMAGE" \
  108. bash -c "pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple -q 2>&1 | tail -1 && python $APP_PY" \
  109. >/dev/null
  110. # ── 6. Health check ─────────────────────────────────────────
  111. echo "[6/6] Waiting for http://127.0.0.1:$PORT/ to become ready ..."
  112. for i in $(seq 1 30); do
  113. code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 3 "http://127.0.0.1:$PORT/" 2>/dev/null || echo "000")
  114. if [ "$code" = "200" ]; then
  115. echo
  116. echo "✅ $CONTAINER ready"
  117. if [ "$BIND" = "127.0.0.1" ]; then
  118. echo " Internal: http://127.0.0.1:$PORT/ (loopback only — set up nginx for external access)"
  119. else
  120. echo " URL: http://127.0.0.1:$PORT/ (公网: http://qa.lambdagent.cn:$PORT/)"
  121. fi
  122. echo " Logs: docker logs -f $CONTAINER"
  123. echo " Knowledge: $KNOW_DIR"
  124. echo " Instance: $INSTANCE_YML"
  125. exit 0
  126. fi
  127. sleep 4
  128. done
  129. echo "⚠️ Container started but not responding on port $PORT after 120s. Check: docker logs $CONTAINER"
  130. exit 4