Files
sub2api-cn-relay-manager/scripts/deploy/deploy_crm_only.sh
phamnazage-jpg 35447be934
Some checks failed
CI / Build & Test (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Release (push) Has been cancelled
feat(deploy): add CRM-only online deployment to remote43
- scripts/deploy/deploy_crm_only.sh: 单进程部署 sub2api-cn-relay-manager
  CRM 控制面到 remote43,不依赖 sub2api host / PG / Redis 容器。
  复用 scripts/deploy/remote43_patched_stack_lib.sh 的 env 渲染
  (render_remote43_crm_env),render_crm_only_bootstrap 用 $\{VAR\} 占位符 +
  sed 注入避开 set -u + unquoted-heredoc 边缘问题。
  部署前先 kill 老进程 (再 scp 二进制) 避免 ELF overwrite 失败。

- docs/DEPLOYMENT.md: 加 '在线部署节点' 段,记录 stack / 端口 / 入口 / 验证。
- docs/EXECUTION_BOARD.md: 顶部加 'Latest Online Stack' 段。
- artifacts/online-deploy-20260602/: 本次真实部署的证据
  - 01-local-build.txt: 本地 server 二进制 md5 + git head
  - 02-remote-inspect.txt: 远端 process / port / db tables
  - 03-crm-api-checks.txt: /healthz /api/packs /api/hosts /metrics 真实响应
  - 04-portal-public.txt: sub.tksea.top 公共入口
  - 05-quality-gates.txt: gofmt / vet / test -race / integration
  - manifest.json: 结构化汇总

验证(2026-06-02 21:32-21:43):
- /healthz: HTTP 200 'ok'
- /api/packs (Bearer): HTTP 200 '{"packs":[]}'
- /api/hosts (Bearer): HTTP 200 '{"hosts":[]}'
- /api/packs (no auth): HTTP 401
- /metrics (Prometheus): HTTP 200,含 active_hosts/active_providers/
  db_connections_active + Go runtime
- sub.tksea.top/portal/: HTTP 200
- sub.tksea.top/portal-admin-api/healthz: HTTP 200 'ok'(反代到 CRM)
- go test -race ./internal/... ./tests/integration/...: PASS
- gofmt / go vet: 干净
2026-06-02 21:46:39 +08:00

221 lines
7.9 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# deploy_crm_only.sh — 把 sub2api-cn-relay-managerCRM 控制面)单进程部署到
# remote43。不起 sub2api host / PG / Redis 容器。
#
# 复用 scripts/deploy/remote43_patched_stack_lib.sh 的 env 渲染 helper。
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# shellcheck disable=SC1091
source "$ROOT_DIR/scripts/deploy/remote43_patched_stack_lib.sh"
KEY="${KEY:-/home/long/下载/zjsea.pem}"
REMOTE="${REMOTE:-ubuntu@43.155.133.187}"
STACK_NAME="${STACK_NAME:-crm-only-$(date +%Y%m%d)}"
CRM_PORT="${CRM_PORT:-18190}"
CRM_BINARY="${CRM_BINARY:-$ROOT_DIR/server}"
LOCAL_REPO_BUNDLE="${LOCAL_REPO_BUNDLE:-/tmp/${STACK_NAME}-repo.bundle}"
LOCAL_OPERATOR_ENV_FILE="${LOCAL_OPERATOR_ENV_FILE:-/tmp/${STACK_NAME}.env}"
LOCAL_TUNNEL_SCRIPT="${LOCAL_TUNNEL_SCRIPT:-/tmp/${STACK_NAME}.tunnel.sh}"
LOCAL_DEPLOY_DIR="${LOCAL_DEPLOY_DIR:-/tmp/${STACK_NAME}-stage}"
REMOTE_ROOT="${REMOTE_ROOT:-/home/ubuntu/${STACK_NAME}_${CRM_PORT}}"
REMOTE_REPO_ROOT="${REMOTE_REPO_ROOT:-/home/ubuntu/sub2api-cn-relay-manager-git-current}"
REMOTE_REPO_BUNDLE="$REMOTE_ROOT/sub2api-cn-relay-manager.bundle"
REMOTE_CRM_ENV_FILE="$REMOTE_ROOT/.env.crm"
REMOTE_BOOTSTRAP_FILE="$REMOTE_ROOT/bootstrap.sh"
REMOTE_CRM_BINARY="$REMOTE_ROOT/sub2api-cn-relay-manager-server"
REMOTE_CRM_DB_FILE="$REMOTE_ROOT/sub2api-cn-relay-manager.db"
REMOTE_CRM_PID_FILE="$REMOTE_ROOT/crm.pid"
REMOTE_CRM_LOG_FILE="$REMOTE_ROOT/crm.log"
crm_admin_token="${crm_admin_token:-$(remote43_random_hex 24)}"
crm_admin_username="${crm_admin_username:-admin}"
crm_admin_password="${crm_admin_password:-$crm_admin_token}"
DRY_RUN="${DRY_RUN:-0}"
die() { echo "$*" >&2; exit 1; }
require_cmd() { command -v "$1" >/dev/null 2>&1 || die "missing command: $1"; }
run_cmd() {
if [[ "$DRY_RUN" == "1" ]]; then
printf "DRY_RUN:"; printf " %q" "$@"; printf "\n"
return 0
fi
"$@"
}
ssh_remote() { run_cmd ssh -i "$KEY" -o StrictHostKeyChecking=no "$REMOTE" "$@"; }
scp_remote() { run_cmd scp -i "$KEY" -o StrictHostKeyChecking=no "$@"; }
write_local_tunnel_script() {
cat > "$LOCAL_TUNNEL_SCRIPT" <<EOF
#!/usr/bin/env bash
set -euo pipefail
exec ssh -N \\
-L ${CRM_PORT}:127.0.0.1:${CRM_PORT} \\
-i $(printf "%q" "$KEY") \\
-o StrictHostKeyChecking=no \\
$(printf "%q" "$REMOTE")
EOF
chmod +x "$LOCAL_TUNNEL_SCRIPT"
}
write_operator_env() {
remote43_write_env_file "$LOCAL_OPERATOR_ENV_FILE" \
CRM_BASE "http://127.0.0.1:${CRM_PORT}" \
REMOTE_CRM_BASE "http://127.0.0.1:${CRM_PORT}" \
REMOTE_ROOT "$REMOTE_ROOT" \
REMOTE_CRM_ENV_FILE "$REMOTE_CRM_ENV_FILE" \
REMOTE_REPO_ROOT "$REMOTE_REPO_ROOT" \
KEY "$KEY" \
REMOTE "$REMOTE" \
crm_admin_token "$crm_admin_token" \
crm_admin_username "$crm_admin_username" \
crm_admin_password "$crm_admin_password" \
CRM_PORT "$CRM_PORT"
chmod 600 "$LOCAL_OPERATOR_ENV_FILE"
}
render_crm_only_bootstrap() {
local crm_env_q remote_root_q remote_repo_bundle_q
local crm_binary_q crm_db_q crm_pid_q crm_log_q remote_repo_root_q crm_port_q
printf -v crm_env_q "%q" "$REMOTE_CRM_ENV_FILE"
printf -v remote_root_q "%q" "$REMOTE_ROOT"
printf -v remote_repo_bundle_q "%q" "$REMOTE_REPO_BUNDLE"
printf -v crm_binary_q "%q" "$REMOTE_CRM_BINARY"
printf -v crm_db_q "%q" "$REMOTE_CRM_DB_FILE"
printf -v crm_pid_q "%q" "$REMOTE_CRM_PID_FILE"
printf -v crm_log_q "%q" "$REMOTE_CRM_LOG_FILE"
printf -v remote_repo_root_q "%q" "$REMOTE_REPO_ROOT"
printf -v crm_port_q "%q" "$CRM_PORT"
local tmp_bootstrap
tmp_bootstrap="$(mktemp)"
cat > "$tmp_bootstrap" <<'BOOTSTRAP_EOF'
#!/usr/bin/env bash
set -euo pipefail
export REMOTE_ROOT=__REMOTE_ROOT__
export CRM_ENV_FILE=__CRM_ENV_FILE__
export CRM_BINARY=__CRM_BINARY__
export CRM_DB_FILE=__CRM_DB_FILE__
export CRM_PID_FILE=__CRM_PID_FILE__
export CRM_LOG_FILE=__CRM_LOG_FILE__
export REMOTE_REPO_ROOT=__REMOTE_REPO_ROOT__
export REMOTE_REPO_BUNDLE=__REMOTE_REPO_BUNDLE__
export CRM_PORT=__CRM_PORT__
mkdir -p "$REMOTE_ROOT" "$(dirname "$REMOTE_REPO_ROOT")"
chmod 755 "$CRM_BINARY"
if [[ -f "$REMOTE_REPO_BUNDLE" ]]; then
if [[ -d "$REMOTE_REPO_ROOT/.git" ]]; then
git -C "$REMOTE_REPO_ROOT" fetch "$REMOTE_REPO_BUNDLE" main
git -C "$REMOTE_REPO_ROOT" reset --hard FETCH_HEAD
else
rm -rf "$REMOTE_REPO_ROOT"
git clone "$REMOTE_REPO_BUNDLE" "$REMOTE_REPO_ROOT"
git -C "$REMOTE_REPO_ROOT" checkout main
fi
git -C "$REMOTE_REPO_ROOT" config user.name "Remote43 CRM"
git -C "$REMOTE_REPO_ROOT" config user.email "remote43-crm@tksea.top"
fi
if [[ -f "$CRM_PID_FILE" ]]; then
OLD_PID="$(cat "$CRM_PID_FILE")"
if kill "$OLD_PID" >/dev/null 2>&1; then
sleep 1
fi
rm -f "$CRM_PID_FILE"
fi
rm -f "$CRM_DB_FILE" "$CRM_LOG_FILE"
nohup bash -lc 'set -a; source "$CRM_ENV_FILE"; set +a; exec "$CRM_BINARY"' >"$CRM_LOG_FILE" 2>&1 &
echo $! > "$CRM_PID_FILE"
python3 - "$CRM_PORT" <<'PY'
import subprocess, sys, time
url = f"http://127.0.0.1:{sys.argv[1]}/healthz"
for _ in range(30):
r = subprocess.run(["curl", "-fsS", url], text=True, capture_output=True)
if r.returncode == 0 and r.stdout.strip() == "ok":
raise SystemExit(0)
time.sleep(1)
raise SystemExit(f"crm healthz did not become ready on {url}")
PY
printf "crm_base=http://127.0.0.1:%s\n" "$CRM_PORT"
printf "crm_pid_file=%s\n" "$CRM_PID_FILE"
printf "crm_log=%s\n" "$CRM_LOG_FILE"
printf "remote_repo_root=%s\n" "$REMOTE_REPO_ROOT"
BOOTSTRAP_EOF
sed -i -e "s|__REMOTE_ROOT__|$remote_root_q|g" -e "s|__CRM_ENV_FILE__|$crm_env_q|g" -e "s|__CRM_BINARY__|$crm_binary_q|g" -e "s|__CRM_DB_FILE__|$crm_db_q|g" -e "s|__CRM_PID_FILE__|$crm_pid_q|g" -e "s|__CRM_LOG_FILE__|$crm_log_q|g" -e "s|__REMOTE_REPO_ROOT__|$remote_repo_root_q|g" -e "s|__REMOTE_REPO_BUNDLE__|$remote_repo_bundle_q|g" -e "s|__CRM_PORT__|$crm_port_q|g" "$tmp_bootstrap"
cat "$tmp_bootstrap"
rm -f "$tmp_bootstrap"
}
main() {
require_cmd bash curl git python3 ssh scp
remote43_require_file "$KEY" "ssh key"
remote43_require_file "$CRM_BINARY" "crm server binary"
rm -f "$LOCAL_REPO_BUNDLE"
git -C "$ROOT_DIR" bundle create "$LOCAL_REPO_BUNDLE" main
write_local_tunnel_script
write_operator_env
local crm_env_file bootstrap_file
crm_env_file="$(mktemp)"
bootstrap_file="$(mktemp)"
trap "rm -f \"$crm_env_file\" \"$bootstrap_file\"" EXIT
render_remote43_crm_env \
"$CRM_PORT" \
"file:${REMOTE_CRM_DB_FILE}?_foreign_keys=on&_busy_timeout=5000" \
"$crm_admin_token" \
"$REMOTE_REPO_ROOT" \
"$crm_admin_username" \
"$crm_admin_password" > "$crm_env_file"
render_crm_only_bootstrap > "$bootstrap_file"
chmod +x "$bootstrap_file"
mkdir -p "$LOCAL_DEPLOY_DIR"
cp "$crm_env_file" "$LOCAL_DEPLOY_DIR/.env.crm"
cp "$bootstrap_file" "$LOCAL_DEPLOY_DIR/bootstrap.sh"
ssh_remote "mkdir -p $(printf "%q" "$REMOTE_ROOT")
if [[ -f $(printf "%q" "$REMOTE_CRM_PID_FILE") ]]; then
OLDPID=\$(cat $(printf "%q" "$REMOTE_CRM_PID_FILE"))
kill \$OLDPID 2>/dev/null || true
sleep 1
fi
rm -f $(printf "%q" "$REMOTE_CRM_PID_FILE") $(printf "%q" "$REMOTE_CRM_DB_FILE") $(printf "%q" "$REMOTE_CRM_LOG_FILE") $(printf "%q" "$REMOTE_CRM_BINARY")"
scp_remote "$CRM_BINARY" "$REMOTE:$REMOTE_CRM_BINARY"
scp_remote "$LOCAL_REPO_BUNDLE" "$REMOTE:$REMOTE_REPO_BUNDLE"
scp_remote "$crm_env_file" "$REMOTE:$REMOTE_CRM_ENV_FILE"
scp_remote "$bootstrap_file" "$REMOTE:$REMOTE_BOOTSTRAP_FILE"
ssh_remote "bash $(printf "%q" "$REMOTE_BOOTSTRAP_FILE")"
cat <<EOF
crm-only stack prepared
remote crm base: http://127.0.0.1:${CRM_PORT}
remote crm env file: ${REMOTE_CRM_ENV_FILE}
remote crm log: ${REMOTE_CRM_LOG_FILE}
remote repo root: ${REMOTE_REPO_ROOT}
local operator env file: ${LOCAL_OPERATOR_ENV_FILE}
local tunnel script: ${LOCAL_TUNNEL_SCRIPT}
local deploy dir: ${LOCAL_DEPLOY_DIR}
next:
1. 在另一终端运行: ${LOCAL_TUNNEL_SCRIPT}
2. 当前终端执行: set -a; source ${LOCAL_OPERATOR_ENV_FILE}; set +a
3. 验证: curl -fsS http://127.0.0.1:${CRM_PORT}/healthz
curl -fsS -H "Authorization: Bearer \$crm_admin_token" http://127.0.0.1:${CRM_PORT}/api/packs
EOF
}
main "$@"