使用 Cloudflare Tunnel 和 Traefik 访问部署的 Docker 服务
✨ 文章记录了个人自建服务的“古法运维”方案:利用 Traefik 作为本地容器的反向代理和动态服务发现,结合 Cloudflare Tunnel 实现内网穿透和安全访问。
自部署的 Docker 服务用起来很舒心,但数量多了后,端口映射管理,防火墙端口规则,DNS 配置等也很麻烦。
Cloudflare Tunnel 服务,可以在不暴露服务器端口的情况下建立一条服务器连接到 Cloudflare 的隧道,再搭配上 Traefik 来实现各容器服务的服务发现和反向代理,从而可以在公网访问自部署的 Docker 服务。核心逻辑如下:
- 流量入口:用户访问域名 -> Cloudflare 边缘节点 -> Cloudflare Tunnel。
- 穿透层:
cloudflared容器通过隧道将流量拉取到本地 Docker 网络。 - 分发层:流量到达 Traefik,Traefik 根据 HTTP Host 规则(Labels)识别目标容器。
- 应用层:流量最终到达具体的业务容器(如
whoami)。
优势:无需公网 IP,无需在防火墙开启 80/443 端口,自带 SSL 证书管理。
流量路径示意
[ 互联网 (Internet) ]
||
\/
+---------------------------------------+
| Cloudflare 全球网络 |
| (DNS 解析 & 安全防护 & CDN) |
+---------------------------------------+
||
<== Cloudflare Tunnel ==> (加密隧道)
||
+--------------||------------------------+
| 宿主机 (Host) |
| || |
| +----------\/----------+ |
| | cloudflared 容器 | |
| +----------||----------+ |
| || (Docker Network: traefik)
| +----------\/----------+ |
| | Traefik 容器 | |
| | (反向代理/服务发现) | |
| +----------||----------+ |
| || (路由分发) |
| +----------\/----------+ |
| | 业务容器 (如 whoami) | |
| +----------------------+ |
+---------------------------------------+这套方案用了很久了,但早前都是手动配置了就开始用了。这次服务器被迫重装顺便记录下这种“古法运维”。
现在 LLM / AI Agent 已经很强了,这些内容都能生成或者直接给你配置完成。
这里就简单贴上配置,不做详细操作说明,毕竟 AI 大概干得更好。
Docker 环境配置略去不表。
Traefik 配置
traefik 容器网络配置脚本
network-setup.sh
#!/bin/bash
NETWORK_NAME=traefik
if [ -z $(docker network ls --filter name=^${NETWORK_NAME}$ --format="{{ .Name }}") ] ; then
# 创建 Docker 网络
output=$(docker network create ${NETWORK_NAME});
# 检查输出是否为 64 位长度的字符串
if [[ ${#output} -eq 64 ]]; then
echo "create docker network for traefik ok"
else
echo "Network creation failed or output is not 64 characters long."
fi
fi执行👆脚本创建一个名为 Traefik 的容器网络。
traefik 容器配置
compose.yaml
services:
traefik:
container_name: traefik
image: traefik
restart: always
command:
- "--global.sendanonymoususage=false"
- "--global.checknewversion=false"
- "--entrypoints.web.address=:80"
- "--api=true"
- "--api.dashboard=true"
- "--ping=true"
- "--log.level=INFO"
- "--log.format=common"
- "--accesslog=false"
- "--providers.docker=true"
- "--providers.docker.watch=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
- "--providers.docker.useBindPortIP=false"
- "--providers.docker.network=traefik"
ports:
- "80:80" # 如本机/局域网也需要访问,则映射;仅走 Tunnel 可不映射
environment:
- TZ=Asia/Shanghai
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.dashboard.rule=Host(`traefik.okhk.net`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.entrypoints=web"
# Traefik 管理面板安全认证配置
- "traefik.http.routers.dashboard.middlewares=auth"
# echo $(htpasswd -nb admin 你的密码) | sed -e s/\\$/\\$\\$/g
- "traefik.http.middlewares.auth.basicauth.users=admin:$$apr1$$..."
- "traefik.http.middlewares.gzip.compress=true"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
extra_hosts:
- "update.traefik.io:127.0.0.1"
- "collect.traefik.io:127.0.0.1"
- "stats.g.doubleclick.net:127.0.0.1"
healthcheck:
test: ["CMD-SHELL", "wget -q --spider --proxy off localhost:8080/ping || exit 1"]
interval: 3s
retries: 10
logging:
driver: "json-file"
options:
max-size: "5m"
networks:
- traefik
networks:
traefik:
external: true自部署服务配置
以 whoami 为例
whoami 容器配置
compose.yaml
services:
whoami:
image: traefik/whoami
container_name: whoami
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.okhk.net`)"
- "traefik.http.routers.whoami.entrypoints=web"
networks:
- traefik
networks:
traefik:
external: trueTraefik 可通过 lables 发现并代理 whoami 服务。
Cloudflare Tunnel 配置
cloudflared 容器配置
compose.yaml
services:
cloudflared:
image: cloudflare/cloudflared:latest
container_name: cloudflared
restart: always
command: tunnel --no-autoupdate run --token $TUNNEL_TOKEN
environment:
- TZ=Asia/Shanghai
env_file:
- .env
healthcheck:
test: ["CMD", "cloudflared", "tunnel", "--metrics", "localhost:20241", "ready"]
interval: 10s
timeout: 10s
retries: 5
start_period: 20s
networks:
- traefik
networks:
traefik:
external: true.env
TUNNEL_TOKEN=TUNNEL_TOKEN 获取
登录 Cloudflare Zero Trust 控制台 → Networks -> Tunnels,点击 Create a Tunnel。
https://one.dash.cloudflare.com/可参考 Cloudflare 官网文档:
https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/get-started/create-remote-tunnel/
复制命令中的 --token 后面的长字符串,将其作为环境变量填入上述文件中,启动 cloudflared 容器,建立 Cloudflare Tunnel 链接。
Cloudflare Tunnel 路由配置
| 公网域名 (Public Hostname) | 服务协议 | 内部服务 URL (Service) | 类型说明 |
|---|---|---|---|
*.okhk.net | HTTP | http://traefik:80 | 泛域名转发 (推荐) |
whoami.okhk.net | HTTP | http://traefik:80 | 精确匹配 |
如果在 Cloudflare 中配置了泛域名(如 *.okhk.net)指向 http://traefik:80,那么之后新增 Docker 服务时,只需在 Docker Labels 里写好域名,无需再回 Cloudflare 页面操作。
如果使用精确域名,则后续每次新增 Docker 服务都需要手动配置域名转发到 http://traefik 。
(当然也可以借用一些工具实现根据 Traefik 服务发现情况自动调用 Cloudflare API 添加对应的 tunnel 路由规则,但出于某些原因(懒得整了)就没再弄了
