单二进制三角色 + Dock 适配器 + Swarm stack 达到可部署态;mgr1 实测订阅经 central-proxy bootstrap,探活 alive=41/52。 Co-authored-by: Cursor <cursoragent@cursor.com>
106 lines
No EOL
5.4 KiB
Bash
Executable file
106 lines
No EOL
5.4 KiB
Bash
Executable file
#!/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 保留)" |