QNAP Nas 架設 `純 Calibre Content Server` 的怪奇方法

前幾天寫了一篇 QNAP Nas 架設 Calibre Web 的方法,但總覺得 Calibre Web 給的 OPDS Feed 不討喜,而且 Calibre Web 因為不支援多書庫所以必須建立多個 Container 來提供多個書庫的方式會造成要維護不同 Container 內的帳號,因此我尋思,到底要如何解決 Calibre 自帶的 Content Server 現有的問題以取代 Calibre Web

  1. linuxserver.io 所提供的 Image 會啟動 VNC 與 UI 程式,所以記憶體占用比較兇。因此我接下來會自己 Build Image 來跑一個純 Server。
  2. 跑純 Server 無法偵測 metadata.db 更新,因此提供的網頁與 Feed 都無法更新,我一樣用 inotifywait 解決。
  3. 書庫的 volume 若是唯讀模式,則 Content Server 無法運作,它就是要求要有寫入權限,雖然官方宣稱只是檢查 metadata.db 是否有升級的必要,若有必要才會進行升級,但這仍讓人擔心,畢竟我們要跑的架構是如同前一篇以 Windows 編輯,然後透過 QSync 同步到 Nas 的方式,基本上仍希望可以用唯讀Volume提供給 Content Server 使用。當然我也解決了,我會用 overlayfs 的方式讓 Content Server 認為可以讀寫,但實際上所有異動不會影響到 Host 層的檔案。

以下提供一個 Compose YAML,我已經在 QNAP Nas 上面運作成功。

services:
  calibre-content-server:
    container_name: calibre-content-server
    image: calibre-content-server:8.1.1.011
    build:
      context: ./
      dockerfile_inline: |
          FROM debian:bookworm
          RUN apt update && \
              apt install -y \
              locales inotify-tools ca-certificates wget xz-utils psmisc \
              python3 python3-pyqt6 libopengl0 libxcb-cursor0 libegl1 libopengl0 libfreetype6  && \
              locale-gen zh_TW.UTF-8
          RUN wget -nv -O- https://download.calibre-ebook.com/linux-installer.sh | sh /dev/stdin version=8.1.1
          RUN apt clean

    ports:
      - 8880:8880
    volumes:
      - "config:/config"
      - "/share/homes/pigo/.Qsync/calibre 書庫:/books-ro:ro"
    environment:
      - LC_ALL=zh_TW.UTF-8
      - TZ=Asia/Taipei
    cap_add:
      - SYS_ADMIN
    entrypoint: >
services:
  calibre-content-server:
    container_name: calibre-content-server
    image: calibre-content-server:8.1.1.011
    build:
      context: ./
      #  RUN apt update && apt install inotify-tools wget python3 libegl-dev libopengl0 libxcb-cursor-dev libfreetype-dev xz-utils python3-pyqt6 -y

      dockerfile_inline: |
          FROM debian:bookworm
          RUN apt update && \
              apt install -y \
              locales inotify-tools ca-certificates wget xz-utils psmisc \
              python3 python3-pyqt6 libopengl0 libxcb-cursor0 libegl1 libopengl0 libfreetype6  && \
              locale-gen zh_TW.UTF-8
          RUN wget -nv -O- https://download.calibre-ebook.com/linux-installer.sh | sh /dev/stdin version=8.1.1
          RUN apt clean

    ports:
      - 8880:8880
    volumes:
      - "config:/config"
      - "/share/homes/pigo/.Qsync/calibre 書庫:/books-ro:ro"
    environment:
      - LC_ALL=zh_TW.UTF-8
      - TZ=Asia/Taipei

    cap_add:
      - SYS_ADMIN
    entrypoint: >
      bash -c '
        set -e

        mkdir -p /books
        mkdir -p /tmp/overlay
        mount -t tmpfs tmpfs /tmp/overlay
        while true; do
          mkdir -p /tmp/overlay/{upper,work}
          mount -t overlay overlay -o lowerdir=/books-ro,upperdir=/tmp/overlay/upper,workdir=/tmp/overlay/work /books

          echo "啟動 content-server"
          calibre-server "/books/漫畫" "/books/漫畫-升頻版" --port=8880 --enable-auth --userdb /config/users.sqlite & echo $! > /tmp/calibre.pid
          SERVER_PID=$$(cat /tmp/calibre.pid)
          echo "monitoring database"
          inotifywait -e attrib "/books-ro/漫畫/metadata.db" "/books-ro/漫畫-升頻版/metadata.db" |
          while read -r file event; do
            now=$$(date "+%Y-%m-%d %H:%M:%S")
            echo "$$now 偵測到 $$file 發生異動。"

            echo "停止 content-server , PID=$$SERVER_PID"
            kill $$SERVER_PID
            while ps -o pid= --ppid $$SERVER_PID > /dev/null; do
              echo "尚有子行程未結束,一秒鐘後再檢查"
              sleep 1
            done
            echo "所有子行程已結束。"

            while kill -0 "$$SERVER_PID" 2>/dev/null; do
              echo "主行程 (PID: $$SERVER_PID) 仍在執行中,一秒鐘後再檢查"
              sleep 1
            done
            echo "主行程已結束。"

            ps aux

            echo "清除 overlayfs 資料"
            umount /books
            rm -rf /tmp/overlay/{upper,work}
          done
        done
      '
volumes:
  config:

稍微解釋一下

  1. entryscript 一開始就先建立 /books 並利用 mount 命令建立 overlayfs 對應到 /books-ro/books-ro 是我的實際書庫 volume,是唯讀模式,因此 content-server 會使用到的書庫路徑是 /books 具備讀寫權限。
  2. 我有兩個書庫名稱為 漫畫漫畫-升頻版 , 透過 inotifywait 命令來監控 /books-ro 這個 volume 的個書庫底下的 metadata.db,當發生異動時則 kill 原本的 content-server 行程,並且 umount 再 mount 後重啟,整個過程都不影響 HOST 的檔案。
  3. 注意有個比較少見的 cap_add: 給了 SYS_ADMIN,如果不給予權限,則不能用 mount 建立 overlayfs。
  4. 這個範例我是用 port 8880 來提供服務。

這段整段丟給 Container station 直接建立專案就可以了,但是會等非常久(大概要10分鐘以上吧,NAS挺慢的),因為會 build 一個乾淨的 image,所以必須等待。

如果跑成功了,必須以 ssh 方式進入 container 內手動建立用戶,這是 Calibre Content Server 唯一不便的,但也只需要做一次,改密碼則可以透過 Web UI 改。

我以 docker stat 列出先前文章建立的 Calibre WebCalibre Content Server 的狀態,可以看到Content Server 的記憶體用量不多,因為沒有跑 VNC 跟 UI,所以記憶體用量不到 100MB,而看看前篇提到的 Calibre Web+Inotify 由於跑的 Container 數量多,加總起來反而比 Content Server 記憶體用量還兇。

CONTAINER ID   NAME                        CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O         PIDS
e0e35ca6ff86   calibre-content-server      0.06%     85.13MiB / 3.676GiB   2.26%     6.76kB / 5.62kB   73.9MB / 65.5kB   18
9528bfe3a8ec   calibre-monitor             0.00%     7.742MiB / 3.676GiB   0.21%     71.5kB / 2.47kB   128MB / 0B        3
6f92e9db435b   calibre-web-xbook           0.13%     116.2MiB / 3.676GiB   3.09%     70.7kB / 3.22kB   833MB / 1.77MB    15
111d528e311a   calibre-web-comic-upscale   0.13%     83.79MiB / 3.676GiB   2.23%     68.3kB / 0B       494MB / 1.77MB    15
d5f6c0c203cc   calibre-web-comic           0.14%     87.12MiB / 3.676GiB   2.31%     168kB / 301kB     736MB / 1.77MB    15

最後仍要提醒,有興趣搭建的朋友,千萬別開放外網,我這種搞法都是家裡用的,畢竟 container 內都能 mount overlayfs 了,這都是給了 container 權限才可能做到,要是不小心被外面的人玩,有可能搞死NAS。

發佈留言