backups werden mit rsync uebertragen
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
## Gitea-Backup mit Portainer Community Edition
|
||||
|
||||
Diese Anleitung beschreibt ein praxistaugliches Backup für eine Gitea-Instanz, die per Docker Compose bereitgestellt und über Portainer Community Edition verwaltet wird. Portainer CE bietet keine nativen Scheduled Tasks wie die Business Edition, daher sollte das eigentliche Backup über den Docker-Host per Cron ausgeführt werden.[web:40]
|
||||
Diese Anleitung beschreibt ein praxistaugliches Backup für eine Gitea-Instanz, die per Docker Compose bereitgestellt und über Portainer Community Edition verwaltet wird. Portainer CE bietet keine nativen Scheduled Tasks wie die Business Edition, daher sollte das eigentliche Backup über den Docker-Host per Cron ausgeführt werden.[web:62][web:65]
|
||||
|
||||
## Ausgangslage
|
||||
|
||||
@@ -10,14 +10,15 @@ In der gezeigten Compose-Datei läuft Gitea als Service `server` mit dem Contain
|
||||
|
||||
## Empfohlene Methode
|
||||
|
||||
Für eine Gitea-Instanz mit SQLite ist der eingebaute Befehl `gitea dump` die sauberste Backup-Methode. Laut Gitea-Dokumentation enthält der Dump die Datenbank, Repositories, Konfiguration und weitere Instanzdaten in einem Archiv.[web:1]
|
||||
Für eine Gitea-Instanz ist der eingebaute Befehl `gitea dump` die offizielle Backup-Methode. Laut Gitea-Dokumentation enthält der Dump Datenbank, Repositories, Konfiguration und weitere Instanzdaten in einem Archiv.[web:1][web:13]
|
||||
|
||||
Gleichzeitig weist die Gitea-Dokumentation darauf hin, dass für vollständige Konsistenz die Instanz während des Backups gestoppt werden sollte, weil Datenbank, Dateien und Repositories sich während des Betriebs ändern können.[web:1]
|
||||
Gleichzeitig weist die Gitea-Dokumentation darauf hin, dass für vollständige Konsistenz die Instanz während des Backups gestoppt werden sollte, weil Datenbank, Dateien und Repositories sich während des Betriebs ändern können.[web:1][web:13]
|
||||
|
||||
Für die vorliegende Portainer-CE-Variante ist deshalb folgendes Vorgehen praxisnah:
|
||||
|
||||
- Standardfall: `gitea dump` im laufenden Betrieb, wenn eine kleine Inkonsistenz im Fehlerfall tolerierbar ist.[web:1]
|
||||
- Strenger Fall: Container kurz stoppen, Backup erstellen, Container wieder starten, wenn maximale Konsistenz wichtiger ist.[web:1]
|
||||
- Offsite-Fall: das lokal erzeugte ZIP anschließend per `rsync` über SSH auf einen zweiten Rechner übertragen.[web:61][web:63]
|
||||
|
||||
## Backup im laufenden Betrieb
|
||||
|
||||
@@ -43,9 +44,9 @@ docker start forgejo
|
||||
|
||||
Diese Variante minimiert Race Conditions zwischen SQLite-Datei, Repositories und Metadaten. Für produktionsnahe Setups ist sie die konservativere und technisch sauberere Lösung.[web:1]
|
||||
|
||||
## Backup-Skript für Portainer CE
|
||||
## Backup-Skript mit rsync-Offsite-Kopie
|
||||
|
||||
Das folgende Skript ist für Portainer CE geeignet, weil es unabhängig von Portainer direkt auf dem Docker-Host läuft. Es erstellt den Dump, verschiebt ihn in ein separates Backup-Verzeichnis und löscht alte Sicherungen automatisch.[web:1][web:40]
|
||||
Das folgende Skript ist für Portainer CE geeignet, weil es unabhängig von Portainer direkt auf dem Docker-Host läuft. Es erstellt den Dump, verschiebt ihn in ein separates lokales Backup-Verzeichnis, überträgt ihn danach per `rsync` auf einen zweiten Rechner und löscht lokale Altbestände automatisch.[web:1][web:61][web:63][web:66]
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
@@ -56,6 +57,12 @@ DATA_DIR="/home/hans/forgejo/data"
|
||||
CONTAINER_NAME="forgejo"
|
||||
RETENTION_DAYS=14
|
||||
|
||||
REMOTE_USER="backup"
|
||||
REMOTE_HOST="192.168.1.50"
|
||||
REMOTE_DIR="/srv/backup/gitea"
|
||||
SSH_KEY="/root/.ssh/id_ed25519_gitea_backup"
|
||||
SSH_OPTS="-i ${SSH_KEY} -o BatchMode=yes -o StrictHostKeyChecking=yes"
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
TIMESTAMP=$(date +%Y%m%d-%H%M)
|
||||
TMP_FILE="$DATA_DIR/gitea-dump.zip"
|
||||
@@ -65,24 +72,47 @@ FINAL_FILE="$BACKUP_DIR/gitea-dump-$TIMESTAMP.zip"
|
||||
|
||||
mv "$TMP_FILE" "$FINAL_FILE"
|
||||
chmod 600 "$FINAL_FILE"
|
||||
|
||||
rsync -a --partial --human-readable --mkpath -e "ssh ${SSH_OPTS}" "$FINAL_FILE" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/"
|
||||
|
||||
find "$BACKUP_DIR" -name 'gitea-dump-*.zip' -mtime +$RETENTION_DAYS -delete
|
||||
```
|
||||
|
||||
Empfohlener Speicherort ist `/usr/local/bin/gitea-backup.sh`. Das Skript sollte nur für root schreibbar sein.
|
||||
`rsync -a` läuft im Archivmodus und erhält Attribute, während `--partial` unvollständige Übertragungen für spätere Wiederaufnahme stehen lässt. `--mkpath` kann das Zielverzeichnis auf dem Remote-System automatisch anlegen, sofern die eingesetzte rsync-Version diese Option unterstützt.[web:61][web:63][web:66]
|
||||
|
||||
Da das Gitea-Backup bereits als ZIP vorliegt, bringt `-z` für zusätzliche Transportkompression meist wenig. Auf langsamen WAN-Strecken kann es trotzdem getestet werden, auf schnellen LAN-Verbindungen ist es meist entbehrlich.[web:63][web:66]
|
||||
|
||||
## SSH-Vorbereitung
|
||||
|
||||
Für Cronjobs sollte ein dedizierter SSH-Schlüssel ohne Passworteingabe verwendet werden. Zusätzlich sollte der Host-Key des Zielsystems vorab in `known_hosts` eingetragen werden, damit `StrictHostKeyChecking=yes` im unbeaufsichtigten Betrieb nicht blockiert.[web:63]
|
||||
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519_gitea_backup
|
||||
ssh-keyscan -H 192.168.1.50 >> /root/.ssh/known_hosts
|
||||
ssh-copy-id -i /root/.ssh/id_ed25519_gitea_backup.pub backup@192.168.1.50
|
||||
```
|
||||
|
||||
Auf dem Zielhost sollte das Backup-Verzeichnis dem dedizierten Backup-User gehören und nicht allgemein schreibbar sein.
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /srv/backup/gitea
|
||||
sudo chown backup:backup /srv/backup/gitea
|
||||
chmod 700 /srv/backup/gitea
|
||||
```
|
||||
|
||||
## Zeitgesteuerte Ausführung
|
||||
|
||||
Da Portainer CE keine Business-Scheduled-Tasks bereitstellt, ist ein Cronjob auf dem Host der robuste Standardweg.[web:40]
|
||||
Da Portainer CE keine Business-Scheduled-Tasks bereitstellt, ist ein Cronjob auf dem Host der robuste Standardweg.[web:62][web:65]
|
||||
|
||||
```cron
|
||||
30 2 * * * root /usr/local/bin/gitea-backup.sh >> /var/log/gitea-backup.log 2>&1
|
||||
```
|
||||
|
||||
Zusätzlich sinnvoll ist ein Monitoring auf Datei-Alter oder Exit-Code, damit fehlgeschlagene Backups nicht unbemerkt bleiben.
|
||||
Zusätzlich sinnvoll ist ein Monitoring auf Datei-Alter, Exit-Code und rsync-Fehler, damit fehlgeschlagene Backups oder unterbrochene Offsite-Übertragungen nicht unbemerkt bleiben.
|
||||
|
||||
## Restore
|
||||
|
||||
Gitea bietet keinen automatischen Restore-Befehl; die Wiederherstellung ist laut Dokumentation ein manueller Prozess. Nach dem Zurückspielen der Daten sollten die Git-Hooks neu generiert werden, damit Pushes wieder korrekt funktionieren.[web:1]
|
||||
Gitea bietet keinen automatischen Restore-Befehl; die Wiederherstellung ist laut Dokumentation ein manueller Prozess. Nach dem Zurückspielen der Daten sollten die Git-Hooks neu generiert werden, damit Pushes wieder korrekt funktionieren.[web:1][web:13]
|
||||
|
||||
```bash
|
||||
docker stop forgejo
|
||||
@@ -101,6 +131,6 @@ Ein Restore sollte regelmäßig in einer Testumgebung geprüft werden. Nur ein e
|
||||
|
||||
## Härtung
|
||||
|
||||
Portainer wird üblicherweise mit Zugriff auf den Docker-Socket betrieben. Dadurch ist Portainer ein mächtiges Verwaltungswerkzeug, aber auch sicherheitsrelevant und sollte per HTTPS und mit restriktivem Zugriff betrieben werden.[web:40]
|
||||
Portainer ist ein mächtiges Verwaltungswerkzeug und sollte nur restriktiv erreichbar sein. Für produktionsnahe Setups sind HTTPS, starke Authentisierung und ein minimaler administrativer Zugriff sinnvoll.[web:68]
|
||||
|
||||
Außerdem sollten Backups nicht nur lokal auf demselben Host liegen. Ein zusätzliches Ziel wie NAS, rsync-Backup oder S3-kompatibler Storage reduziert das Risiko bei Host-Ausfall erheblich.
|
||||
Zusätzlich sollten Backups nicht nur lokal auf demselben Host liegen. Eine zusätzliche Kopie auf einem zweiten Rechner per `rsync` über SSH reduziert das Risiko bei Host-Ausfall, Dateisystemfehlern oder versehentlichem Löschen deutlich.[web:61][web:63]
|
||||
|
||||
+15
-20
@@ -1,32 +1,27 @@
|
||||
#!/bin/bash
|
||||
# /usr/local/bin/gitea-backup.sh
|
||||
set -euo pipefail
|
||||
|
||||
BACKUP_DIR="/backup/gitea"
|
||||
DATA_DIR="/home/hans/forgejo/data"
|
||||
CONTAINER_NAME="forgejo" # Dein container_name aus der Compose-Datei
|
||||
CONTAINER_NAME="forgejo"
|
||||
RETENTION_DAYS=14
|
||||
|
||||
REMOTE_USER="hans"
|
||||
REMOTE_HOST="10.0.0.104"
|
||||
REMOTE_DIR="/volume1/homes/hans/backup_gitea"
|
||||
SSH_KEY="/root/.ssh/id_ed25519_gitea_backup"
|
||||
SSH_OPTS="-i ${SSH_KEY} -o BatchMode=yes -o StrictHostKeyChecking=yes"
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
TIMESTAMP=$(date +%Y%m%d-%H%M)
|
||||
TMP_FILE="$DATA_DIR/gitea-dump.zip"
|
||||
FINAL_FILE="$BACKUP_DIR/gitea-dump-$TIMESTAMP.zip"
|
||||
|
||||
echo "[$(date)] Starting Gitea backup..."
|
||||
/usr/bin/docker exec -u git -w /data "$CONTAINER_NAME" /app/gitea/gitea dump -c /data/gitea/conf/app.ini --file /data/gitea-dump.zip
|
||||
|
||||
# Backup im Container erstellen
|
||||
docker exec -u git -w /data "$CONTAINER_NAME" \
|
||||
/app/gitea/gitea dump -c /data/gitea/conf/app.ini \
|
||||
--file /data/gitea-dump.zip
|
||||
mv "$TMP_FILE" "$FINAL_FILE"
|
||||
chmod 600 "$FINAL_FILE"
|
||||
|
||||
# Vom gemappten Volume ins Backup-Verzeichnis verschieben
|
||||
if [ -f "$DATA_DIR/gitea-dump.zip" ]; then
|
||||
mv "$DATA_DIR/gitea-dump.zip" "$BACKUP_DIR/gitea-dump-$TIMESTAMP.zip"
|
||||
chmod 600 "$BACKUP_DIR/gitea-dump-$TIMESTAMP.zip"
|
||||
rsync -a --partial --human-readable -e "ssh ${SSH_OPTS}" "$FINAL_FILE" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/"
|
||||
|
||||
# Alte Backups entfernen
|
||||
find "$BACKUP_DIR" -name "gitea-dump-*.zip" -mtime +$RETENTION_DAYS -delete
|
||||
|
||||
SIZE=$(du -h "$BACKUP_DIR/gitea-dump-$TIMESTAMP.zip" | cut -f1)
|
||||
echo "[$(date)] Backup completed: gitea-dump-$TIMESTAMP.zip ($SIZE)"
|
||||
else
|
||||
echo "[$(date)] ERROR: Backup file not found!"
|
||||
exit 1
|
||||
fi
|
||||
find "$BACKUP_DIR" -name 'gitea-dump-*.zip' -mtime +$RETENTION_DAYS -delete
|
||||
Reference in New Issue
Block a user