远程服务器运行 PostgreSQL、Redis 和 Weaviate 时,可以用一个 SSH 连接转发多个端口。服务器上的 Docker 端口只绑定本机,管理电脑上的客户端统一连接自己的 127.0.0.1。

配置好以后,只需执行一次 ssh -N home-services,不必在每个客户端里分别配置 SSH,也不需要把数据库发布到公网。

连接经过哪几个端口

以 PostgreSQL 为例:

管理电脑的数据库客户端
    ↓ 127.0.0.1:15432
管理电脑的 SSH 本地转发监听
    ↓ 加密 SSH 连接
服务器的 SSH 服务
    ↓ 127.0.0.1:15432
服务器上的 Docker 端口映射
    ↓ 容器端口 5432
PostgreSQL

两边都出现 127.0.0.1,但属于不同电脑。容器中的 5432、服务器上的 15432、管理电脑上的 15432,也分别属于三个位置;后两者用相同数字只是便于记忆。

本篇从普通 SSH 登录已经可用开始。Mac 配合 Tailscale 的完整配置见 用 Tailscale 和 SSH 远程管理家中 Mac。Tailscale 负责服务器可达,端口转发由 OpenSSH 完成;其他可用的 SSH 网络也适用。

在服务器发布本机管理端口

在已有 compose.yaml 的对应服务中合并以下 ports,保留原来的镜像、认证、环境变量和数据挂载:

services:
  postgres:
    ports:
      - '127.0.0.1:15432:5432'
  redis:
    ports:
      - '127.0.0.1:16379:6379'
  weaviate:
    ports:
      - '127.0.0.1:18080:8080'
      - '127.0.0.1:50051:50051'

15432:5432 没有指定宿主机地址,默认可能发布到所有宿主机接口;前面加上 127.0.0.1,把访问入口限制在宿主机回环地址。

Weaviate 的 8080 是 HTTP 端口,50051 用于需要 gRPC 的客户端。HTTP 通了,不代表使用 gRPC 的客户端也已具备全部连接条件。

应用配置前,在服务器检查相关宿主机端口是否已被其他程序占用。Mac 可以执行:

lsof -nP -iTCP:15432 -sTCP:LISTEN
lsof -nP -iTCP:16379 -sTCP:LISTEN
lsof -nP -iTCP:18080 -sTCP:LISTEN
lsof -nP -iTCP:50051 -sTCP:LISTEN

如果已经有这套容器运行,出现它的监听是正常的,先确认进程归属。然后在服务器项目目录执行:

docker compose -f compose.yaml config --quiet
docker compose -f compose.yaml up -d --no-deps postgres redis weaviate
docker compose -f compose.yaml ps postgres redis weaviate

修改端口映射可能重建相关容器并短暂中断连接。保留数据挂载,不为应用端口配置删除数据。预期 PORTS 显示 127.0.0.1:15432->5432/tcp 等映射。

继续在服务器检查:

nc -vz 127.0.0.1 15432
nc -vz 127.0.0.1 16379
nc -vz 127.0.0.1 18080
nc -vz 127.0.0.1 50051

先确保服务器本机端口可达,再处理管理电脑的转发。

在管理电脑配置一次 SSH 转发

编辑管理电脑的 ~/.ssh/config,加入一个专用别名,替换服务器地址、账户和密钥路径:

Host home-services
    HostName <服务器IP或可解析名称>
    User <服务器登录账户>
    IdentityFile ~/.ssh/id_ed25519_home
    IdentitiesOnly yes
    ServerAliveInterval 30
    ServerAliveCountMax 3
    ExitOnForwardFailure yes
    LocalForward 127.0.0.1:15432 127.0.0.1:15432
    LocalForward 127.0.0.1:16379 127.0.0.1:16379
    LocalForward 127.0.0.1:18080 127.0.0.1:18080
    LocalForward 127.0.0.1:50051 127.0.0.1:50051

每条 LocalForward 的左侧是管理电脑要监听的地址,右侧是服务器能访问的目标。使用独立别名,可以避免日常登录服务器时顺带占用所有管理端口。

在管理电脑执行:

ssh -N home-services

-N 表示不执行远端命令。终端没有输出、一直保持运行是正常状态。保持它打开,另开终端或客户端使用这些端口;按 Ctrl+C 关闭转发。

ExitOnForwardFailure yes 能发现建立转发监听失败,但不会提前验证所有远端目标,也不能保证连接后数据库持续可用。OpenSSH LocalForward 与 ExitOnForwardFailure

客户端连接管理电脑本机

服务客户端填写的地址认证信息
PostgreSQL127.0.0.1:15432数据库用户、密码和实际库名
Redis127.0.0.1:16379当前 Redis ACL 用户及密码,或单密码配置
Weaviate HTTPhttp://127.0.0.1:18080按服务实际配置提供 API Key
Weaviate gRPC127.0.0.1:50051按客户端配置认证和协议

已经建立系统 SSH 转发时,Navicat 等客户端不再开启内置 SSH 隧道。这里填写的主机是管理电脑本机,用户名和密码是数据库凭据,不是服务器的 SSH 登录凭据。

连接 PostgreSQL 后,可以查询:

SELECT current_database(), current_user;

检查是否连到了预期的库和角色。Redis 可以执行 PING,预期返回 PONG。端口的 nc 检查只验证 TCP 连通,这些操作才进一步验证服务协议和认证。

示例内部服务若使用明文 HTTP/gRPC,客户端也按相同协议配置,由 SSH 保护跨电脑传输。若原服务启用了 TLS,则继续保留它的 TLS 配置,不能因为有隧道就自动改成明文。

端口冲突和连接失败怎样查

如果管理电脑本身已经占用 15432,只改 LocalForward 左侧即可:

LocalForward 127.0.0.1:25432 127.0.0.1:15432

客户端也改为 25432,服务器映射和数据库容器不需要修改。如果是服务器的 15432 冲突,才需要改 Docker 宿主机端口和 LocalForward 右侧。

现象检查位置
Address already in use管理电脑本地监听端口是否被占用
SSH 登录失败网络、服务器账户、密钥及访问规则
channel ... connect failed服务器是否能连接转发目标,Docker 映射是否已应用
数据库认证失败数据库用户、密码、库名及服务端访问策略
HTTP 正常,Weaviate 客户端仍失败是否还需要 gRPC,客户端端口和协议是否匹配

这套配置只转发选定的端口,不会把远程 Docker 网络整体搬到管理电脑。仅需查看日志或临时执行数据库命令时,直接 SSH 登录后使用 docker compose exec,也可以不发布这些管理端口。