deploy: 一键部署内置 locale/时区/SimNow 前置探测与 CTP 验证

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-24 11:59:02 +08:00
parent b4250171d5
commit ca894dfd4d
3 changed files with 149 additions and 19 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ CTP_AUTO_RECONNECT=true
SIMNOW_USER= SIMNOW_USER=
SIMNOW_PASSWORD= SIMNOW_PASSWORD=
SIMNOW_BROKER_ID=9999 SIMNOW_BROKER_ID=9999
# 7×24 环境示例(以 SimNow 官网最新为准 # 7×24 / 日盘前置(deploy.sh 会自动 nc 探测并写入可用线路
SIMNOW_TD_ADDRESS=tcp://180.168.146.187:10201 SIMNOW_TD_ADDRESS=tcp://180.168.146.187:10201
SIMNOW_MD_ADDRESS=tcp://180.168.146.187:10211 SIMNOW_MD_ADDRESS=tcp://180.168.146.187:10211
SIMNOW_APP_ID=simnow_client_test SIMNOW_APP_ID=simnow_client_test
+130 -11
View File
@@ -1,6 +1,14 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# 国内期货监控系统 - Ubuntu 一键部署 # 国内期货监控系统 - Ubuntu 一键部署 / 更新
# root 用户 | 目录 /opt/qihuo | 端口 6600 | PM2 # root 用户 | 目录 /opt/qihuo | 端口 6600 | PM2
#
# 已内置修复(避免重复踩坑):
# - vnpy_ctp 编译:build-essential python3-dev pkg-config
# - CTP 登录崩溃:zh_CN.GB18030 + zh_CN.UTF-8 locale
# - 时区:Asia/Shanghai(与 SimNow 交易时段一致)
# - SimNow 前置:自动探测可用线路并写入 .env
# - PM2 环境变量:LANG/LC_ALL(见 ecosystem.config.cjs
# - .env 缺项补全:SIMNOW_ENV、CTP_AUTO_RECONNECT
set -euo pipefail set -euo pipefail
@@ -8,12 +16,81 @@ APP_DIR="/opt/qihuo"
REPO_URL="https://git.bz121.com/dekun/qihuo.git" REPO_URL="https://git.bz121.com/dekun/qihuo.git"
SERVICE_NAME="qihuo" SERVICE_NAME="qihuo"
# SimNow 前置候选(按优先级;部署时自动 nc 探测)
SIMNOW_FRONTS=(
"182.254.243.31:30001:30011"
"180.168.146.187:10201:10211"
"180.168.146.187:10130:10131"
"218.202.237.33:10203:10213"
)
if [ "$(id -u)" -ne 0 ]; then if [ "$(id -u)" -ne 0 ]; then
echo "请使用 root 用户运行: sudo bash deploy.sh" echo "请使用 root 用户运行: sudo bash deploy.sh"
exit 1 exit 1
fi fi
probe_tcp() {
local host="$1" port="$2"
if command -v nc &>/dev/null; then
nc -z -w 3 "$host" "$port" &>/dev/null
return $?
fi
timeout 3 bash -c "echo >/dev/tcp/${host}/${port}" 2>/dev/null
}
ensure_locale_gen() {
local pat="$1"
if [ -f /etc/locale.gen ] && grep -q "^# ${pat}" /etc/locale.gen; then
sed -i "s/^# ${pat}/${pat}/" /etc/locale.gen
fi
}
ensure_env_key() {
local file="$1" key="$2" val="$3"
if [ ! -f "$file" ]; then
return
fi
if grep -q "^${key}=" "$file"; then
return
fi
echo "${key}=${val}" >>"$file"
echo " .env 补全: ${key}=${val}"
}
pick_simnow_front() {
local host td_port md_port
for entry in "${SIMNOW_FRONTS[@]}"; do
IFS=: read -r host td_port md_port <<<"$entry"
if probe_tcp "$host" "$td_port" && probe_tcp "$host" "$md_port"; then
echo "tcp://${host}:${td_port}|tcp://${host}:${md_port}"
return 0
fi
echo " SimNow 前置不可达: ${host} ${td_port}/${md_port}" >&2
done
return 1
}
update_simnow_front_in_env() {
local env_file="$1"
local picked td md
picked="$(pick_simnow_front)" || {
echo "警告: 未能探测到可用 SimNow 前置,请手动编辑 ${env_file}(见 docs/SIMNOW.md"
return 1
}
td="${picked%%|*}"
md="${picked##*|}"
echo "==> SimNow 可用前置: TD=${td} MD=${md}"
if grep -q "^SIMNOW_TD_ADDRESS=" "$env_file"; then
sed -i "s|^SIMNOW_TD_ADDRESS=.*|SIMNOW_TD_ADDRESS=${td}|" "$env_file"
sed -i "s|^SIMNOW_MD_ADDRESS=.*|SIMNOW_MD_ADDRESS=${md}|" "$env_file"
else
echo "SIMNOW_TD_ADDRESS=${td}" >>"$env_file"
echo "SIMNOW_MD_ADDRESS=${md}" >>"$env_file"
fi
}
echo "==> 检查系统依赖..." echo "==> 检查系统依赖..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq apt-get update -qq
need_install() { need_install() {
@@ -26,14 +103,28 @@ need_install python3 python3
need_install python3-venv python3-venv need_install python3-venv python3-venv
need_install git git need_install git git
# vnpy_ctp 在 Linux 上需本地编译(Meson + pkg-config 查找 python3-dev echo "==> 安装 vnpy_ctp 编译依赖..."
echo "==> 安装 vnpy_ctp 编译依赖与 CTP 中文 locale..." apt-get install -y build-essential python3-dev pkg-config locales netcat-openbsd
apt-get install -y build-essential python3-dev pkg-config locales
# CTP C++ API 登录回调需要 zh_CN.GB18030vnpy/vnpy_ctp#24 echo "==> 配置时区 Asia/Shanghai..."
sed -i '/^# zh_CN.GB18030/s/^# //' /etc/locale.gen 2>/dev/null || true if command -v timedatectl &>/dev/null; then
sed -i '/^# zh_CN.UTF-8/s/^# //' /etc/locale.gen 2>/dev/null || true timedatectl set-timezone Asia/Shanghai || true
fi
echo "==> 配置 CTP 所需 localezh_CN.GB18030 等)..."
ensure_locale_gen "zh_CN.GB18030 GB18030"
ensure_locale_gen "zh_CN.UTF-8 UTF-8"
ensure_locale_gen "en_US.UTF-8 UTF-8"
locale-gen zh_CN.GB18030 zh_CN.UTF-8 en_US.UTF-8 2>/dev/null || locale-gen locale-gen zh_CN.GB18030 zh_CN.UTF-8 en_US.UTF-8 2>/dev/null || locale-gen
update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 2>/dev/null || true update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8 2>/dev/null || true
export LANG=zh_CN.UTF-8
export LC_ALL=zh_CN.UTF-8
if ! locale -a 2>/dev/null | grep -qi gb18030; then
echo "错误: zh_CN.GB18030 未生成,CTP 连接后会崩溃"
exit 1
fi
echo " locale OK: $(locale -a 2>/dev/null | grep -i gb18030 | head -1)"
if ! command -v pm2 &>/dev/null; then if ! command -v pm2 &>/dev/null; then
echo "==> 安装 PM2..." echo "==> 安装 PM2..."
@@ -64,33 +155,61 @@ echo "==> Python 虚拟环境与依赖..."
if [ ! -d "$APP_DIR/venv" ]; then if [ ! -d "$APP_DIR/venv" ]; then
python3 -m venv "$APP_DIR/venv" python3 -m venv "$APP_DIR/venv"
fi fi
# shellcheck disable=SC1091
source "$APP_DIR/venv/bin/activate" source "$APP_DIR/venv/bin/activate"
pip install --upgrade pip -q pip install --upgrade pip -q
pip install -r "$APP_DIR/requirements.txt" pip install -r "$APP_DIR/requirements.txt"
python -c "from vnpy_ctp import CtpGateway; print('vnpy_ctp OK')" python -c "from vnpy_ctp import CtpGateway; print('vnpy_ctp OK')"
echo "==> 配置 .env..."
if [ ! -f "$APP_DIR/.env" ]; then if [ ! -f "$APP_DIR/.env" ]; then
echo "==> 生成 .env(请编辑 ADMIN_PASSWORD 后重启)..."
cp "$APP_DIR/.env.example" "$APP_DIR/.env" cp "$APP_DIR/.env.example" "$APP_DIR/.env"
RAND_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))") RAND_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))")
sed -i "s/change-this-to-a-random-secret-key/${RAND_KEY}/" "$APP_DIR/.env" sed -i "s/change-this-to-a-random-secret-key/${RAND_KEY}/" "$APP_DIR/.env"
echo " 已生成 .env,请编辑 SIMNOW_USER / ADMIN_PASSWORD"
fi fi
ensure_env_key "$APP_DIR/.env" "SIMNOW_ENV" "实盘"
ensure_env_key "$APP_DIR/.env" "CTP_AUTO_RECONNECT" "true"
ensure_env_key "$APP_DIR/.env" "SIMNOW_BROKER_ID" "9999"
ensure_env_key "$APP_DIR/.env" "SIMNOW_APP_ID" "simnow_client_test"
ensure_env_key "$APP_DIR/.env" "SIMNOW_AUTH_CODE" "0000000000000000"
update_simnow_front_in_env "$APP_DIR/.env" || true
mkdir -p "$APP_DIR/logs" mkdir -p "$APP_DIR/logs"
echo "==> 验证 CTP 环境..."
if grep -q "^SIMNOW_USER=.\+" "$APP_DIR/.env" 2>/dev/null && \
grep -q "^SIMNOW_PASSWORD=.\+" "$APP_DIR/.env" 2>/dev/null; then
set +e
python "$APP_DIR/scripts/test_simnow.py"
CTP_TEST=$?
set -e
if [ "$CTP_TEST" -ne 0 ]; then
echo "警告: SimNow 连接测试未通过,请检查 .env 账号与网络(见 docs/SIMNOW.md"
else
echo " SimNow CTP 连接测试通过"
fi
else
echo " 跳过 CTP 测试(未配置 SIMNOW_USER / SIMNOW_PASSWORD"
fi
echo "==> PM2 启动/重启服务..." echo "==> PM2 启动/重启服务..."
cd "$APP_DIR" cd "$APP_DIR"
pm2 delete "$SERVICE_NAME" 2>/dev/null || true if pm2 describe "$SERVICE_NAME" &>/dev/null; then
pm2 restart ecosystem.config.cjs --update-env
else
pm2 start ecosystem.config.cjs pm2 start ecosystem.config.cjs
fi
pm2 save pm2 save
echo "" echo ""
echo "==========================================" echo "=========================================="
echo " 部署完成" echo " 部署完成"
echo " 目录: ${APP_DIR}" echo " 目录: ${APP_DIR}"
echo " 用户: root" echo " 时区: $(date +%Z) $(date '+%Y-%m-%d %H:%M:%S')"
echo " 端口: 6600" echo " 端口: 6600"
echo " 访问: http://<服务器IP>:6600" echo " 访问: http://$(hostname -I 2>/dev/null | awk '{print $1}'):6600"
echo " 日志: pm2 logs ${SERVICE_NAME}" echo " 日志: pm2 logs ${SERVICE_NAME}"
echo " SimNow 注册: docs/SIMNOW.md"
echo " 开机自启: pm2 startup && pm2 save" echo " 开机自启: pm2 startup && pm2 save"
echo "==========================================" echo "=========================================="
+17 -6
View File
@@ -46,14 +46,20 @@ bash deploy.sh
`deploy.sh` 会自动完成: `deploy.sh` 会自动完成:
1. 安装 `python3``python3-venv``git``nodejs``npm``pm2` 1. 安装系统依赖:`python3``git``build-essential``python3-dev``pkg-config``locales``netcat-openbsd``pm2`
2. `git pull``git clone``/opt/qihuo` 2. **时区**设为 `Asia/Shanghai`(与 SimNow 交易时段一致)
3. 创建虚拟环境 `venv``pip install -r requirements.txt` 3. **locale**:生成 `zh_CN.GB18030``zh_CN.UTF-8`CTP 登录必需,缺则进程崩溃)
4. 首次生成 `.env`(随机 `SECRET_KEY` 4. `git pull``git clone``/opt/qihuo`
5. `pm2 start ecosystem.config.cjs``pm2 save` 5. 创建/保留虚拟环境 `venv``pip install -r requirements.txt`,验证 `vnpy_ctp`
6. 首次生成 `.env`,并补全 `SIMNOW_ENV=实盘``CTP_AUTO_RECONNECT=true` 等缺项
7. **自动探测 SimNow 前置**`nc` 测端口),写入可用的 `SIMNOW_TD/MD_ADDRESS`(优先 `182.254.243.31`,其次 `180.168.146.187`
8. 若已配置 SimNow 账号,运行 `scripts/test_simnow.py` 验证连接
9. `pm2 restart --update-env` 或首次 `pm2 start`,并 `pm2 save`
部署完成后访问:`http://<服务器IP>:6600` 部署完成后访问:`http://<服务器IP>:6600`
> 再次部署只需 `cd /opt/qihuo && bash deploy.sh`,无需手工装 locale 或改前置地址。
--- ---
## 手动部署 ## 手动部署
@@ -62,7 +68,12 @@ bash deploy.sh
```bash ```bash
apt update apt update
apt install -y python3 python3-venv python3-pip python3-dev pkg-config git nodejs npm build-essential apt install -y python3 python3-venv python3-pip python3-dev pkg-config git nodejs npm build-essential locales netcat-openbsd
timedatectl set-timezone Asia/Shanghai
sed -i '/^# zh_CN.GB18030/s/^# //' /etc/locale.gen
sed -i '/^# zh_CN.UTF-8/s/^# //' /etc/locale.gen
locale-gen zh_CN.GB18030 zh_CN.UTF-8
update-locale LANG=zh_CN.UTF-8 LC_ALL=zh_CN.UTF-8
npm install -g pm2 npm install -g pm2
``` ```