linuxserver/calibre容器通过USB同步kobo

我在 linuxserver/colibre-web 实践中尝试通过内置的 Kobo Sync 功能来实现 Kobo Libra H2O 书籍同步,但是似乎由于书城功能无法激活而失败。所以我退而求其次,想要通过USB线直连方式进行书籍同步。

由于是在Linuxserver容器中运行 calibre-backend ,底层是全功能的Udev和Mnt挂载机制,所以方法是透传Kobo设备(对Linux系统显示为 /dev/sda 磁盘):

  • 首先确认在Host主机上连接的Kobo设备对应的块设备:

检查确认 /dev/sda 设备文件权限和主次设备号
$ ls -lh /dev/sda
brw-rw---- 1 root disk 8, 0 Jun 26 03:18 /dev/sda

$ sudo fdisk -l
...
Disk /dev/sda: 6.74 GiB, 7241267712 bytes, 14143101 sectors
Disk model: File-Stor Gadget
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x00000000
  • 修改 docker-compose.yml 透传设备:

    • devices : 将Host主机的 /dev/sda 映射为容器内的 /dev/sda

    • privileged: true (特权模式): 由于容器默认没有物理主机的 mount (挂载)内核特权,如果不放开权限,Calibre 内部的 Linux 系统在执行挂载时会报 Permission Denied 。直接开启 privileged(特权模式)设置比较简单

透传 /dev/sda 设备到容器内部
  calibre-backend:
    image: lscr.io/linuxserver/calibre:latest
    container_name: calibre-backend
    restart: always

    #privileged: true # 👈 关键:物理放开内核特权,允许容器在内部执行 mount/umount 挂载操作(不过这个权限过宽所以最终取消)
    #
    # **我现在取消 /dev/sda 映射进容器,改为在Host主机挂载sda到 /mnt/kobo ,然后映射目录,可避免容器高危权限
    #ipc: host         # 👈 核心修正 1:与宿主机共享 IPC 内存总线
    #cap_add:
    #  - SYS_ADMIN     # 👈 核心修正 2:允许容器在内部执行 mount/umount 挂载物理 Kobo 设备
    #  - SYS_RAWIO     # 👈 核心修正 3: 允许容器直接对透传的 /dev/sda 块设备进行原始 I/O 读写
    #devices:
    #  - "/dev/sda:/dev/sda" # 👈 核心:将物理主机的整个 Kobo 磁盘透传给容器内部
    # ************

    # ports: # 👈 注意:这里绝对不要再把 8080 映射到宿主机了!由 Nginx 独占宿主机 8080
    expose:
      - "8080"
    networks:
  • 重建 calibre-backend 容器是配置生效:

重建 calibre-backend 容器
docker compose up -d calibre-backend

docker-compose.yml 配置在 linuxserver/colibre使用GPU加速 按照gemini建议,上述 privileged: true 在新的 Linux 内核或 Docker 运行时(Runtime)中可能无法继承宿主机的 虚拟终端控制台(TTY)和共享内存(/dev/shm) ,所以调整增加 ipc: host ,以便能够继承主机的IPC内存总线

另外,虽然 privileged: true 是一种简单粗暴的开启容器特权的方法,但是这个方法使得容器能够完全访问物理主机的资源,破坏性极大非常不安全。所以,建议关闭 privileged: true ,而采用单独添加 cap_add 添加必要的权限。

警告

不过,我的实践 关闭 privileged: true ,而采用单独添加 cap_add 添加必要的权限 似乎遇到问题:

mount: /media/kobo: cannot mount /dev/sda read-only.
dmesg(1) may have more information after failed mount system call.

检查 dmesg

printk: dmesg (106582): Attempt to access syslog with CAP_SYS_ADMIN but no CAP_SYSLOG (deprecated).

所以我还是回滚使用 privileged: true

  • 由于 linuxserver/calibre 容器内桌面环境不会想常规Ubuntu自动挂载磁盘,所以需要进入容器内部手工创建挂载点:

进入容器内部:

进入容器内部
docker compose exec -it calibre-backend bash

在容器内部创建挂载目录( /media/kobo )

创建挂载目录并挂载
mkdir -p /media/kobo
mount -o uid=1000,gid=1000,utf8,dmask=002,fmask=113 /dev/sda /media/kobo

备注

一定要确保 /dev/sda 挂载采用 Calibre 进程的属主 abc 能够访问的方式挂载(abc用户的UID=1000,GID=1000),否则Calibre在执行 Connect to folder 会提示报错 Error communicating with device : 因为对设备目录没有写权限。

为方便执行,可以直接在Host主机上执行一条命令完成上述工作

一条命令完成挂载
docker exec -it calibre-backend bash -c 'mkdir -p /media/kobo && mount -o uid=1000,gid=1000,utf8,dmask=002,fmask=113 /dev/sda /media/kobo'

备注

我最初以为这种手工创建目录和挂载目录不能自动触发Calibre发现设备,需要执行下面的 Connect to folder 步骤。但是实践发现,只要执行了上述目录创建和挂载,Calibre就自动识别出了Kobo设备,就可以进行设备管理(自动出现设备按钮)

  • (废弃) 由于是通过Linux命令行挂载,Calibre的GUI界面无法通过底层USB自动弹窗显示"已连接Kobo设备" ,所以需要使用Calibre的 Connect to folder (连接到文件夹)功能,效果和插入USB电子书是一样的:

    • 通过浏览器访问 https://192.168.1.9:8080 介入Calibre桌面

    • 在工具栏,点击 Connect/Share 按钮,并在下拉菜单中选择 Connect to folder (连接到文件夹)

      • 在文件浏览器中,定位到刚才创建的 /media/kobo 目录,点击确认

      • 勾选 "Connect as device"

      • 选择对应的电子书设备 "kobo Libre H20"

  • 完成电子书管理之后,一定要将挂载卸载,然后再拔出kobo设备

卸载挂载才能拔出kobo设备
docker exec -it calibre-backend umount /media/kobo
  • 为了方便使用,可以建立alias,即修订 ~/.bashrc 添加:

建立alias
alias mount-kobo='docker exec -it calibre-backend bash -c "mkdir -p /media/kobo; mount -o uid=1000,gid=1000,utf8,dmask=002,fmask=113 /dev/sda /media/kobo"'
alias umount-kobo='docker exec -it calibre-backend umount /meida/kobo'

改进: 采用host挂载kobo,然后映射进容器

上述在容器内部直接挂载目录有一个非常大的缺陷是需要将磁盘设备透传进容器,并且赋予容器极大的权限,这不仅存在安全隐患,而且一旦物理上kobo被拔出,容器内的挂载点会直接变成死锁状态(D状态进程),整个 calibre-backend 会卡死,甚至无法终止。

更好的办法是在Host物理主机上挂载目录 /mnt/kobo ,然后映射到容器内部使用,这就不必给容器赋予危险的权限

  • 在Host主机上执行目录挂载设置 mount-kobo alias:

目录挂载设置 ~/.bashrc
alias mount-kobo='sudo mount -o uid=1000,gid=1000,utf8,dmask=002,fmask=113 /dev/sda /mnt/kobo'
alias umount-kobo='sudo umount /mnt/kobo'

挂载完成后,修订 docker-compose.yml 只需要映射目录就可以:

映射目录
volumes:
      - ./calibre-config:/config
      - ./library:/library       
      - ./import:/import         
      # 🪐 物理降维打击:直接把宿主机已经挂载好的 Kobo 目录,映射进容器的 /media/kobo
      - /mnt/kobo:/media/kobo:shared