Files
knowledge-governance/deploy/cloud-test/pg-forward.sh
T
姚章浩 e6472b0ec6 feat(library): 云测试发布目标接入的角色分离、快照搬运与图谱门禁
为什么改:发布执行链是"与目标同机"假设写死的(本机 docker.sock、本机 drain.json、
本机 Redis/PG),把目标换成云测试不能只改展示 URL;直接搬整套 worker 又会抢走
本机封存/QA/周报队列并按本机配置写错目标。所以先把"谁消费哪段队列"变成显式角色,
再补齐跨主机搬运与目标侧门禁。

角色(LIBRARY_WORKER_ROLE=full/release-executor/capture)不只影响 handler 列表,
还决定是否拥有 DDL(release-executor 只读校验迁移版本,落后即拒绝启动)、
收拾哪些 library_jobs.kind、是否跑 release 接管扫描、是否起 scheduler/QA/周报/探测。
capture 保留本机原有全部业务;release-executor 只挂 sync/restore/recover/baseline。

快照跨主机:新增内容寻址的 bundle(版本清单先落、blob 按 sha256 命名、清单最后
Link 作提交点),pack/unpack 幂等且拒绝覆盖与同名不同内容;执行器使用前仍逐 blob
重算 sha256,因此传输不可变有两道证据。

目标侧门禁:目标知识库 graph_enabled=true 时拒绝发布(发布只写 PG/ES/OSS,不写
Neo4j;未知也拒绝),只读检测、不改目标配置、不做图谱迁移。

云端入口:新增 drain 感知的 ingress 闸门(建连超时与读超时分离,半关闭单向传播,
SIGTERM 排空后写 drain.json 并 exit 0/2)、compose 叠加片段(app 不再发布端口、
frontend 与 adapter 的入口收敛到闸门、指向真实网络 weknora-test-v071-network)、
执行器启动脚本与治理 PG 反向隧道脚本(默认 unix socket,不占 TCP)。

验证:deploy/cloud-test/test_gate.py 13 项全 PASS(含 12 秒静默流、半关闭、2MiB 完整、
排空 exit 0/active_at_close=1、排空超时 exit 2、上游不可达不挂住);go test 相关包全绿;
scripts/ci_checks.py 16 项全部 success(EXIT=0,1241 用例通过/0 失败/191 已声明可选集成跳过);
云测试只读预检 17 项 / 6 FAIL,FAIL 项即当前部署缺口(无闸门、app 仍发布端口、
adapter 与 frontend 仍直连 app、云端归档根未建),未部署、未改云端任何状态。
2026-10-30 18:38:38 +08:00

63 lines
3.5 KiB
Bash
Executable File
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
# 治理库 → 云测试发布执行器:SSH 加密反向转发(在**本机控制面**上运行)。
#
# 为什么不能写 localhost:5432:本机治理 PG 跑在 Docker 里,没有 5432 宿主机发布,
# 也不允许为它开公网/主机端口。所以只能:
# 1. 先解析 PG 容器在它自己网络里的真实 IP;
# 2. 用 ssh -R 建反向转发,让云侧出现一个**只属于云主机/容器**的入口;
# 3. 执行器通过这个入口连库,凭据只在本机与云端 0600 env 之间传递。
#
# 两种模式(默认 streamlocal,不占 TCP 端口、不开监听):
# streamlocal 云侧生成 unix socket(如 /run/kgtest-pg/pg.sock),执行器容器把该目录
# 挂进去,DSN 用 host=/run/kgtest-pg/pg.sock。**推荐**:没有 TCP 暴露,
# 不受"容器看不到宿主机 127.0.0.1"影响。
# tcp 云侧 127.0.0.1:15433 loopback。⚠️ 只有云**宿主机**上的进程能用;
# 容器里的进程看到的 127.0.0.1 是容器自己。要用必须在云侧再加一个
# 从 docker bridge 到 127.0.0.1 的桥接(socat/haproxy),那属于父运维决定,
# 本脚本不替你开。
#
# 用法:
# MODE=streamlocal bash deploy/cloud-test/pg-forward.sh
# MODE=tcp REMOTE_PORT=15433 bash deploy/cloud-test/pg-forward.sh
set -euo pipefail
SSH_HOST="${SSH_HOST:-weknora-test}"
PG_CONTAINER="${PG_CONTAINER:-knowledge-governance-p1-postgres-1}"
MODE="${MODE:-streamlocal}"
REMOTE_SOCKET="${REMOTE_SOCKET:-/run/kgtest-pg/pg.sock}"
REMOTE_PORT="${REMOTE_PORT:-15433}"
die() { echo "pg-forward: $*" >&2; exit 1; }
command -v docker >/dev/null || die "需要 docker(解析治理 PG 容器 IP)"
docker inspect "$PG_CONTAINER" >/dev/null 2>&1 || die "找不到容器 $PG_CONTAINER"
# 只取第一个网络里的 IP;多网络时打印提示,由人确认走哪个。
IPS=$(docker inspect -f '{{range $name, $net := .NetworkSettings.Networks}}{{$name}}={{$net.IPAddress}} {{end}}' "$PG_CONTAINER")
PG_IP=$(echo "$IPS" | tr ' ' '\n' | sed -n 's/^[^=]*=\([0-9.]\+\)$/\1/p' | head -n1)
[ -n "$PG_IP" ] || die "解析不到 $PG_CONTAINER 的容器 IP(网络:$IPS)"
echo "pg-forward: 治理 PG 容器 $PG_CONTAINER -> $PG_IP:5432(网络:$IPS)"
case "$MODE" in
streamlocal)
ssh -o BatchMode=yes -o ExitOnForwardFailure=yes -o ServerAliveInterval=15 -o ServerAliveCountMax=3 \
"$SSH_HOST" "install -d -m 0700 -o root -g root $(dirname "$REMOTE_SOCKET")"
DSN_SHAPE="postgres://<user>@/<db>?host=$REMOTE_SOCKET&sslmode=disable"
echo "pg-forward: 云侧入口 unix socket = $REMOTE_SOCKET(仅云主机与挂载它的容器可见)"
echo "pg-forward: 执行器容器必须挂载 -v $(dirname "$REMOTE_SOCKET"):$(dirname "$REMOTE_SOCKET")"
echo "pg-forward: DATABASE_URL 形状 = $DSN_SHAPE"
exec ssh -N -o BatchMode=yes -o ExitOnForwardFailure=yes \
-o ServerAliveInterval=15 -o ServerAliveCountMax=3 \
-R "$REMOTE_SOCKET:$PG_IP:5432" "$SSH_HOST"
;;
tcp)
echo "pg-forward: 云侧 127.0.0.1:$REMOTE_PORT -> 本机 $PG_IP:5432(仅云宿主机可用)"
echo "pg-forward: 容器内进程不能用这个 loopback;要么用 MODE=streamlocal,要么另做 bridge(父决定)"
exec ssh -N -o BatchMode=yes -o ExitOnForwardFailure=yes \
-o ServerAliveInterval=15 -o ServerAliveCountMax=3 \
-R "127.0.0.1:$REMOTE_PORT:$PG_IP:5432" "$SSH_HOST"
;;
*)
die "MODE 只能是 streamlocal 或 tcp(当前 $MODE)"
;;
esac