生产环境用 Docker 跑 Cloudflare Tunnel:内网服务安全暴露实战
Cloudflare Docker部署教程|生产环境实测
在生产环境中,很多团队都会遇到一个共同问题:内部服务需要安全、稳定地暴露到公网,但又不希望直接开放服务器端口,更不想频繁折腾公网 IP、Nginx 反向代理、HTTPS 证书和防火墙规则。Cloudflare 提供的 Cloudflare Tunnel 正好可以解决这个问题。
本文将以生产环境实测的方式,介绍如何使用 Docker 部署 Cloudflare Tunnel,将内网服务安全地通过 Cloudflare 暴露到公网。文章会覆盖环境准备、域名配置、Tunnel 创建、Docker Compose 部署、服务映射、安全加固、日志排查以及生产环境注意事项。
说明:本文中的“Cloudflare Docker 部署”主要指通过 Docker 部署
cloudflared,也就是 Cloudflare Tunnel 客户端。
一、Cloudflare Tunnel 是什么?
Cloudflare Tunnel 是 Cloudflare 提供的一种安全隧道服务。它的核心作用是:
让内网服务器主动连接 Cloudflare 边缘网络,然后通过 Cloudflare 将外部请求转发到你的内部服务。
传统方式中,如果要让一个内网服务可以从公网访问,通常需要:
- 服务器有公网 IP;
- 在防火墙开放端口;
- 配置 Nginx 或其他反向代理;
- 申请并续期 HTTPS 证书;
- 做安全防护和访问控制。
而使用 Cloudflare Tunnel 后,你的服务器不需要暴露任何入站端口,只需要能够访问公网即可。请求路径大致如下:
用户浏览器
↓
Cloudflare CDN / WAF / DNS
↓
Cloudflare Tunnel
↓
Docker 容器中的 cloudflared
↓
内网服务
这种模式非常适合以下场景:
- 家庭服务器、NAS、软路由;
- 公司内部测试环境;
- 私有 GitLab、Jenkins、Harbor;
- 内部管理后台;
- Kubernetes、Docker 服务;
- 没有公网 IP 的服务器;
- 不希望开放 80、443、22 等端口的生产环境。
二、为什么生产环境推荐用 Docker 部署?
cloudflared 可以直接安装在 Linux 主机上,也可以使用 Docker 部署。生产环境中,我更推荐 Docker 方式,主要有以下几个原因。
1. 环境隔离更清晰
Docker 容器不会污染宿主机环境,也不会和系统自带依赖冲突。对于生产服务器来说,越少改动系统环境越好。
2. 版本管理更方便
通过镜像标签可以固定版本,例如:
image: cloudflare/cloudflared:2025.1.0
这样可以避免 latest 自动升级带来的不可控问题。
3. 易于迁移和恢复
Docker Compose 文件可以直接纳入 Git 管理。服务器迁移时,只需要恢复配置文件和 Token 即可快速上线。
4. 重启策略简单
Docker 支持:
restart: unless-stopped
当服务器重启或者容器异常退出时,隧道可以自动恢复。
5. 日志管理统一
通过 Docker logs、Portainer、ELK、Grafana Loki 等工具可以统一采集日志,方便生产环境排查问题。
三、部署前准备
正式开始之前,需要准备以下内容。
1. 一个 Cloudflare 账号
访问:
https://dash.cloudflare.com/
注册并登录 Cloudflare。
2. 一个已托管到 Cloudflare 的域名
例如你有一个域名:
example.com
需要将域名的 DNS 解析托管到 Cloudflare。通常步骤是:
- 在 Cloudflare 添加站点;
- Cloudflare 会分配两条 NS 记录;
- 到域名注册商后台修改 NS;
- 等待解析生效。
可以使用以下命令检查 NS 是否已生效:
dig NS example.com
如果返回的是 Cloudflare 的名称服务器,说明托管成功。
3. 一台可运行 Docker 的服务器
本文以 Ubuntu Server 为例,推荐系统版本:
Ubuntu 22.04 LTS
Ubuntu 24.04 LTS
Debian 12
Rocky Linux 9
服务器需要能访问公网,至少可以连接 Cloudflare 的相关服务。
4. 安装 Docker 和 Docker Compose
如果服务器还没有安装 Docker,可以使用以下命令快速安装:
curl -fsSL https://get.docker.com | bash
安装完成后检查版本:
docker version
docker compose version
如果当前用户不是 root,还可以加入 docker 用户组:
sudo usermod -aG docker $USER
然后重新登录终端。
四、创建 Cloudflare Tunnel
Cloudflare Tunnel 有两种常见部署方式:
- 使用 Token 快速运行;
- 使用配置文件和证书方式运行。
生产环境中,最简单且稳定的方式是使用 Token 模式。这种方式不需要在服务器上手动保存 Cloudflare 账号证书,只需要保存 Tunnel Token 即可。
1. 进入 Zero Trust 控制台
登录 Cloudflare 后,进入:
Zero Trust → Networks → Tunnels
如果是第一次使用 Cloudflare Zero Trust,需要先创建一个 Team Name,例如:
myteam
创建后进入 Tunnel 页面。
2. 创建 Tunnel
点击:
Create a tunnel
选择:
Cloudflared
然后输入隧道名称,例如:
prod-docker-tunnel
创建完成后,Cloudflare 会提供一段运行命令,类似:
docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token eyJhIjoi...
其中最重要的是 --token 后面的内容。
注意:Token 是敏感信息,拥有该 Token 的人可以连接你的 Tunnel,务必妥善保管,不要提交到公开仓库。
五、使用 Docker Compose 部署 cloudflared
生产环境建议不要直接使用 docker run,而是使用 Docker Compose。这样便于管理、升级和迁移。
创建目录:
mkdir -p /opt/cloudflared
cd /opt/cloudflared
创建 .env 文件:
vim .env
写入以下内容:
TUNNEL_TOKEN=你的CloudflareTunnelToken
为了安全,修改权限:
chmod 600 .env
创建 docker-compose.yml:
vim docker-compose.yml
写入以下配置:
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
environment:
- TZ=Asia/Shanghai
networks:
- tunnel-net
networks:
tunnel-net:
name: tunnel-net
driver: bridge
启动服务:
docker compose up -d
查看容器状态:
docker ps
查看日志:
docker logs -f cloudflared
如果看到类似信息:
Registered tunnel connection
Connection established
说明隧道已经成功连接到 Cloudflare。
六、配置公网域名访问内网服务
部署好 cloudflared 后,还需要在 Cloudflare 控制台配置具体的访问规则。
进入:
Zero Trust → Networks → Tunnels → 选择你的 Tunnel → Public Hostname
点击:
Add a public hostname
假设你有一个本地服务运行在服务器的 8080 端口,例如:
http://127.0.0.1:8080
你希望通过以下域名访问:
app.example.com
则配置如下:
Subdomain: app
Domain: example.com
Path: 留空
Type: HTTP
URL: localhost:8080
保存后,Cloudflare 会自动创建对应 DNS 记录。几秒钟后,访问:
https://app.example.com
即可通过 Cloudflare Tunnel 访问到本地服务。
七、Docker 网络下的服务访问方式
在生产环境中,服务通常也是 Docker 容器。这里需要注意一个关键点:
cloudflared 容器里的 localhost,指的是 cloudflared 容器自身,而不是宿主机,也不是其他容器。
因此,如果你的业务服务运行在另一个 Docker 容器里,不建议写:
localhost:8080
而应让 cloudflared 和业务容器加入同一个 Docker 网络,通过容器名访问。
示例:部署一个 Nginx 测试服务
在 /opt/cloudflared/docker-compose.yml 中增加一个 nginx 服务:
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: unless-stopped
command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
environment:
- TZ=Asia/Shanghai
networks:
- tunnel-net
nginx:
image: nginx:alpine
container_name: nginx-demo
restart: unless-stopped
networks:
- tunnel-net
networks:
tunnel-net:
name: tunnel-net
driver: bridge
重新启动:
docker compose up -d
然后在 Cloudflare Tunnel 的 Public Hostname 中配置:
Subdomain: nginx
Domain: example.com
Type: HTTP
URL: nginx-demo:80
访问:
https://nginx.example.com
即可看到 Nginx 默认页面。
八、生产环境推荐配置方式
如果是单个服务,直接在 Cloudflare 控制台配置 Public Hostname 就足够了。但如果你有多个服务,建议统一规划域名,例如:
git.example.com → GitLab
ci.example.com → Jenkins
registry.example.com → Harbor
grafana.example.com → Grafana
portainer.example.com → Portainer
api.example.com → API 服务
对应配置示例:
| 公网域名 | 内部地址 | 类型 |
|---|---|---|
| git.example.com | http://gitlab:80 | HTTP |
| ci.example.com | http://jenkins:8080 | HTTP |
| grafana.example.com | http://grafana:3000 | HTTP |
| portainer.example.com | https://portainer:9443 | HTTPS |
| api.example.com | http://api-service:8080 | HTTP |
如果内部服务使用 HTTPS,但证书是自签名证书,可能需要在 Cloudflare Tunnel 的服务配置中关闭 TLS 校验,或者更推荐使用内部可信证书。
九、使用 Cloudflare Access 做访问控制
Cloudflare Tunnel 只是解决“如何访问”的问题,但生产环境还必须考虑“谁能访问”。
例如 Jenkins、Grafana、Portainer、内部后台等服务,不应该裸露给所有公网用户。此时推荐使用 Cloudflare Access 做身份认证。
进入:
Zero Trust → Access → Applications
点击:
Add an application
选择:
Self-hosted
配置应用域名,例如:
grafana.example.com
然后设置访问策略,例如:
允许邮箱后缀为 @example.com 的用户访问
或者只允许指定邮箱:
admin@example.com
devops@example.com
用户访问该域名时,会先跳转到 Cloudflare 登录验证页面,通过验证后才能进入内部服务。
生产环境强烈建议为以下服务开启 Access:
- Jenkins;
- Grafana;
- Prometheus;
- Portainer;
- GitLab 管理后台;
- 数据库 Web 管理工具;
- 任意内部管理系统。
十、安全加固建议
Cloudflare Tunnel 本身已经避免了服务器端口暴露,但生产环境仍需做好安全加固。
1. 不开放业务端口到公网
如果使用 Tunnel,就尽量不要再开放业务端口。例如 8080、3000、9000 等端口不应对公网开放。
可以使用防火墙限制:
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw enable
如果 SSH 也通过其他方式管理,可以进一步限制 SSH 来源 IP。
2. Token 不要写进 Compose 文件
不推荐这样写:
command: tunnel --token eyJhIjoi...
更推荐放到 .env 文件中,并设置权限:
chmod 600 .env
如果使用 Git 管理 Compose 文件,务必把 .env 加入 .gitignore。
3. 固定镜像版本
生产环境不建议长期使用:
image: cloudflare/cloudflared:latest
更推荐固定版本:
image: cloudflare/cloudflared:2025.1.0
升级时先在测试环境验证,再更新生产环境。
4. 开启 Cloudflare WAF
如果服务面向公众用户,建议开启 Cloudflare 的 WAF、安全规则、Bot Fight Mode 等能力。对于登录接口、API 接口,可根据需要设置速率限制。
5. 最小化服务暴露
不是所有服务都需要暴露公网。生产环境应遵循最小暴露原则:
- 只暴露必须访问的服务;
- 管理后台必须加认证;
- 调试接口禁止暴露;
- 数据库禁止通过 Tunnel 直接对公网开放,除非有非常严格的访问控制。
十一、日志查看与故障排查
1. 查看 cloudflared 日志
docker logs -f cloudflared
常见正常日志:
Registered tunnel connection
Connection established
Updated to new configuration
如果出现连接失败,通常需要检查服务器是否可以访问 Cloudflare。
2. 检查容器是否运行
docker ps
如果容器退出,查看退出原因:
docker logs cloudflared
也可以查看 Docker Compose 状态:
docker compose ps
3. 检查 Docker 网络
如果 Public Hostname 配置的是容器名,例如:
nginx-demo:80
但访问失败,需要确认 cloudflared 和 nginx-demo 是否在同一个网络:
docker network inspect tunnel-net
如果不在同一个网络,需要修改 Compose 配置。
4. 检查内部服务是否可访问
进入 cloudflared 容器测试:
docker exec -it cloudflared sh
部分 cloudflared 镜像可能没有 curl,可以临时用 busybox 或 alpine 测试:
docker run --rm -it --network tunnel-net alpine sh
apk add --no-cache curl
curl -I http://nginx-demo:80
如果这里都访问不了,说明问题在 Docker 网络或业务容器,而不是 Cloudflare。
5. DNS 未生效
如果访问域名提示无法解析,可以检查 DNS:
dig app.example.com
Cloudflare Tunnel 创建 Public Hostname 后,一般会自动创建 CNAME 记录。如果没有创建,可以到 DNS 页面确认。
6. 502 / 1033 错误
常见错误原因如下:
| 错误 | 可能原因 |
|---|---|
| 502 Bad Gateway | cloudflared 无法访问内部服务 |
| 1033 Argo Tunnel error | Tunnel 未连接或配置异常 |
| 404 | 服务本身路径错误或反向代理配置错误 |
| TLS handshake error | 内部 HTTPS 证书异常 |
| Connection refused | 目标端口未监听或容器网络不通 |
十二、生产环境实测部署结构
下面是一个比较典型的生产环境结构:
/opt
├── cloudflared
│ ├── docker-compose.yml
│ └── .env
├── grafana
│ └── docker-compose.yml
├── portainer
│ └── docker-compose.yml
└── app
└── docker-compose.yml
为了让多个项目都能被 cloudflared 访问,可以创建一个外部 Docker 网络:
docker network create tunnel-net
cloudflared 使用该网络:
networks:
tunnel-net:
external: true
业务服务也加入该网络:
services:
app:
image: your-app:latest
container_name: app
networks:
- default
- tunnel-net
networks:
tunnel-net:
external: true
这样 cloudflared 就可以通过容器名访问不同项目的服务:
http://app:8080
http://grafana:3000
http://portainer:9000
这种方式非常适合多服务部署,也便于后续扩展。
十三、完整生产版 docker-compose.yml 示例
下面给出一个更适合生产环境的示例:
services:
cloudflared:
image: cloudflare/cloudflared:2025.1.0
container_name: cloudflared
restart: unless-stopped
command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
environment:
- TZ=Asia/Shanghai
networks:
- tunnel-net
logging:
driver: json-file
options:
max-size: "20m"
max-file: "5"
security_opt:
- no-new-privileges:true
networks:
tunnel-net:
external: true
部署步骤:
docker network create tunnel-net
mkdir -p /opt/cloudflared
cd /opt/cloudflared
vim .env
vim docker-compose.yml
docker compose up -d
查看运行状态:
docker compose ps
docker logs -f cloudflared
十四、升级与回滚
1. 升级 cloudflared
如果使用固定版本,例如:
image: cloudflare/cloudflared:2025.1.0
升级时修改为新版本:
image: cloudflare/cloudflared:2025.2.0
然后执行:
docker compose pull
docker compose up -d
2. 回滚版本
如果新版本出现异常,改回旧版本:
image: cloudflare/cloudflared:2025.1.0
执行:
docker compose pull
docker compose up -d
由于 Tunnel 配置主要在 Cloudflare 云端,容器本地状态很少,因此回滚非常简单。
十五、备份建议
Cloudflare Tunnel 的核心配置主要包括:
- Tunnel Token;
- Docker Compose 文件;
- Cloudflare 控制台中的 Public Hostname 配置;
- Access 访问策略。
建议将以下内容备份:
/opt/cloudflared/docker-compose.yml
/opt/cloudflared/.env
但 .env 中包含 Token,应使用安全方式保存,例如:
- 密码管理器;
- 私有密钥库;
- GitHub Actions Secrets;
- GitLab CI Variables;
- 服务器加密备份。
不要把 Token 明文提交到公开 Git 仓库。
十六、常见问题
1. Cloudflare Tunnel 免费吗?
Cloudflare Tunnel 本身可以免费使用,适合个人和中小团队。但部分高级功能,例如更复杂的 Zero Trust 策略、日志分析、企业级支持等,可能需要付费版本。
2. 没有公网 IP 可以用吗?
可以。Cloudflare Tunnel 最大的优势之一就是不需要公网 IP。只要你的服务器可以主动访问公网,就可以建立隧道。
3. 是否还需要 Nginx?
不一定。如果只是简单暴露几个服务,可以直接通过 Cloudflare Tunnel 转发。如果内部有复杂路由、灰度发布、统一认证、负载均衡等需求,仍然可以在内部部署 Nginx、Traefik 或 Caddy。
4. 适合暴露数据库吗?
一般不建议。数据库属于高风险服务,不应该直接暴露公网。如果确实需要远程访问,建议使用 Cloudflare Access、WARP、私有网络策略,或者 VPN,并限制用户和设备。
5. Tunnel 断开怎么办?
Docker 配置了:
restart: unless-stopped
容器异常退出会自动重启。Cloudflare Tunnel 本身也会自动重连。生产环境可结合监控工具检测容器状态和域名可用性。
十七、总结
使用 Docker 部署 Cloudflare Tunnel,是一种非常适合生产环境的内网服务暴露方案。它不需要公网 IP,不需要开放入站端口,也可以自动接入 Cloudflare 的 HTTPS、CDN、WAF 和 Access 认证能力。
生产环境推荐实践如下:
- 使用 Docker Compose 管理 cloudflared;
- 使用
.env保存 Tunnel Token; - 不将 Token 提交到代码仓库;
- 使用独立 Docker 网络连接业务容器;
- 公网域名统一规划;
- 管理后台必须启用 Cloudflare Access;
- 固定 cloudflared 镜像版本;
- 配置日志轮转和自动重启;
- 尽量关闭服务器不必要的入站端口。
如果你的服务部署在家用宽带、公司内网、NAS、Docker 主机或没有公网 IP 的云服务器上,Cloudflare Tunnel 都是一个非常值得尝试的方案。相比传统公网端口暴露方式,它在安全性、易用性和可维护性上都有明显优势。生产环境实测下来,只要网络稳定、Docker 配置规范,Cloudflare Tunnel 可以长期稳定运行,并显著降低服务暴露和运维管理成本。