反向迁移——从Unifi Cloud Key到群晖Docker运行Unifi控制器

Unifi Cloud Key是什么?

从直觉上来说,Unifi全家桶的结构类似于AC+AP,所以Cloud Key一定就是AC了——如果你这么想,并且想当然的认为既然是AC,那一定是网络里必须的组件,于是咬咬牙花了四位数的价格买了Cloud Key,那你可就踏入陷阱了(没错,这里就有一个这么想当然的傻子)——虽然买得起全屋unifi的土豪可能也不是很在乎这点钱,不过没必要花的钱也确实是真的没必要花。

实际上Unifi的控制器的主要作用就是向局域网内的所有Unifi设备下发配置以及监控网络中的情况并在特定的警报情况发生时进行信息的汇报,在配置好以后,AP是完全可以自己独立运行的,此后控制器是否在线都不会影响网络的正常使用,不论是新设备接入网络、设备在节点之间无缝漫游,都不会有任何影响——事实上,在我接下来的整个迁移过程里,家里的所有设备都一直保持着正常的网络使用,没有任何感知——唯一的例外是,如果你设置了来宾访问网络,并且使用了网页密码验证的话,那你必须要让控制器保持在线,因为这个认证是由控制器来进行的,如果控制器不在线,那么来宾访问网络的网页密码验证将会变为不可用。

Unifi控制器本质上其实只是一套软件程序,而Cloud key实际就是运行着一套Debian8的系统的主机,然后在上面运行着控制器的软件。除了linux,这套软件也可以在安装了Java环境的Windows系统中运行,所以就算你没有购买Cloud Key,也可以在家里的电脑上运行控制器并对网络里的Unifi设备进行配置。如上一段所说,当配置完成并下发给各个设备后,整个网络环境就已经可以脱离控制器运行了,此时你已经可以关闭甚至卸载控制器(当然,卸载之前一定要记得备份配置)。

也就是说,除非你有特别的折腾的欲望,甚至人在外面也想要随时掌控家里的网络情况,而你自己的电脑主机又不可能7*24小时开机提供服务,你才会需要考虑使用一台始终在线的Cloud Key主机来运行并维持一个一直在线的控制器,但是仅仅只是一般家庭的需求的话,根本用不到这么大张旗鼓的配置——尤其是现在Unifi居然把初代控制器下架了,京东一搜直接就是Gen2和Gen2Plus拍在脸上,在我写这篇文章的时候,这俩的标价分别是1699和1780(这还是双十二你敢信?),属实是抢钱了。实际上1代的性能给一般家庭用都已经是大炮打蚊子了,2代这8核CPU2G内存(plus版还标配1T存储)的豪华配置直接写明了可以带50个节点,摆明了就是给大企业用的,自己家里买来这种东西简直就是浪费电凭空为弱电箱制造散热压力。

你的下一台Cloud Key,何必是Cloud Key?

既然上面解释了,控制器的原理不过只是一套一直在运行着的软件而已。那么我们当然可以用其他7*24小时运行的设备代替Cloud Key来做这件事。家里面满足这个条件的设备一般有2个,一个是软路由,一个就是NAS——当然你要用树莓派我也不会拦着就是了——显然性能过剩的NAS是更适合做这种闲差的。

一切能用Docker解决的问题那都不是问题,而现在我们遇到的恰好就是这样的一个问题——UBNT官方并未提供UniFi Controller的Docker部署方式,但是在Docker Hub上却有非官方提供的镜像,jacobalberty/unifilinuxserver/unifi。前者的文档明显详细很多,而且网上的教程大多都是前者的,于是这里我也就选了前者,其实两者采用的都是Unifi官方发布的控制器程序,所以实际使用时候理应是没有差别的。

备份Cloud Key的配置文件

转移首先要做的就是备份配置文件,Unifi控制器的配置备份非常重要,里面保存了控制器的全部信息,可以说就是这个控制器证明自己身份的一切,只要有这份配置,其他的控制器分分钟就可以接管代替原来的控制器继续工作,而且完全不会影响整个网络的运行。从PC临时控制器迁移到Cloud Key时,从初代Cloud key迁移至Gen2时,从坏掉的旧Cloud Key迁移到新拿回来的Cloud key时,还有现在我们即将做的——从Cloud key迁移到Docker版控制器环境时,我们都只需要这一个文件就足够了。

用浏览器访问控制器的8443端口,登录并依次打开 Settings -> System -> Maintenance -> Backup / Restore。点击Download Backup,控制器就会在后台打包配置文件,并在打包完成后自动将一个backup开头,后缀为.unf的文件下载到本地。

保存好这个文件,以后如果你搞砸了什么东西,这将会是救命稻草
生成配置文件需要一定时间,而且没有进度提示,所以不需要连续多次点击,等就是了。

关于内网访问

通过DDNS与端口映射配置外网访问NAS想必对大家来说都不是难事。例如说,我通过DDNS让我的域名固定指向路由器的公网地址。再通过端口映射,将对路由器特定端口的访问转发至NAS,这样,我在外面就可以通过域名与端口访问NAS上的内容了。

但是这样的设置只会针对外网,当你在内网中访问这个地址时,是需要设置端口回流(NAT Loopback)才能通过域名直接访问对应的主机的。否则就会出现手机或电脑在外网可以通过域名访问NAS,一旦连入家里wifi就反而无法通过域名访问NAS的情况。

这里通过设定主机名对应IP地址的方式让内网中通过这两个地址也会固定访问nas。

下文中所有的nas.xxxxx.com对应的都是nas的内网地址。

从Docker安装Unifi Controller的镜像

正常来讲,Docker的配置明明应该非常简单,但是就光是控制器的配置这一步,各路教程可真是五花八门众说纷纭,其中还有很多明显是在走弯路的。不过好在前面也说过,Unifi控制器本身并不是整个Unifi网络的中枢,而只是一个负责传话的日志记录员,所以在这一步可以尽情试错,不用担心把网络搞崩。

这里我使用的NAS是群晖的DS718+,算一台比较有年头的机器了,Unifi控制器的镜像本身拖家带口的带了mongo等一系列组件,在启动初始化的过程中,内存占用的峰值可以达到惊人的650多兆,虽然离吃满2G的内存还有不少距离,但也算是相当离谱了。不过实际运行的时候大多稳定在300+M的内存占用,CPU占用也不到1%,压力并不是很大。

首先,如果没有安装Docker的话先从套件中心安装Docker,安装完毕后启动Docker,在左侧的“注册表”项里,搜索Unifi,并双击下载,版本如果没有特殊需求的话直接选择默认的latest即可。会自动选择最新版的镜像进行下载。根据Docker的文档介绍,这里发布的版本基本是和官方发布的控制器版本同步的。

镜像有将近700兆,如果没挂梯子,下载可能会很慢。

下载结束后,群晖右上角会提示镜像下载完成。打开Docker的映像页,选择我们刚刚下载的镜像并点击左上角的“启动”。并在弹出的创建容器窗口中对容器进行设置。

可以给容器起一个容易记的名字,不能使用空格或中文,但是直接使用默认的容器名也是没有问题的

点击高级设置,勾选“高级设置”页的“启用自动重新启动”。以便在容器异常退出时自动重启。

存储空间”,“添加文件夹”。创建一个本地硬盘上的文件夹用于保存Docker中控制器的配置文件信息,例如我这里用了docker/unifi,装载路径填写/unifi全小写,这里的意思是将容器中的/unifi路径映射至我们NAS的硬盘上,这样当容器对自己的/unifi路径下的文件进行读写时,实际是在将配置文件与日志等保存在我们NAS的硬盘上,即使容器重启、重置甚至被删除,保存在这里的文件也不会消失。这里一定不能写错,否则容器将无法正常工作。

这里的装载路径必须按照如图所示进行填写,并且区分大小写

网络”,如果嫌麻烦的话可以直接勾选“使用与 Docker Host 相同的网络”,可以在接下来的步骤中节约大量的操作。具体原因后面会讲。Host模式下,Docker将会完全使用NAS的网络环境,因此通过访问NAS的8443端口,就等于直接访问控制器的8443端口。这里的选项会影响到后面控制器采用设备时的逻辑,但是不论此处是否选择使用Host模式,经过后面的配置后,控制器都是可以正常工作的。所以如果不愿意使用Host模式也可以不用纠结直接选择默认的Bridge桥接模式。

这里用Host虽然省心,但Host模式也不是万能药,平时使用如果所有容器都使用Host模式的话,那么有些热门端口(例如8080)在使用时就可能会出现冲突。不过在Unifi控制器这里区别并不是很大,因为Unifi控制器中很多端口是写死了的,下游设备只认这些端口来和控制器进行通信,所以即使选择Bridge模式,控制器的端口自由度也非常有限。
选择Host模式时,就不需要配置下一步的端口映射了,可以直接跳过。

端口设置”,如果上一步没有勾选 “使用与 Docker Host 相同的网络” ,那么在这里就需要对桥接的端口映射进行设置。映射的意思是,比如我有2个Docker容器,都通过80端口对外提供web服务,那么为了让他们不冲突,我们就需要设置端口映射,例如为A容器设置“本地端口:10080=容器端口:80”,为B容器设置 “本地端口:10081=容器端口:80”。那么所有访问本地10080端口的流量都会被转发给A容器的80端口,而访问本地10081端口的流量则会被转发给B容器。容器自己只需要自顾自地在80端口提供服务即可,完全不需要操心和其他容器发生冲突时需要做什么兼容操作。但是很不幸的是,如同上一段所讲,Unifi中的很多端口已经被写死了,随意改变端口的话,会导致采用、发现新设备等功能在使用时出现问题。因此我们在这里只能将本地的相同端口映射给容器,没有什么自由选择的余地。如果上一步勾选了“使用与 Docker Host 相同的网络” ,那么这里你应该是没有可以设置的内容的,可以直接跳过。

将本地端口由默认的自动选择改为和右侧的容器端口相同,并在最上方点击“新增”,添加一个10001端口的UDP类型映射,这个端口是Unifi用于发现设备的端口,虽然很多教程都有写到这点,但是我一直想知道为什么Docker的维护者至今没有把它加入到默认的配置中去………?

本地端口直接复制右侧即可,其他地方都不需要改

链接”和“环境”2项都不需要改。虽然很多教程里有说需要修改几项环境变量,并且Docker官方也推荐修改默认的运行账户信息,但是在个人使用的环境中并没有必要。直接点击应用,然后点击下一步应用,确认创建容器并运行即可。

镜像第一次启动需要一段时间初始化,这个时候直接访问NAS的地址https://<NAS-IP>:8443大概率会直接吃瘪,被各种400和404糊脸,让你产生自己是不是有什么地方搞错了的错觉。可以在Docker中观察容器的内存和CPU占用,等一小段时间稳定下来以后,就可以通过浏览器输入地址访问控制器的页面了。

用之前的配置设置新的Unifi控制器

如果你使用的是Chrome或者其他Chromium内核浏览器比如Edge,那么在打开页面的时候会弹出证书警告,这里选择继续访问(不安全)即可继续访问。

第一次启动Unifi控制器,向导会引导你进行网络的配置,但是我们并不是要配置一个新的网络,所以在这里直接选择从配置文件恢复->上传本地文件,上传我们之前备份的.unf文件,然后根据提示稍等片刻,然后我们就会发现他说的这个片刻是真的不靠谱。我在中间数次以为配置文件出了问题无法恢复导致控制器卡住了,然后不停的刷新,不停的看这个让人抓狂的“你的链接不是专用连接”的提示,我大概估算了一下前后起码得有十来分钟。在这个过程中Docker的内存占用也会来到峰值,可以通过群晖的Docker面板上查看容器的CPU和内存占用情况推测进度。总而言之,在经过一段不短时间的恢复配置文件后,再次刷新页面,就会发现自己已经可以打开控制器的登录页面了,输入用户名与密码登录,即可看到站点的配置全都保留着,完全不需要重新手动接管设备等操作,一切都和老的Cloud key还在的时候一模一样,没有任何人发现他们的控制器已经换了个人——甚至,就如同我在前面所说的,整个过程中连网络连接都不会中断。家里的所有设备都可以正常无碍的上网、无缝漫游,因为Unifi的AP的运行是自动的,完全不需要控制器的介入。

如果你在前面配置容器的时候选择了 “使用与 Docker Host 相同的网络” ,那么网络里的设备至此应该已经自动采用完毕,迁移过程到这里就已经结束了,你已经可以打开闲鱼,把旧的控制器50包邮了(请优先考虑出给我,谢谢)。但如果你是选择了桥接模式,那么你还会碰到一个让人非常迷惑的问题,那就是网络里的设备在反复重复着采用中-离线的循环。这个问题网络上的解法也是五花八门,实际上在unifi的官网Docker的文档(下拉到Adopting Access Points/Switches/Security Gateway部分)中都有说明。

桥接模式的额外设置

网络上对这个问题的解释都很模糊,其实这个问题的产生原因非常简单:Unifi控制器在采用设备的时候,会将自己的通知地址发送给被采用的客户端,默认情况下,这个地址是http://unifi:8080/inform。当Unifi控制器处于Host模式和NAS共享网络环境时,它自己的环境就是NAS的网络环境,它广播出去的“自己的访问地址”自然也就是NAS的地址,所以其他设备根据这个地址直接找到了NAS,并且访问NAS就等于在访问控制器,两边就成功地建立了连接。

然而在桥接模式下,Unifi控制器知道的只有自己处于桥接环境下的内网地址,它将这地址广播了出去——然而这个地址在主网络里的设备们是无法直接访问到的。它们要想访问到处于桥接模式下的控制器,唯一的办法就是去访问NAS,然后通过NAS将请求转发给桥接的控制器。这样,两边就可以成功地建立连接了。

网上的很多教程建议这里使用UniFi Discovery或者SSH对每一台设备进行修改,通过执行set-inform http://<NAS的IP地址>:8080/inform指令,手动将每一台设备所要连接的管理地址从原本无法访问的控制器内网地址改为NAS的地址。只有一台设备时候这么搞还好说,设备多了的话,每次一台一台的修改的工作量简直让人头皮发麻…

其实既然知道了原理,那么要解决这个问题就并不需要这么麻烦了。问题出在控制器广播出去的地址本身有问题,那么我们只要让控制器直接广播NAS的地址就好了——事实上,如果你的控制器运行在云端而不是本地,那么你也可以通过只有的操作来让设备可以正常地和控制器建立连接。

登录控制器,打开 Settings -> System -> Application Configuration -> Host for Inform,设置Host for Inform的地址为NAS的地址或域名后,开启Override Inform Host选项并点击右下角的Apply应用设置。

很快,所有设备就都成功地完成采用过程并正常在线了。

额外的小知识点

既然说到了SSH,那么就再提两点相关的内容吧神经病…

其实在设备的页面中点开对应的设备,点开 Settings -> Manage -> Debug选项。可以直接在网页中打开直接连接设备的终端,并且不需要输入用户名和密码确认。也不需要自己复制粘贴设备的IP到putty里。在调试设备的时候非常有用。

执行info可以直接查看到设备当前所连接的inform地址

如果真到了一定要用putty等进行SSH登录的时候,却发现忘记了要连接的设备的用户名和密码了,怎么办呢?难道只能重置设备了吗?

其实不是的,设备的管理用户和密码其实也是当初通过控制器下发的,在设置页面直接搜索Device SSH Authentication,点击小眼睛图标,就可以看到自己设置的SSH用户名和密码了。

配置SSL

Unifi控制器使用自签名证书来进行HTTPS访问,在Chrome和它的兄弟例如Edge等浏览器面前这种小把戏显然是行不通的——即使你将证书加入信任也没用。虽然点击继续访问(不安全)就可以暂时忽略警告,但是左上角的红叹号和“不安全”的提示对于强迫症来说是毫无疑问的折磨。如果按F12打开控制台,你还会看到漫山遍野的报错信息。隔一段时间没有访问,页面就会跳错,刷新一下就会发现页面回到了之前的“你的连接不是专用连接”的页面,必须重新选择“继续访问(不安全)”,简直让人抓狂。

要想解决这个问题其实理论上来说很简单,只要将unifi控制器的自签名证书替换为正规机构颁发的证书就可以了,例如大名鼎鼎的Let's Encrypt——不过其实现在免费的SSL证书提供商很多,Let's Encrypt不再是唯一选择,大家常用的acme.sh也早已更换了默认申请的SSL服务商ZeroSSL,参考其首页的和Let's Encrypt进行对比,其基本完全继承了Let's Encrypt的特性,并且还拥有web在线管理的功能,API调用也没有限制,使用起来更加方便……诶呀,跑题了——虽然搞一张SSL证书和域名现在都不是什么难事,但是在相当长的一段时间里,Cloud key想要更换证书都是一件非常麻烦的事。8443端口本身基于java,不但证书打包需要额外的操作,而且Cloud key每次重启的时候还会执行脚本自动恢复默认的自签名证书。我在当初还在用Cloudkey的时候就已经不堪其扰,顺着谷歌找到了这篇教程,看完以后我当时就觉得,算了吧,反正控制器平时也不怎么会看,没必要折腾。不就多点2下鼠标,没事,无所谓…

反复分析但是现在控制器跑在了群晖上,那证书这种事情还会由控制器说了算吗?

我直接掏出无敌的反代!

打开群晖的 控制面板 -> 登录门户 -> 高级 -> 反向代理服务器。点击新增,然后设置反代NAS域名的8444(随便一个什么没用过的端口都行)端口到本地的HTTPS 8443也就是Unifi控制器的端口。

由于Unifi控制器的页面中也有Websocket协议,所以不要忘记添加Websocket,不然还是会有报错。

群晖这里完全是傻瓜式添加,直接在第二页的自定义标题 新增 下选择 Websocket,群晖就会自动帮你把需要的内容填好,保存即可。

哦对了,也不要忘记在 群晖控制面板的 安全性 证书 设置 里,为反代指定对应域名的证书——不过如果你设置了默认证书的话,这里应该会自动为你选好证书的,并不需要你手动设置。

访问反代指定的8444端口,即可看到久违的小锁。页面终于不会在控制台里报一堆错,也不会没事就弹回首页要求重新确认不安全的网站了,感觉整个世界都变得美好了起来神经病…

配置SSL 2

如果谁要是看了上面这一段内容以后觉得“哇,好方便,反代真牛逼,群晖YYDS”,那就说明你肯定根本没有认真看前面镜像给出的文档,或者最起码你没有读到最后反复分析

仔细看,在这个文档的最后,赫然便是Certificate Support,而且操作过程简单到让人落泪。我们只需要将申请到的证书中对应的证书,密钥,中间证书文件,分别重命名为cert.pemprivkey.pemchain.pem,放在/unifi/cert目录下,然后启动容器即可。

诶呀其实你们一定没发现我放错文件了(狗头

直接访问8443端口,就能看到熟悉而又亲切的小锁头了神经病…

实际上这里就是Docker替我们在每次启动前执行了替换证书的操作。具体可以参考/usr/unifi/init.d/import_cert的内容。根据Docker文档所说,你也可以将自己的脚本放在/usr/local/unifi/init.d下,容器将在每次启动时自动执行,如果你同时在NAS的Docker上运行了ACME的话,那就可以让它每次启动时候把最新的证书文件复制过来,一劳永逸。

更新控制器版本

Docker中运行的控制器是不能够直接点击后台的页面进行升级的。需要通过直接更换新版本Docker镜像的方法进行升级。由于镜像所保存的配置文件挂载在本地硬盘上,停止、删除、更新容器都不会影响到配置文件本身。所以可以放心大胆地进行升级。

Docker的镜像升级非常简单,和安装时的步骤几乎一样,打开Docker的 注册表 搜索unifi,下载镜像,选择latest最新版本或你想要升级到的版本下载。等到下载完毕后,群晖的右上角会弹出提示。

这个时候,关闭容器,并在上方选择 重置。重置后,容器内的镜像会被替换为刚刚下载的最新版本镜像,直接启动容器即可,不需要修改或调整任何设置。等到加载完毕,就可以看到控制器已经升级完毕了,容器的设置和Unifi控制器的设置都还保留原样,可以说比Unifi官方升级要直观得多。官方后台的升级点了以后压根没反应,总是让我怀疑到底是不是我根本没点上或者是不是出了什么差错下载失败了

网上也看到好多舍近求远甚至非常离谱的升级教程,比如让你把旧的容器删掉,重新新建容器,输入相同的配置,挂载相同目录,看得我是目瞪口呆。甚至有一个问题下面的回复是建议后台备份配置然后整个删掉重装的,可真是把我看麻了神经病…

评论

  1. choco
    iPhone Safari 14.1.2
    2年前
    2021-12-30 2:37:35

    很好,关于证书的后半段双加好

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇