本文内容较新 · 27天前更新
最后更新: 2026年02月02日
预计阅读时间: 8.9 分钟
2226 字 250 字/分

技术笔记:解决 Docker 容器间“回环访问”导致的 cURL 28 超时

1. 问题背景

在群晖(Synology)NAS 上,通过 Docker 同时部署了 FreshRSS(RSS 阅读器)和 RSSHub(订阅源生成)。

现象:FreshRSS 更新订阅时报错 cURL error 28: Operation timed out

矛盾点:在群晖 SSH 宿主机环境下 curl 订阅地址秒回结果,但在 FreshRSS 容器内部访问却超时。

网络拓扑

外部访问域名:https://rsshub.xxx.xxx:8083(通过 NPM 反向代理)。

内部局域网 IP:192.168.0.101:9200(超时)。

Docker 默认网桥:172.17.0.1(有效)。

2. 核心原因分析

为什么会超时?

回环 NAT 限制:许多路由器和 Docker 默认网络环境不支持“回环访问”(NAT Loopback)。当容器尝试通过公网域名或宿主机局域网 IP 访问同一台机器上的另一个容器时,数据包发往网关后往往无法正确折返。

代理干扰:FreshRSS 容器若配置了环境变量 HTTP_PROXY,请求可能会被发往代理服务器,而代理服务器可能无法解析你的内网域名或内网 IP。

容器环境缺乏工具:Alpine 或精简版的 PHP 镜像通常不带 curl,导致通过 docker exec 调试困难。

3. 解决方案:本地 DNS 劫持(Extra Hosts)

为了不修改成百上千个订阅链接,最优雅的方法是让 FreshRSS 容器在发起对 rsshub.todtom.tech 的请求时,直接在容器内部将其指向 Docker 宿主机的网桥 IP。

为什么指向 172.17.0.1 而不用管端口?

这是最容易产生误解的地方。Hosts 文件只负责解析域名到 IP,不负责转发端口。

你在浏览器或 FreshRSS 里填写的地址是 https://rsshub.xxx.xxx:8083

当你在 Hosts 里设置 172.17.0.1 rsshub.xxx.xxx 后,FreshRSS 实际上是在访问 https://172.17.0.1:8083

关键点:只要你的 NPM(Nginx Proxy Manager)也将 8083 端口映射到了宿主机上,这个请求就会精准地命中 NPM,再由 NPM 转发给 RSSHub。

4. 实施步骤(Docker Compose)

修改 docker-compose.yml

为了保证容器更新或重启后配置不丢失,不要手动修改 /etc/hosts,而应在配置文件中加入 extra_hosts

YAML

version: '3'
services:
  freshrss:
    image: freshrss/freshrss:latest
    container_name: freshrss-new
    # ... 其他配置 ...
    extra_hosts:
      - "rsshub.todtom.tech:172.17.0.1"
    environment:
      - TZ=Asia/Shanghai
      # 如果代理导致了干扰,确保这些变量是空的或者正确配置了 NO_PROXY
      - HTTP_PROXY=http://your-proxy:port
      - HTTPS_PROXY=http://your-proxy:port
      - NO_PROXY=localhost,127.0.0.1,172.17.0.1,rsshub.xxx.xxx

更新容器命令

docker-compose.yml 文件所在目录下执行:

Bash

# 1. 拉取最新镜像(可选)
docker-compose pull

# 2. 重新启动并应用配置(后台运行)
docker-compose up -d

注:该命令会自动检测配置更改,仅重建受影响的容器。

5. 进阶疑问解答

关于 FreshRSS 代理失效的问题

你发现 env 中看到了代理,但 FreshRSS 界面上似乎没生效。

FreshRSS 机制:FreshRSS 作为一个 PHP 应用,有时会优先使用其 Web 管理界面 -> 设置 -> 管理 -> 代理 中的配置,而不是系统环境变量。

TRUSTED\_PROXY:这个环境变量是用来告诉 FreshRSS 哪些 IP 是反向代理(如 NPM),以便正确获取客户端的真实 IP,它不负责 FreshRSS 访问外部时的请求代理。

为什么第一次更新还是超时,第二次才成功?

这种情况通常由以下原因引起:

DNS 缓存:PHP-FPM 或 cURL 进程可能缓存了旧的解析结果,第一次尝试失败后触发了缓存刷新。

RSSHub 唤醒/冷启动:如果 RSSHub 开启了某些缓存机制,或者目标网站(如抖音)响应较慢,第一次请求可能在建立连接或抓取数据时刚好超过了 FreshRSS 默认的 20 秒限制,而第二次请求因为有了缓存,速度变快从而通过。

6. 总结建议

容器间通信:优先使用 extra_hosts 劫持域名到网桥 IP,保持订阅链接的一致性。

配置持久化:始终通过 docker-compose.yml 修改配置,避免 docker exec 临时修改在更新后丢失。

代理避让:在 NO_PROXY 中加入内网域名,防止内网流量“绕远路”导致超时。