背景
去年我在家里搞了一台双卡 RTX 4090 服务器,跑 Qwen2-72B-Instruct + vLLM,本地测试很稳——curl http://localhost:8000/v1/chat/completions 秒回。但问题来了:想让手机、公司电脑、甚至朋友临时试用下模型能力,总不能让他们连我内网 VPN,更不敢开 8000 端口到公网(上个月刚看到某位同行被扫描器爆破进去了)。我试过 frp,配置复杂还老掉线;也试过 ngrok,免费版带宽限速、域名随机、每小时断一次。直到上个月在 Cloudflare 控制台点到 Tunnel 页,发现它居然支持 TCP 和 HTTP 两种模式,还能自动 HTTPS、自带 WAF、支持自定义子域名——而且完全免费。我当天晚上就搭通了,现在我的 ai.rwj.dev 已稳定运行 47 天,零中断。
五步上线:从注册到 curl 成功
以下所有命令均在我 Ubuntu 22.04 + NVIDIA Driver 535.129.03 + vLLM 0.6.3 环境下实测通过。注意:不要用 root 用户运行 cloudflared,我专门建了 cf-tunnel 用户。
# 1. 下载并安装 cloudflared(官方推荐方式)
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared
chmod +x /usr/local/bin/cloudflared
sudo useradd -r -s /bin/false cf-tunnel
# 2. 登录(会打开浏览器,选你自己的 Cloudflare 账户)
sudo -u cf-tunnel cloudflared tunnel login
# ✅ 成功后会在 ~/.cloudflared/ 下生成 cert.pem
# 3. 创建隧道(名字必须全小写、无下划线)
sudo -u cf-tunnel cloudflared tunnel create ai-rwj-dev
# 输出类似:
# Created tunnel ai-rwj-dev with id 7a2b3c4d-ef56-7890-abcd-ef1234567890
# 记下这个 ID,后面要用
# 4. 编辑配置文件(路径必须是 ~/.cloudflared/<tunnel-id>.yml)
sudo -u cf-tunnel mkdir -p ~/.cloudflared
sudo -u cf-tunnel nano ~/.cloudflared/7a2b3c4d-ef56-7890-abcd-ef1234567890.yml
配置文件内容如下(重点看 ingress 规则顺序和 service 地址):
tunnel: 7a2b3c4d-ef56-7890-abcd-ef1234567890
credentials-file: /home/cf-tunnel/.cloudflared/7a2b3c4d-ef56-7890-abcd-ef1234567890.json
ingress:
- hostname: ai.rwj.dev
service: http://127.0.0.1:8000
originRequest:
httpHostHeader: ai.rwj.dev
timeout: 300s
keepAliveConnections: 100
- hostname: api.ai.rwj.dev
service: http://127.0.0.1:8000
originRequest:
httpHostHeader: api.ai.rwj.dev
- service: http_status:404
⚠️ 注意三点:
① hostname 必须已在 Cloudflare DNS 中添加 CNAME 记录(稍后说明);
② httpHostHeader 必须与 hostname 一致,否则 vLLM 的 CORS 或 OpenAI 兼容层可能拒绝请求;
③ 最后一行 service: http_status:404 是必需的兜底规则,否则未匹配域名会返回 502。
# 5. 在 Cloudflare DNS 控制台添加两条 CNAME 记录:
# 名称:ai 内容:<tunnel-id>.cfargotunnel.com 代理状态:✅ ON
# 名称:api 内容:<tunnel-id>.cfargotunnel.com 代理状态:✅ ON
# ✅ 保存后等待 1–2 分钟生效(不用等 TTL)
# 启动隧道(后台常驻,加 --no-autoupdate 避免半夜重启)
sudo -u cf-tunnel cloudflared tunnel run ai-rwj-dev --no-autoupdate &
# 查看日志确认上线
tail -f /var/log/cloudflared.log # 或用 journalctl -u cloudflared
# 正常输出含:"Connected to tunnel" 和 "Registered tunnel connection"
实测记录
我用 vLLM 0.6.3 启动 Qwen2-7B-Instruct(--tensor-parallel-size 2),监听 0.0.0.0:8000,然后从三地发起测试:
- 北京联通家庭宽带:
curl -s -w '\n%{time_total}s\n' -H "Content-Type: application/json" -d '{"model":"qwen2-7b","messages":[{"role":"user","content":"你好"}]}' https://ai.rwj.dev/v1/chat/completions | jq '.choices[0].message.content' 2>/dev/null→ 平均耗时 327ms(P95 412ms),首 token 延迟 280ms; - 上海移动 4G 手机:用 Termux 运行相同命令,延迟波动大(290–680ms),但全部成功,无 TLS 错误;
- 公司笔记本(深圳,企业防火墙):能访问,但首次连接慢(约 1.2s),因为 Cloudflare 需要完成证书 OCSP Stapling;后续复用连接稳定在 340ms 左右。
我还压测了并发能力:autocannon -u https://ai.rwj.dev/v1/chat/completions -b '{"model":"qwen2-7b","messages":[{"role":"user","content":"你是谁"}]}' -c 10 -d 30 → 平均 RPS 8.2,vLLM GPU 利用率峰值 72%,nvidia-smi 显示显存占用 14.2/24GB,一切健康。没有出现连接重置或 522 错误——这得益于配置里设了 timeout: 300s(vLLM 生成长文本可能超 60s)。
踩坑备忘
这些是我亲手撞过的墙,按发生频率排序:
- 「502 Bad Gateway」但日志显示 Connected:原因是 ingress 规则没匹配到 hostname。检查
cloudflared tunnel route dns是否已绑定,且 DNS 控制台 CNAME 的「代理状态」是否为 ON(灰色 OFF 表示直连,绕过隧道); - vLLM 返回 400:"Invalid request":因为没配
originRequest.httpHostHeader,导致 vLLM 的fastapi中间件认为 Host 不合法。加了这行立刻解决; - 隧道启动后立即退出:常见于
~/.cloudflared/权限不对。执行sudo chown -R cf-tunnel:cf-tunnel ~/.cloudflared; - HTTPS 请求被拦截(ERR_SSL_VERSION_OR_CIPHER_MISMATCH):旧安卓/Win7 设备不支持 TLS 1.3。Cloudflare 默认强制 TLS 1.3,需在隧道设置里手动降级:控制台 → Workers & Pages → Tunnels → 选中隧道 → Configuration → TLS → 改为 "TLS 1.2+";
- 手机 Safari 打不开,提示「无法建立安全连接」:因为 iOS 对自签名根证书敏感。解决方案不是装证书(太麻烦),而是确保你的域名(如
rwj.dev)已通过 Cloudflare 的 SSL/TLS → Origin Server → Create Certificate 流程申请了有效证书(免费,5 分钟搞定)。
我的结论
Cloudflare Tunnel 不是万能胶,但对中小规模本地 AI 服务暴露来说,它是目前我用过最省心、最安全、最稳定的方案。它特别适合:
✅ 自建 GPU 服务器跑 vLLM/Ollama/Qwen-WebUI 的个人开发者;
✅ 需要临时分享 API 给测试同事、客户试用的 MLOps 工程师;
✅ 拒绝公网 IP、路由器无管理权限、又不想折腾 DDNS+反向代理的极客。
但它不适合:
❌ 日均请求超 10 万次的生产级 API(免费版有连接数上限,建议升级 Teams 套餐);
❌ 需要 WebSocket 长连接的实时流式 UI(Tunnel 对 WS 支持弱,偶发 1006 错误,我改用 service: tcp://127.0.0.1:8000 模式才稳定);
❌ 完全离线环境(毕竟依赖 Cloudflare 全球边缘节点)。
最后说句实在话:如果你只是想把 http://localhost:3000 的前端页面丢给朋友看看,用 cloudflared tunnel --url http://localhost:3000 一行命令就够了;但如果你暴露的是 AI API,务必配好 ingress 规则、HTTP Host Header、超时和兜底策略——细节决定成败。我已经把它写成 systemd 服务(/etc/systemd/system/cloudflared.service),开机自启、日志轮转、内存限制全配齐。需要配置模板的话,评论区喊一声,我下期就发。