自己跑 Docker 服務,平時最容易忽略的一件事,就是你以為“容器都在正常執行”,所以備份這件事可以晚點再說。真正出問題時,麻煩往往不是把容器重新拉起來,而是卷裡的資料、面板裡的設定、還有你自己改過的那些東西,到底還能不能找回來。

我最後定了一套比較剋制的方案,不追求很重的備份系統,也不先上物件儲存。思路很簡單:每天把 Docker 卷和 1Panel 設定打包一份,本地只留最近 7 天,再把壓縮包同步到 GitHub 私有儲存庫。這樣做不花哨,但它至少把“最近幾天能快速還原”和“遠端還有一份兜底”這兩件事先解決了。

如果你現在正好在搜這些關鍵詞,比如 Docker 自動備份、Docker 卷備份到 GitHub、1Panel 備份方案,這篇文章基本就是圍繞這幾個問題展開的。

我為什麼沒把它做成一套很重的備份系統

很多人一提備份,第一反應就是物件儲存、版本歸檔、異地多副本、專門備份平臺。方向當然沒問題,但對個人伺服器或者小團隊機器來說,真正卡住的往往不是“方案不夠先進”,而是根本沒有一套能穩定每天跑的東西。

我更在意的是三件事:

  • 先自動跑起來,不靠人記著手工執行
  • 出問題時最近幾天能快速還原
  • 備份檔案別隻放在當前這臺伺服器上

所以這次沒有把鏈路做得特別複雜,而是壓成了下面這個結構:

Docker 卷 / 1Panel 配置
        ↓
  docker-sync 脚本
        ↓
本地生成 backup_<timestamp>
        ↓
删除 7 天前旧备份
        ↓
复制压缩包到 Git 仓库目录
        ↓
Git LFS 跟踪 .tar.gz
        ↓
推送到 GitHub 私有仓库

它不適合特別大的備份量,也不是嚴格意義上的企業級容災。但如果你現在是用 1Panel 跑幾個站、幾個資料庫、幾個服務,這種“7 天本地 + GitHub 私有儲存庫同步”的組合其實挺實用。

先把 GitHub 信任打通,不然後面都白搭

這件事我建議先做,而且一定要先手工驗證一次。原因很簡單,後面無論你用 Shell、1Panel 計劃任務還是別的自動化方式,最終都得落到“這臺伺服器能不能把東西推到 GitHub”。

在伺服器上先執行:

ssh-keygen -t ed25519 -C "[email protected]"
cat ~/.ssh/id_ed25519.pub
ssh -T [email protected]

第一條是生成金鑰,第二條把公鑰拷出來放進 GitHub 的 SSH keys,第三條是第一次手工驗證連線。

這一步別偷懶。很多自動任務一開始就失敗,不是腳本有問題,而是 SSH 壓根沒通。你把這件事提前確認掉,後面排查會省很多時間。

順手說一下,備份儲存庫最好單獨建,不要和業務程式碼混在一起。我這裡用的是這種形式:

[email protected]:kuangyongjie/my-docker-backup.git

儲存庫最好設成 Private,這個沒什麼好猶豫的。

我把整個流程壓成了一個 docker-sync 命令

如果備份邏輯只是臨時放在某個腳本目錄裡,過兩個月你大機率就忘了它到底在哪、該怎麼跑、改了什麼。我的習慣是,能長期維護的東西,入口最好固定。

所以我最後直接把它封裝成了一個系統命令:

/usr/local/bin/docker-sync

這樣無論是我自己手工執行,還是後面讓 1Panel 排程,呼叫方式都一樣。下面這段腳本就是核心:

cat << 'EOF' > /usr/local/bin/docker-sync
#!/bin/bash

GITHUB_REPO="[email protected]:kuangyongjie/my-docker-backup.git"
GIT_USER_NAME="kuangyongjie"
GIT_USER_EMAIL="[email protected]"
LOCAL_SYNC_PATH="/root/github_sync_space"

if ! command -v git-lfs &> /dev/null; then
    apt-get update && apt-get install -y git git-lfs && git lfs install
fi

if [ ! -d "$LOCAL_SYNC_PATH/.git" ]; then
    echo "--- 第一次运行:初始化同步目录 ---"
    rm -rf "$LOCAL_SYNC_PATH"
    GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" git clone "$GITHUB_REPO" "$LOCAL_SYNC_PATH"
    cd "$LOCAL_SYNC_PATH"
    git lfs track "*.tar.gz" && git add .gitattributes
    git config user.name "$GIT_USER_NAME" && git config user.email "$GIT_USER_EMAIL"
    git commit -m "Init LFS"
else
    cd "$LOCAL_SYNC_PATH" && git pull origin main --rebase
fi

TAG=$(date +%Y%m%d_%H%M%S)
DIR="/root/docker_backups/backup_$TAG"
mkdir -p "$DIR"

echo "--- 正在抓取本地数据 ---"
for vol in $(docker volume ls -q); do
    tar -zcf "$DIR/vol_$vol.tar.gz" -C $(docker volume inspect $vol --format '{{.Mountpoint}}') .
done

tar -zcf "$DIR/1panel_apps_config.tar.gz" /opt/1panel/apps

echo "--- 正在清理本地 7 天前的旧备份 ---"
find /root/docker_backups/ -maxdepth 1 -name "backup_*" -type d -mtime +7 -exec rm -rf {} +

echo "--- 正在同步到 GitHub ---"
cp "$DIR"/*.tar.gz "$LOCAL_SYNC_PATH/"
cd "$LOCAL_SYNC_PATH"
git add .
git commit -m "Auto Backup $TAG"
git push origin main
echo "--- 完美!所有任务已完成 ---"
EOF

chmod +x /usr/local/bin/docker-sync

這段腳本其實沒那麼複雜,拆開看就六件事

很多人一看到長一點的 Shell 腳本就不想看了,但這段邏輯其實挺直白。

它無非就是:

  1. 如果沒裝 git-lfs,先裝
  2. 第一次執行時,克隆備份儲存庫並初始化 LFS
  3. 遍歷 Docker 卷,把每個卷單獨打成 tar.gz
  4. 順手把 /opt/1panel/apps 也打進去
  5. 清理 7 天前的本地備份目錄
  6. 把壓縮包複製到同步目錄,提交併推到 GitHub

這裡我比較喜歡的一點是,鏈路沒有額外引入很重的依賴。Shell、Git、Git LFS、Docker 卷路徑,這些東西本來就在機器上或者很容易裝。後面哪一步壞了,也比較容易定位。

真正需要注意的反而不是“腳本長不長”,而是兩個細節:

  • git commit 在沒有新檔案變更時會直接失敗
  • docker volume ls -q 會把所有卷都掃進去,所以你最好知道自己這臺機器上到底有哪些卷

如果你後面要繼續打磨,可以先從這兩個點入手。

1Panel 這邊別塞長腳本,計劃任務只負責呼叫

我不太建議把一大段備份腳本直接貼進 1Panel 計劃任務裡。短期看似乎省事,長期維護會很煩,尤其是你以後要改儲存庫地址、保留天數、使用者資訊的時候。

更省心的方式是:

  1. 先在命令列裡把 docker-sync 手工跑通
  2. 再進 1Panel 建立計劃任務
  3. 任務型別選 Shell 腳本
  4. 每天跑一次,比如凌晨 03:00
  5. 腳本內容只寫:
docker-sync

這樣後面如果某天備份失敗,你只需要 SSH 上去手工跑一次 docker-sync,馬上就能看日誌,不用先回面板裡翻長腳本。

真要還原的時候,流程其實比備份更重要

很多備份方案寫到最後,只停在“檔案已經存起來了”。但實際出事時,你最關心的是下一步怎麼還原,而不是備份當初多優雅。

如果伺服器掛了,還原大致就是這條線:

  1. 在新伺服器克隆備份儲存庫
  2. 找到你要的那個時間點壓縮包
  3. 解壓對應的 vol_xxx.tar.gz
  4. 用 docker volume create 重新建立目標卷
  5. 把資料拷回這個卷的 Mountpoint
  6. 重新建立容器並掛載這個卷

1Panel 相關設定也是類似思路,先把設定目錄拿回來,再重新掛到新的執行環境裡。

這裡真正麻煩的不是“會不會解壓”,而是你平時有沒有把卷、服務、用途對應清楚。否則到了還原現場,檔案在,但你不知道哪個卷對應哪個服務,時間照樣會被拖很久。

我自己會常看的幾個命令

平時維護時,我比較常用的其實就這幾個:

需求命令
手動立即備份docker-sync
看本地備份佔了多少空間du -sh /root/docker_backups
看本地備份目錄ls -lh /root/docker_backups
看最近同步記錄cd /root/github_sync_space && git log -n 5
調整本地保留天數nano /usr/local/bin/docker-sync

如果你只是想把 7 天改成 14 天,或者改成 3 天,直接改 find ... -mtime +7 那一段就行,不需要重構整個腳本。

這套方案最容易翻車的地方

先說結論,通常不是壓縮命令本身有多難,而是下面這些地方最容易出問題:

  • 伺服器到 GitHub 的 SSH 信任沒配完整
  • git-lfs 沒裝或者沒初始化
  • 本地同步目錄第一次初始化時權限不對
  • 計劃任務能跑,但儲存庫裡其實沒有新檔案,所以 git commit 失敗

還有一個現實問題也得提前接受:GitHub 私有儲存庫可以拿來做這種 Docker 備份同步,但它更適合設定、小型資料庫卷、常見應用資料這類體量還算可控的場景。真要是備份越來越大,後面遲早還是要往物件儲存或者專門的備份介質遷。

所以這套方案的定位我覺得要看準一點,它不是“最終版容災體系”,而是“先讓你的 1Panel / Docker 伺服器真正擁有一套穩定可用的自動備份”。