扩展课 E2

Cloudflare Tunnel:不开端口也能上线

扩展课。第 24 课的做法要开放 80/443 端口、维护证书。Cloudflare Tunnel 换了个思路:服务器主动连到 Cloudflare,外网连接不进你的服务器,只能经过这条隧道。

本课目标

  • 理解 Cloudflare Tunnel 和第 24 课做法的区别。
  • 创建一条隧道,用 Docker 运行 cloudflared。
  • 把访问切换到隧道,关闭 80/443 端口。

先懂概念:从“开门迎客”到“主动打电话”

第 24 课:Caddy + 源站证书Cloudflare Tunnel
连接方向Cloudflare 连到你服务器的 443 端口你的服务器主动连到 Cloudflare
防火墙要开放 80、443只开 22,甚至可以都不开
证书自己申请、放到服务器上不需要
依赖只依赖标准的 HTTPS依赖 Cloudflare 的隧道服务

隧道就像服务器主动给 Cloudflare 打了一个电话并一直不挂:访问者的请求通过这通电话送到服务器,而服务器本身不接任何来电。

跟着做

1. 创建隧道

  1. 登录 Cloudflare,进入 Zero Trust 控制台(第一次进入需要选择免费套餐,可能要求绑定付款方式但不会扣费)。
  2. 打开 Networks → Tunnels,点 Create a tunnel,类型选 Cloudflared,名字填 ai-server。
  3. 安装方式选 Docker。页面会给出一条 docker run ... --token eyJ... 命令,只复制 --token 后面那一长串令牌。

Cloudflare 的界面会调整,菜单名称可能略有不同。

2. 配置公开地址

在隧道的 Public Hostname(公开主机名)里添加一条:

字段填写
Subdomainreport
Domainexample.com
Service TypeHTTP
URLapp:3000

如果提示 report 已经有一条 DNS 记录,先到 DNS 页面删除第 23 课添加的那条 A 记录,再回来保存。隧道会自动创建新的记录。

3. 在服务器上运行 cloudflared

令牌和 Key 一样敏感,单独放在一个文件里:

cd ~/apps/weekly-report
vim tunnel.env

写入一行(换成你的令牌),保存:

TUNNEL_TOKEN=eyJ你的令牌
chmod 600 tunnel.env

用下面的内容替换 compose.yaml(版本号和你当前用的保持一致):

cat > compose.yaml <<'EOF'
services:
  app:
    image: 你的用户名/weekly-report:v4
    env_file: .env
    restart: unless-stopped
    ports:
      - "127.0.0.1:3000:3000"

  tunnel:
    image: cloudflare/cloudflared:latest
    command: tunnel --no-autoupdate run
    env_file: tunnel.env
    restart: unless-stopped
EOF
docker compose up -d --remove-orphans
docker compose ps
docker compose logs tunnel

--remove-orphans 会停掉不再写在配置里的 Caddy。日志里出现 Registered tunnel connection 就说明隧道连上了,Cloudflare 控制台里隧道的状态也会变成 Healthy。

4. 关闭 80/443

访问 https://report.example.com,确认能正常打开并要求登录。然后到云服务商的防火墙里删除 TCP 80 和 443 的规则,只保留 22。

再访问一次,依然正常,因为流量走的是隧道。

你应该看到

  • 网站照常可用,有锁形图标,需要登录。
  • 防火墙只开放 22 端口。
  • sudo ss -tlnp 里没有程序在监听 80、443。

常见问题

访问显示 502 Bad Gateway? 隧道连上了,但找不到周报助手。检查 Public Hostname 的 URL 是否为 app:3000,app 服务是否在运行。

隧道状态是 Down? 看 docker compose logs tunnel,多半是令牌复制不完整,或者 tunnel.env 里等号两边多了空格。

想换回 Caddy 的方式? 按这个顺序:

  1. 按第 24 课的内容重写 compose.yaml,app 的镜像版本保持你现在用的版本,不要退回 v2。
  2. 运行 docker compose up -d --remove-orphans,停掉 tunnel、启动 caddy,用 docker compose ps 确认。
  3. 在防火墙里重新开放 TCP 80 和 443。
  4. 在 Cloudflare 里删除隧道的 Public Hostname 和它创建的 DNS 记录,再按第 23 课添加 A 记录(橙色云朵)。
  5. 访问网站确认正常。

自检

  • 我能说出隧道和开放端口两种方式的区别。
  • 隧道状态是 Healthy,网站通过隧道访问。
  • 防火墙已经关闭 80 和 443。