Backup dei volumi Docker su GitHub: disaster recovery a basso costo per 1Panel
Quando gestisci servizi in Docker, la parte fastidiosa raramente è riavviare i container. Il vero problema inizia quando un disco si rompe, una macchina va ricostruita o la configurazione del pannello viene persa e ti accorgi che i dati nei volumi non sono mai stati salvati in un modo che permetta davvero di ripristinarli.
Per questo ho finito per usare un modello di backup volutamente semplice sul mio server 1Panel: archiviare i volumi Docker ogni giorno, mantenere localmente gli ultimi sette giorni e sincronizzare i file compressi verso un repository GitHub privato tramite Git LFS. Non è il sistema di backup Docker più sofisticato possibile, ma è uno dei setup più semplici che offre comunque un percorso reale di recovery.
Se cerchi backup dei volumi Docker su GitHub, backup automatico Docker o un workflow pratico per 1Panel, questo è esattamente il problema affrontato qui.
Perché ho mantenuto il modello di backup volutamente piccolo
La tentazione è sempre quella di passare subito a uno stack più elaborato: object storage, policy di retention, replica off-site o perfino un prodotto dedicato al backup.
Più avanti può essere la scelta giusta. Ma su un server personale o una macchina di un piccolo team, il primo guadagno concreto è di solito molto più semplice:
- fare il backup ogni giorno
- tenere copie recenti sulla macchina per un recovery rapido
- conservare un’altra copia fuori dalla macchina
L’intero workflow ruota attorno a questa idea:
Docker volumes / 1Panel config
↓
docker-sync
↓
Create backup_<timestamp>
↓
Delete local backups older than 7 days
↓
Copy archives into Git working tree
↓
Track .tar.gz with Git LFS
↓
Push to a private GitHub repository
È abbastanza leggero da poter continuare a funzionare nel tempo, cosa più importante di un sistema teoricamente perfetto che non viene mai completato.
Prima fai funzionare bene la fiducia SSH con GitHub
Non toccherei nemmeno l’automazione finché il server non riesce a fare push su GitHub in modo pulito.
Sul server esegui:
ssh-keygen -t ed25519 -C "[email protected]"
cat ~/.ssh/id_ed25519.pub
ssh -T [email protected]
Il primo comando crea la chiave, il secondo permette di copiare la parte pubblica nelle SSH keys di GitHub, mentre il terzo è quello che conta di più: un test manuale della connessione.
Questo passaggio fa risparmiare tempo dopo. Molti job di backup schedulati non falliscono perché la logica di backup è sbagliata, ma perché SSH non è mai stato configurato completamente.
Preferisco inoltre usare un repository privato dedicato ai backup invece di mischiarli con un repository applicativo:
[email protected]:kuangyongjie/my-docker-backup.git
Privato è il default naturale in questo caso.
Ho racchiuso tutto in un unico comando docker-sync
Una cosa che cerco di evitare è lasciare logica operativa importante sepolta in qualche cartella casuale di script.
Se questo workflow deve durare mesi o anni, il punto di ingresso deve essere stabile. Per questo l’ho trasformato in un comando di sistema:
/usr/local/bin/docker-sync
Così lo stesso comando funziona sia nelle esecuzioni manuali sia nei task schedulati da 1Panel.
Ecco lo script principale:
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
Cosa fa davvero lo script
Lo script sembra più lungo di quanto sia.
In pratica svolge sei operazioni:
- installa
git-lfsse manca - clona e inizializza il repository di backup al primo avvio
- percorre i volumi Docker e archivia ognuno
- archivia la directory di configurazione delle app 1Panel
- rimuove i backup locali più vecchi di sette giorni
- copia i nuovi archivi nel repo di sincronizzazione, fa commit e push
Questa semplicità è esattamente ciò che mi piace del setup. Le parti mobili rimangono facili da capire.
Ci sono due punti che terrei d’occhio:
git commitfallisce se non c’è nulla di nuovo da committaredocker volume ls -qesegue il backup di ogni volume, quindi conviene sapere cosa esiste sulla macchina
Nessuno dei due è difficile da gestire, ma entrambi contano quando il processo inizia a funzionare senza supervisione.
In 1Panel mantieni minimale il task schedulato
Non incollerei l’intero script di backup nel task 1Panel.
È più pulito rendere solido prima il comando shell e lasciare a 1Panel soltanto lo scheduling:
- esegui
docker-syncmanualmente una volta e verifica che funzioni - crea un task schedulato in 1Panel
- scegli Shell Script
- eseguilo una volta al giorno, per esempio alle
03:00 - usa questo come intero body:
docker-sync
Questo rende molto più semplice il debugging. Se il job fallisce in futuro, puoi collegarti via SSH ed eseguire lo stesso comando direttamente invece di leggere uno script lungo dentro l’interfaccia del pannello.
Il recovery conta più dell’eleganza del backup
Molte guide si fermano a “i file ora sono salvati da qualche parte”. Non è la parte importante quando qualcosa si rompe davvero.
Il flusso di recovery è il test reale:
- clonare il repository di backup su un nuovo server
- trovare l’archivio relativo al momento corretto
- estrarre il
vol_xxx.tar.gznecessario - ricreare il volume Docker di destinazione
- copiare i dati nel mountpoint del volume
- ricreare il container e montare nuovamente il volume
I comandi esatti dipendono dal servizio da ripristinare, ma il percorso rimane grosso modo lo stesso.
La vera difficoltà di solito non è l’estrazione. È ricordare quale volume appartiene a quale servizio quando sei sotto pressione. Se adotti questo workflow, mantieni chiari naming dei volumi e mapping con i servizi.
Il piccolo insieme di comandi che uso davvero
Nella maggior parte dei casi mi servono solo questi controlli:
| Necessità | Comando |
|---|---|
| Avviare subito un backup | docker-sync |
| Controllare lo spazio usato localmente | du -sh /root/docker_backups |
| Vedere le cartelle locali | ls -lh /root/docker_backups |
| Controllare la cronologia recente | cd /root/github_sync_space && git log -n 5 |
| Cambiare i giorni di retention | nano /usr/local/bin/docker-sync |
Per modificare la finestra di retention basta cambiare la riga find ... -mtime +7. Non serve riprogettare tutto.
Dove è più probabile che questo setup fallisca
Nella pratica le parti fragili raramente sono i comandi tar.
Sono più spesso:
- fiducia SSH incompleta tra server e GitHub
git-lfsnon installato o non inizializzato- permessi errati nella directory locale di sincronizzazione
- esecuzioni schedulate in cui non è cambiato nulla e quindi
git committermina con codice non zero
C’è anche un problema di scala da non ignorare. Un repository GitHub privato va bene per configurazioni, quantità moderate di dati Docker e backup comuni delle app. Quando la dimensione degli archivi cresce rapidamente, object storage o un target di backup dedicato iniziano probabilmente ad avere più senso.
Quindi inquadrerei correttamente questo setup: non è la forma definitiva del disaster recovery. È un modo pratico per dare a un server Docker e 1Panel una vera abitudine di backup automatizzato senza introdurre subito molto overhead.