Post

Cloudflare Tunnel 多节点实战手册 - 跨地域调度与 SSH 安全接入(脱敏版)

本文记录一次真实的多节点落地过程:将同地域内网节点与跨地域节点接入同一调度平面,并用 Cloudflare Tunnel 解决跨网段 Redis 访问与 SSH 运维可达性问题。全文已做脱敏处理,所有域名、IP、隧道 ID、用户名、机器名均为示例占位符,可直接按模板替换。

更新日志

  • 2026-03-31: 首发。新增多节点拓扑、Redis 代理路径、R9 SSH 隧道化方案、故障排查清单与可复用脚本模板。

摘要

在多节点调度场景中,最常见的问题不是“算法不可用”,而是“网络不可达”。本次实践中,核心调度后端位于内网控制节点,部分节点可直连,部分跨地域节点无法访问内网地址。我们采用两条 Tunnel 路径:

  1. 跨地域 Worker 到 Redis 的 TCP 代理通道(cloudflared access tcp)。
  2. 受限节点 SSH 运维通道(cloudflared access ssh + tunnel ingress ssh://localhost:22)。

最终实现:

  • 控制面保持单点(Redis 在控制节点)。
  • 跨地域节点通过本地回环端口接入调度后端。
  • SSH 管理从“偶发不可达”切换为“稳定可登录”。

导读

如果你的团队正在做跨机器训练、批处理任务或多地协作,几乎一定会遇到这类问题:

  • 内网节点性能很好,但跨地域节点接不进来。
  • 调度系统逻辑没问题,网络路径却经常“抽风”。
  • SSH 一会儿能连、一会儿连不上,故障恢复全靠运气。

这篇文章给出一套实战可复用方案:

  1. 用 Cloudflare Tunnel 把跨地域 Worker 稳定接入控制面 Redis。
  2. 用 Access SSH 把“受限节点”变成可持续运维节点。
  3. 通过统一队列入口,保持调度策略不变、网络能力升级。

你将得到的不是“单条命令”,而是一套可长期运行的工程化模板:架构图、配置片段、SOP、巡检清单、脱敏发布规范全部齐全。

一图看懂

Cloudflare 多节点架构一图总览

一、问题背景与目标

1.1 原始挑战

  • 节点分散在不同网络地点,存在 NAT、ACL、防火墙差异。
  • 调度后端仅在内网地址暴露,跨地域节点无法直接连通。
  • 某些节点 SSH 连接偶发失败,影响运维与故障恢复。

1.2 目标定义

  • 让所有 Worker 统一接入同一队列后端。
  • 跨地域节点无需开放入站端口。
  • SSH 通过域名稳定访问,便于日常巡检与自动化。

二、总体架构(脱敏)

graph TB
    subgraph "用户侧"
        OPS[运维终端]
        DEV[开发终端]
    end

    subgraph "Cloudflare"
        EDGE[Edge Network]
        DNS[DNS]
    end

    subgraph "地域A(控制面)"
        CP[control-node]
        REDIS[(Redis Backend)]
        W1[worker-a]
        W2[worker-b]
    end

    subgraph "地域B(跨地域)"
        WB[worker-remote]
        PXY[cloudflared access tcp\n127.0.0.1:16379]
        R9[ops-node]
        SSHD[sshd:22]
    end

    OPS --> EDGE
    DEV --> EDGE
    DNS --> EDGE

    CP --> REDIS
    W1 --> REDIS
    W2 --> REDIS

    WB --> PXY
    PXY -. encrypted tunnel .-> EDGE
    EDGE -.-> REDIS

    OPS -. access ssh .-> EDGE
    EDGE -.-> SSHD
    R9 --> SSHD

三、关键通道设计

3.1 通道 A:跨地域 Worker -> Redis

目标:将远端节点本地 127.0.0.1:16379 映射到控制面 Redis。

1
2
3
4
# 远端节点上启动本地 TCP 代理(示例)
cloudflared access tcp \
  --hostname redis-control.example.com \
  --url 127.0.0.1:16379

Worker 进程改为连接本地代理端口:

1
2
3
4
5
python worker_agent.py \
  --name worker-remote \
  --redis-host 127.0.0.1 \
  --redis-port 16379 \
  --workers 4

3.2 通道 B:受限节点 SSH 运维

目标:不依赖内网直连 SSH,统一走 Cloudflare Access。

R9(示例节点)上的 tunnel ingress:

1
2
3
4
5
6
tunnel: <tunnel-id-redacted>
credentials-file: /home/<user>/.cloudflared/<tunnel-id-redacted>.json
ingress:
  - hostname: ssh-node.example.com
    service: ssh://localhost:22
  - service: http_status:404

客户端 SSH:

1
2
3
ssh \
  -o ProxyCommand='cloudflared access ssh --hostname ssh-node.example.com' \
  <user>@ssh-node.example.com

四、落地步骤(可复用)

4.1 准备与认证

1
2
3
4
5
# 控制节点/目标节点均安装 cloudflared
cloudflared --version

# 首次登录授权(浏览器)
cloudflared tunnel login

4.2 创建或复用 Tunnel

1
2
3
4
5
# 创建(若已有可跳过)
cloudflared tunnel create <tunnel-name>

# 查看所有隧道
cloudflared tunnel list

4.3 绑定 DNS

1
2
cloudflared tunnel route dns <tunnel-name> redis-control.example.com
cloudflared tunnel route dns <tunnel-name> ssh-node.example.com

4.4 启动守护进程

1
2
3
4
5
6
7
8
# Redis 通道(远端节点)
nohup cloudflared access tcp \
  --hostname redis-control.example.com \
  --url 127.0.0.1:16379 > /tmp/cloudflared_redis.log 2>&1 &

# SSH 通道(运维节点)
nohup cloudflared tunnel --config ~/.cloudflared/config-ssh.yml run <tunnel-name> \
  > /tmp/cloudflared_ssh.log 2>&1 &

五、提交任务的生产 SOP

5.1 提交入口原则

  • 统一提交到控制面(推荐)。
  • Worker 自动从全局队列消费,不直接向每台机器单独投递。

5.2 任务 JSON 模板(脱敏示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
  "type": "cpu",
  "owner": "team-a",
  "priority": 60,
  "timeout": 3600,
  "max_retries": 2,
  "command": "cd /home/<user>/project && python3 run.py --mode batch",
  "resources": {
    "cpu_cores": 4,
    "memory_gb": 16,
    "gpu_count": 0
  },
  "labels": ["project:demo", "env:prod"]
}

5.3 提交命令(推荐)

1
bash scheduler_ops/scripts/submit_job_r203.sh scheduler_ops/jobs/example_gpu_job.json

5.4 查询任务状态(按 job_id)

1
2
3
4
from distributed_scheduler import JobPortal

portal = JobPortal(redis_host="<control-redis-ip>", redis_port=6379)
print(portal.get_job_status("job_xxx"))

六、故障处理与经验总结

6.1 常见症状与定位

症状可能原因快速检查
Worker 在线但不消费连接到错误 Redis 地址检查 --redis-host--redis-port
SSH 偶发失败网络 ACL / NAT 路径抖动切换 Cloudflare Access SSH
Tunnel 进程在但业务不通ingress hostname/service 不匹配检查 config.yml 与 DNS route
多进程互相干扰重启脚本重复启动未清理pkill 后单实例拉起

6.2 建议的巡检命令

1
2
3
4
5
6
7
8
9
# 检查 worker
ps -ef | grep worker_agent.py | grep -v grep

# 检查 cloudflared
ps -ef | grep cloudflared | grep -v grep

# 检查日志
 tail -n 50 /tmp/cloudflared_redis.log
 tail -n 50 /tmp/cloudflared_ssh.log

七、图示

  1. 多节点拓扑总览:

多节点拓扑总览

  1. Tunnel 列表:

Cloudflare Tunnel 列表

  1. 远端节点本地 TCP 代理日志:

TCP 代理日志

  1. SSH over Cloudflare 登录:

SSH over Cloudflare

  1. 调度任务状态面板:

任务状态

八、附录:脱敏发布自检清单

为避免泄露环境信息,建议在发布前逐项核对:

  • 所有真实域名已替换为示例域名(如 *.example.com)。
  • 所有真实 IP 已替换为占位符(如 <control-redis-ip>)。
  • 隧道 UUID、connector ID、证书路径不出现在正文或截图中。
  • 用户名、主机名、目录名中的组织/个人标识已打码。
  • 终端截图中的公网出口信息、地区标记、账号信息已隐藏。

九、结语

这套方案的核心价值不在“把某一台机器连通”,而在“把异构网络环境统一成可运维、可调度、可恢复的运行面”。Cloudflare Tunnel 解决的是连通性基础设施问题,调度系统解决的是资源利用问题;两者结合后,跨地域多节点的交付成本会显著下降。

如果你也在做分布式训练、批处理任务、跨地协作,建议先把网络路径标准化,再做策略优化。先连通,后优化,效率最高。

This post is licensed under CC BY 4.0 by the author.