64 lines
3.7 KiB
Bash
Executable File
64 lines
3.7 KiB
Bash
Executable File
#!/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/.s.PGSQL.5432),执行器容器把该目录
|
||
# 挂进去,DSN 用 host=/run/kgtest-pg。**推荐**:没有 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/.s.PGSQL.5432}"
|
||
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)
|
||
[[ "$REMOTE_SOCKET" =~ ^/[A-Za-z0-9_/-]+/\.s\.PGSQL\.5432$ ]] || die "REMOTE_SOCKET 必须是绝对目录下的 .s.PGSQL.5432"
|
||
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=$(dirname "$REMOTE_SOCKET")&port=5432&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 |