Cloudflare Workers 指定区域运行

 

UptimeFlare 是我之前写的一个开源项目, 用途类似 uptime-kuma 或 UptimeRobot, 用于监控网站/服务器的在线状态, 展示状态页. 区别是 UptimeFlare 是完全 serverless 的, 可以在 Cloudflare Workers 上部署.

同时, UptimeFlare 还有个自建方案无法比拟的优势: 它能利用 Cloudflare 在全球广泛部署的边缘网络, 让用户自定义发起探测的地点. 也就是能类似拨测, 测试全球多个不同地区的连通性和延迟情况.

本篇文章就是主要介绍我是如何做到强制一个 Worker 在特定区域运行的, 这个方法并没有被官方文档记载, 也没有其他详尽的说明, 是我根据网上的一些讨论实验和总结出来的, 记录于此.

UptimeFlare 项目中的英文 Wiki 详细介绍了操作流程, 故此处更侧重详细的原理和思路.

Anycast

首先我们知道, Cloudflare 的一个主要特色就是使用 Anycast 的 CDN.
Cloudflare 会将它拥有的 IP 段在多个*边缘节点(Edge)*上宣告, 客户端访问 Cloudflare 的流量会被 ISP 就近路由到靠近用户的一个边缘节点上, 再由边缘节点进行回源, 从而达到最大可能降低访问延迟的效果. (部分海外地区能做到惊人的 1ms ping 值.)

类似下图中, 假设 www.example.com 的源站为 5.6.7.8, 站长设置了一个 Proxied 的 A 记录, 指向5.6.7.8.
假设有此时有 US 和 UK 的两个用户想要访问 www.example.com, 他们的 DNS 请求不会直接得到源站的地址, 而会得到一个 Cloudflare 边缘服务器的地址, 例如 1.2.3.4.
此时会 Client1 就近访问位于 US 的 Edge1, Client2 就近访问位于 UK 的 Edge2, Edge 再自行回源获取内容并缓存, 充当 CDN.
需要注意的是, 与一些通过 DNS 分区解析实现负载均衡的 CDN 不同, 此时 Edge1 和 Edge2 可能共享同一个 IP 地址, 却对应了多台不同的服务器, 这种多台服务器共享同一个 IP 的行为就是 Anycast.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 www.example.com A RECORD 5.6.7.8 Proxied                       


+---------------------------+
| |
| Cloudflare Network |
| |
+-----------+ | +-----------------+ |
|Client1(US)+---+---->Edge1(US,1.2.3.4)+----+----------+
+-----------+ | +-----------------+ | |
| | |
| | |
| | |
| | |
+-----------+ | +-----------------+ | +-------v-------+
|Client2(UK)+---+---->Edge2(UK,1.2.3.4)+----+-->Origin(5.6.7.8)|
+-----------+ | +-----------------+ | +---------------+
| |
| |
| |
+---------------------------+

Workers

Cloudflare Workers 就是由用户编写并上传, 可以处理自定义逻辑, 运行在上述边缘(Edge)服务器上的一小段代码, 它使用了 V8 Javascript 引擎, 且不包含 NodeJS 那套 API.

Workers 适合处理一些比较简单的后端逻辑或写一些小项目, 整体比自己部署的服务器在延迟和维护开销方面都会优秀很多, 且赛博菩萨 Cloudflare 也给了慷慨的免费额度(10w请求/天). 唯一的缺点可能是开发的代码会和 Cloudflare 平台强绑定, 暂时没有其他较大的平台提供类似的服务.

指定 Workers 运行位置

介绍了一堆背景, 终于到了本文的正题.

在上述情况下, 如果位于 US 的用户访问 www.example.com, 由于 IP 的具体路由是运营商处理的, 用户无法干涉, 所以请求永远会被最近的位于 US 的 Edge 节点处理, 用户无法指定除 US 外的 Edge 来处理我们的请求.

这在大部分情况下不会导致问题, 但有些特殊情况下, 我们希望能指定我们请求发出的位置, 比如开头提到的: UptimeFlare 在不同地区监测网站延迟和连通性.

Cloudflare Community 中的帖子 [1] [2] 中, 有用户发现了一种解决方法:
每个 Edge 节点, 除了 Anycast IP, 还有一个自己专属的内部 IP, 这个 IP 属于AS13335, 且落在 8.0.0.0/8, 且不被用于 Anycast, 用户可以用 nmap 的 PING 扫描找到一些有效的内部 IP. (假设此时 Edge1 的这个内部 IP 为 8.1.2.3, Edge2 的内部 IP 为 8.4.5.6.)
这些内部 IP 不直接向外提供服务, 虽然能 Ping 到, 但并没有开放 http/https 端口, 所以我们并不能直接用这些 IP 访问.
为此, 我们设置一个 A 记录, 例如 uk.example.com 指向 8.4.5.6.
但此时, 我们还是不能直接使用 uk.example.com 访问到位于 UK 的 Edge, 因为我们的请求仍然会发给 1.2.3.4, 就近的 Edge1 如果发现这个域名绑定了 Worker, 会优先本地处理, 不会进一步访问目标 IP, A 记录的 IP 被忽略了.

最终的解决方法是: 我们在 Worker 中编写一段代码, 从就近的 Edge1 使用 fetch 访问 uk.example.com, 此时我们的请求从 Cloudflare 网络内部发出, 会真正到达 8.4.5.6 的 Edge2. 实际的访问顺序为 Client1(US) => Edge1(US,1.2.3.4) => Edge2(UK,8.4.5.6)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 www.example.com A RECORD 5.6.7.8 Proxied                       
uk.exmaple.com A RECORD 8.4.5.6 Proxied

+---------------------------+
| |
| Cloudflare Network |
| |
+-----------+ | +-----------------+ |
|Client1(US)+---+---->Edge1(US,1.2.3.4)+----+----------+
+-----------+ | +---+-------------+ | |
| |Also:8.1.2.3 | |
| | | |
| | | |
| | | |
+-----------+ | +---v-------------+ | +-------v-------+
|Client2(UK)+---+---->Edge2(UK,1.2.3.4)+----+-->Origin(5.6.7.8)|
+-----------+ | +-----------------+ | +---------------+
| Also:8.4.5.6 |
| |
| |
+---------------------------+

UptimeFlare 中的 CRON 任务会在随机一个较为空闲的地区的 Edge 被执行, 但这不影响我们使用上述的方法将其重定向到需要的 Edge.

本文采用 CC BY-NC-SA 4.0 许可协议发布.

作者: lyc8503, 文章链接: https://blog.lyc8503.net/post/cloudflare-worker-region/
如果本文给你带来了帮助或让你觉得有趣, 可以考虑赞助我¬_¬