onesvm-browser-server/scripts/deploy-mgr1.sh
chii eb972dfa93 feat: 落地 browser-server 控制面并打通 mgr1 海外订阅
单二进制三角色 + Dock 适配器 + Swarm stack 达到可部署态;mgr1 实测订阅经 central-proxy bootstrap,探活 alive=41/52。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-02 15:05:12 +08:00

106 lines
No EOL
5.4 KiB
Bash
Executable file
Raw Permalink 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
# scripts/deploy-mgr1.sh — browser-server 传输/部署 mgr1(192.168.1.51)。
#
# 🟡🟡🟡 高危操作声明(devops-role-boundaries 分级)🟡🟡🟡
# 本脚本面向 **primary 生产节点 mgr1**(非 dev-swarm),属 🟡 级高危操作:
# - mgr1 在跑 PG / MinIO / WSG-prod :8562 / ASW 等生产服务;
# - 执行前必须经用户「确认执行」明确授权(含糊的「好」「修吧」不算确认);
# - 默认 dry-run:不带 --apply 参数时只做只读探测与打印,不写任何远端状态。
#
# 🔴 红线(Contract §9):本轮(run-20260901-browser-impl)绝不 SSH mgr1、
# 绝不 stack deploy 生产、绝不 git push。本脚本只交付不执行;
# 部署轮须另走 🟡 前置确认(free -g / ss -tln / 影响面 / 回滚)。
#
# 回滚:docker stack rm browser-server(命名卷 browser-server-data 保留,数据不丢)。
#
# 前置密钥:仓库根 deploy.env(gitignore,不入 git)需含:
# BROWSER_SERVER_KEYS_SEED=...
# BROWSER_SERVER_ADMIN_TOKEN=...
# PROXY_SUB_URLS=...
# SEARXNG_SECRET=...
# 现网凭据交接纪律:明文仅 gitignore 文件 + 线下传递(service-secret-protocol §4)。
set -euo pipefail
# ---------- 变量区 ----------
MGR1="${MGR1:-swarm-mgr1}" # primary mgr1(~/.ssh/config 别名:ubuntu + id_ed25519_onesvm_daas)
STACK_NAME="browser-server"
STACK_FILE="stacks/browser-server.yml"
DEPLOY_ENV="deploy.env"
SELF_IMG="onesvm/browser-server:dev"
TRAF_IMG="onesvm/trafilatura-http:dev"
# 第三方镜像清单(repo:tag@amd64-manifest-digest;Mac 拉取 → save/load,
# 生产节点禁直连 Docker Hub)。注意必须钉 amd64 manifest digest 而非 index
# digest:--resolve-image never 下 index digest 与载入镜像不匹配会导致容器无法启动。
THIRD_PARTY_IMAGES=(
"searxng/searxng:2026.8.29-d226b78bc@sha256:61ad36fcf71eb300f7d64216a6a04e010754755c7f5d33a526bd52e597e78c69"
"metacubex/mihomo:v1.19.24@sha256:9e37208fae8afa4c8b83d14ff2e9771b99178ebaf65a2c4fb388bc67ecefe4dc"
"lightpanda/browser:0.3.7@sha256:447598e4c103b70b0fb610813e72795da44341c564e1ec872c3a490c1bc6aebe"
"chromedp/headless-shell:151.0.7922.109@sha256:5f877a2a559dea1a99fb750da695d28a020cdd49db660aead6c78a46e3c7dd50"
"alpine:3.20@sha256:c64c687cbea9300178b30c95835354e34c4e4febc4badfe27102879de0483b5e"
)
APPLY=0
[ "${1:-}" = "--apply" ] && APPLY=1
# ---------- 步骤 ①:前置只读探测(free -g / ss -tln;dry-run 也会做,只读无副作用) ----------
log() { printf '[deploy-mgr1] %s\n' "$*"; }
log "== 前置探测(只读): mgr1 内存余量 / 8640 端口占用 =="
ssh "$MGR1" 'free -g | head -2; echo ---; ss -tln | grep -E ":8640\b" || echo "端口 8640 空闲"'
log "(🟡 请将上述输出发给用户确认内存余量与端口空闲后再 --apply)"
if [ "$APPLY" -ne 1 ]; then
log "DRY-RUN 结束(未做任何变更)。确认执行请运行:$0 --apply"
exit 0
fi
# ---------- 密钥加载(fail-closed:deploy.env 缺失即拒绝) ----------
if [ ! -f "$DEPLOY_ENV" ]; then
log "错误:缺 ${DEPLOY_ENV}(gitignore;模板见 server/.env.example)——fail-closed 退出"
exit 1
fi
# shellcheck disable=SC1090
set -a; . "$DEPLOY_ENV"; set +a
for var in BROWSER_SERVER_KEYS_SEED BROWSER_SERVER_ADMIN_TOKEN PROXY_SUB_URLS SEARXNG_SECRET; do
if [ -z "${!var:-}" ]; then
log "错误:$var 未设置(${DEPLOY_ENV})——fail-closed 退出"
exit 1
fi
done
# ---------- 步骤 ②:Mac 构建 amd64 镜像(自研两枚) ----------
log "== 构建 $SELF_IMG / ${TRAF_IMG}(linux/amd64)=="
make image PLATFORM=linux/amd64
make image-trafilatura PLATFORM=linux/amd64
# ---------- 步骤 ③:自研镜像 save | load 到 mgr1 ----------
log "== save|load 自研镜像 → mgr1 =="
docker save "$SELF_IMG" "$TRAF_IMG" | ssh "$MGR1" 'docker load'
# ---------- 步骤 ④:第三方镜像逐枚 save | load ----------
# (searxng / mihomo / lightpanda / headless-shell / alpine;swarm 多节点时每节点都要 load)
for ref in "${THIRD_PARTY_IMAGES[@]}"; do
img="${ref%@*}" # repo:tag(save/load 与 stack 引用统一走此 tag)
log "== pull(amd64) + save|load 第三方镜像 $img → mgr1 =="
docker pull --platform linux/amd64 "$ref"
docker tag "$ref" "$img" # 钉 amd64 变体到 tag(Mac 本地 tag 可能仍是 arm64 变体)
docker save "$img" | ssh "$MGR1" 'docker load'
done
# ---------- 步骤 ⑤:stack deploy(--resolve-image never,env 由 deploy.env 注入) ----------
# 同步 stack 引用的挂载文件(searxng 两份 settings)到 mgr1(stack 相对路径按执行目录解析)。
log "== 同步 $STACK_FILE 与挂载配置 → mgr1:/tmp/${STACK_NAME} =="
ssh "$MGR1" "mkdir -p /tmp/${STACK_NAME}"
scp -r stacks "$MGR1:/tmp/${STACK_NAME}/"
scp "$DEPLOY_ENV" "$MGR1:/tmp/${STACK_NAME}/"
log "== stack deploy(🟡 生产写入动作,前面已获 --apply 授权)=="
ssh "$MGR1" "cd /tmp/${STACK_NAME} && set -a && . ./deploy.env && set +a && \
docker stack deploy -c stacks/browser-server.yml --resolve-image never ${STACK_NAME}"
# ---------- 步骤 ⑥:尾部清理(仅 dangling;安全,不删命名镜像) ----------
log "== 本机 dangling 清理(docker image prune -f)=="
docker image prune -f
log "== 部署完成。验证建议:ssh $MGR1 'docker stack ps ${STACK_NAME}' / curl :8640/healthz =="
log "回滚:ssh $MGR1 'docker stack rm ${STACK_NAME}'(命名卷 browser-server-data 保留)"