深入解析绿联云 DX4600 Pro UGOS 系统 (2)
我们会发现教程教学和 Docker 页面里的“常用镜像”使用的镜像全都不是来自于官方,而是来自于一个叫做 linuxserver 的开发者,所以需要介绍一下 linuxserver.io 到底是何方神圣。
linuxserver 由一群志愿者组成,致力于为开源软件构建改良的 docker 镜像,并且在官方镜像的基础上提供以下特性:
添加统一的 Ubuntu 系统底层,定期更新
跟随上游版本发布 CI 定期更新
增加 User Mapping 功能( PGID 、PUID )
将所有软件的用户配置文件解耦并统一管理
linuxserver 维护的所有软件配置文件都在/config 文件夹下,可以保证只要/config 不变,镜像版本无痛更新
友好的 fork 支持
配置文件解耦这一点很重要,每个 linuxserver.io 的镜像你只需要把他们的/config 映射到本地永久化储存,镜像就能够随意更新不受影响。
市面上非常多热门的“增强版”Docker 镜像都是基于 linuxserver.io 开发的
绿联云套件已经内置了“离线下载”和“迅雷”APP 。这个“离线下载”实际上就是 Aria2 的封包,屏蔽了所有的参数设置;同时我们也知道吸血鬼迅雷是个什么东西,走在路上都要捏着鼻子避开。因此,一个正常的具有互联网精神的网民都应当会选择自建 docker 来完成远程下载。2023 年的 Linux 下载器不外乎就 2 个,qBittorrent 负责 BT ,Aria2 负责直链。
无它,因为在 BT 这方面 qBittorrent 实在是强太多。
部署 BT 下载器的时候一定要注意一件事情,把容器放在 Bridge 里面会导致网络严重受限,一切涉及到 BT 下载的容器请部署在 Host 网络。绿联官方的教程就犯了这个错误。
很多人在部署 aria2c 的 docker 镜像的时候都会去优先寻找国人优化的“增强版”,我也一样,因此有个注意事项:
热度和下载量很高的p3terx/aria2-pro
在绿联系统上有兼容性问题,会一直自动结束,无法使用
建议使用superng6/aria2
代替。
同时,因为系统内置了 aria2c ,所以 6800 端口被占用,使用的时候需要更改成别的端口。
qb 增强版我个人使用的是superng6/qbittorrentee
,基于 linuxserver 镜像,并提供了各种喜闻乐见的优化,详情可以通过Github repo了解。
需要注意的是,网络模式设置成 Host 后,创建容器时的“端口设置”条目是无效的,程序的监听地址仅由环境变量中的WEBUIPORT
指定。
如果你按照官方的**教程设置PGID
和PUID
为 0 的话,所有下载的文件 owner 会是 root ,默认权限 755 ,则通过 SMB 访问时不可写。
实战当中,我推荐将 QB 的容器用户指定为你在“网络服务”中创建的账户,通过 Linux 基础命令可以查询 UID 和 GID:
id <user>
比如我的账户叫做 nas ,查询得知 UID 和 GID 均为24****0000
:
则创建容器时,在环境变量中指定他们为查询到的 ID:
在superng6/qbittorrentee
镜像中,指定 UMASK 的环境变量名为UMASK_SET
,而在 linuxserver.io 官方镜像中(应该)是UMASK
。设置为 000 使得所有用户都能够读写,UMASK 的定义参见Wiki,反正绿联的文件权限管理哲学就是全盘 777 ,已经很危险了,所以不用害怕安全问题。
这至少能够保证挂载的 SMB 能够正常读写,其他成员通过套件访问时都会被 UGOS 自动重置成 root777 。
至于 QB 的内部设置,增强版基本已经做到开箱即用,没有什么注意事项,qb 本身的更多设置优化可以参考网上大量的文章。
存储空间映射则看个人喜好,我自己会喜欢为每个子文件夹单独创建一个映射,保持空间侵入最小化,而不是直接映射一个根文件夹让 QB 在内部自由访问,二者皆可。
强烈推荐增加一条 SSD 用来单独存放 config 和临时文件,这样可以降低机械硬盘负载,增加休眠几率,理论上延长写寿命。
- 在 QB 设置中,保存未完成的 torrent 到 SSD
- Jellyfin 的媒体库很庞大时,Metadata 和 Cache 容量可能高达数十 GB ,放在 SSD 可以大幅加快加载速度,减少机械硬盘的小文件读
在《深入解析》中我们已经分析过,UGOS 的 Nginx 把所有套件的端口都聚合到 9999 反向代理,但是没有做任何的手段去阻止端口对外暴露,所有套件的端口依旧监听在 0.0.0.0 ,这是一个非常大的安全隐患。我们可以简单在公网资产嗅探引擎简单搜索一下“绿联”或者“绿联私有云”,可以得到 2023 年光是中国大陆内就有约 5000 多台绿联 NAS 暴露在公网上,并且随便选个 IP 蒙常用端口都能进得去:
公网的高阶防护,端口隐藏之类可以通过 Docker lucky 来完成,但是在这里我不打算教这个东西,因为我也没部署。公网和论坛里有不少关于这个的教程都可以参考。
既然我们部署在大内网,那么优先的手段是利用大内网的优势,即从软件本身限制某些不希望公网访问的服务的监听范围。
Meta 属于第二种情况,具体的配置方法已经在上面第一个容器:配置文件中给出
QB 的情况也属于第二种,监听全端口,设置强密码并对 localhost 、LAN 、docker 、Tailscale 跳过验证:
Aria2 的 RPC 目前只支持监听 127.0.0.1 或 0.0.0.0 ,并且并没有网域白名单,即第三种情况,因此创建容器时请务必给 RPC 设置好一个强密码,指定密码的环境变量是SECRET
:
如果你按照绿联云帮助中心的教程部署 Jellyfin ,那么恭喜你,这个 Jellyfin 基本只有 40%可用。你将会面临以下问题:
想要得到一个比较完美的 Jelly server ,还需要更多的步骤。
在买这台 NAS 之前,我的 Jellyfin 服务器是搭建在主力 Windows PC 上的,而我的 PC 又开机启动隧道,导致我以为 Jellyfin 的 IMDB 和 TMDB 源都是开箱即用的,直到我把几个 T 的影视迁移到 NAS 后发现什么东西都刮削不出来。
我在交流群中看到有些用户会使用适配 Jellyfin 的第三方刮削源,比如 tinyMediaManager 来解决这个问题,不过我更喜欢原生的 IMDB 和 TMDB ,因为我已有的媒体库在内置源下已经全部准确识别,所以适合继续沿用。
说白了还是网络问题,解决的方法也很简单,要么改 hosts ,要么上隧道,而我肯定是义无反顾地选择上隧道的,只要在环境变量里加上这两条就好,Jellyfin 能够识别并且自动走代理:
绿联的官方教程根本没有提及这个问题。
Jellyfin 的硬解稍微有一点学问,但不多,总结下来大概有这么几点:
--device /dev/dri:/dev/dri
透传显卡设备,官方教程中让用户设置PUID
和PGID
为 0 是因为/dev/dri/renderD128
和/dev/dri/card0
权限为 root@600附:我的个人设置
高清影视收藏早就迈入了 4K HDR 时代。现在越来越多的电影和 TV 都是动辄 HDR 、Dolby Vision 等先进编码格式,对解码工作有不小的挑战。
就我的使用经验总结,以下情况会不可避免地强制触发转码:
以下情况能够避免转码:
Windows 使用 Jellyfin Media Player ,或者 Jellyfin MPV Shim
使用基于 MPV 的客户端,如 vidhub
使用基于 VLC Kit 的客户端,如 swiftfin
以上 3 种选择,共同点是 HDR 内容会在本地 tone mapping ,并且具备终端字幕的处理能力,但不支持 HDR 输出。据称 MPV Shim 在配置得当的情况下能够支持 HDR 输出,但是我还没调通。
iOS 使用 native player 播放,并且同时选择 SRT 基础字幕( iOS 的 native player 不具备高级字幕的处理能力,ASS 会触发服务端烧录,不推荐)
Apple 生态的宇宙终极解决方案 Infuse ,支持全格式+全种类 HDR 解码+HDR 输出,支持终端字幕处理
Android TV 使用 Jellyfin Media Player 或者 Kodi 客户端(解码能力遵守电视硬件和系统设置,支持终端字幕处理)
Android 手机端、Shield 、Apple TV 等环境因为我没有无法测试,希望网友们补充。
可以看到,一旦涉及浏览器和 iOS 环境,坑和雷就很多,而在原厂情况下 Jasper Lake 核显在尝试处理 HDR 编码( DV, HDR10 and HLG )时会转码失败,无法播放,查询 log 会有device not found
字样,多见于 4K 电影和美剧。这种情况仅会在 HEVC HDR 环境发生,但并不代表 N5105/6005 的核显不支持这个规格的视频转码。Linuxserver.io 的文档已经提到了相关的问题,你需要在环境变量中加一条参数:
DOCKER_MODS=linuxserver/mods:jellyfin-opencl-intel
以开启基于 OpenCL 的 Tone Mapping 能力。本身 Jasper Lake 就不支持 VPP TM ,同时遇到上述高规格编码时普通 Tone Mapping 无法工作,所以会导致播放失败。
绿联的官方教程根本没有提及这个问题。
Linuxserver.io 的原厂镜像并没有对 CJK 语境的支持,一旦在不支持终端字幕处理的设备上观看中文内容,就会导致中文全部变成框框、中文的媒体库封面也变成框框。在网上有很多传统教程教你用别的 ttf 字体替换系统内置的 Deja-Vu Sans ,其实 2023 年并不需要这么麻烦,Linuxserver 的镜像内部已经支持自动加载/usr/share/fonts
下的字体文件,你只要用docker cp
把字体放进去就好。
还有更加简单的方法,Linuxserver 镜像底层是 Ubuntu ,所以一条命令就能够解决中文环境问题:
apt update && apt install fonts-noto-cjk-extra -y
会自动在系统中安装 Noto CJK (思源黑体)。系统显示用的非衬线体我只推荐两个:Noto和更纱黑体,只有他们对字形的支持能够覆盖到操作系统的所有显示需求。
绿联的官方教程根本没有提及这个问题。
Jellyfin 才在半年之内的版本实现对 IPV6 的支持,而且出厂似乎默认没有开启。记得去 Networking-IP Protocols 把 Enable IPV6 勾选上
参考前文,因为调用 GPU 必须指定用户为 root ,所以默认情况下镜像产生的所有配置文件和文件夹通过 SMB 访问没有写入权限,因此我们必须在环境变量中加入:
UMASK=000
默认的主题对比 Emby 稍微有些死板,不够灵动,所以我去 Github 找到了一套我最喜欢的主题Ultrachromic,并且进行了一点点优化配置,供各位参考
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/fixes.css');
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/base.css');
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/accentlist.css');
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/rounding.css');
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/smallercast.css');
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/episodelist/episodes_compactlist.css');
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/header/header_transparent.css');
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/login/login_minimalistic.css');
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/type/dark_withaccent.css');
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/fields/fields_noborder.css');
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/cornerindicator/indicator_floating.css');
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/titlepage/title_simple-logo.css');
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/progress/floating.css');
@import url('https://cdn.jsdelivr.net/gh/CTalvio/Ultrachromic/effects/glassy.css');
@import url("https://cdn.jsdelivr.net/gh/prayag17/Jellyfin-Icons/round.css");
/* Fonts */
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@500;600;700&display=swap');
body, h1, h2, h3, h4 {
font-family: 'Quicksand', 'Noto Sans SC', sans-serif;
}
.emby-textarea {
font-family: monospace;
font-size: 10pt;
}
/*Style backdrop*/
.backdropImage {filter: blur(18px) saturate(120%) contrast(120%) brightness(20%);}
/*Login background*/
#loginPage {background: url(/* 公共图床的背景图片 URL ,推荐 1080P */) !important;}
/*Accent and rounding*/
/* 可以调配你自己喜欢的颜色 */
:root {--accent: 195,140,255;}
:root {--rounding: 12px;}
/* Disable carousel */
/* 在大屏幕设备上关闭首页卡片的横向滚动,更加易用 */
@media all and (min-width: 50em) {
.homePage .emby-scroller {
margin-right: 0;
}
.homePage .emby-scrollbuttons {
display: none;
}
.homePage .itemsContainer {
flex-wrap: wrap;
}
}
我们使用Jellyscrub来提供进度条预览功能,这个插件能在 Plugins-Catalog 直接安装,并且与各种客户端、浏览器和第三方插件的适配都很好:
请记得在插件设置里把 HW Acceleration 更改为 No Encode ( Intel 核显不支持 mjpeg 编码,所以不能选择 Full ),不然会通过 CPU 编码,整台机器发烧到天昏地暗,速度还慢得出奇。
同时,上面提到的 DOCKER_MODS 环境变量还会影响这个插件的缩略图采集,如果没有打 MOD ,这个插件也不支持生成高规格 HDR 视频的进度条预览图。
观看统计通过插件 Playback Report 实现,直接从 Plugins-Catalog 安装就好,在你的影视库用户量比较大的时候能够给你一个统计报告,观察大家都喜欢看什么:
https://jellyfin.org/docs/general/server/media/shows/
遵守 Jellyfin 官方的命名守则能够让你更有序地管理剧集和番剧资源,目前我库里的所有番剧内容都会按照 Jellyfin 的命名守则重新改名字,用 Power Rename 正则表达式其实挺方便的:
TMDB 本身就有番剧资源的元数据,并不依赖 AniDB 等第三方插件(除了里番、OVA 等),只要稍加改名就能够正确识别:
固件破破烂烂,我们缝缝补补
s/1ejEUsEYZzIi5vu5NSrej0g?pwd=9uv7
在《深入解析》的时候我们就已经知道了这个系统用的是 busybox+ash 。但是,更离谱的是,UGOS 在编译时忽略了接近 50%的 busybox 命令,导致 shell 基本上处于一个半残废状态。
为了补足功能的缺陷,我连夜学习编译了一个静态库版本的 busybox ,包含了官方出厂能用的 310 条命令,并且对比系统固件所缺的命令列表,总结出了缺失命令,写了一个脚本来创建动态链接,缝补好这个残缺的 busybox:
#!/bin/bash
busybox_path="/opt/bin/busybox"
target_dir="/usr/bin/"
items=(
"acpid" "add-shell" "adjtimex" "ar" "arch" "arp" "base32" "base64" "bc"
"beep" "blkdiscard" "bootchartd" "bzip2" "cal" "chat" "chpst" "chrt" "chvt"
"cksum" "comm" "conspy" "cpio" "crc32" "cryptpw" "cttyhack" "dc" "deallocvt"
"depmod" "dhcprelay" "diff" "dnsd" "dnsdomainname" "dos2unix" "dpkg" "dpkg-deb"
"dumpkmap" "dumpleases" "ed" "eject" "envdir" "envuidgid" "ether-wake" "expand"
"factor" "fakeidentd" "fallocate" "fatattr" "fbset" "fbsplash" "fdflush"
"fdformat" "fgconsole" "fold" "freeramdisk" "fsck" "fsck.minix" "fsfreeze"
"fstrim" "ftpd" "ftpget" "ftpput" "getopt" "getty" "hd" "hexedit" "hostid"
"hostname" "httpd" "hush" "ifenslave" "ifplugd" "inetd" "install" "ionice"
"ipaddr" "ipcalc" "ipcrm" "iplink" "ipneigh" "iproute" "iprule" "iptunnel"
"kbd_mode" "killall5" "klogd" "last" "link" "linux32" "linux64" "linuxrc"
"loadfont" "loadkmap" "logname" "lpd" "lpq" "lpr" "lsscsi" "lzcat" "lzma"
"lzop" "makedevs" "makemime" "man" "mdev" "mesg" "microcom" "mim" "mkdosfs"
"mkfs.minix" "mkfs.reiser" "mkpasswd" "more" "mountpoint" "mt" "nameif"
"nanddump" "nandwrite" "nbd-client" "netcat" "nl" "nmeter" "nohup" "nproc"
"nsenter" "od" "openvt" "paste" "patch" "pipe_progress" "popmaildir" "powertop"
"printenv" "pscan" "pstree" "raidautorun" "rdate" "rdev" "readahead" "readprofile"
"realpath" "reformime" "remove-shell" "renice" "resume" "rev" "rpm" "rpm2cpio"
"run-init" "run-parts" "runlevel" "runsv" "runsvdir" "script" "scriptreplay"
"seedrng" "sendmail" "setarch" "setconsole" "setfont" "setkeycodes" "setlogcons"
"setpriv" "setserial" "setsid" "setuidgid" "sha1sum" "sha3sum" "sha512sum"
"showkey" "shred" "shuf" "slattach" "smemcap" "softlimit" "split" "ssl_client"
"stty" "sulogin" "sum" "sv" "svc" "svlogd" "svok" "syslogd" "tac" "tc"
"tcpsvd" "telnet" "telnetd" "tftp" "tftpd" "truncate" "ts" "tsort" "tty" "ttysize"
"tunctl" "ubiattach" "ubidetach" "ubimkvol" "ubirename" "ubirmvol" "ubirsvol"
"ubiupdatevol" "udhcpc6" "udhcpd" "udpsvd" "uevent" "unexpand" "unix2dos" "unlink"
"unlzma" "unshare" "unxz" "users" "usleep" "uudecode" "uuencode" "vconfig" "vlock"
"volname" "wall" "watchdog" "who" "whoami" "whois" "xxd" "xz" "xzcat" "zcip"
)
for item in "${items[@]}"; do
ln -s "$busybox_path" "$target_dir$item"
if [ $? -eq 0 ]; then
echo "Created symlink for $item"
else
echo "Failed to create symlink for $item"
fi
done
请根据自己的情况修改 busybox binary 的存放位置。
rsync 对于高级 Linux 玩家不可或缺,同样是由于 libc 的原因,从 opkg 安装的 rsync 无法运行。所以我又编译了一个 static 版本的 rsync ,还是一样:
ln -s /opt/bin/rsync-static /usr/bin/rsync
同理,还有:
论坛里面还是有高手的,有一位大哥实现了用户态 nfs 软件 nfs-ganesha 的部署,并且看起来像是自己编译的安装包、自己写的 init.d 配置文件,移步论坛帖子查看:
https://bbs.ugreengroup.com/forum.php?mod=viewthread&tid=10961&highlight=ganesha
它写的 init.d 配置文件默认只分享一个目录,我进行了一些适当的修改,具体可以参照帖子里最新的一楼。
最后这一部分是一个小吐槽。
买之前我看博主是这样吹的:
买之后固件更新失踪一个月、官方更新路线失踪 2 个月,官方四处叫天天不应、叫地地不灵,技术支持客服 80%的时间在用套话跟你聊天。
网上面的很多博主都会说绿联听取用户意见,那我就不得不说说绿联现在的用户反馈模式了:
外网,全部都是投放和软文,并且定位都是轻度体验和开箱,绝口不提深度的软件内容,没有任何公开的用户讨论社群
论坛,admin 直接和用户打交道的地方,必须购买产品并且注册账号才能够访问,论坛本身有 token 验证,不携带 token 拒之门外,放一个二维码给你扫
2.1 这一步将用户的沟通渠道从公网收缩到了买了产品、注册而且会上论坛的人
2.2 现在论坛的建议反馈区域已经没有 admin 回答,admin 只活跃在解答用户疑问的“求助解答”板块
11 月中绿联官方短暂复活,发布帖子邀请用户加入“绿联私有云官方交流群”,方式是添加一个“工具人”,提交你的账号、设备 SN 码,经核实才会允许入群,群规模 500 ,我加入的是第 5 群
3.1 这一步将用户的沟通渠道进一步收缩到了 3000 人
3.2 我时不时会看到自己那篇《深入解析》被转载到群里交流,但是好像没怎么发现是我写的
群里面有一个 bot 提供基本的解答,并且 500 人的大群,意见反馈和需求统一需要主动加群里的 1 个人私聊,没错,只有这 1 个人,收缩比例 1:500:
目前更新计划已经彻底不公开,论坛“官方公告”的升级规划已经全部成为过去式,如果想要了解系统的更新计划,现在的做法是蹲点等待“内测组报名”不定期开启,每次内测只会邀请数十名用户直接面对产品组体验 beta 固件,免费测试打工,除此之外请排队找上图的这位爷。
然后群里日常吐槽系统问题,官方的机器人突然活人上号出来让我们不要诋毁,被我骂了
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.