diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 4122427..52acf41 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -9,7 +9,8 @@ "Bash(git add *)", "Bash(git commit *)", "Bash(git push *)", - "PowerShell(Test-NetConnection -ComputerName 192.168.1.50 -Port 27017 -InformationLevel Detailed)" + "PowerShell(Test-NetConnection -ComputerName 192.168.1.50 -Port 27017 -InformationLevel Detailed)", + "Bash(mkdir -p \"U:\\\\PycharmProjects\\\\Backup\\\\PostGRESQLTower\")" ] } } diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..753161d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,90 @@ +# Backup Project - Infrastructure Info + +## 🖥️ Servery + +| Server | Hostname | IP | OS | +|--------|----------|----|----| +| Tower | tower / tower.lan | 192.168.1.76 | Unraid | +| Tower1 | tower1 | 192.168.1.50 | Unraid | + +--- + +## 🔐 SSH přístup + +**Credentials (oba servery):** +- User: `root` +- Password: `Vlado7309208104++` + +**Připojení:** +```bash +ssh root@192.168.1.76 # Tower +ssh root@192.168.1.50 # Tower1 +``` + +**Důležité:** Tower a Tower1 mají vzájemně nahrané SSH klíče - Tower1 se může připojit na Tower bez hesla a naopak. + +--- + +## 🐳 Docker kontejnery + +| Server | Kontejner | Port | +|--------|-----------|------| +| Tower | `MongoDB` | 27017 | +| Tower1 | `MongoDB` | 27017 | +| Tower | `MySQL` | 3306 | + +--- + +## 📁 Důležité cesty + +### Tower +| Co | Cesta | +|----|-------| +| MongoDB zálohy | `/mnt/user/Backup/Critical/MongoDBBackup/` | +| MySQL zálohy | `/mnt/user/MySQLBackup/` | +| User Scripts | `/boot/config/plugins/user.scripts/scripts/` | +| Test share | `/mnt/user/#test/` | + +### Windows (lokální) +| Co | Cesta | +|----|-------| +| MongoDB zálohy | `\\tower\Backup\Critical\MongoDBBackup\` | +| MySQL zálohy | `\\tower\MySQLBackup\` | +| Test share | `\\tower1\#test\` | + +--- + +## 🗄️ MongoDB + +- **Verze:** 8.2.9 +- **Auth:** Žádná (otevřené) +- **Databáze k zálohování:** `admin`, `edc` (+ další dle skriptu) +- **Připojení z Pythonu:** +```python +from pymongo import MongoClient +client = MongoClient('mongodb://192.168.1.76:27017') # Tower +client = MongoClient('mongodb://192.168.1.50:27017') # Tower1 +``` + +--- + +## 🗄️ MySQL + +- **User:** `root` +- **Password:** `Vlado9674+` +- **Databáze:** `fio`, `torrents`, `OrdinaceDropBoxBackup`, `medevio`, `kanboard`, `medicus`, `studie`, `puzzle` + +--- + +## 📜 User Scripts na Tower (Unraid) + +| Název | Popis | Schedule | +|-------|-------|----------| +| `3_MYSQL_BACKUP_WITH_GZIP` | MySQL backup všech DB | Daily | +| `MONGODB_BACKUP` | MongoDB backup (`--archive --gzip`) | Daily | + +## 📜 User Scripts na Tower1 (Unraid) + +| Název | Popis | +|-------|-------| +| `MONGODB_RESTORE` | Restore edc DB z Tower přes SSH stream | diff --git a/MongTower/README.md b/MongTower/README.md deleted file mode 100644 index 805f3ed..0000000 --- a/MongTower/README.md +++ /dev/null @@ -1,236 +0,0 @@ -# MongoDB Backup & Restore Scripts - -Kompletní řešení pro zálohování a obnovu MongoDB databází na Unraid. - -## 📋 Obsah backup - -Skripty zálohují a obnovují **KOMPLETNĚ VŠECHNO**: -- ✅ **Všechny databáze** (včetně `admin` pro credentials a roles) -- ✅ **Všechny kolekce** a dokumenty (BSON formát) -- ✅ **Všechny indexy** (včetně unique, compound, text indexů) -- ✅ **Metadata kolekcí** (validators, options, atd.) -- ✅ **Uživatele a role** (v admin DB) -- ✅ **Komprimace gzipem** pro úsporu místa - -**Oveřeno zachowáno:** -- Indexové struktury se zachovávají identicky -- Collection validators (JSONSchema) se zachovávají -- Collection options (capped collections, atd.) se zachovávají -- Všechny system collections (`system.indexes`, `system.users`, atd.) -- Opaque object IDs, binary data, všechny BSON typy - -## 🔄 Použití - -### 1. **Backup skript** (`mongodbbackup_with_gzip.sh`) - -Zálohuje všechny DB do `/mnt/user/Backup/Critical/MongoDBBackup/tower/{DB_NAME}/{YYYY-MM-DD_HHMM}/` - -```bash -chmod +x mongodbbackup_with_gzip.sh -./mongodbbackup_with_gzip.sh -``` - -**Přidej do Unraid scheduled tasksu:** -``` -Schedule: Daily (nebo jak chceš) -Command: /mnt/user/path/mongodbbackup_with_gzip.sh -``` - -**Struktura záloh:** -``` -/mnt/user/Backup/Critical/MongoDBBackup/tower/ -├── admin/ -│ └── 2026-05-23_1530/ -│ ├── admin.archive.gz -│ └── admin.err (jen pokud je chyba) -├── fio/ -│ └── 2026-05-23_1530/ -│ ├── fio.archive.gz -│ └── ... -└── ... -``` - -**Konfigurace:** -- `CONTAINER_NAME` - jméno MongoDB kontejneru -- `KEEP_DAYS` - Jak dlouho se uchovávají staré zálohy (default: 3 dny) -- `WHAT_TO_BACKUP` - Seznam DB k zálohování - ---- - -### 2. **Restore skript** (`mongodbrestore_from_backup.sh`) - -Obnovuje všechny DB ze zálohy na LOCAL MongoDB. - -```bash -chmod +x mongodbrestore_from_backup.sh -./mongodbrestore_from_backup.sh /mnt/user/Backup/Critical/MongoDBBackup/tower -``` - -**Co skript dělá:** -1. ✅ Najde nejnovější backup pro KAŽDOU DB -2. ✅ Extrahuje .tar.gz archiv -3. ✅ Obnoví do MongoDB (s `--drop` na přepsání existujících) -4. ✅ Validuje úspěch -5. ✅ Čistí temp soubory - ---- - -### 3. **Ověřovací skript** (`verify_backup_integrity.sh`) - -Kontroluje že se VŠECHNO správně obnovilo (indexy, metadata, kolekce). - -```bash -chmod +x verify_backup_integrity.sh -./verify_backup_integrity.sh -``` - -**Co skript ověřuje:** -1. ✅ Zda všechny DB existují -2. ✅ Počet a jména kolekcí -3. ✅ Indexy pro každou kolekci (včetně jejich definic) -4. ✅ Počet dokumentů v každé kolekci -5. ✅ Velikost dat v MB -6. ✅ Uživatele a role (admin DB) - -**Příklad výstupu:** -``` -Verifying database: fio -✅ Database exists: fio -✅ Collections count: 8 - Collections: - - users - - products - - logs - Indexes per collection: - fio.users: 3 indexes - - { "_id": 1 } - - { "email": 1 } - - { "created_at": -1 } - Collection stats: - users: - Documents: 12345 - Size: 45.23 MB -``` - ---- - -## 🧪 Testování na 2 Unraid serverech - -### Scénář: Tower1 → Tower2 (Kompletní test) - -#### **Krok 1: Na Tower1 (zdroj) - Spusť backup:** -```bash -/mnt/user/Backup/Critical/MongoDBBackup/mongodbbackup_with_gzip.sh - -# Ověř že backup existuje -ls -lh /mnt/user/Backup/Critical/MongoDBBackup/tower/*/*/ -``` - -**Očekávaný výstup:** -``` -/mnt/user/Backup/Critical/MongoDBBackup/tower/ -├── admin/2026-05-23_1530/admin.archive.gz (234K) -├── fio/2026-05-23_1530/fio.archive.gz (45M) -├── torrents/2026-05-23_1530/torrents.archive.gz (... -└── ... -``` - -#### **Krok 2: Zkopíruj backup do Tower2:** -```bash -# Přes SMB share / rsync / fyzicky disku -# Zkopíruj celou strukturu: /mnt/user/Backup/Critical/MongoDBBackup/tower/ -``` - -#### **Krok 3: Na Tower2 (cíl) - Obnovte zálohu:** -```bash -/mnt/user/Backup/Critical/MongoDBBackup/mongodbrestore_from_backup.sh /mnt/user/Backup/Critical/MongoDBBackup/tower - -# Výstup by měl být: -# SUCCESS: admin restored successfully -# SUCCESS: fio restored successfully -# ... -``` - -#### **Krok 4: Ověř INTEGRITU na Tower2 - Nejdůležitější!** -```bash -/mnt/user/Backup/Critical/MongoDBBackup/verify_backup_integrity.sh -``` - -**To by mělo ověřit:** -- ✅ Všechny DB existují -- ✅ Všechny kolekce se obnovily -- ✅ Všechny indexy jsou tam (se stejnými definicemi!) -- ✅ Počet dokumentů je identický -- ✅ Uživatelé a role jsou obnovení - -#### **Krok 5: Manuální spot-check (volitelně):** -```bash -docker exec -it MongoDB mongosh --host localhost --port 27017 - -# Ověř indexy v konkrétní kolekci -use fio -db.some_collection.getIndexes() # Měly by být stejné jako na Tower1 - -# Ověř počet dokumentů -db.some_collection.countDocuments() # Měl by být identický - -# Ověř že data jsou bezpečná -db.some_collection.findOne() -``` - ---- - -## ⚠️ Důležité poznámky - -1. **`--drop` flag** - Restore skript obnovuje s `--drop`, což PŘEPÍŠE existující databázi. To je zvláště důležité pro test. - -2. **Admin DB** - Obsahuje uživatele a role. Pokud máš auth, musíš je správně obnovit. - -3. **Čištění** - Po úspěšném testu si můžeš vymazat temp soubory: - ```bash - rm -rf /tmp/mongodb_restore_* - ``` - -4. **Disk space** - Při restore se vytváří tmp soubory. Ověř prostor na `/tmp` (nebo `/mnt/user`). - -5. **MongoDB port** - Oba skripty očekávají MongoDB na `localhost:27017` (default). - ---- - -## 📊 Příklad výstupu - -``` -Starting scheduled backup for: admin fio torrents OrdinaceDropBoxBackup medevio kanboard medicus studie puzzle ------------------------------------------- -Processing database: admin -SUCCESS: admin backed up successfully -Dump size: 234K -Cleaning up old backups for admin... ------------------------------------------- -Processing database: fio -SUCCESS: fio backed up successfully -Dump size: 45M -Cleaning up old backups for fio... ------------------------------------------- -... -All backup tasks completed at Fri May 23 15:30:45 UTC 2026 -``` - ---- - -## 🔧 Troubleshooting - -| Problém | Řešení | -|---------|--------| -| `ERROR: Container not found` | Ověř jméno kontejneru v `CONTAINER_NAME` | -| `Dump file too small` | Ověř že MongoDB běží a má data | -| `mongodump: command not found` | Mongodump by měl být v MongoDB image, zkontroluj Docker image | -| `Permission denied` | Ověř práva na složky: `chmod 755 /mnt/user/Backup/Critical/MongoDBBackup` | - ---- - -## 📝 Tipy - -- **Archivuj staré zálohy** - Pokud chceš uchovávat dlouhodobější zálohy, zkopíruj složky před KEEP_DAYS expirací -- **Ověř čitelnost** - Po backup jdi ověřit že soubor lze extrahovat: `tar -tzf admin.archive.gz | head` -- **Monitoruj velikost** - `du -sh /mnt/user/Backup/Critical/MongoDBBackup/` - kontroluj disk space diff --git a/MongoDBBackup/README.md b/MongoDBBackup/README.md new file mode 100644 index 0000000..8d0ee84 --- /dev/null +++ b/MongoDBBackup/README.md @@ -0,0 +1,175 @@ +# MongoDB Backup & Restore - Tower / Tower1 + +Kompletní řešení pro zálohování a obnovu MongoDB na Unraid. +**Ověřeno v praxi** na dvou Unraid serverech Tower (192.168.1.76) a Tower1 (192.168.1.50). + +--- + +## 🏗️ Infrastruktura + +| Server | IP | Role | MongoDB kontejner | +|--------|-----|------|-------------------| +| Tower | 192.168.1.76 | Primární (zdroj záloh) | `MongoDB` | +| Tower1 | 192.168.1.50 | Záloha (restore cíl) | `MongoDB` | + +**SSH:** Tower1 ↔ Tower mají vzájemně nahrané SSH klíče (bez hesla). + +--- + +## 📋 Co backup zachovává + +- ✅ Všechny kolekce a dokumenty (BSON formát) +- ✅ Všechny indexy (unique, compound, sparse, text) +- ✅ Metadata kolekcí (validators, options) +- ✅ Komprimace gzipem (`--archive --gzip`) + +> **Poznámka k admin DB:** MongoDB je provozováno bez autentifikace, admin DB obsahuje pouze `system.version` (1 dokument, 360 B). Není nutné ji zálohovat pro obnovení dat. + +--- + +## 📁 Struktura záloh + +``` +/mnt/user/Backup/Critical/MongoDBBackup/tower/ +├── admin/ +│ └── 2026-05-23_0532/ +│ └── admin.archive.gz (~360 B) +├── edc/ +│ └── 2026-05-23_0532/ +│ └── edc.archive.gz (~436 KB) +└── ... +``` + +**Dostupné přes Windows:** `\\tower\Backup\Critical\MongoDBBackup\` + +--- + +## ⚙️ Konfigurace backup skriptu + +Soubor: `mongodbbackup_with_gzip.sh` + +| Proměnná | Hodnota | Popis | +|----------|---------|-------| +| `CONTAINER_NAME` | `MongoDB` | Jméno Docker kontejneru | +| `BASE_PATH` | `/mnt/user/Backup/Critical/MongoDBBackup` | Cesta k zálohám | +| `KEEP_DAYS` | `3` | Smazat zálohy starší než N dní | +| `KEEP_COUNT` | `3` | Ponechat max N posledních záloh | +| `WHAT_TO_BACKUP` | `("admin" "edc" ...)` | Seznam DB k zálohování | + +**Klíčové:** Backup používá `mongodump --archive --gzip` který streamuje přes stdout - nepotřebuje volume mount v kontejneru. + +--- + +## 🔄 Backup + +Skript je registrován v Unraid User Scripts jako **MONGODB_BACKUP** na Tower. + +``` +TOOLS → USER SCRIPTS → MONGODB_BACKUP → RUN SCRIPT +``` + +**Cesta na Unraidu:** +``` +/boot/config/plugins/user.scripts/scripts/MONGODB_BACKUP/script +``` + +**Očekávaný výstup:** +``` +Processing database: edc +SUCCESS: edc backed up successfully +Dump size: 436K +Cleaning up old backups for edc... +``` + +--- + +## 🔁 Restore + +### Metoda 1: Ze sdíleného share (doporučeno, ověřeno) + +Záloha je dostupná na Tower1 přes SMB - `/mnt/user/Backup/Critical/MongoDBBackup/`. +Pokud není automaticky namountováno, zkopíruj soubor na Tower1 share `#test`: + +```bash +# Z Windows: +Copy-Item "\\tower\Backup\Critical\MongoDBBackup\tower\edc\2026-05-23_0532\edc.archive.gz" "\\tower1\#test\edc.archive.gz" +``` + +Restore skript na Tower1 (User Scripts → **MONGODB_RESTORE**): +```bash +docker exec -i MongoDB mongorestore --archive --gzip --drop < /mnt/user/#test/edc.archive.gz +``` + +### Metoda 2: SSH stream Tower → Tower1 (ověřeno, funguje) + +```bash +ssh -o StrictHostKeyChecking=no root@192.168.1.76 \ + 'cat /mnt/user/Backup/Critical/MongoDBBackup/tower/edc/2026-05-23_0532/edc.archive.gz' \ + | docker exec -i MongoDB mongorestore --archive --gzip --drop +``` + +**Cesta skriptu na Tower1:** +``` +/boot/config/plugins/user.scripts/scripts/MONGODB_RESTORE/script +``` + +--- + +## ⏱️ Reálné časy (ověřeno) + +| Databáze | Velikost zálohy | Dokumentů | Čas restore | +|----------|----------------|-----------|-------------| +| admin | 360 B | 1 | < 1 min | +| edc | 436 KB | 4563 | ~7 minut | + +> **Pozor:** Restore vypadá že "stojí" - ale probíhá indexace. Například `edc.queries_snapshots` (2091 docs, 5 indexů včetně compound unique) trvá několik minut. **Nezavírat okno!** + +--- + +## ✅ Výsledek úspěšného restore (edc) + +``` +finished restoring edc.MDD3003_DateofVisit (381 documents, 0 failures) +finished restoring edc.queries_snapshots (2091 documents, 0 failures) +finished restoring edc.queries (2091 documents, 0 failures) +restoring indexes for collection edc.queries from metadata + - queryId_1 (sparse, unique) + - queryStatus_1 + - openedDate_1 + - site.number_1 + - subject.label_1 +restoring indexes for collection edc.queries_snapshots from metadata + - queryId_1_snapshotDate_1 (compound, unique) + - snapshotDate_1 + - queryStatus_1 + - site.number_1 + - subject.label_1 +4563 document(s) restored successfully. 0 document(s) failed to restore. +Exit: True +``` + +--- + +## 🔧 Troubleshooting + +| Problém | Příčina | Řešení | +|---------|---------|--------| +| Restore "stojí" minuty | Indexace probíhá | Počkej, nezavírej okno | +| `--dumpDbUsersAndRoles` error | MongoDB bez auth nemá users/roles | Flag odstraněn ze skriptů | +| `tar: Cannot stat` | mongodump zapisuje do kontejneru, ne na host | Použít `--archive --gzip` (stdout stream) | +| SSH stream visí | SSH drží stdin otevřený | Přidat `-n` flag nebo použít lokální soubor | +| `Permission denied` | Práva na složky | `chmod 755 /mnt/user/Backup/Critical/MongoDBBackup` | + +--- + +## 📝 Historie vývoje a naučené lekce + +1. **mongodump `--out` do kontejneru nefunguje** - kontejner zapisuje do svého filesystému, host soubory nevidí. Řešení: `--archive --gzip` streamuje přes stdout. + +2. **`--dumpDbUsersAndRoles` selže** na MongoDB bez auth - odstraněno. + +3. **tar cesta** - mongodump vytváří `DUMP_DIR/DB_NAME/kolekce.bson`, ne přímo `DUMP_DIR/kolekce.bson`. + +4. **Restore je pomalý** - indexace compound a unique indexů trvá. Normální chování, není to chyba. + +5. **SSH stream** - funguje ale při pomalé indexaci může paramiko timeout. Bezpečnější je lokální soubor přes share. diff --git a/MongTower/mongodbbackup_with_gzip.sh b/MongoDBBackup/mongodbbackup_with_gzip.sh similarity index 100% rename from MongTower/mongodbbackup_with_gzip.sh rename to MongoDBBackup/mongodbbackup_with_gzip.sh diff --git a/MongTower/mongodbrestore_from_backup.sh b/MongoDBBackup/mongodbrestore_from_backup.sh similarity index 100% rename from MongTower/mongodbrestore_from_backup.sh rename to MongoDBBackup/mongodbrestore_from_backup.sh diff --git a/MongTower/verify_backup_integrity.sh b/MongoDBBackup/verify_backup_integrity.sh similarity index 100% rename from MongTower/verify_backup_integrity.sh rename to MongoDBBackup/verify_backup_integrity.sh diff --git a/PostGRESQLTower/README.md b/PostGRESQLTower/README.md new file mode 100644 index 0000000..2e3d598 --- /dev/null +++ b/PostGRESQLTower/README.md @@ -0,0 +1,68 @@ +# PostgreSQL Backup - Tower + +PostgreSQL 18 (Docker: `postgresql18`) na Tower (192.168.1.76). + +## Konfigurace + +| Parametr | Hodnota | +|----------|---------| +| Server | Tower (192.168.1.76) | +| Container | `postgresql18` | +| Port | 5432 | +| User | `vladimir.buzalka` | +| Metoda | `pg_dumpall` (všechny DB najednou) | +| Záloha | `/mnt/user/Backup/Critical/PostgreSQLBackup/tower/YYYY-MM-DD_HHMM/` | +| Retence | 7 dní / posledních 7 záloh | + +## Skripty + +### `postgresqlbackup_with_gzip.sh` +Provede `pg_dumpall` všech databází, rolí a tablespaces. Výstup je komprimovaný gzip soubor `all_databases.sql.gz`. + +```bash +bash postgresqlbackup_with_gzip.sh +``` + +Výsledná struktura: +``` +/mnt/user/Backup/Critical/PostgreSQLBackup/tower/ +└── 2026-05-23_0200/ + └── all_databases.sql.gz +``` + +### `postgresqlrestore_from_backup.sh` +Obnoví všechny databáze z nejnovější zálohy (nebo ze zadané cesty). + +```bash +# Nejnovější záloha (automaticky) +bash postgresqlrestore_from_backup.sh + +# Konkrétní záloha +bash postgresqlrestore_from_backup.sh /mnt/user/Backup/Critical/PostgreSQLBackup/tower/2026-05-23_0200 +``` + +> **POZOR:** Dump obsahuje `--clean --if-exists`, takže restore přepíše existující data. + +### `verify_backup_integrity.sh` +Ověří po restore že jsou všechny DB, tabulky, indexy a role přítomny a funkční. + +```bash +bash verify_backup_integrity.sh +``` + +## Nastavení Unraid User Script + +Na Tower přidat User Script `POSTGRESQL_BACKUP`: + +```bash +#!/bin/bash +bash /boot/config/plugins/user.scripts/scripts/POSTGRESQL_BACKUP/postgresqlbackup_with_gzip.sh +``` + +Schedule: **Daily** + +## Windows přístup k zálohám + +``` +\\tower\Backup\Critical\PostgreSQLBackup\ +``` diff --git a/PostGRESQLTower/postgresqlbackup_with_gzip.sh b/PostGRESQLTower/postgresqlbackup_with_gzip.sh new file mode 100644 index 0000000..8eceddc --- /dev/null +++ b/PostGRESQLTower/postgresqlbackup_with_gzip.sh @@ -0,0 +1,84 @@ +#!/bin/bash +set -x + +# ========================================================== +# CONFIGURATION +# ========================================================== +CONTAINER_NAME="postgresql18" +PG_HOST="localhost" +PG_PORT="5432" +PG_USER="vladimir.buzalka" +export PGPASSWORD="Vlado7309208104++" + +UNRAID_NAME="tower" +BASE_PATH="/mnt/user/Backup/Critical/PostgreSQLBackup" + +KEEP_DAYS=7 +KEEP_COUNT=7 # počet posledních záloh k udržení (mimo time-based) + +# ========================================================== +# START +# ========================================================== +echo "Starting PostgreSQL full backup (pg_dumpall)" +START_TS=$(date '+%Y-%m-%d %H:%M:%S') + +DATE=$(date +%Y-%m-%d_%H%M) +FINAL_PATH="$BASE_PATH/$UNRAID_NAME/$DATE" +mkdir -p "$FINAL_PATH" + +DUMP_FILE="$FINAL_PATH/all_databases.sql.gz" +ERR_FILE="$FINAL_PATH/all_databases.err" + +# ========================================================== +# DUMP + GZIP +# pg_dumpall zachovává: všechny DB, role, tablespaces, grants +# Streamuje přes stdout, nepotřebuje volume mount +# ========================================================== +docker exec \ + -e PGPASSWORD="$PGPASSWORD" \ + "$CONTAINER_NAME" \ + pg_dumpall \ + --host="$PG_HOST" \ + --port="$PG_PORT" \ + --username="$PG_USER" \ + --clean \ + --if-exists \ + | gzip > "$DUMP_FILE" 2> "$ERR_FILE" + +EXIT_CODE=${PIPESTATUS[0]} + +# ========================================================== +# VALIDATION +# ========================================================== +if [ $EXIT_CODE -eq 0 ] && [ -s "$DUMP_FILE" ] && [ ! -s "$ERR_FILE" ]; then + echo "SUCCESS: Full PostgreSQL backup completed" + echo "Dump size: $(du -h "$DUMP_FILE" | cut -f1)" + rm -f "$ERR_FILE" +else + echo "ERROR: Backup failed" + echo "Exit code: $EXIT_CODE" + echo "Dump file:" + ls -lh "$DUMP_FILE" 2>/dev/null || echo " (not created)" + echo "Error output:" + [ -s "$ERR_FILE" ] && cat "$ERR_FILE" || echo " (no stderr output)" +fi + +# ========================================================== +# CLEANUP OLD BACKUPS +# 1. Smaž zálohy starší než KEEP_DAYS dnů +# 2. Z toho co zbyde, ponech jen posledních KEEP_COUNT +# ========================================================== +echo "Cleaning up old backups..." +find "$BASE_PATH/$UNRAID_NAME" \ + -mindepth 1 -maxdepth 1 -type d -mtime +$KEEP_DAYS \ + -exec rm -rf {} \; + +# Nech jen posledních KEEP_COUNT záloh +ls -td "$BASE_PATH/$UNRAID_NAME"/*/ 2>/dev/null \ + | tail -n +$((KEEP_COUNT + 1)) \ + | xargs -r rm -rf + +echo "------------------------------------------" +echo "Backup task completed at $(date)" +echo "Started at: $START_TS" +set +x diff --git a/PostGRESQLTower/postgresqlrestore_from_backup.sh b/PostGRESQLTower/postgresqlrestore_from_backup.sh new file mode 100644 index 0000000..9e7ed31 --- /dev/null +++ b/PostGRESQLTower/postgresqlrestore_from_backup.sh @@ -0,0 +1,87 @@ +#!/bin/bash +set -x + +# ========================================================== +# CONFIGURATION +# ========================================================== +CONTAINER_NAME="postgresql18" +PG_HOST="localhost" +PG_PORT="5432" +PG_USER="vladimir.buzalka" +export PGPASSWORD="Vlado7309208104++" + +# Cesta k záloze kterou chceš obnovit +# Lze předat jako argument, nebo nechá najít nejnovější +# Např: $0 /mnt/user/Backup/Critical/PostgreSQLBackup/tower/2026-05-23_0200 +BACKUP_DIR="$1" + +BASE_PATH="/mnt/user/Backup/Critical/PostgreSQLBackup/tower" + +# ========================================================== +# FIND BACKUP +# ========================================================== +if [ -z "$BACKUP_DIR" ]; then + echo "No backup path specified - using latest available backup" + BACKUP_DIR=$(ls -td "$BASE_PATH"/*/ 2>/dev/null | head -1) + + if [ -z "$BACKUP_DIR" ]; then + echo "ERROR: No backups found in $BASE_PATH" + exit 1 + fi +fi + +if [ ! -d "$BACKUP_DIR" ]; then + echo "ERROR: Backup directory does not exist: $BACKUP_DIR" + exit 1 +fi + +BACKUP_FILE="$BACKUP_DIR/all_databases.sql.gz" + +if [ ! -f "$BACKUP_FILE" ]; then + echo "ERROR: Backup file not found: $BACKUP_FILE" + exit 1 +fi + +echo "Starting PostgreSQL restore from: $BACKUP_FILE" +echo "Backup date: $(stat -c %y "$BACKUP_FILE" | cut -d' ' -f1-2)" +echo "Backup size: $(du -h "$BACKUP_FILE" | cut -f1)" +START_TS=$(date '+%Y-%m-%d %H:%M:%S') + +ERR_FILE="$BACKUP_DIR/all_databases.restore.err" + +# ========================================================== +# RESTORE +# pg_dumpall dump se obnovuje přes psql +# --clean + --if-exists v dumpu zajišťuje drop před recreate +# ========================================================== +echo "Restoring all databases to PostgreSQL..." +gunzip -c "$BACKUP_FILE" \ + | docker exec -i \ + -e PGPASSWORD="$PGPASSWORD" \ + "$CONTAINER_NAME" \ + psql \ + --host="$PG_HOST" \ + --port="$PG_PORT" \ + --username="$PG_USER" \ + --dbname="postgres" \ + 2> "$ERR_FILE" + +RESTORE_EXIT=${PIPESTATUS[1]} + +# ========================================================== +# VALIDATION +# ========================================================== +if [ $RESTORE_EXIT -eq 0 ]; then + echo "SUCCESS: All databases restored successfully" + rm -f "$ERR_FILE" +else + echo "ERROR: Restore failed" + echo "Exit code: $RESTORE_EXIT" + echo "Error output:" + [ -s "$ERR_FILE" ] && cat "$ERR_FILE" || echo " (no stderr output)" +fi + +echo "------------------------------------------" +echo "Restore task completed at $(date)" +echo "Started at: $START_TS" +set +x diff --git a/PostGRESQLTower/verify_backup_integrity.sh b/PostGRESQLTower/verify_backup_integrity.sh new file mode 100644 index 0000000..69e9d17 --- /dev/null +++ b/PostGRESQLTower/verify_backup_integrity.sh @@ -0,0 +1,171 @@ +#!/bin/bash +set -x + +# ========================================================== +# VERIFY BACKUP INTEGRITY +# Kontroluje že se všechny DB, tabulky a data +# správně obnovily po restore +# ========================================================== + +CONTAINER_NAME="postgresql18" +PG_HOST="localhost" +PG_PORT="5432" +PG_USER="vladimir.buzalka" +export PGPASSWORD="Vlado7309208104++" + +echo "Starting PostgreSQL backup integrity verification..." +echo "==========================================" + +ALL_OK=true + +# ========================================================== +# 1. CONNECTIVITY CHECK +# ========================================================== +echo "" +echo "Checking PostgreSQL connectivity..." +docker exec \ + -e PGPASSWORD="$PGPASSWORD" \ + "$CONTAINER_NAME" \ + psql \ + --host="$PG_HOST" \ + --port="$PG_PORT" \ + --username="$PG_USER" \ + --dbname="postgres" \ + --tuples-only \ + --command="SELECT 'connected';" > /dev/null 2>&1 + +if [ $? -ne 0 ]; then + echo "ERROR: Cannot connect to PostgreSQL!" + exit 1 +fi +echo "OK: Connected to PostgreSQL" + +# ========================================================== +# 2. LIST ALL DATABASES +# ========================================================== +echo "" +echo "Databases present:" +docker exec \ + -e PGPASSWORD="$PGPASSWORD" \ + "$CONTAINER_NAME" \ + psql \ + --host="$PG_HOST" \ + --port="$PG_PORT" \ + --username="$PG_USER" \ + --dbname="postgres" \ + --tuples-only \ + --command="SELECT datname FROM pg_database WHERE datistemplate = false ORDER BY datname;" + +# Ulož seznam DB do proměnné pro iteraci +DATABASES=$(docker exec \ + -e PGPASSWORD="$PGPASSWORD" \ + "$CONTAINER_NAME" \ + psql \ + --host="$PG_HOST" \ + --port="$PG_PORT" \ + --username="$PG_USER" \ + --dbname="postgres" \ + --tuples-only \ + --command="SELECT datname FROM pg_database WHERE datistemplate = false ORDER BY datname;" \ + | tr -d ' ') + +# ========================================================== +# 3. PER-DATABASE CHECKS +# ========================================================== +for DB_NAME in $DATABASES; do + [ -z "$DB_NAME" ] && continue + + echo "" + echo "Verifying database: $DB_NAME" + echo "------------------------------------------" + + # Počet tabulek + TABLE_COUNT=$(docker exec \ + -e PGPASSWORD="$PGPASSWORD" \ + "$CONTAINER_NAME" \ + psql \ + --host="$PG_HOST" \ + --port="$PG_PORT" \ + --username="$PG_USER" \ + --dbname="$DB_NAME" \ + --tuples-only \ + --command="SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public';" \ + | tr -d ' ') + + echo "OK: Tables in public schema: $TABLE_COUNT" + + # Seznam tabulek s počtem řádků + echo " Table row counts:" + docker exec \ + -e PGPASSWORD="$PGPASSWORD" \ + "$CONTAINER_NAME" \ + psql \ + --host="$PG_HOST" \ + --port="$PG_PORT" \ + --username="$PG_USER" \ + --dbname="$DB_NAME" \ + --tuples-only \ + --command=" +SELECT + schemaname || '.' || relname AS table_name, + n_live_tup AS estimated_rows, + pg_size_pretty(pg_total_relation_size(relid)) AS total_size +FROM pg_stat_user_tables +ORDER BY relname;" + + # Počet indexů + INDEX_COUNT=$(docker exec \ + -e PGPASSWORD="$PGPASSWORD" \ + "$CONTAINER_NAME" \ + psql \ + --host="$PG_HOST" \ + --port="$PG_PORT" \ + --username="$PG_USER" \ + --dbname="$DB_NAME" \ + --tuples-only \ + --command="SELECT COUNT(*) FROM pg_indexes WHERE schemaname = 'public';" \ + | tr -d ' ') + + echo "OK: Indexes in public schema: $INDEX_COUNT" + + # Celková velikost DB + DB_SIZE=$(docker exec \ + -e PGPASSWORD="$PGPASSWORD" \ + "$CONTAINER_NAME" \ + psql \ + --host="$PG_HOST" \ + --port="$PG_PORT" \ + --username="$PG_USER" \ + --dbname="$DB_NAME" \ + --tuples-only \ + --command="SELECT pg_size_pretty(pg_database_size('$DB_NAME'));" \ + | tr -d ' ') + + echo "OK: Database size: $DB_SIZE" + +done + +# ========================================================== +# 4. CHECK ROLES / USERS +# ========================================================== +echo "" +echo "PostgreSQL roles:" +docker exec \ + -e PGPASSWORD="$PGPASSWORD" \ + "$CONTAINER_NAME" \ + psql \ + --host="$PG_HOST" \ + --port="$PG_PORT" \ + --username="$PG_USER" \ + --dbname="postgres" \ + --command="SELECT rolname, rolsuper, rolcreatedb, rolcanlogin FROM pg_roles WHERE rolname NOT LIKE 'pg_%' ORDER BY rolname;" + +echo "" +echo "==========================================" +if [ "$ALL_OK" = true ]; then + echo "OK: All verifications passed!" +else + echo "ERROR: Some verifications failed!" + exit 1 +fi +set +x