在 Docker 容器中发现 Apple TV:mDNS、多播与 Avahi

Home Assistant(HA)在物理机上可以自动发现 Apple TV(ATV),
但当 HA 跑在 Docker 容器里、使用 bridge 网络时,经常会出现下面这些现象:

  • HA 发现不了 ATV

  • 容器里 atvremote scan 看不到 ATV,宿主机可以

  • 容器里和宿主机里 avahi-browse 都能看到 ATV

下面是我排查这个问题的过程。


Avahi 能看到,但 HA 看不到

按照 HA 的推荐配置,把宿主机的 Avahi socket 挂进容器:

volumes: - /var/run/avahi-daemon:/var/run/avahi-daemon - /run/dbus:/run/dbus

在容器里跑:

avahi-browse -a

能看到 Apple TV,但 HA 依然发现不了。

查了一下才意识到一点:
HA 并不通过 Avahi 发现 Apple TV。

HA 用的是 pyatvpyatv 用的是 zeroconf,完全不走 Avahi。
avahi-browse 能看到,是因为它通过 socket 连的是宿主机上的 Avahi。

这意味着:
HA 是在容器里自己直接发 mDNS 包。


mDNS 在跑,但容器里收不到

mDNS 用的是 UDP 5353,多播地址 224.0.0.251。

在局域网里随便抓:

tcpdump -i any udp port 5353

能看到 Apple TV 的广播。

但在容器里:

  • 抓不到任何 5353 流量

  • 在容器里跑 atvremote scan,查询包看起来“发了”,但宿主机物理网卡上完全看不到

基本可以确认两件事:

  1. Docker bridge 网络 收不到 外部 mDNS 广播

  2. Docker bridge 网络 发不出 mDNS 多播查询


主动扫描 vs 被动发现

atvremote scan 是主动扫描:
发 mDNS 查询,等设备回应 —— 在 bridge 网络里直接死掉。

但 mDNS 还有一条路:
设备会周期性广播 announce 包,只要你在监听,就能“被动发现”。

这也解释了一个现象:
即使容器里发不出查询,只要能收到广播,理论上还是能发现 ATV。


Avahi reflector

Avahi 有个选项叫 reflector,可以把一个接口上的 mDNS 包转发到另一个接口。

宿主机开启:

# /etc/avahi/avahi-daemon.conf [reflector] enable-reflector=yes
sudo systemctl restart avahi-daemon

开启之后再抓包,可以看到:

  • Apple TV 的 mDNS 广播被转进了 Docker bridge

  • 容器里终于能收到 5353 流量了


atvremote 还是不行,但 HA 行了

此时再跑:

atvremote scan

还是啥也没有。

抓包确认后发现原因没变:
容器里的主动 mDNS 查询还是发不出去。

但这时 HA 却已经能发现 Apple TV 了。

推测原因很简单:

  • HA 持续监听 mDNS

  • Apple TV 定期发广播

  • 广播被 Avahi reflector 转进容器

  • HA 通过被动发现拿到了设备信息

至于 atvremote scan,它依赖主动扫描,所以依然失败。


总结

  • Docker bridge 网络不支持 multicast 发送

  • 挂 Avahi socket 只能救 Avahi 客户端,救不了 HA

  • 开启宿主机 Avahi reflector,可以把 mDNS 广播转进容器

  • 主动扫描仍然不工作,但被动发现足够 HA 使用

评论

此博客中的热门博文

《比特币:一种点对点电子货币系统》详解