Featured image of post 使用 Docker 與 Traefik 部署高效能 Memos 與 Telegram 整合機器人

使用 Docker 與 Traefik 部署高效能 Memos 與 Telegram 整合機器人

介紹如何以 Docker Compose 與 Traefik 部署 Memos 筆記服務及 Memogram Telegram 機器人,採用 SSD + HDD 混合儲存架構解決 SQLite I/O 效能瓶頸,並涵蓋自動化備份排程與常見故障排除。

簡介

Memos 是一款具備高度隱私與開源特性的輕量級筆記服務。隨著筆記與附件數量的增加,傳統將資料庫與附件統一存放在高容量但低速的傳統硬碟 (HDD) 架構,容易導致 SQLite 發生嚴重的 I/O 瓶頸與 RPC 逾時錯誤。

本指南將帶領您透過 Docker Compose 與 Portainer,部署一套結合 Memos 與 Memogram (Telegram 整合機器人) 的系統。透過「混合儲存架構」(資料庫存放於高效能 SSD,大型附件存放於大容量 HDD),徹底解決讀取卡頓問題,並結合自動化備份腳本與 Traefik 進行安全連線管理,打造兼具效能與安全的個人知識庫。

先決條件

在開始部署之前,請確保您的伺服器具備以下環境與資源:

  • 一台運行 Linux (如 Ubuntu/Debian) 的伺服器,且使用者具有 sudo 權限。
  • 伺服器已安裝 Docker 與 Docker Compose。
  • 伺服器已安裝並設定好 Traefik 作為反向代理。
  • 伺服器具備雙硬碟架構:系統碟 (SSD,本例為 256GB) 與資料碟 (HDD,本例為 4TB)。
  • 已在 Telegram 透過 BotFather 取得專屬的 Bot Token。
  • 伺服器已安裝 sqlite3 命令列工具。

環境參數總覽

本指南所使用的所有關鍵路徑、網路設定與環境變數如下表所示。若您過去曾進行過多次測試,請以本表的最終確認版本為準。

參數類型參數名稱 / 路徑說明
儲存路徑 (SSD)/home/YOUR_USERNAME/memos存放 memos_prod.db 資料庫,提供極速查詢效能。
儲存路徑 (HDD)/mnt/cloud1/dockerData/memos/memosdb/assets存放圖片、影片等大型附件資源。
備份路徑 (HDD)/mnt/cloud1/dockerData/memos/backups存放每日自動產生的資料庫備份檔。
內部網路default (bridge)供 Memos 與 Memogram 內部通訊使用的 Docker 橋接網路。
外部網路traefik_network供 Traefik 進行外部流量路由的網路。
連線變數SERVER_ADDR=dns:memos:5230Memogram 連線至 Memos 的 gRPC 內部解析位址。
授權變數ALLOWED_USERNAMES限制可使用 Telegram 機器人的使用者名稱清單 (半形逗號分隔,不含 @)。

逐步教學

步驟一:建立混合儲存目錄架構

為了將資料庫的 I/O 負載與大型檔案的儲存負載分離,我們需要分別在 SSD 與 HDD 上建立對應的目錄。請在終端機執行以下指令:

# 建立 SSD 資料庫目錄
mkdir -p /home/YOUR_USERNAME/memos

# 建立 HDD 附件與備份目錄
mkdir -p /mnt/cloud1/dockerData/memos/memosdb/assets
mkdir -p /mnt/cloud1/dockerData/memos/backups

# 確保目錄擁有者正確 (避免 Docker 權限衝突)
sudo chown -R YOUR_USERNAME:YOUR_USERNAME /home/YOUR_USERNAME/memos
sudo chown -R YOUR_USERNAME:YOUR_USERNAME /mnt/cloud1/dockerData/memos

步驟二:撰寫與部署 Docker Compose

我們將使用 Portainer 的 Stack 功能來進行部署。請建立一個新的 Stack,並填入以下 docker-compose.yml 內容。此設定檔已將隱私資料抽離為環境變數,並妥善設定了內部網路。

version: "3.8"

networks:
  # 確保所有服務都接入 Traefik 的共用網路
  traefik_network:
    external: true
  # 明確宣告內部預設網路,確保同設定檔內跨網路服務 (如 App 與 DB) 可以互相溝通
  default:
    driver: bridge

services:
  memos:
    image: neosmemo/memos:stable
    container_name: memos
    environment:
      - MEMOS_PUBLIC=${MEMOS_PUBLIC_MODE}
      - TZ=Asia/Taipei
    volumes:
      # (1) 資料庫本體:掛載到 SSD 以獲取極速的 I/O 查詢
      - /home/YOUR_USERNAME/memos:/var/opt/memos
      # (2) 附件目錄:掛載到 4TB HDD 以利用大儲存空間
      - /mnt/cloud1/dockerData/memos/memosdb/assets:/var/opt/memos/assets
    restart: unless-stopped
    networks:
      - traefik_network
      - default
    labels:
      - "traefik.enable=true"
      # 網址設定:使用 .env 中的 APP_DOMAIN 變數來設定外部連線網址
      - "traefik.http.routers.${ROUTER_NAME}.rule=Host(`${APP_DOMAIN}`)"
      # 進入點:指定只接收 443 通訊埠 (HTTPS) 的安全連線
      - "traefik.http.routers.${ROUTER_NAME}.entrypoints=websecure"
      # 憑證解析器:呼叫 Traefik 主程式中的 myresolver 來處理 SSL 憑證
      - "traefik.http.routers.${ROUTER_NAME}.tls.certresolver=myresolver"
      # 內部通訊埠:使用 .env 中的 INTERNAL_PORT 告訴 Traefik 容器的真實通訊埠
      - "traefik.http.services.${ROUTER_NAME}.loadbalancer.server.port=${INTERNAL_PORT}"
      # 嚴格安全防護 (包含 HSTS,適合多數對外服務,預設開啟)
      - "traefik.http.routers.${ROUTER_NAME}.middlewares=sec-strict@file"
      # 指定 Traefik 路由時使用的網路,避免多網路環境下抓錯 IP 導致 500 錯誤
      - "traefik.docker.network=traefik_network"

  memogram:
    # 官方專案目前並未提供打包好的 Docker 映像檔,直接指定官方 GitHub 專案網址進行建置
    build: https://github.com/usememos/telegram-integration.git
    container_name: memogram
    environment:
      # Memos 正在運行的 gRPC 伺服器位址,使用內網連線直接指向 memos 容器名稱
      - SERVER_ADDR=${SERVER_ADDR}
      # 你的 Telegram 機器人 Token
      - BOT_TOKEN=${BOT_TOKEN}
      # (選用) Telegram API 的代理伺服器位址,若不需要請留空
      - BOT_PROXY_ADDR=${BOT_PROXY_ADDR}
      # (選用) 允許使用的 Telegram 使用者名稱列表,使用半形逗號分隔且不含 @ 符號
      - ALLOWED_USERNAMES=${ALLOWED_USERNAMES}
    restart: unless-stopped
    depends_on:
      - memos
    networks:
      # 只需要加入 default 網路即可與 memos 容器互相通訊,不需要對外暴露給 Traefik
      - default

在 Portainer 部署介面下方,請新增對應的 Environment variables (環境變數),例如 APP_DOMAIN, SERVER_ADDR (設定為 dns:memos:5230), BOT_TOKEN 等。

步驟三:設定 Telegram 機器人與儲存機制

容器啟動後,請進行以下關鍵設定:

  1. 設定 Memos 儲存路徑:登入 Memos 網頁介面,進入「設定」>「儲存」,將儲存服務改為「Local」,並確保路徑為 assets/{filename}。這能防止未來的附件被塞入 SQLite 資料庫中。
  2. 綁定 Telegram:在 Memos 系統中取得您的 Access Token。開啟 Telegram 並向您的機器人發送 /start <您的 Access Token> 完成綁定。

進階優化

自動化備份腳本與 Cron 排程

為防範系統碟 (SSD) 故障,我們將實作「熱備份」機制,將資料庫每日備份至 HDD。

  1. 建立腳本檔案:
nano /home/YOUR_USERNAME/backup_memos.sh
  1. 填入以下內容:
#!/bin/bash
# --- 變數設定 ---
DB_SOURCE="/home/YOUR_USERNAME/memos/memos_prod.db" 
BACKUP_DEST="/mnt/cloud1/dockerData/memos/backups" 
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p $BACKUP_DEST

if [ ! -f "$DB_SOURCE" ]; then
    echo "錯誤:找不到來源資料庫 $DB_SOURCE"
    exit 1
fi

# 使用 sqlite3 的 .backup 指令進行安全熱備份
sqlite3 $DB_SOURCE ".backup $BACKUP_DEST/memos_$DATE.db"

# 刪除超過 30 天的舊備份
find $BACKUP_DEST -mtime +30 -type f -name "*.db" -delete

echo "Memos 備份完成於 $DATE"
  1. 賦予執行權限並加入 Crontab 排程:
chmod +x /home/YOUR_USERNAME/backup_memos.sh
crontab -e

在檔案末端加入此行 (每日凌晨 03:00 執行):

00 03 * * * /bin/bash /home/YOUR_USERNAME/backup_memos.sh

常見問題與故障排除

1. Memos 讀取清單時發生 failed to list attachments (RPC 逾時錯誤)

  • 現象:Memos 網頁載入極慢,日誌顯示 internal: rpc error: code = Internal desc = failed to list attachments
  • 原因:SQLite 資料庫放置於傳統 HDD 上,當附件紀錄眾多且檔案碎片化時,磁碟的隨機讀寫速度無法負荷,導致 gRPC 連線逾時。
  • 最終解決邏輯:將資料庫檔案 (memos_prod.db) 移至 SSD 掛載路徑,並透過指令開啟 SQLite 的 WAL (Write-Ahead Logging) 模式以提升併發效能:
sudo sqlite3 /home/YOUR_USERNAME/memos/memos_prod.db "PRAGMA journal_mode=WAL;"

2. 資料庫檔案異常肥大 (數百 MB)

  • 現象:純文字筆記的資料庫高達 200MB 以上。
  • 原因:舊版設定將大型檔案 (如 MP4, PDF) 的實體二進位資料存入了資料庫的資料表中,而非外部的 assets 資料夾。
  • 最終解決邏輯:查詢並刪除大於 1MB 的異常紀錄,隨後執行 VACUUM 重組資料庫。 (注意:在 Memos v0.26.x 中,資源表已從 resource 更名為 attachment。)
# 刪除大於 1MB 的附件紀錄
sudo sqlite3 /home/YOUR_USERNAME/memos/memos_prod.db "DELETE FROM attachment WHERE size > 1000000;"
# 回收與重組空間
sudo sqlite3 /home/YOUR_USERNAME/memos/memos_prod.db "VACUUM;"

3. 執行 VACUUM 時出現 readonly database (8)

  • 現象:嘗試優化資料庫時報錯。
  • 原因:由 Docker 建立的資料庫檔案擁有者通常為容器內的使用者 (如 UID 10001),主機端的一般使用者缺乏寫入與建立暫存檔的權限。
  • 最終解決邏輯:在 sqlite3 指令前方加上 sudo 提升權限執行。

4. 終端機顯示 zsh: corrupt history file

  • 現象:操作終端機時出現歷史紀錄損毀警告。
  • 原因:系統意外斷電或硬碟 I/O 阻塞導致 .zsh_history 寫入不完整,產生亂碼。
  • 最終解決邏輯:過濾出正常字元並覆蓋原檔案。
cd ~
mv .zsh_history .zsh_history_bad
strings .zsh_history_bad > .zsh_history
fc -R .zsh_history

5. 清理無效的系統排程

  • 現象:系統持續每 5 分鐘執行已廢棄的 DuckDNS 更新腳本。
  • 最終解決邏輯:透過 crontab -e 刪除對應的排程行,並使用 rm -rf /home/YOUR_USERNAME/duckdns 徹底移除相關資料夾。

結論

透過上述指南,我們成功解決了 Memos 在傳統硬碟上的效能瓶頸。藉由將輕量級的資料庫遷移至 SSD,並將大容量附件卸載至 HDD,我們在極小的 SSD 空間消耗下獲得了顯著的效能提升。配合 Memogram 的 Docker 源碼建置部署、Traefik 的安全路由,以及每日自動化的資料庫備份與雲端同步規劃,您的知識庫系統現在已具備企業級的穩定度與災難復原能力。