From 0f12e0a8ea1c46756b11329b95899b515fd79675 Mon Sep 17 00:00:00 2001 From: dekun Date: Fri, 17 Jul 2026 15:40:25 +0800 Subject: [PATCH] Add single-exchange instance-only deploy menu options. Manage menu 4/5/6 install OKX/Binance/Gate Flask only without hub or agents; reuse setup_env --only and narrowed PM2 verify. Co-authored-by: Cursor --- deploy/README.md | 7 +- deploy/lib/common.sh | 147 +++++++++++++++++++++++++++++++++------- deploy/lib/install.sh | 92 ++++++++++++++++++++++--- deploy/manage.sh | 18 ++++- deploy/pm2_start_all.sh | 50 ++++++++++++-- docs/更新文档.md | 30 ++++++++ 6 files changed, 300 insertions(+), 44 deletions(-) diff --git a/deploy/README.md b/deploy/README.md index 6baec07..4be8ede 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -24,11 +24,16 @@ bash /opt/crypto_monitor/deploy/manage.sh | 选项 | 功能 | |------|------| -| **1) 一键部署** | 装系统依赖 + Node/PM2 + venv + 自动生成密钥 + 启动 7 进程 | +| **1) 一键部署(全套)** | 装系统依赖 + Node/PM2 + venv + 自动生成密钥 + 启动 7 进程(三所+中控) | +| **4) 仅 OKX 实例** | 只部署/启动 `crypto_okx`(不含中控/agent) | +| **5) 仅 Binance 实例** | 只部署/启动 `crypto_binance` | +| **6) 仅 Gate 实例** | 只部署/启动 `crypto_gate` | | **2) 一键卸载** | 备份 `.env` / `hub_settings.json` → 停 PM2 → 移走目录 | | **3) 更新** | 快速更新 / 依赖更新 / 深度重装 | | **0) 退出** | | +单所模式仍 clone 整仓到 `/opt/crypto_monitor`(共用 `lib/`),但 `setup_env.sh --only <所>` 只建该所 venv,且 PM2 只起对应 Flask. + ### 部署完成后 脚本自动验收(PM2 7 进程 + 页面可访问),并提示: diff --git a/deploy/lib/common.sh b/deploy/lib/common.sh index f8a6bd9..5e38042 100644 --- a/deploy/lib/common.sh +++ b/deploy/lib/common.sh @@ -330,9 +330,57 @@ pm2_count_online() { is_deployed() { local root="$1" [[ -x "${root}/crypto_monitor_binance/.venv/bin/python" ]] \ + || [[ -x "${root}/crypto_monitor_gate/.venv/bin/python" ]] \ + || [[ -x "${root}/crypto_monitor_okx/.venv/bin/python" ]] \ || [[ -x "${root}/manual_trading_hub/.venv/bin/python" ]] } +# 单所实例: key -> 目录 / PM2 名 / 端口 / 展示名 +exchange_dir() { + case "$1" in + okx) echo "crypto_monitor_okx" ;; + binance) echo "crypto_monitor_binance" ;; + gate) echo "crypto_monitor_gate" ;; + *) return 1 ;; + esac +} + +exchange_pm2_name() { + case "$1" in + okx) echo "crypto_okx" ;; + binance) echo "crypto_binance" ;; + gate) echo "crypto_gate" ;; + *) return 1 ;; + esac +} + +exchange_http_port() { + case "$1" in + okx) echo "5004" ;; + binance) echo "5001" ;; + gate) echo "5000" ;; + *) return 1 ;; + esac +} + +exchange_label() { + case "$1" in + okx) echo "OKX" ;; + binance) echo "Binance" ;; + gate) echo "Gate" ;; + *) echo "$1" ;; + esac +} + +normalize_exchange_key() { + local k + k="$(echo "${1:-}" | tr '[:upper:]' '[:lower:]' | xargs)" + case "${k}" in + okx|binance|gate) echo "${k}" ;; + *) return 1 ;; + esac +} + check_http() { local url="$1" local code @@ -342,37 +390,62 @@ check_http() { verify_deployment() { local ip="${1:-$(detect_server_ip)}" + local mode="${2:-all}" local ok=1 local online total + local ex_key="" step "部署验收" - total="${#PM2_APPS[@]}" - online="$(pm2_count_online)" - if [[ "${online}" -eq "${total}" ]]; then - echo " [✓] PM2 进程 ${online}/${total} online" - else - echo " [✗] PM2 进程 ${online}/${total} online" - ok=0 - pm2 list 2>/dev/null || true - fi - - local checks=( - "中控:http://127.0.0.1:5100/" - "Binance:http://127.0.0.1:5001/" - "Gate:http://127.0.0.1:5000/" - "OKX:http://127.0.0.1:5004/" - ) - local item label url - for item in "${checks[@]}"; do - label="${item%%:*}" - url="${item#*:}" - if check_http "${url}"; then - echo " [✓] ${label} 可访问" + if [[ "${mode}" == "all" || -z "${mode}" ]]; then + total="${#PM2_APPS[@]}" + online="$(pm2_count_online)" + if [[ "${online}" -eq "${total}" ]]; then + echo " [✓] PM2 进程 ${online}/${total} online" else - echo " [✗] ${label} 不可访问 (${url})" + echo " [✗] PM2 进程 ${online}/${total} online" + ok=0 + pm2 list 2>/dev/null || true + fi + local checks=( + "中控:http://127.0.0.1:5100/" + "Binance:http://127.0.0.1:5001/" + "Gate:http://127.0.0.1:5000/" + "OKX:http://127.0.0.1:5004/" + ) + local item label url + for item in "${checks[@]}"; do + label="${item%%:*}" + url="${item#*:}" + if check_http "${url}"; then + echo " [✓] ${label} 可访问" + else + echo " [✗] ${label} 不可访问 (${url})" + ok=0 + fi + done + else + if ! ex_key="$(normalize_exchange_key "${mode}")"; then + echo " [✗] 未知验收模式: ${mode}" + return 1 + fi + local pm2_name port label + pm2_name="$(exchange_pm2_name "${ex_key}")" + port="$(exchange_http_port "${ex_key}")" + label="$(exchange_label "${ex_key}")" + if pm2_app_exists "${pm2_name}"; then + echo " [✓] PM2 ${pm2_name} 已注册" + else + echo " [✗] PM2 缺少进程 ${pm2_name}" + ok=0 + pm2 list 2>/dev/null || true + fi + if check_http "http://127.0.0.1:${port}/"; then + echo " [✓] ${label} 可访问 (http://127.0.0.1:${port}/)" + else + echo " [✗] ${label} 不可访问 (http://127.0.0.1:${port}/)" ok=0 fi - done + fi if [[ "${ok}" -eq 1 ]]; then echo "" @@ -410,3 +483,29 @@ print_post_install_guide() { ═══════════════════════════════════════════ EOF } + +print_post_install_guide_instance() { + local ex_key="$1" + local ip="${2:-$(detect_server_ip)}" + local label port + label="$(exchange_label "${ex_key}")" + port="$(exchange_http_port "${ex_key}")" + cat <&2 + exit 1 + ;; + esac +done + install_fresh() { step "克隆仓库" if [[ -d "${INSTALL_ROOT}" ]]; then @@ -45,6 +65,28 @@ run_install_pipeline() { print_post_install_guide "$(detect_server_ip)" } +run_instance_pipeline() { + local ex_key="$1" + local dir_name + dir_name="$(exchange_dir "${ex_key}")" + + step "单所环境部署 setup_env.sh --only ${ex_key}(不含中控)" + bash "${REPO_ROOT}/deploy/setup_env.sh" --only "${ex_key}" --install-system-deps + + step "启动 PM2 仅 ${dir_name}" + bash "${REPO_ROOT}/deploy/pm2_start_all.sh" --only "${ex_key}" + + pm2_save_startup + local cron_script="${REPO_ROOT}/${dir_name}/scripts/install_backup_cron.sh" + if [[ -x "${cron_script}" ]]; then + step "安装 ${ex_key} 备份 cron" + bash "${cron_script}" || true + fi + + verify_deployment "$(detect_server_ip)" "${ex_key}" || true + print_post_install_guide_instance "${ex_key}" "$(detect_server_ip)" +} + handle_existing_install() { echo "" echo "检测到已部署安装: ${INSTALL_ROOT}" @@ -72,10 +114,47 @@ handle_existing_install() { esac } +ensure_repo_ready() { + if repo_ready "${INSTALL_ROOT}"; then + REPO_ROOT="${INSTALL_ROOT}" + elif [[ -n "${REPO_ROOT:-}" ]] && repo_ready "${REPO_ROOT}"; then + : + else + REPO_ROOT="" + fi + + if [[ -z "${REPO_ROOT}" ]]; then + install_system_packages + install_node_pm2 + install_fresh + REPO_ROOT="${INSTALL_ROOT}" + else + install_system_packages + install_node_pm2 + fi +} + +main_install_instance() { + local ex_key="$1" + require_root + require_ubuntu + ensure_repo_ready + run_instance_pipeline "${ex_key}" +} + main_install() { require_root require_ubuntu + if [[ -n "${EXCHANGE}" ]]; then + local ex_key="" + if ! ex_key="$(normalize_exchange_key "${EXCHANGE}")"; then + die "无效 --exchange: ${EXCHANGE} (期望 okx|binance|gate)" + fi + main_install_instance "${ex_key}" + return 0 + fi + if repo_ready "${INSTALL_ROOT}"; then REPO_ROOT="${INSTALL_ROOT}" elif repo_ready "${REPO_ROOT}"; then @@ -89,16 +168,7 @@ main_install() { return 0 fi - if [[ -z "${REPO_ROOT}" ]]; then - install_system_packages - install_node_pm2 - install_fresh - REPO_ROOT="${INSTALL_ROOT}" - else - install_system_packages - install_node_pm2 - fi - + ensure_repo_ready run_install_pipeline } diff --git a/deploy/manage.sh b/deploy/manage.sh index 27e1827..9f1cc0a 100644 --- a/deploy/manage.sh +++ b/deploy/manage.sh @@ -105,7 +105,10 @@ show_banner() { } show_menu() { - echo " 1) 一键部署" + echo " 1) 一键部署(全套:三所+中控)" + echo " 4) 仅 OKX 实例(不含中控)" + echo " 5) 仅 Binance 实例(不含中控)" + echo " 6) 仅 Gate 实例(不含中控)" echo " 2) 一键卸载" echo " 3) 更新" echo " 0) 退出" @@ -122,11 +125,20 @@ main_menu() { show_banner show_menu local choice="" - cm_read choice "请选择 [0-3]: " + cm_read choice "请选择 [0-6]: " case "${choice}" in 1) bash "${LIB_DIR}/install.sh" ;; + 4) + bash "${LIB_DIR}/install.sh" --exchange okx + ;; + 5) + bash "${LIB_DIR}/install.sh" --exchange binance + ;; + 6) + bash "${LIB_DIR}/install.sh" --exchange gate + ;; 2) bash "${LIB_DIR}/uninstall.sh" ;; @@ -138,7 +150,7 @@ main_menu() { exit 0 ;; *) - echo "无效选项,请输入 0-3" + echo "无效选项,请输入 0-6" ;; esac echo "" diff --git a/deploy/pm2_start_all.sh b/deploy/pm2_start_all.sh index 560281b..4e60ce7 100644 --- a/deploy/pm2_start_all.sh +++ b/deploy/pm2_start_all.sh @@ -2,6 +2,9 @@ # 按推荐顺序启动三所 Flask + 中控 hub/三 agent(PM2). # 用法(仓库根或任意目录): # bash deploy/pm2_start_all.sh +# bash deploy/pm2_start_all.sh --only okx +# bash deploy/pm2_start_all.sh --only binance +# bash deploy/pm2_start_all.sh --only gate # # 与 deploy/setup_env.sh 独立:setup_env 只建 venv;本脚本负责 PM2 启动. set -e @@ -12,6 +15,26 @@ fi DEPLOY_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "${DEPLOY_DIR}/.." && pwd)" +# shellcheck source=lib/common.sh +source "${DEPLOY_DIR}/lib/common.sh" + +ONLY="all" +while [[ $# -gt 0 ]]; do + case "$1" in + --only) + ONLY="${2:-all}" + shift 2 + ;; + -h|--help) + sed -n '2,10p' "$0" | sed 's/^# \?//' + exit 0 + ;; + *) + echo "未知参数: $1" >&2 + exit 1 + ;; + esac +done start_one() { local dir_name="$1" @@ -22,7 +45,14 @@ start_one() { return 0 fi echo "==> pm2 start ${dir_name}" - (cd "${proj}" && pm2 start ecosystem.config.cjs) + # 已存在则 restart,避免重复 start 导致 set -e 退出 + if (cd "${proj}" && pm2 start ecosystem.config.cjs); then + return 0 + fi + echo " (已存在,改为 restart)" + (cd "${proj}" && pm2 startOrReload ecosystem.config.cjs --update-env) \ + || (cd "${proj}" && pm2 reload ecosystem.config.cjs --update-env) \ + || true } if ! command -v pm2 >/dev/null 2>&1; then @@ -30,10 +60,20 @@ if ! command -v pm2 >/dev/null 2>&1; then exit 1 fi -start_one crypto_monitor_binance -start_one crypto_monitor_gate -start_one crypto_monitor_okx -start_one manual_trading_hub +if [[ "${ONLY}" == "all" ]]; then + start_one crypto_monitor_binance + start_one crypto_monitor_gate + start_one crypto_monitor_okx + start_one manual_trading_hub +else + ex_key="$(normalize_exchange_key "${ONLY}" || true)" + if [[ -z "${ex_key}" ]]; then + echo "未知 --only 值: ${ONLY} (期望 okx|binance|gate|all)" >&2 + exit 1 + fi + dir_name="$(exchange_dir "${ex_key}")" + start_one "${dir_name}" +fi pm2 save 2>/dev/null || true echo "" diff --git a/docs/更新文档.md b/docs/更新文档.md index f20c681..9c3b16f 100644 --- a/docs/更新文档.md +++ b/docs/更新文档.md @@ -4,6 +4,36 @@ --- +## 2026-07-17 · 一键部署支持单所仅实例(不含中控) + +### 修改原因 + +新机或专用机只需跑某一所 Flask 时,全套 7 进程过重;希望菜单可直接选「仅 OKX / Binance / Gate」,不起中控与 agent. + +### 修改的地方 + +| 文件 | 改动摘要 | +|------|----------| +| `deploy/manage.sh` | 菜单增加 4/5/6 单所实例入口 | +| `deploy/lib/install.sh` | `--exchange okx\|binance\|gate` 单所流水线 | +| `deploy/pm2_start_all.sh` | `--only` 只启对应 ecosystem | +| `deploy/lib/common.sh` | 单所验收 / 完成提示 / 辅助映射 | +| `deploy/README.md` | 菜单说明 | + +### 达成的目标 + +1. 选 4/5/6: `setup_env --only <所>` + 仅启动该所 PM2,不含 hub/agent. +2. 选 1: 全套行为与改前一致. +3. 仍共用整仓 `/opt/crypto_monitor`,不拆仓库. + +### 交付之后的验收 + +1. 菜单可见 4/5/6. +2. `bash deploy/lib/install.sh --exchange okx` 后仅 `crypto_okx` 起来,`:5004` 可访问. +3. 全套选项 1 仍可部署三所+中控. + +--- + ## 2026-07-17 · 永续估算盈亏统一扣双边 taker 手续费 ### 修改原因