CloudFlare Tunnels建立隧道
核心要点:Docker 版 cloudflared 的本质就是一个容器,它通过 Token 或 配置文件 向 Cloudflare 建立出站连接,然后把流量转发给同网络的其他容器。全程不需要暴露端口、不需要备案、不需要域名解析到服务器 IP。
**模拟场景:**有一个SpringBoot + Vue的前后端分离项目,部署在了国内云服务器上,想通过建立隧道实现域名绑定
前期准备:
- 准备一个域名
- 一台国内云服务器
- 注册好cloudflare账号
将域名DNS托管到Cloudflare
如果域名还未添加到 Cloudflare,先需要将域名托管,否则后面的步骤无法继续。
- 登录cloudflare账号
- 点击【添加域名】,输入你的域名,选择Free计划
- Cloudflare 会给出两台专属的 NS 地址,你需要去域名注册商(如阿里云、NameSilo)后台,将域名的 DNS 服务器修改为这两个地址。
- 等待 DNS 生效(通常几分钟到几小时),Cloudflare 控制台会提示“活动”状态。
完成后,你的域名 DNS 解析完全由 Cloudflare 管理。后面的所有 DNS 记录都在 Cloudflare 控制台添加
服务器安装Cloudflare
这里选docker模式 这是目前 Cloudflare 官方主推的方式,不需要在宿主机安装 cloudflared CLI,只需要一个 Token 字符串。
cloudflare创建tunnels隧道
- 打开cloudflare页面点击网络-Tunnels
- 创建隧道-输入你的隧道名即可
- 选择docker方式,并复制给出的command命令
安装cloudflare
先获取docker镜像
bashdocker pull cloudflare/cloudflared:latest准备docker环境
在你的项目根目录创建
.env文件:bashTUNNEL_TOKEN=eyJh... # 粘贴你复制的 Token
修改docker compose文件
version: '3.8'
services:
backend:
build: ./backend
container_name: chat-server
restart: always
# ❌ 移除端口映射,完全走 Docker 内网 + Cloudflare Tunnel
# 如需服务器本地调试,可临时取消注释(仅监听 127.0.0.1)
# ports:
# - "127.0.0.1:8089:8089"
volumes:
- ./logs:/app/logs
networks:
- app-network
frontend:
build: ./frontend
container_name: chat-client
restart: always
# ❌ 移除 80 端口映射,完全通过 cloudflared 访问
# ports:
# - "80:80"
depends_on:
- backend
networks:
- app-network
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: always
command: tunnel run --token ${TUNNEL_TOKEN}
environment:
- TUNNEL_TOKEN=${TUNNEL_TOKEN}
networks:
- app-network
depends_on:
- frontend
- backend
networks:
app-network:
driver: bridge在cloudflare中配置Hostname
回到刚才的 Cloudflare 控制台页面, Tunnels页面中找到刚刚创建的隧道,然后添加路由:
| Public Hostname | Service Type | URL | 说明 |
|---|---|---|---|
www.yourdomain.com | HTTP | nginx:80 | Vue 前端(Docker 服务名) |
api.yourdomain.com | HTTP | backend:8089 | Spring Boot API(Docker 服务名) |
为什么可以用 nginx:80 和 backend:8089?
因为这三个容器在同一个 Docker 网络 app-network 中,Docker 内置 DNS 会自动把服务名解析为容器 IP。cloudflared 容器访问 http://nginx:80 就像访问内网一样,流量不经过宿主机网络栈。
调整Nginx配置
因为你现在所有服务都在 Docker 内网,Nginx 配置要做一点微调:
server {
listen 80;
server_name _; # 接受任何 Host,因为内部访问
# 前端
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
# API
location /api/ {
# 注意:Docker 内网直接用服务名 backend
proxy_pass http://backend:8089/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 关键:让后端知道这是经过 Cloudflare 的
proxy_set_header X-Forwarded-Proto $scheme;
}
# Socket.IO WebSocket
location /socket.io/ {
proxy_pass http://backend:9092/socket.io/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
proxy_buffering off;
}
}启动
docker-compose up -d --build关键注意事项
不要暴露任何端口到宿主机
# ❌ 错误示范
services:
backend:
ports:
- "8089:8089" # 不要写这个!
nginx:
ports:
- "80:80" # 不要写这个!服务完全通过 Docker 内网和 cloudflared 隧道访问,不需要、也不应该暴露任何端口到宿主机公网 IP。
防火墙设置
因为 cloudflared 是主动出站连接,你只需要确保服务器能访问外网:
# 测试连通性
curl -I https://www.cloudflare.com
# 测试 cloudflared 连接端口(可选)
nc -vz region1.v2.argotunnel.com 7844完整目录结构参考
/opt/project/
├── docker-compose.yml
├── .env # TUNNEL_TOKEN=eyJh...
├── nginx/
│ └── default.conf # 上面的 Nginx 配置
├── frontend/
│ └── dist/ # Vue 打包产物
└── backend/
└── Dockerfile # Spring Boot拓展配置
Minio配置
假设springboot中需要用到minio服务,但是minio服务不和前后端服务一起构建
1.先创建共享网络
Docker 默认每个 docker-compose.yml 创建独立的网络,容器互相隔离。需要创建一个外部网络,两个项目共用:
# 手动创建外部网络
docker network create shared-network2.MinIO 的 docker-compose.yml
# /opt/minio/docker-compose.yml
version: '3.8'
services:
minio:
image: minio/minio:latest
container_name: minio
restart: always
command: server /data --console-address ":9001"
volumes:
- ./minio-data:/data
environment:
- MINIO_ROOT_USER=your-access-key
- MINIO_ROOT_PASSWORD=your-secret-key
networks:
- shared-network # 使用外部网络
# ❌ 不映射任何端口到宿主机
networks:
shared-network:
external: true # 引用外部网络,不自己创建启动:
docker-compose up -d3.前后端的 docker-compose.yml
# /opt/chat-app/docker-compose.yml
version: '3.8'
services:
backend:
build: ./backend
container_name: chat-server
restart: always
volumes:
- ./logs:/app/logs
environment:
- MINIO_ENDPOINT=https://minio.misonomayu.top
- MINIO_ACCESS_KEY=xxx
- MINIO_SECRET_KEY=xxx
networks:
- shared-network # 同一个外部网络
frontend:
build: ./frontend
container_name: chat-client
restart: always
depends_on:
- backend
networks:
- shared-network
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: always
command: tunnel run --token ${TUNNEL_TOKEN}
environment:
- TUNNEL_TOKEN=${TUNNEL_TOKEN}
networks:
- shared-network
depends_on:
- frontend
- backend
networks:
shared-network:
external: true # 引用同一个外部网络4.Cloudflare Tunnel 路由配置
| Subdomain | Domain | Type | URL |
|---|---|---|---|
www | misonomayu.top | HTTP | http://frontend:80 |
minio | misonomayu.top | HTTP | http://minio:9000 |
minio-console | misonomayu.top | HTTP | http://minio:9001 |
注意:http://minio:9000 里的 minio 是容器名,Docker DNS 在 shared-network 里能解析到 MinIO 容器的内网 IP。
5.Spring Boot 配置
# application.yml
minio:
endpoint: https://minio.misonomayu.top # 公网 HTTPS 域名(给前端用的)
internal-endpoint: http://minio:9000 # Docker 内网地址(后端直连,可选优化)
access-key: xxx
secret-key: xxx如果后端需要直连 MinIO(不走 Cloudflare 回环),可以用内网地址加速:
@Bean
public MinioClient minioClient() {
// 生成预签名 URL 时用公网域名(前端能访问)
return MinioClient.builder()
.endpoint("https://minio.misonomayu.top")
.credentials(accessKey, secretKey)
.build();
}6.总结
| 步骤 | 操作 |
|---|---|
| 1 | docker network create shared-network |
| 2 | MinIO compose 使用 networks: shared-network: external: true |
| 3 | 前后端 compose 同样使用这个外部网络 |
| 4 | Cloudflare Tunnel 路由填 http://minio:9000 |
| 5 | Spring Boot 预签名 URL 用 https://minio.misonomayu.top |
两个项目完全独立,但容器通过共享的 Docker 网络互相通信,和放在同一个 compose 文件里效果一样。