UniFi 控制器设备采用丢失问题排查

0

家里无线网络组网采用了 AC+AP 的组网方式,部署了两个 UBNT 的 UniFi U6 Lite AP 来组网,由于预算有限(主要还是穷)没有购买硬件控制器,但是 UniFi 的控制器软件(后简称为AC)可以安装在各种系统中,甚至还能安装在树莓派上,于是就使用 Docker 把 AC 安装在了刷了 Armbian 的玩客云上,Docker 镜像使用的是 linuxserver/unifi-controller

按照镜像文档介绍安装配置都比较简单,但是使用一段时间后发现,AC 经常会出现已采用的设备丢失的问题,只要 AC 或 AP 一重启,就会丢失,于是乎只能一次次的通过 ssh 登录 AP 手动执行采用的命令才能重新让 AC 再次采用 AP。

set-inform http://$AC_IP:8080/inform

$AC_IP 为 AC 运行系统的局域网 IP。

发现这个问题后,就开始了头痛的折腾之路,也正是因为通过各种瞎折腾更加熟悉了 UniFi 的 AC 和 AP 的工作原理。

如果你也遇到了同样的问题,又想快速解决,可直接跳转到本文的”固定 AC inform 接口的 IP“。

简单粗暴的自动化采用的尝试

既然 AC 重启后就会采用丢失,那么是不是可以定时去采用呢?UniFi 的 AP 实际上是 Linux 系统(和 OpenWrt 类似),并且系统也内置了 Crontab 程序,可以通过 Crontab 来设置自动化任务来自动采用。于是在 AP 中使用 Crontab 配置了上面的定时任务,比如每小时自动采用一次。同时还可以把采用的命令写入到启动项中,AP 重启后就会执行。

把采用的命令 set-inform 配置到 Crontab 和 /etc/rc.local 中后,重启尝试效果,在 AC 中仍然无法自动采用,并且 Crontab 和 /etc/rc.local 命令在重启后都被清除了。看来 Unifi 的 AP 无法直接对系统进行修改,重启后就会被还原,只有通过 AC 来对 AP 进行常规的配置,应该是通过这种设计来保障 AP 运行的稳定性。后来在官方论坛上也确认了无法保存系统配置。

既然 AP 的系统不能动,但是在 AP 的系统中可以手动执行 set-infom 命令,那么是不是可以在 AC 的系统中通过 ssh 配置免登录,然后远程使用 ssh 来执行自动化命令呢?一番尝试后,虽能通过 AC 配置好免登录,但是在执行命令的时候就失败了,但是执行常规的命令又是没问题的。

# 执行失败
ssh $USER@$AP_IP "set-inform http://$AC_IP:8080/inform" 
# 执行返回正常
ssh $USER@$AP_IP "ls" 

至此,自动化采用的尝试陷入了困境,看来 Unifi 的 AP 对系统有严格的限制,不要尝试去瞎折腾

无论是 Docker 镜像的官网还是 UniFi 的论坛都没有相关的问题介绍,升级了 AC 的版本和 AP 的固件,都是老样子,打算放弃了,因为看介绍 AC 不一定要经常在线,只有在管理时启动 AC,采用到 AP 就能正常使用,大不了就麻烦点。

但是又有些不死心,抱着死马当活马医的心态,在 AP 的系统里瞎摸索,找到了 AP 运行的日志,于是终于找到了问题所在。

固定 AC inform 接口的 IP

/var/log/messages 里找到了 AP 运行的日志,发现了一些端倪,就是 set-inform 失败的日志中有一部分 IP 并不是我每次手动执行的 IP,而是 172.17.0.2,而 AC 系统的局域网 IP 是 192 开头的,这个 172 的 IP 是 Docker 容器的虚拟 IP。通过重启 AC 的方式进一步观察出错时的问题发现了问题所在。

异常日志如下。

ace_reporter.reporter_fail(): Unreachable (http://192.168.0.7:8080/inform)
daemon.info mcad: mcad[2323]: ace_wss.ace_wss_reconnect(): url changed form 'http://192.168.0.7:8080/inform' to 'http://172.17.0.2:8080/inform'
daemon.err mcad: mcad[2323]: ace_reporter_trsp_curl.check_multi_info(): inform failed with curl code 28
daemon.err mcad: mcad[2323]: ace_reporter.reporter_fail(): Timeout (http://172.17.0.2:8080/inform)
daemon.err mcad: mcad[2323]: ace_reporter.reporter_fail(): initial contact failed #1, url=http://172.17.0.2:8080/inform, rc=4

现在来分析一下上面的日志。

AC 刚刚重启时会有大量的 Unreachable 错误,说明 AP 在手动执行了 set-inform 的命令后会保存成功采用的 IP,但是由于重启,有很多连接失败的错误。

ace_reporter.reporter_fail(): Unreachable (http://192.168.0.7:8080/inform)

关键的错误是下面这一行,当 AC 重启好后,AP 重新连接时,AC 宿主系统的 IP 变成了容器的 IP,推测是 http://192.168.0.7:8080/inform 接口返回了错误的 IP 地址。

daemon.info mcad: mcad[2323]: ace_wss.ace_wss_reconnect(): url changed form 'http://192.168.0.7:8080/inform' to 'http://172.17.0.2:8080/inform'

后面的日志就是 AP 一直尝试以错误的 IP 去连接出现的错误。

既然 AC 运行时获取的局域网 IP 可能存在问题,那么有什么办法可以解决呢?

Docker 镜像官方推荐的容器启动方式默认采用的是 bridge,该模式会给容器创建一个新的虚拟 IP,一般就是 172 开头的,也可以直接复用宿主机的网络,于是将 Docker 的启动参数进行了修改,指定网络为复用宿主模式。

docker run -d \
  --name=unifi-ac \
  --network=host \
  -e PUID=1000 \
  -e PGID=1000 \
  # host 模式下不用指定 -p 参数,因为默认就会暴露
  # -p 3478:3478/udp \
  # -p 10001:10001/udp \
  # -p 8080:8080 \
  # -p 8443:8443 \
  # -p 1900:1900/udp `#optional` \
  # -p 8843:8843 `#optional` \
  # -p 8880:8880 `#optional` \
  # -p 6789:6789 `#optional` \
  # -p 5514:5514/udp `#optional` \
  -v /home/xxx/unifi:/config \
  --restart unless-stopped \
  linuxserver/unifi-controller

修改了容器的网络后,重新创建了 AC 的容器就能正常的自动采用 AP 了,有点曲折,但总算把问题解决了。推测这个问题应该是命中了 Docker 镜像的 Bug,于是给 Docker 镜像的官方提了一个 issue。

发文完并提完 issue 的第二天,收到了官方的回复,这其实不是一个 Bug,而是一个容易忽略的设置问题(主要还是英文文档的阅读不够仔细),需要在 AC 中主动配置宿主机的 IP,为啥不在容器启动的时候提供配置参数呢,太容易踩坑了 🙁

empty bakfile

本次的问题排查,更让我意识到在面对正儿八经的系统时,还是先想办法去查日志,通过日志定位问题才是“正道”,其他试图绕过问题的投机取巧的作法只有在通过常规流程解决不了时再考虑。