跳到主要内容

使用 fnOS 将老旧 USB 打印机改造成网络打印机

以联想 M7206 为例,零成本让没有网口的老打印机支持 Windows / macOS / 安卓 / iPhone 全家桶无线打印。

本文不仅给出标准流程,更重要的是复盘了实践中真实踩过的 9 个坑 和一套 分层排障方法论——即使你的打印机型号不同,这套思路也通用。


一、为什么这么做

很多人家里都有一台"还能打但没网口"的老激光打印机,比如联想 M7206。想让它变成网络打印机,常见选择:

方案成本评价
买打印服务器盒子100~200 元能用,但功能单一
挂在常开的电脑上共享0 元电脑必须开机,体验差
ESP32 等单片机 DIY40 元极客玩具,无队列、稳定性一般
NAS(fnOS)+ Docker CUPS0 元NAS 本来就 24 小时开机,体验最接近真正的网络打印机

思路很简单:打印机 USB 线插到 fnOS 主机上,在 Docker 里跑一个 CUPS 打印服务,通过 IPP 协议共享给全家设备。CUPS 是 Linux/macOS 的标准打印系统,自带 Web 管理界面、打印队列、IPP 共享,配合 Avahi 广播还能实现 AirPrint/安卓自动发现。

最终效果:

Windows / Mac / 手机 ──IPP──> fnOS(Docker CUPS) ──USB──> 联想 M7206

二、准备工作

2.1 硬件连接

打印机 USB 线插到 fnOS 主机本体的 USB 口(不是路由器、不是电脑),打印机开机。

2.2 确认系统识别

SSH 登录 fnOS,执行:

lsusb

看到类似这样的行就说明硬件识别 OK:

Bus 003 Device 003: ID 17ef:5629 Lenovo M7206

如果看不到,先换 USB 口、换线、确认打印机开机,这一步不通后面全白搭


三、部署 CUPS 容器

3.1 为什么用命令行而不是 fnOS 图形界面

fnOS 的 Docker 图形界面可以拉镜像建容器,但已创建的容器不能补加"特权模式"和"设备映射",而这两个恰恰是 USB 打印机能不能被容器看到的关键。所以直接命令行一步到位,避免返工。(坑 #1,详见第五章)

3.2 一条命令部署

fnOS 的 Docker 镜像仓库里搜 CUPS,ydkn/cups 是下载量最大的一个,就用它:

fnOS 镜像仓库搜索 CUPS

# 建配置持久化目录(注意:只挂 /etc/cups,千万别挂 /var/spool/cups,原因见坑 #8)
sudo mkdir -p /vol1/1000/docker/cups/etc

# 如果你的存储路径不是 /vol1/1000,先 ls /vol1 确认
sudo docker run -d \
--name cups \
--restart unless-stopped \
--privileged \
-p 631:631 \
-v /dev/bus/usb:/dev/bus/usb \
-v /vol1/1000/docker/cups/etc:/etc/cups \
-e CUPSADMIN=admin \
-e CUPSPASSWORD=改成你的密码 \
ydkn/cups

参数说明:

  • --privileged:特权模式,容器访问 USB 设备必需;
  • -v /dev/bus/usb:/dev/bus/usb:把整个 USB 总线映射进容器;
  • -p 631:631:CUPS 的 Web 管理和 IPP 打印端口;
  • -v ...:/etc/cups:配置持久化(打印机定义、共享设置),重建容器不丢;
  • CUPSADMIN / CUPSPASSWORD:Web 管理界面的账号密码。

3.3 验证 USB 进了容器

sudo docker exec cups ls /dev/bus/usb/003/

能列出数字(如 003)就 OK。


四、配置 CUPS

4.1 打开 Web 管理页

浏览器访问 http://你的NAS-IP:631

坑 #2:点 Administration / Add Printer 时可能跳出一个"需要升级"页面,让你访问 https://172.17.0.2:631/admin/——那是容器内部 IP,永远打不开。这是 CUPS 强制管理操作走 HTTPS 的机制。解决办法:手动把地址改成 https://你的NAS-IP:631/admin/(浏览器会提示自签名证书不受信任,点"继续前往"即可)。

坑 #2:"需要升级"页面,地址是容器内部 IP 172.17.0.2

4.2 开启共享(必须)

Administration → 右侧 Server Settings,勾选:

  • Share printers connected to this system
  • Allow printing from the Internet(名字吓人,实际意思是"允许局域网内其他机器提交任务",必须勾)
  • ✅ Allow remote administration

点 Change Settings,服务会自动重启几秒。

Server Settings 共享开关,三项全勾

4.3 添加打印机

  1. Administration → Add Printer(登录账号密码就是环境变量里设的);

  2. Local Printers 里应该能看到 Lenovo M7206,选中 → Continue;

    • 如果是空的 → 容器没认到 USB,回到 3.3 检查(坑 #3);

    USB 映射成功后,Local Printers 里出现 Lenovo M7206

  3. Name 改成 M7206 之类纯英文无空格的名字(会成为共享 URL 的一部分),✅ 勾 Share This Printer

  4. 驱动选择见下一节。

4.4 驱动选择(贴牌机型的关键知识)

联想 M 系列激光机(M7206/M7216/M7400 等)大多是兄弟(Brother)方案贴牌,CUPS 里没有 Lenovo 驱动,要用兄弟的:

  • 首选 brlaser:社区给兄弟 GDI 激光机写的开源驱动,M7206 与 DCP-1510/HL-1110 同代引擎,实测 DCP-1510 完美兼容;
  • 备选:Brother HL-2140 的 Foomatic 驱动(hl1250 / hpijs-pcl5e),但依赖 foomatic 组件,链条更长更容易出问题(我们就栽在这里,见坑 #6)。

驱动选择页:Make 列表里没有 Lenovo,贴牌机要选兄弟(Brother)

brlaser 不在基础镜像里,先补装组件(这里连着坑 #4、#5、#6,建议直接照抄第五章对应段落一次装对):

# ydkn/cups 基于 Debian 10(buster),官方源已下线,先切换到存档源
sudo docker exec -it cups bash -c "printf 'deb http://archive.debian.org/debian buster main\ndeb http://archive.debian.org/debian-security buster/updates main\n' > /etc/apt/sources.list && apt-get -o Acquire::Check-Valid-Until=false update"

# 一次装齐:过滤器链 + 字体 + brlaser 驱动 + PDF 工具
sudo docker exec -it cups bash -c "apt-get -o Acquire::Check-Valid-Until=false install -y cups-filters ghostscript poppler-utils fontconfig fonts-dejavu-core poppler-data qpdf printer-driver-brlaser printer-driver-hpijs"

装完在容器里直接指定驱动(比 Web 界面点来点去快):

sudo docker exec cups lpadmin -p M7206 -m "drv:///brlaser.drv/br1510.ppd"
sudo docker exec cups cupsenable M7206

可以用 sudo docker exec cups lpinfo -m | grep -i brlaser 查看所有 brlaser 支持的型号,如果你的联想机型打出来是白纸,就换 br7055、brl2300d 等挨个试。

4.5 打测试页验证

Web 界面:Printers → 点打印机 → Maintenance 下拉 → Print Test Page。

或命令行(更可控):

sudo docker exec cups bash -c "printf 'CUPS PRINT TEST OK\n' > /tmp/t.txt"
sudo docker exec cups lp -d M7206 /tmp/t.txt

正常出纸,服务器侧就大功告成了。 如果失败,别慌,直接进第五章对号入座。


五、踩坑实录与排障方法论(本文精华)

我们这次部署总共踩了 9 个坑,按出现顺序复盘。每个坑都给出现象 → 原因 → 解法

坑1:fnOS 图形界面建不了"完整"的容器

  • 现象:容器建好后 CUPS 的 Add Printer 页 Local Printers 是空的。
  • 原因:fnOS 的 Docker 界面不允许给已建容器补加特权模式和 /dev/bus/usb 设备映射,而界面上的"命令"输入框是容器内启动命令,干不了这个事。

fnOS 的容器设置界面:没有特权模式和设备映射的入口

  • 解法:删掉容器,用 3.2 的 docker run 命令行重建。凡是需要特权/设备映射的容器,直接命令行建。

坑2:管理页跳转"需要升级 / 172.17.0.2"

  • 现象:点 Add Printer 跳到 https://172.17.0.2:631/admin/,打不开。
  • 原因:CUPS 强制管理操作走 HTTPS,自动跳转用了容器内部 IP。
  • 解法:手动访问 https://NAS真实IP:631/admin/,接受自签名证书。

坑3:Local Printers 空白

  • 现象lsusb 在宿主机能看到打印机,容器里 Add Printer 却看不到。

    坑 #3:Local Printers 里没有 USB 打印机,只剩虚拟打印机和网络协议选项

  • 原因:USB 总线没映射进容器,或特权模式没开。

  • 解法:重建容器时确认 --privileged-v /dev/bus/usb:/dev/bus/usb 都在;验证 docker exec cups ls /dev/bus/usb/003/。另外 USB 打印机是插拔后才被容器感知的,后插打印机就重启一次容器。

坑4:apt 全部 404

  • 现象:容器里 apt update 一堆 404 Not Found
  • 原因:ydkn/cups 基于 Debian 10(buster),官方源已下架,迁移到了存档站。
  • 解法:换源到 archive.debian.org,并加 Acquire::Check-Valid-Until=false 跳过过期时间戳校验(命令见 4.4)。

坑5:管理页突然 404 / "Filter ... not found"

  • 现象:装完几个包之后,Administration 页面直接"未找到";日志里 admin.cgi not availableFilter "texttopdf" not found

    坑 #5:/admin 页面 404"未找到"

  • 原因:镜像本身精简,缺组件;安装/卸载过程的依赖解析还可能把 cups 本体搞坏。

  • 解法apt-get install --reinstall cups cups-common 补回 Web 组件,再装 cups-filters。教训:在容器里做 apt 操作后,先看一眼 Web 界面和日志再往下走。

坑6:所有驱动都报 "Filter failed"

  • 现象:换什么驱动,测试页都是 stopped "Filter failed"

    坑 #6:测试页任务 stopped,原因 Filter failed

  • 排查过程(这是全套方法论的第一次实战):

    1. 开调试日志:docker exec cups cupsctl --debug-logging,重打后 tail -80 /var/log/cups/error_log
    2. 日志显示 bannertopdf/pdftopdf 报 "can't find PDF header",foomatic-rip 报 "Unknown filetype",Sent 0 bytes
    3. 逐级定位:texttopdf 失败 → 发现容器一款字体都没装(渲染 PDF 需要字体)→ 装 fonts-dejavu-core
    4. 字体好了 foomatic-rip 还是不行 → 发现 foomatic-filters 包里的古董版 foomatic-rip(不认识 PDF 工作流)覆盖了 cups-filters 的现代版apt remove foomatic-filters foomatic-db-engineapt install --reinstall cups-filters
  • 教训:老教程让你装的 foomatic-filters 在 PDF 工作流时代是负资产,现代的 foomatic-rip 在 cups-filters 包里。最终我们干脆放弃 foomatic 路线,换了完全绕开它的 brlaser 驱动。

坑7:任务卡住不动(pending)

  • 现象:测试页提交后一直 pending,打印机动都不动。

  • 原因:之前某次失败后,CUPS 按默认策略(ErrorPolicy=stop-printer)自动把打印机暂停了,Web 界面状态是 Paused

    坑 #7:打印机状态 Paused,任务永远 pending

  • 解法docker exec cups cupsenable M7206 恢复;并根治:

sudo docker exec cups lpadmin -p M7206 -o printer-error-policy=retry-job

坑8(终极坑):过滤器单测全过、cupsd 调度必挂

  • 现象:手动跑 texttopdfpdftopdfgstorasterrastertobrlaser 每一个都正常,甚至 -o raw 把数据直接灌给打印机都能出纸——但只要通过 cupsd 正常打印,过滤器链就拿到"损坏的 PDF",全链崩溃。
  • 排查:对比两种场景的环境变量,发现 cupsd 强制 TMPDIR=/var/spool/cups/tmp——而我们把 /var/spool/cups 挂载到了宿主机目录。fnOS 的存储文件系统对这些过滤器创建临时文件的操作不兼容,导致临时文件损坏。
  • 解法重建容器,去掉 /var/spool/cups 挂载(打印队列本来就是易失数据,不需要持久化)。改完立刻全链路打通。
  • 教训:NAS 上玩 Docker,挂载目录要克制——只挂真正需要持久化的(这里是 /etc/cups),随手多挂的目录可能引入诡异的文件系统兼容性问题。

坑9:手机搜不到 / 显示"连接异常"

  • 现象 A(搜不到):容器是 bridge 网络 + avahi 没运行,mDNS 广播出不来。

    • 解法:装 avahi-daemon dbus libnss-mdns,容器改 --network host 重建,启动命令改为 bash -c "service dbus start && service avahi-daemon start && cupsd -f"。注意 host 模式和 -p 端口映射不能同时用。另外检查路由器的 AP 隔离/访客网络会阻断组播。
  • 现象 B(能搜到但连接异常):mDNS 把 NAS 的 IPv6 地址给了手机,而 cupsd 只监听 IPv4。

    坑 #9:手机能发现打印机,但提示"打印机连接异常"

    • 解法:手机端手动按 IPv4 地址添加(填 NAS 的 192.168.x.x),一劳永逸。验证监听情况:docker exec cups bash -c 'grep -i 0277 /proc/net/tcp /proc/net/tcp6'(0277 即 631 端口的十六进制)。

附:通用排障方法论(比答案更重要)

这次排查最大的收获是一套分层定位法,任何打印问题都能套:

第 1 层:硬件识别 lsusb(宿主机)→ ls /dev/bus/usb(容器)
第 2 层:服务可用 Web 界面能开?lpstat -t 队列状态?
第 3 层:过滤器链 开 debug 日志,看哪个 PID 退出码非 0
第 4 层:单件验证 手动跑过滤器(注意全路径 /usr/lib/cups/filter/xxx)
第 5 层:端到端 手动串联整个链,最后 -o raw 直灌验证硬件

两个特别好用的技巧:

  1. 手动逐级跑过滤器docker exec cups bash -c '/usr/lib/cups/filter/texttopdf 1 root t 1 "" /tmp/t.txt > /tmp/t.pdf',每一步的产物用 qpdf --checkls -la 验证,哪一步产物坏了哪一步就是凶手;
  2. raw 直灌验证硬件:链条产物(如 brlaser 输出的 .br 文件)用 lp -d M7206 -o raw 文件 绕过所有过滤器直接发给打印机——出纸就证明"驱动+硬件"没问题,问题只在调度层。这一招直接把排查范围砍掉一半。

还有三个日志的正确打开方式:

sudo docker exec cups lpstat -t # 队列状态(paused 是常客)
sudo docker exec cups tail -50 /var/log/cups/error_log # 过滤器报错
sudo docker exec cups tail -20 /var/log/cups/access_log # 客户端到底来没来、返回什么码

六、各终端连接方式

服务器调通后,全家设备连接都是一分钟的事。

Windows

  1. 先装联想官网的 M7206 Windows 驱动;

  2. 设置 → 打印机和扫描仪 → 添加设备 → 手动添加 → 按名称选择共享打印机,填:

    http://NAS-IP:631/printers/M7206
  3. 选择 M7206 驱动 → 完成。Windows 用自己的驱动渲染,效果和直插电脑完全一致。

macOS

添加打印机,协议选 IPP,地址 ipp://NAS-IP:631/printers/M7206。开了 Avahi 的话会直接在列表里自动出现。

安卓

  • 自动发现:开了 Avahi + host 网络后,设置 → 打印 → 默认打印服务,直接能看到 M7206 @ fnos-主机名
  • 连不上就手动:添加打印机 → 按 IP 添加 → 填 NAS 的 IPv4 地址(绕开 IPv6 解析问题,见坑 #9);
  • 系统打印服务不好使就装 Mopria Print Service(打印行业标准,兼容性最好)。

iPhone / iPad

开了 Avahi 后 AirPrint 直接搜到,照片/文件里分享 → 打印即可。


七、固化与日常维护

7.1 固化镜像(重要!)

容器里 apt 装的所有组件,容器一删就没。调通后立刻提交成自己的镜像:

sudo docker commit cups cups-m7206:fixed

以后重建容器用这个镜像,命令模板(注意依然没有 spool 挂载):

sudo docker run -d \
--name cups \
--restart unless-stopped \
--privileged \
--network host \
-v /dev/bus/usb:/dev/bus/usb \
-v /vol1/1000/docker/cups/etc:/etc/cups \
-e CUPSADMIN=admin \
-e CUPSPASSWORD=你的密码 \
cups-m7206:fixed \
bash -c "service dbus start && service avahi-daemon start && cupsd -f"

7.2 日常"罢工"三板斧

打印机突然打不了,按顺序查:

sudo docker exec cups lpstat -t # 队列是不是 paused/disabled → cupsenable 恢复
sudo docker restart cups # USB 拔插过/容器异常 → 重启容器
sudo docker exec cups tail -20 /var/log/cups/error_log # 看具体报错

7.3 一张备忘表

项目
管理页面http://NAS-IP:631
共享地址http://NAS-IP:631/printers/M7206
驱动brlaser(Brother DCP-1510)
固化镜像cups-m7206:fixed
配置目录/vol1/1000/docker/cups/etc(别删)
铁律重建容器不要挂 /var/spool/cups

八、总结

整个方案零新增硬件成本,把一台只能插 USB 的老联想 M7206 变成了支持 Windows / macOS / 安卓 / iPhone 的全功能网络打印机,还有打印队列、Web 管理这些"企业级"功能。

回头看,技术本身不复杂(CUPS + Docker),真正的难点全在工程细节里:设备映射、发行版源退役、驱动血缘(联想贴牌兄弟)、容器环境差异、存储挂载的兼容性。希望这篇复盘里的 9 个坑和分层排障法,能让后来者把两三天的摸索缩短到一个下午。

Happy printing!