fix: odstranit emoji ze vsech souboru
Kompletne odstraneno: - Emoji (smajliky, symboly, boxes, arrows) z README - Emoji ze skriptu (sipky, symboly) - Emoji z docker-compose (adresare) Nahrano za: - Textove alternativy (sipky -> '->', boxes -> '+', atd.) - ASCII pouze kde to slo Ciste vsechny 7 repo (overeno Python skriptem).
This commit is contained in:
@@ -3,11 +3,11 @@
|
|||||||
Bash skripty pro správu Dockeru na Macu i Linuxu.
|
Bash skripty pro správu Dockeru na Macu i Linuxu.
|
||||||
Jedná se o **přesné kopie** skriptů z tvého lokálního `~/Skripty/docker-domov/`.
|
Jedná se o **přesné kopie** skriptů z tvého lokálního `~/Skripty/docker-domov/`.
|
||||||
|
|
||||||
> **Jsi začátečník?** Čti od [Začínáme](#zaciname) – vše je krok za krokem.
|
> **Jsi začátečník?** Čti od [Začínáme](#zaciname) - vše je krok za krokem.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 Co tu je
|
## Co tu je
|
||||||
|
|
||||||
| Skript | Alias | Co dělá |
|
| Skript | Alias | Co dělá |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
@@ -17,13 +17,13 @@ Jedná se o **přesné kopie** skriptů z tvého lokálního `~/Skripty/docker-d
|
|||||||
| `docker-restore.sh` | `dk-restore` | Obnovuje Docker projekty ze zálohy |
|
| `docker-restore.sh` | `dk-restore` | Obnovuje Docker projekty ze zálohy |
|
||||||
| `disk-watch.sh` | `dk-watch` | Sleduje volné místo na Docker datech |
|
| `disk-watch.sh` | `dk-watch` | Sleduje volné místo na Docker datech |
|
||||||
| `mount-nas.sh` | `dk-nas` | Připojí/odpojí NAS SMB share do `/Volumes/<ssd-data-mount>/multimedia` |
|
| `mount-nas.sh` | `dk-nas` | Připojí/odpojí NAS SMB share do `/Volumes/<ssd-data-mount>/multimedia` |
|
||||||
| `update-macos.sh` | – | Aktualizace macOS přes Software Update |
|
| `update-macos.sh` | - | Aktualizace macOS přes Software Update |
|
||||||
|
|
||||||
Všechny skripty jsou **idempotentní** (můžeš spustit víckrát), mají `--help`, podporují **dry-run** a bez potvrzení nic destruktivního neprovádějí.
|
Všechny skripty jsou **idempotentní** (můžeš spustit víckrát), mají `--help`, podporují **dry-run** a bez potvrzení nic destruktivního neprovádějí.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Začínáme
|
## Začínáme
|
||||||
|
|
||||||
### Krok 1: Naklonuj repo
|
### Krok 1: Naklonuj repo
|
||||||
|
|
||||||
@@ -73,22 +73,22 @@ Příklad struktury:
|
|||||||
|
|
||||||
```
|
```
|
||||||
~/DockerProjects/
|
~/DockerProjects/
|
||||||
├── portainer/
|
+-- portainer/
|
||||||
│ ├── docker-compose.yml
|
| +-- docker-compose.yml
|
||||||
│ └── data/
|
| +-- data/
|
||||||
├── vaultwarden/
|
+-- vaultwarden/
|
||||||
│ ├── docker-compose.yml
|
| +-- docker-compose.yml
|
||||||
│ └── data/
|
| +-- data/
|
||||||
└── nextcloud/
|
+-- nextcloud/
|
||||||
└── docker-compose.yml
|
+-- docker-compose.yml
|
||||||
```
|
```
|
||||||
|
|
||||||
**Konfigurace cesty:**
|
**Konfigurace cesty:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export DOCKERY="/Volumes/<ssd-data-mount>/Dockery" # macOS
|
export DOCKERY="/Volumes/<ssd-data-mount>/Dockery" # macOS
|
||||||
# nebo
|
# nebo
|
||||||
export DOCKERY="/home/deploy/docker" # Linux
|
export DOCKERY="/home/deploy/docker" # Linux
|
||||||
```
|
```
|
||||||
|
|
||||||
### Krok 4: První použití
|
### Krok 4: První použití
|
||||||
@@ -104,16 +104,16 @@ dk-backup --dry-run
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📚 Detail ke každému skriptu
|
## Detail ke každému skriptu
|
||||||
|
|
||||||
### `docker-update.sh` → `dk-update`
|
### `docker-update.sh` -> `dk-update`
|
||||||
|
|
||||||
Bezpečná aktualizace Docker Compose projektů.
|
Bezpečná aktualizace Docker Compose projektů.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dk-update # aktualizuje všechny projekty v DOCKERY
|
dk-update # aktualizuje všechny projekty v DOCKERY
|
||||||
dk-update portainer # aktualizuje jen portainer
|
dk-update portainer # aktualizuje jen portainer
|
||||||
dk-update --dry-run # ukáže co by se dělalo, neprovede
|
dk-update --dry-run # ukáže co by se dělalo, neprovede
|
||||||
```
|
```
|
||||||
|
|
||||||
**Workflow:**
|
**Workflow:**
|
||||||
@@ -122,15 +122,15 @@ dk-update --dry-run # ukáže co by se dělalo, neprovede
|
|||||||
3. Restartuje s novými image
|
3. Restartuje s novými image
|
||||||
4. Ověří, že vše běží
|
4. Ověří, že vše běží
|
||||||
|
|
||||||
### `docker-backup.sh` → `dk-backup`
|
### `docker-backup.sh` -> `dk-backup`
|
||||||
|
|
||||||
Záloha Docker projektů na NAS.
|
Záloha Docker projektů na NAS.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dk-backup # zalohuj vse
|
dk-backup # zalohuj vse
|
||||||
dk-backup --only portainer # jen jeden projekt
|
dk-backup --only portainer # jen jeden projekt
|
||||||
dk-backup --dry-run # ukaz co by se delalo
|
dk-backup --dry-run # ukaz co by se delalo
|
||||||
dk-backup --auto # bez dotazovani (cron-friendly)
|
dk-backup --auto # bez dotazovani (cron-friendly)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Výchozí nastavení:**
|
**Výchozí nastavení:**
|
||||||
@@ -138,57 +138,57 @@ dk-backup --auto # bez dotazovani (cron-friendly)
|
|||||||
- Používá `tar --zstd` (moderní komprese)
|
- Používá `tar --zstd` (moderní komprese)
|
||||||
- Staré zálohy se **nemazou** (musíš ručně)
|
- Staré zálohy se **nemazou** (musíš ručně)
|
||||||
|
|
||||||
### `docker-cleanup.sh` → `dk-cleanup`
|
### `docker-cleanup.sh` -> `dk-cleanup`
|
||||||
|
|
||||||
Bezpečné čištění Docker odpadu.
|
Bezpečné čištění Docker odpadu.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dk-cleanup # zobrazi plan, zepta se na souhlas
|
dk-cleanup # zobrazi plan, zepta se na souhlas
|
||||||
dk-cleanup --dry-run # jen vypise co by smazal
|
dk-cleanup --dry-run # jen vypise co by smazal
|
||||||
dk-cleanup --auto # bez ptani
|
dk-cleanup --auto # bez ptani
|
||||||
dk-cleanup --all # dangling + build cache + sites + tmp
|
dk-cleanup --all # dangling + build cache + sites + tmp
|
||||||
```
|
```
|
||||||
|
|
||||||
**Nikdy nemaže:**
|
**Nikdy nemaže:**
|
||||||
- ❌ Používané images
|
- Používané images
|
||||||
- ❌ Bind-mount ani named volume data
|
- Bind-mount ani named volume data
|
||||||
- ❌ Běžící kontejnery
|
- Běžící kontejnery
|
||||||
|
|
||||||
### `docker-restore.sh` → `dk-restore`
|
### `docker-restore.sh` -> `dk-restore`
|
||||||
|
|
||||||
Obnova ze zálohy.
|
Obnova ze zálohy.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dk-list # seznam dostupnych zaloh
|
dk-list # seznam dostupnych zaloh
|
||||||
dk-restore # obnov vse (nejnovejsi)
|
dk-restore # obnov vse (nejnovejsi)
|
||||||
dk-restore --only portainer # jen jeden projekt
|
dk-restore --only portainer # jen jeden projekt
|
||||||
dk-restore --from 20240801-120000 # konkretni zaloha
|
dk-restore --from 20240801-120000 # konkretni zaloha
|
||||||
```
|
```
|
||||||
|
|
||||||
### `disk-watch.sh` → `dk-watch`
|
### `disk-watch.sh` -> `dk-watch`
|
||||||
|
|
||||||
Sledování volného místa.
|
Sledování volného místa.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dk-watch # jen info o aktualnim stavu
|
dk-watch # jen info o aktualnim stavu
|
||||||
dk-watch --alert # posli Telegram alert kdyz < 10% volneho
|
dk-watch --alert # posli Telegram alert kdyz < 10% volneho
|
||||||
```
|
```
|
||||||
|
|
||||||
### `mount-nas.sh` → `dk-nas`
|
### `mount-nas.sh` -> `dk-nas`
|
||||||
|
|
||||||
Připojí NAS SMB share.
|
Připojí NAS SMB share.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
dk-nas-status # je pripojeno?
|
dk-nas-status # je pripojeno?
|
||||||
dk-nas-mount # pripoj (auto = chybejici chybu)
|
dk-nas-mount # pripoj (auto = chybejici chybu)
|
||||||
dk-nas --umount # odpoj
|
dk-nas --umount # odpoj
|
||||||
```
|
```
|
||||||
|
|
||||||
**Konfigurace:**
|
**Konfigurace:**
|
||||||
- `SMB_SERVER` – default `<nas-ip>`
|
- `SMB_SERVER` - default `<nas-ip>`
|
||||||
- `SMB_SHARE` – default `Multimedia`
|
- `SMB_SHARE` - default `Multimedia`
|
||||||
- `SMB_USER` – default `mates`
|
- `SMB_USER` - default `mates`
|
||||||
- `TARGET` – default `/Volumes/<ssd-data-mount>/multimedia`
|
- `TARGET` - default `/Volumes/<ssd-data-mount>/multimedia`
|
||||||
- Heslo je v **macOS keychainu** pod `SMB:<server>/<share>`
|
- Heslo je v **macOS keychainu** pod `SMB:<server>/<share>`
|
||||||
|
|
||||||
### `update-macos.sh`
|
### `update-macos.sh`
|
||||||
@@ -201,33 +201,33 @@ Spustí `softwareupdate` na macOS.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔐 Bezpečnost
|
## Bezpečnost
|
||||||
|
|
||||||
- ✅ **Všechny skripty kontrolují `$EUID == 0`** (musíš být root pro sudo)
|
- **Všechny skripty kontrolují `$EUID == 0`** (musíš být root pro sudo)
|
||||||
- ✅ **Všechny skripty podporují `--dry-run`**
|
- **Všechny skripty podporují `--dry-run`**
|
||||||
- ✅ **Žádné destruktivní akce bez potvrzení**
|
- **Žádné destruktivní akce bez potvrzení**
|
||||||
- ✅ **Hesla v macOS keychainu**, ne v skriptech
|
- **Hesla v macOS keychainu**, ne v skriptech
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🏗 Struktura
|
## Struktura
|
||||||
|
|
||||||
```
|
```
|
||||||
mates-install-scripts/
|
mates-install-scripts/
|
||||||
├── README.md # Tento soubor
|
+-- README.md # Tento soubor
|
||||||
├── LICENSE # MIT
|
+-- LICENSE # MIT
|
||||||
├── docker-update.sh # Aktualizace Docker
|
+-- docker-update.sh # Aktualizace Docker
|
||||||
├── docker-backup.sh # Záloha na NAS
|
+-- docker-backup.sh # Záloha na NAS
|
||||||
├── docker-cleanup.sh # Čištění odpadu
|
+-- docker-cleanup.sh # Čištění odpadu
|
||||||
├── docker-restore.sh # Obnova ze zálohy
|
+-- docker-restore.sh # Obnova ze zálohy
|
||||||
├── disk-watch.sh # Sledování místa
|
+-- disk-watch.sh # Sledování místa
|
||||||
├── mount-nas.sh # Mount NAS
|
+-- mount-nas.sh # Mount NAS
|
||||||
└── update-macos.sh # Aktualizace macOS
|
+-- update-macos.sh # Aktualizace macOS
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📚 Související
|
## Související
|
||||||
|
|
||||||
- **Jotty:** knowledge base (detailní postupy, troubleshooting)
|
- **Jotty:** knowledge base (detailní postupy, troubleshooting)
|
||||||
- **mates-docker-services:** Docker Compose šablony pro 14 služeb
|
- **mates-docker-services:** Docker Compose šablony pro 14 služeb
|
||||||
|
|||||||
+118
-118
@@ -1,140 +1,140 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# disk-watch.sh — sledování volného místa na Docker datech
|
# disk-watch.sh -- sledování volného místa na Docker datech
|
||||||
#
|
#
|
||||||
# Kontroluje:
|
# Kontroluje:
|
||||||
# - <ssd-data-mount> (Docker data)
|
# - <ssd-data-mount> (Docker data)
|
||||||
# - <backup-disk-mount> (zálohy)
|
# - <backup-disk-mount> (zálohy)
|
||||||
# - Docker disk usage (images, containers, volumes, build cache)
|
# - Docker disk usage (images, containers, volumes, build cache)
|
||||||
#
|
#
|
||||||
# Při podkročení prahu pošle notifikaci (Telegram přes Hermes cron, nebo lokální alert).
|
# Při podkročení prahu pošle notifikaci (Telegram přes Hermes cron, nebo lokální alert).
|
||||||
# V defaultu jen vypíše stav; s --alert pošle zprávu přes Hermes Telegram doručení.
|
# V defaultu jen vypíše stav; s --alert pošle zprávu přes Hermes Telegram doručení.
|
||||||
#
|
#
|
||||||
# Použití:
|
# Použití:
|
||||||
# ./disk-watch.sh # jen výpis
|
# ./disk-watch.sh # jen výpis
|
||||||
# ./disk-watch.sh --alert # pošle notifikaci při podkročení
|
# ./disk-watch.sh --alert # pošle notifikaci při podkročení
|
||||||
# SSDDATA_THRESHOLD=80 ./disk-watch.sh # 80 % prah pro SSD-data
|
# SSDDATA_THRESHOLD=80 ./disk-watch.sh # 80 % prah pro SSD-data
|
||||||
#
|
#
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SSD_DATA="${SSD_DATA:-<ssd-data-mount>}"
|
SSD_DATA="${SSD_DATA:-<ssd-data-mount>}"
|
||||||
SSD_BACKUP="${SSD_BACKUP:-<backup-disk-mount>}"
|
SSD_BACKUP="${SSD_BACKUP:-<backup-disk-mount>}"
|
||||||
SSD_DATA_THRESHOLD="${SSD_DATA_THRESHOLD:-85}" # %
|
SSD_DATA_THRESHOLD="${SSD_DATA_THRESHOLD:-85}" # %
|
||||||
SSD_BACKUP_THRESHOLD="${SSD_BACKUP_THRESHOLD:-85}" # %
|
SSD_BACKUP_THRESHOLD="${SSD_BACKUP_THRESHOLD:-85}" # %
|
||||||
DOCKER_THRESHOLD="${DOCKER_THRESHOLD:-80}" # %
|
DOCKER_THRESHOLD="${DOCKER_THRESHOLD:-80}" # %
|
||||||
|
|
||||||
ALERT=0
|
ALERT=0
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--alert|-a) ALERT=1 ;;
|
--alert|-a) ALERT=1 ;;
|
||||||
--help|-h)
|
--help|-h)
|
||||||
sed -n '2,15p' "$0"
|
sed -n '2,15p' "$0"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
*) echo "Unknown arg: $arg" >&2; exit 1 ;;
|
*) echo "Unknown arg: $arg" >&2; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Barevné výstupy
|
# Barevné výstupy
|
||||||
if [ -t 1 ]; then
|
if [ -t 1 ]; then
|
||||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
|
||||||
else
|
else
|
||||||
RED=''; GREEN=''; YELLOW=''; BLUE=''; NC=''
|
RED=''; GREEN=''; YELLOW=''; BLUE=''; NC=''
|
||||||
fi
|
fi
|
||||||
|
|
||||||
WARNINGS=()
|
WARNINGS=()
|
||||||
|
|
||||||
check_disk() {
|
check_disk() {
|
||||||
local path="$1" threshold="$2" label="$3"
|
local path="$1" threshold="$2" label="$3"
|
||||||
if [ ! -d "$path" ]; then
|
if [ ! -d "$path" ]; then
|
||||||
printf '%s%s%s %s — adresář neexistuje\n' "$YELLOW" "⚠" "$NC" "$label"
|
printf '%s%s%s %s -- adresář neexistuje\n' "$YELLOW" "" "$NC" "$label"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
# df v macOS nevrací % použití přímo; parsujeme z výstupu
|
# df v macOS nevrací % použití přímo; parsujeme z výstupu
|
||||||
local line used avail capacity
|
local line used avail capacity
|
||||||
line=$(df -h "$path" | tail -n +2 | head -n 1)
|
line=$(df -h "$path" | tail -n +2 | head -n 1)
|
||||||
capacity=$(echo "$line" | awk '{print $5}' | tr -d '%')
|
capacity=$(echo "$line" | awk '{print $5}' | tr -d '%')
|
||||||
used=$(echo "$line" | awk '{print $3}')
|
used=$(echo "$line" | awk '{print $3}')
|
||||||
avail=$(echo "$line" | awk '{print $4}')
|
avail=$(echo "$line" | awk '{print $4}')
|
||||||
total=$(echo "$line" | awk '{print $2}')
|
total=$(echo "$line" | awk '{print $2}')
|
||||||
|
|
||||||
if [ -z "$capacity" ]; then
|
if [ -z "$capacity" ]; then
|
||||||
printf '%s%s%s %s — nelze přečíst\n' "$YELLOW" "⚠" "$NC" "$label"
|
printf '%s%s%s %s -- nelze přečíst\n' "$YELLOW" "" "$NC" "$label"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$capacity" -ge "$threshold" ]; then
|
if [ "$capacity" -ge "$threshold" ]; then
|
||||||
printf '%s●%s %s %s/%s (%s%%) — %sPŘEKROČEN PRAH%s (≥%s%%)\n' \
|
printf '%s%s %s %s/%s (%s%%) -- %sPŘEKROČEN PRAH%s (%s%%)\n' \
|
||||||
"$RED" "$NC" "$label" "$used" "$total" "$capacity" "$RED" "$NC" "$threshold"
|
"$RED" "$NC" "$label" "$used" "$total" "$capacity" "$RED" "$NC" "$threshold"
|
||||||
WARNINGS+=("$label: ${capacity}% (prah ${threshold}%)")
|
WARNINGS+=("$label: ${capacity}% (prah ${threshold}%)")
|
||||||
else
|
else
|
||||||
printf '%s●%s %s %s/%s (%s%%) — OK\n' \
|
printf '%s%s %s %s/%s (%s%%) -- OK\n' \
|
||||||
"$GREEN" "$NC" "$label" "$used" "$total" "$capacity"
|
"$GREEN" "$NC" "$label" "$used" "$total" "$capacity"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
check_docker_disk() {
|
check_docker_disk() {
|
||||||
if ! docker info >/dev/null 2>&1; then
|
if ! docker info >/dev/null 2>&1; then
|
||||||
printf '%s●%s Docker — není dostupný\n' "$YELLOW" "$NC"
|
printf '%s%s Docker -- není dostupný\n' "$YELLOW" "$NC"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Získat velikost images + containers + volumes
|
# Získat velikost images + containers + volumes
|
||||||
local out
|
local out
|
||||||
out=$(docker system df --format '{{.Type}}\t{{.Size}}\t{{.Reclaimable}}' 2>/dev/null || true)
|
out=$(docker system df --format '{{.Type}}\t{{.Size}}\t{{.Reclaimable}}' 2>/dev/null || true)
|
||||||
|
|
||||||
if [ -z "$out" ]; then
|
if [ -z "$out" ]; then
|
||||||
printf '%s●%s Docker disk — nešlo přečíst\n' "$YELLOW" "$NC"
|
printf '%s%s Docker disk -- nešlo přečíst\n' "$YELLOW" "$NC"
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Spočítat celkové využití Dockeru (obtížné bez du -s na /var/lib/docker;
|
# Spočítat celkové využití Dockeru (obtížné bez du -s na /var/lib/docker;
|
||||||
# protože jsme na Docker Desktop macOS, kontrolujeme jen velikost images)
|
# protože jsme na Docker Desktop macOS, kontrolujeme jen velikost images)
|
||||||
local images_size_bytes
|
local images_size_bytes
|
||||||
images_size_bytes=$(docker system df --format '{{.Type}}\t{{.Size}}' \
|
images_size_bytes=$(docker system df --format '{{.Type}}\t{{.Size}}' \
|
||||||
| awk -F'\t' '$1 == "Images" {print $2}')
|
| awk -F'\t' '$1 == "Images" {print $2}')
|
||||||
|
|
||||||
printf '%s●%s Docker images: %s\n' "$BLUE" "$NC" "${images_size_bytes:-neznámé}"
|
printf '%s%s Docker images: %s\n' "$BLUE" "$NC" "${images_size_bytes:-neznámé}"
|
||||||
|
|
||||||
# Velké images (>500 MB), které nejsou používané
|
# Velké images (>500 MB), které nejsou používané
|
||||||
printf '%s●%s Velké images (>500 MB):\n' "$BLUE" "$NC"
|
printf '%s%s Velké images (>500 MB):\n' "$BLUE" "$NC"
|
||||||
docker images --format '{{.Size}}\t{{.Repository}}:{{.Tag}}\t{{.ID}}' \
|
docker images --format '{{.Size}}\t{{.Repository}}:{{.Tag}}\t{{.ID}}' \
|
||||||
| awk -F'\t' '{
|
| awk -F'\t' '{
|
||||||
size = $1
|
size = $1
|
||||||
# převod na MB
|
# převod na MB
|
||||||
if (size ~ /GB/) {mb = size * 1024} else if (size ~ /MB/) {mb = size + 0} else if (size ~ /kB/) {mb = size / 1024} else {mb = 0}
|
if (size ~ /GB/) {mb = size * 1024} else if (size ~ /MB/) {mb = size + 0} else if (size ~ /kB/) {mb = size / 1024} else {mb = 0}
|
||||||
if (mb + 0 > 500) print $2, size
|
if (mb + 0 > 500) print $2, size
|
||||||
}' \
|
}' \
|
||||||
| head -10 | while read -r line; do
|
| head -10 | while read -r line; do
|
||||||
printf ' %s\n' "$line"
|
printf ' %s\n' "$line"
|
||||||
done
|
done
|
||||||
|
|
||||||
# Nepoužívané images (pozor, ne "používané" — bezpečnost)
|
# Nepoužívané images (pozor, ne "používané" -- bezpečnost)
|
||||||
unused=$(docker images --format '{{.Size}}\t{{.Repository}}:{{.Tag}}' \
|
unused=$(docker images --format '{{.Size}}\t{{.Repository}}:{{.Tag}}' \
|
||||||
| awk -F'\t' '
|
| awk -F'\t' '
|
||||||
/^([0-9.]+[kMG]B)\s/ {
|
/^([0-9.]+[kMG]B)\s/ {
|
||||||
size = $1
|
size = $1
|
||||||
if (size ~ /GB/) mb = size * 1024
|
if (size ~ /GB/) mb = size * 1024
|
||||||
else if (size ~ /MB/) mb = size + 0
|
else if (size ~ /MB/) mb = size + 0
|
||||||
else if (size ~ /kB/) mb = size / 1024
|
else if (size ~ /kB/) mb = size / 1024
|
||||||
if (mb + 0 > 200) print size, $2
|
if (mb + 0 > 200) print size, $2
|
||||||
}' | head -5)
|
}' | head -5)
|
||||||
if [ -n "$unused" ]; then
|
if [ -n "$unused" ]; then
|
||||||
printf '%s●%s Kandidáti na vyčištění (>200 MB):\n' "$YELLOW" "$NC"
|
printf '%s%s Kandidáti na vyčištění (>200 MB):\n' "$YELLOW" "$NC"
|
||||||
echo "$unused" | while read -r line; do printf ' %s\n' "$line"; done
|
echo "$unused" | while read -r line; do printf ' %s\n' "$line"; done
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
check_top_offenders() {
|
check_top_offenders() {
|
||||||
if [ ! -d "$SSD_DATA/Dockery" ]; then
|
if [ ! -d "$SSD_DATA/Dockery" ]; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
printf '\n%s●%s Top 5 největších adresářů v <docker-projects-root>:\n' "$BLUE" "$NC"
|
printf '\n%s%s Top 5 největších adresářů v <docker-projects-root>:\n' "$BLUE" "$NC"
|
||||||
/bin/du -sh "$SSD_DATA"/Dockery/*/ 2>/dev/null \
|
/bin/du -sh "$SSD_DATA"/Dockery/*/ 2>/dev/null \
|
||||||
| /usr/bin/sort -rh \
|
| /usr/bin/sort -rh \
|
||||||
| /usr/bin/head -5 \
|
| /usr/bin/head -5 \
|
||||||
| /usr/bin/sed 's/^/ /'
|
| /usr/bin/sed 's/^/ /'
|
||||||
}
|
}
|
||||||
|
|
||||||
printf '%s%s%s\n' "$BLUE" "=== DISK WATCH ===" "$NC"
|
printf '%s%s%s\n' "$BLUE" "=== DISK WATCH ===" "$NC"
|
||||||
@@ -148,38 +148,38 @@ check_docker_disk
|
|||||||
check_top_offenders
|
check_top_offenders
|
||||||
|
|
||||||
if [ "${#WARNINGS[@]}" -gt 0 ]; then
|
if [ "${#WARNINGS[@]}" -gt 0 ]; then
|
||||||
printf '\n%s●%s VAROVÁNÍ:\n' "$RED" "$NC"
|
printf '\n%s%s VAROVÁNÍ:\n' "$RED" "$NC"
|
||||||
for w in "${WARNINGS[@]}"; do
|
for w in "${WARNINGS[@]}"; do
|
||||||
printf ' - %s\n' "$w"
|
printf ' - %s\n' "$w"
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$ALERT" = "1" ] && [ "${#WARNINGS[@]}" -gt 0 ]; then
|
if [ "$ALERT" = "1" ] && [ "${#WARNINGS[@]}" -gt 0 ]; then
|
||||||
# Alert přes Hermes Telegram cron
|
# Alert přes Hermes Telegram cron
|
||||||
MSG="⚠️ Disk Watch — překročen prah
|
MSG="️ Disk Watch -- překročen prah
|
||||||
$(printf '%s\n' "${WARNINGS[@]}")
|
$(printf '%s\n' "${WARNINGS[@]}")
|
||||||
|
|
||||||
Datum: $(date '+%Y-%m-%d %H:%M:%S')
|
Datum: $(date '+%Y-%m-%d %H:%M:%S')
|
||||||
Host: $(hostname)"
|
Host: $(hostname)"
|
||||||
|
|
||||||
# Vytvoř one-shot cron pro doručení
|
# Vytvoř one-shot cron pro doručení
|
||||||
CRON_TS=$(date -u -v+1M +%Y-%m-%dT%H:%M:%S 2>/dev/null \
|
CRON_TS=$(date -u -v+1M +%Y-%m-%dT%H:%M:%S 2>/dev/null \
|
||||||
|| date -u -d '+1 minute' +%Y-%m-%dT%H:%M:%S)
|
|| date -u -d '+1 minute' +%Y-%m-%dT%H:%M:%S)
|
||||||
PAYLOAD=$(printf '%s' "$MSG" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))')
|
PAYLOAD=$(printf '%s' "$MSG" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))')
|
||||||
|
|
||||||
if command -v hermes >/dev/null 2>&1; then
|
if command -v hermes >/dev/null 2>&1; then
|
||||||
hermes cron add \
|
hermes cron add \
|
||||||
--name "disk-watch-alert-$(date +%s)" \
|
--name "disk-watch-alert-$(date +%s)" \
|
||||||
--at "$CRON_TS" \
|
--at "$CRON_TS" \
|
||||||
--deliver all \
|
--deliver all \
|
||||||
--message "$MSG" 2>/dev/null \
|
--message "$MSG" 2>/dev/null \
|
||||||
&& log "Alert naplánován na $CRON_TS" \
|
&& log "Alert naplánován na $CRON_TS" \
|
||||||
|| err "Nepodařilo se naplánovat alert (hermes cron)"
|
|| err "Nepodařilo se naplánovat alert (hermes cron)"
|
||||||
else
|
else
|
||||||
# Fallback: jen vypiš, že by se měl alert poslat
|
# Fallback: jen vypiš, že by se měl alert poslat
|
||||||
printf '\n%s[ALERT by se poslal:]%s\n' "$YELLOW" "$NC"
|
printf '\n%s[ALERT by se poslal:]%s\n' "$YELLOW" "$NC"
|
||||||
printf '%s\n' "$MSG"
|
printf '%s\n' "$MSG"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
+97
-97
@@ -1,30 +1,30 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# docker-backup.sh — bezpečná záloha Docker projektů a named volumes
|
# docker-backup.sh -- bezpečná záloha Docker projektů a named volumes
|
||||||
#
|
#
|
||||||
# - vytvoří timestamp složku v <backup-root>/
|
# - vytvoří timestamp složku v <backup-root>/
|
||||||
# - pro každý projekt v <docker-projects-root> zastaví kontejnery,
|
# - pro každý projekt v <docker-projects-root> zastaví kontejnery,
|
||||||
# zálohuje tar.zst, ověří integritu, znovu spustí
|
# zálohuje tar.zst, ověří integritu, znovu spustí
|
||||||
# - zvlášť zálohuje Docker named volumes
|
# - zvlášť zálohuje Docker named volumes
|
||||||
# - ponechá posledních N záloh (default 3)
|
# - ponechá posledních N záloh (default 3)
|
||||||
#
|
#
|
||||||
# Použití:
|
# Použití:
|
||||||
# ./docker-backup.sh # všechny projekty + named volumes
|
# ./docker-backup.sh # všechny projekty + named volumes
|
||||||
# ./docker-backup.sh nginx mattermost # vybrané projekty (volumes vždy)
|
# ./docker-backup.sh nginx mattermost # vybrané projekty (volumes vždy)
|
||||||
# BACKUP_KEEP=7 ./docker-backup.sh # ponechá 7 záloh
|
# BACKUP_KEEP=7 ./docker-backup.sh # ponechá 7 záloh
|
||||||
# ./docker-backup.sh --help # nápověda
|
# ./docker-backup.sh --help # nápověda
|
||||||
#
|
#
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'
|
sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'
|
||||||
}
|
}
|
||||||
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--help|-h) usage; exit 0 ;;
|
--help|-h) usage; exit 0 ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
DOCKERY="${DOCKERY:-<docker-projects-root>}"
|
DOCKERY="${DOCKERY:-<docker-projects-root>}"
|
||||||
@@ -35,9 +35,9 @@ DEST="${BACKUP_ROOT}/${TS}"
|
|||||||
|
|
||||||
# Barevné výstupy
|
# Barevné výstupy
|
||||||
if [ -t 1 ]; then
|
if [ -t 1 ]; then
|
||||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||||
else
|
else
|
||||||
RED=''; GREEN=''; YELLOW=''; NC=''
|
RED=''; GREEN=''; YELLOW=''; NC=''
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log() { printf '%s[%s]%s %s\n' "$GREEN" "$(date '+%H:%M:%S')" "$NC" "$*"; }
|
log() { printf '%s[%s]%s %s\n' "$GREEN" "$(date '+%H:%M:%S')" "$NC" "$*"; }
|
||||||
@@ -46,31 +46,31 @@ err() { printf '%s[%s]%s %s\n' "$RED" "$(date '+%H:%M:%S')" "$NC" "$*"; }
|
|||||||
|
|
||||||
# Kontrola
|
# Kontrola
|
||||||
if ! docker info >/dev/null 2>&1; then
|
if ! docker info >/dev/null 2>&1; then
|
||||||
err "Docker není dostupný."
|
err "Docker není dostupný."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ ! -d "$DOCKERY" ]; then
|
if [ ! -d "$DOCKERY" ]; then
|
||||||
err "Dockery adresář neexistuje: $DOCKERY"
|
err "Dockery adresář neexistuje: $DOCKERY"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ ! -d "$BACKUP_ROOT" ]; then
|
if [ ! -d "$BACKUP_ROOT" ]; then
|
||||||
err "Backup adresář neexistuje: $BACKUP_ROOT"
|
err "Backup adresář neexistuje: $BACKUP_ROOT"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "$DEST"
|
mkdir -p "$DEST"
|
||||||
|
|
||||||
# Najdi projekty
|
# Najdi projekty
|
||||||
if [ "$#" -gt 0 ]; then
|
if [ "$#" -gt 0 ]; then
|
||||||
PROJECTS=("$@")
|
PROJECTS=("$@")
|
||||||
else
|
else
|
||||||
PROJECTS=()
|
PROJECTS=()
|
||||||
for d in "$DOCKERY"/*/; do
|
for d in "$DOCKERY"/*/; do
|
||||||
[ -d "$d" ] || continue
|
[ -d "$d" ] || continue
|
||||||
if compgen -G "$d/docker-compose.y*ml" > /dev/null; then
|
if compgen -G "$d/docker-compose.y*ml" > /dev/null; then
|
||||||
PROJECTS+=("$(basename "$d")")
|
PROJECTS+=("$(basename "$d")")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Projektů k záloze: ${#PROJECTS[@]}"
|
log "Projektů k záloze: ${#PROJECTS[@]}"
|
||||||
@@ -81,13 +81,13 @@ log "Uchovat posledních: $KEEP záloh"
|
|||||||
log ""
|
log ""
|
||||||
log "=== STOP: zastavuji všechny kontejnery v ${PROJECTS[*]} ==="
|
log "=== STOP: zastavuji všechny kontejnery v ${PROJECTS[*]} ==="
|
||||||
for project in "${PROJECTS[@]}"; do
|
for project in "${PROJECTS[@]}"; do
|
||||||
cd "$DOCKERY/$project"
|
cd "$DOCKERY/$project"
|
||||||
if [ -f docker-compose.yml ] || [ -f docker-compose.yaml ]; then
|
if [ -f docker-compose.yml ] || [ -f docker-compose.yaml ]; then
|
||||||
if docker compose ps -q 2>/dev/null | grep -q .; then
|
if docker compose ps -q 2>/dev/null | grep -q .; then
|
||||||
log " [$project] docker compose stop"
|
log " [$project] docker compose stop"
|
||||||
docker compose stop 2>&1 || true
|
docker compose stop 2>&1 || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# === FÁZE 2: záloha projektů (paralelně pro velké projekty) ===
|
# === FÁZE 2: záloha projektů (paralelně pro velké projekty) ===
|
||||||
@@ -95,78 +95,78 @@ log ""
|
|||||||
log "=== BACKUP projekty (tar.zst) ==="
|
log "=== BACKUP projekty (tar.zst) ==="
|
||||||
FAILED=()
|
FAILED=()
|
||||||
for project in "${PROJECTS[@]}"; do
|
for project in "${PROJECTS[@]}"; do
|
||||||
cd "$DOCKERY/$project"
|
cd "$DOCKERY/$project"
|
||||||
ARCHIVE="$DEST/${project}.tar.zst"
|
ARCHIVE="$DEST/${project}.tar.zst"
|
||||||
log " [$project] tar -c <docker-projects-root>/$project/ → $ARCHIVE"
|
log " [$project] tar -c <docker-projects-root>/$project/ -> $ARCHIVE"
|
||||||
# absolutní cesta (background tar pitfall: vždy absolutní)
|
# absolutní cesta (background tar pitfall: vždy absolutní)
|
||||||
if /usr/bin/tar --exclude='._*' --exclude='.DS_Store' \
|
if /usr/bin/tar --exclude='._*' --exclude='.DS_Store' \
|
||||||
--use-compress-program='/opt/homebrew/bin/zstd -1 -T0' \
|
--use-compress-program='/opt/homebrew/bin/zstd -1 -T0' \
|
||||||
-cf "$ARCHIVE" \
|
-cf "$ARCHIVE" \
|
||||||
"<docker-projects-root>/$project/" 2>>"$DEST/.errors"; then
|
"<docker-projects-root>/$project/" 2>>"$DEST/.errors"; then
|
||||||
# Ověření
|
# Ověření
|
||||||
if /opt/homebrew/bin/zstd -t "$ARCHIVE" >/dev/null 2>&1; then
|
if /opt/homebrew/bin/zstd -t "$ARCHIVE" >/dev/null 2>&1; then
|
||||||
SIZE=$(stat -f '%z' "$ARCHIVE")
|
SIZE=$(stat -f '%z' "$ARCHIVE")
|
||||||
log " OK ($(numfmt --to=iec --suffix=B "$SIZE" 2>/dev/null || echo "$SIZE B"))"
|
log " OK ($(numfmt --to=iec --suffix=B "$SIZE" 2>/dev/null || echo "$SIZE B"))"
|
||||||
else
|
else
|
||||||
err " zstd -t selhal pro $ARCHIVE"
|
err " zstd -t selhal pro $ARCHIVE"
|
||||||
FAILED+=("$project")
|
FAILED+=("$project")
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
err " tar selhal pro $project"
|
err " tar selhal pro $project"
|
||||||
FAILED+=("$project")
|
FAILED+=("$project")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# === FÁZE 3: named volumes (per-volume záloha přes Docker) ===
|
# === FÁZE 3: named volumes (per-volume záloha přes Docker) ===
|
||||||
log ""
|
log ""
|
||||||
log "=== BACKUP named volumes ==="
|
log "=== BACKUP named volumes ==="
|
||||||
VOLUMES=$(docker volume ls --format '{{.Name}}' \
|
VOLUMES=$(docker volume ls --format '{{.Name}}' \
|
||||||
| grep -vE '^(host-services|docker-desktop)$' || true)
|
| grep -vE '^(host-services|docker-desktop)$' || true)
|
||||||
|
|
||||||
for vol in $VOLUMES; do
|
for vol in $VOLUMES; do
|
||||||
ARCHIVE="$DEST/${vol}.tar.zst"
|
ARCHIVE="$DEST/${vol}.tar.zst"
|
||||||
log " [volume:$vol] → $ARCHIVE"
|
log " [volume:$vol] -> $ARCHIVE"
|
||||||
# Nejdřív dekomprimuj na hostiteli, pak pošli do kontejneru (alpine tar neumí zstd)
|
# Nejdřív dekomprimuj na hostiteli, pak pošli do kontejneru (alpine tar neumí zstd)
|
||||||
if docker run --rm \
|
if docker run --rm \
|
||||||
-v "${vol}:/src:ro" \
|
-v "${vol}:/src:ro" \
|
||||||
-v "$DEST:/dest" \
|
-v "$DEST:/dest" \
|
||||||
alpine:3.20 \
|
alpine:3.20 \
|
||||||
sh -c "cd /src && tar --exclude='._*' --exclude='.DS_Store' -cf - ." \
|
sh -c "cd /src && tar --exclude='._*' --exclude='.DS_Store' -cf - ." \
|
||||||
> "$DEST/${vol}.tar" 2>>"$DEST/.errors"; then
|
> "$DEST/${vol}.tar" 2>>"$DEST/.errors"; then
|
||||||
/opt/homebrew/bin/zstd -1 -T0 -f "$DEST/${vol}.tar" -o "$ARCHIVE" 2>>"$DEST/.errors"
|
/opt/homebrew/bin/zstd -1 -T0 -f "$DEST/${vol}.tar" -o "$ARCHIVE" 2>>"$DEST/.errors"
|
||||||
rm -f "$DEST/${vol}.tar"
|
rm -f "$DEST/${vol}.tar"
|
||||||
if /opt/homebrew/bin/zstd -t "$ARCHIVE" >/dev/null 2>&1; then
|
if /opt/homebrew/bin/zstd -t "$ARCHIVE" >/dev/null 2>&1; then
|
||||||
SIZE=$(stat -f '%z' "$ARCHIVE")
|
SIZE=$(stat -f '%z' "$ARCHIVE")
|
||||||
log " OK ($(numfmt --to=iec --suffix=B "$SIZE" 2>/dev/null || echo "$SIZE B"))"
|
log " OK ($(numfmt --to=iec --suffix=B "$SIZE" 2>/dev/null || echo "$SIZE B"))"
|
||||||
else
|
else
|
||||||
err " zstd -t selhal pro $ARCHIVE"
|
err " zstd -t selhal pro $ARCHIVE"
|
||||||
FAILED+=("volume:$vol")
|
FAILED+=("volume:$vol")
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
err " docker run selhal pro volume $vol"
|
err " docker run selhal pro volume $vol"
|
||||||
FAILED+=("volume:$vol")
|
FAILED+=("volume:$vol")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# === FÁZE 4: znovu spustit projekty ===
|
# === FÁZE 4: znovu spustit projekty ===
|
||||||
log ""
|
log ""
|
||||||
log "=== UP: spouštím projekty ==="
|
log "=== UP: spouštím projekty ==="
|
||||||
for project in "${PROJECTS[@]}"; do
|
for project in "${PROJECTS[@]}"; do
|
||||||
cd "$DOCKERY/$project"
|
cd "$DOCKERY/$project"
|
||||||
if [ -f docker-compose.yml ] || [ -f docker-compose.yaml ]; then
|
if [ -f docker-compose.yml ] || [ -f docker-compose.yaml ]; then
|
||||||
log " [$project] docker compose up -d"
|
log " [$project] docker compose up -d"
|
||||||
docker compose up -d 2>&1 || warn " [$project] up -d selhal (zkontroluj)"
|
docker compose up -d 2>&1 || warn " [$project] up -d selhal (zkontroluj)"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# === FÁZE 5: hash report ===
|
# === FÁZE 5: hash report ===
|
||||||
log ""
|
log ""
|
||||||
log "=== SHA-256 hashe ==="
|
log "=== SHA-256 hashe ==="
|
||||||
for f in "$DEST"/*.tar.zst; do
|
for f in "$DEST"/*.tar.zst; do
|
||||||
[ -f "$f" ] || continue
|
[ -f "$f" ] || continue
|
||||||
H=$(shasum -a 256 "$f" | awk '{print $1}')
|
H=$(shasum -a 256 "$f" | awk '{print $1}')
|
||||||
S=$(stat -f '%z' "$f")
|
S=$(stat -f '%z' "$f")
|
||||||
log " $(basename "$f") sha256=$H size=$S"
|
log " $(basename "$f") sha256=$H size=$S"
|
||||||
done
|
done
|
||||||
|
|
||||||
# === FÁZE 6: retention ===
|
# === FÁZE 6: retention ===
|
||||||
@@ -175,29 +175,29 @@ log "=== RETENTION: ponechávám posledních $KEEP záloh ==="
|
|||||||
# set -u a prázdné pole
|
# set -u a prázdné pole
|
||||||
existing=()
|
existing=()
|
||||||
while IFS= read -r d; do
|
while IFS= read -r d; do
|
||||||
[ -d "$d" ] && existing+=("$d")
|
[ -d "$d" ] && existing+=("$d")
|
||||||
done < <(find "$BACKUP_ROOT" -maxdepth 1 -mindepth 1 -type d | sort)
|
done < <(find "$BACKUP_ROOT" -maxdepth 1 -mindepth 1 -type d | sort)
|
||||||
|
|
||||||
# Pokud něco selhalo, nepromazávej
|
# Pokud něco selhalo, nepromazávej
|
||||||
if [ "${#FAILED[@]}" -gt 0 ]; then
|
if [ "${#FAILED[@]}" -gt 0 ]; then
|
||||||
warn "Některé zálohy selhaly (${#FAILED[@]}), RETENTION se neprovádí"
|
warn "Některé zálohy selhaly (${#FAILED[@]}), RETENTION se neprovádí"
|
||||||
else
|
else
|
||||||
if [ "${#existing[@]}" -gt "$KEEP" ]; then
|
if [ "${#existing[@]}" -gt "$KEEP" ]; then
|
||||||
for old in "${existing[@]:0:${#existing[@]}-KEEP}"; do
|
for old in "${existing[@]:0:${#existing[@]}-KEEP}"; do
|
||||||
log " mažu starou zálohu: $(basename "$old")"
|
log " mažu starou zálohu: $(basename "$old")"
|
||||||
rm -rf "$old"
|
rm -rf "$old"
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log ""
|
log ""
|
||||||
log "======================================"
|
log "======================================"
|
||||||
log " HOTOVO"
|
log " HOTOVO"
|
||||||
log "======================================"
|
log "======================================"
|
||||||
log "Záloha: $DEST"
|
log "Záloha: $DEST"
|
||||||
if [ "${#FAILED[@]}" -gt 0 ]; then
|
if [ "${#FAILED[@]}" -gt 0 ]; then
|
||||||
err "Selhalo: ${#FAILED[@]}"
|
err "Selhalo: ${#FAILED[@]}"
|
||||||
for f in "${FAILED[@]}"; do err " ✗ $f"; done
|
for f in "${FAILED[@]}"; do err " [FAIL] $f"; done
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
log "Vše OK."
|
log "Vše OK."
|
||||||
+193
-193
@@ -1,27 +1,27 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# docker-cleanup.sh — bezpečné čištění Docker odpadu
|
# docker-cleanup.sh -- bezpečné čištění Docker odpadu
|
||||||
#
|
#
|
||||||
# Cíle (vše s výslovným potvrzením nebo dry-run):
|
# Cíle (vše s výslovným potvrzením nebo dry-run):
|
||||||
# - dangling images (<none>:<none>)
|
# - dangling images (<none>:<none>)
|
||||||
# - starý build cache (docker builder prune -f)
|
# - starý build cache (docker builder prune -f)
|
||||||
# - opuštěné (orphaned) Docker sítě — nikdy bridge/host/none
|
# - opuštěné (orphaned) Docker sítě -- nikdy bridge/host/none
|
||||||
# - zastaralé images s tagem <none> ale s referencí (REPO@sha256…)
|
# - zastaralé images s tagem <none> ale s referencí (REPO@sha256...)
|
||||||
# - dočasné soubory (XDG_CACHE_HOME pro docker, /tmp/docker-*)
|
# - dočasné soubory (XDG_CACHE_HOME pro docker, /tmp/docker-*)
|
||||||
#
|
#
|
||||||
# Nikdy nemaže:
|
# Nikdy nemaže:
|
||||||
# - používané images
|
# - používané images
|
||||||
# - bind-mount ani named volume data
|
# - bind-mount ani named volume data
|
||||||
# - běžící kontejnery
|
# - běžící kontejnery
|
||||||
#
|
#
|
||||||
# Použití:
|
# Použití:
|
||||||
# ./docker-cleanup.sh # zobrazí plán, zeptá se na souhlas
|
# ./docker-cleanup.sh # zobrazí plán, zeptá se na souhlas
|
||||||
# ./docker-cleanup.sh --dry-run # jen vypíše co by smazal
|
# ./docker-cleanup.sh --dry-run # jen vypíše co by smazal
|
||||||
# ./docker-cleanup.sh --auto # bez ptaní (default akce)
|
# ./docker-cleanup.sh --auto # bez ptaní (default akce)
|
||||||
# ./docker-cleanup.sh --dangling # jen dangling images
|
# ./docker-cleanup.sh --dangling # jen dangling images
|
||||||
# ./docker-cleanup.sh --build-cache # jen build cache
|
# ./docker-cleanup.sh --build-cache # jen build cache
|
||||||
# ./docker-cleanup.sh --networks # jen orphaned sítě
|
# ./docker-cleanup.sh --networks # jen orphaned sítě
|
||||||
# ./docker-cleanup.sh --all # všechno výše uvedené
|
# ./docker-cleanup.sh --all # všechno výše uvedené
|
||||||
#
|
#
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -37,42 +37,42 @@ AUTO=0
|
|||||||
DRY_RUN=0
|
DRY_RUN=0
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'
|
sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'
|
||||||
}
|
}
|
||||||
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--dry-run|-n) DRY_RUN=1 ;;
|
--dry-run|-n) DRY_RUN=1 ;;
|
||||||
--auto|-y) AUTO=1 ;;
|
--auto|-y) AUTO=1 ;;
|
||||||
--dangling) DO_DANGLING=1 ;;
|
--dangling) DO_DANGLING=1 ;;
|
||||||
--build-cache) DO_BUILDER=1 ;;
|
--build-cache) DO_BUILDER=1 ;;
|
||||||
--networks) DO_NETWORKS=1 ;;
|
--networks) DO_NETWORKS=1 ;;
|
||||||
--tmp) DO_TMP=1 ;;
|
--tmp) DO_TMP=1 ;;
|
||||||
--stopped-containers) DO_STOPPED=1 ;;
|
--stopped-containers) DO_STOPPED=1 ;;
|
||||||
--all) DO_ALL=1 ;;
|
--all) DO_ALL=1 ;;
|
||||||
--help|-h) usage; exit 0 ;;
|
--help|-h) usage; exit 0 ;;
|
||||||
*) echo "Unknown arg: $arg" >&2; exit 1 ;;
|
*) echo "Unknown arg: $arg" >&2; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "$DO_ALL" = "1" ]; then
|
if [ "$DO_ALL" = "1" ]; then
|
||||||
DO_DANGLING=1
|
DO_DANGLING=1
|
||||||
DO_BUILDER=1
|
DO_BUILDER=1
|
||||||
DO_NETWORKS=1
|
DO_NETWORKS=1
|
||||||
DO_TMP=1
|
DO_TMP=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Bez přepínače: nic neprovádět
|
# Bez přepínače: nic neprovádět
|
||||||
if [ "$DO_DANGLING" = "0" ] && [ "$DO_BUILDER" = "0" ] && [ "$DO_NETWORKS" = "0" ] && [ "$DO_TMP" = "0" ] && [ "$DO_STOPPED" = "0" ]; then
|
if [ "$DO_DANGLING" = "0" ] && [ "$DO_BUILDER" = "0" ] && [ "$DO_NETWORKS" = "0" ] && [ "$DO_TMP" = "0" ] && [ "$DO_STOPPED" = "0" ]; then
|
||||||
usage
|
usage
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Barevné výstupy
|
# Barevné výstupy
|
||||||
if [ -t 1 ]; then
|
if [ -t 1 ]; then
|
||||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
|
||||||
else
|
else
|
||||||
RED=''; GREEN=''; YELLOW=''; BLUE=''; NC=''
|
RED=''; GREEN=''; YELLOW=''; BLUE=''; NC=''
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log() { printf '%s[%s]%s %s\n' "$GREEN" "$(date '+%H:%M:%S')" "$NC" "$*"; }
|
log() { printf '%s[%s]%s %s\n' "$GREEN" "$(date '+%H:%M:%S')" "$NC" "$*"; }
|
||||||
@@ -81,179 +81,179 @@ err() { printf '%s[%s]%s %s\n' "$RED" "$(date '+%H:%M:%S')" "$NC" "$*"; }
|
|||||||
|
|
||||||
# Kontrola dockeru
|
# Kontrola dockeru
|
||||||
if ! docker info >/dev/null 2>&1; then
|
if ! docker info >/dev/null 2>&1; then
|
||||||
err "Docker není dostupný."
|
err "Docker není dostupný."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
PLANNED_TOTAL=0
|
PLANNED_TOTAL=0
|
||||||
PLANNED_ITEMS=()
|
PLANNED_ITEMS=()
|
||||||
|
|
||||||
show_plan_header() {
|
show_plan_header() {
|
||||||
printf '\n%s%s%s\n' "$BLUE" "=== PLÁN ČIŠTĚNÍ ===" "$NC"
|
printf '\n%s%s%s\n' "$BLUE" "=== PLÁN ČIŠTĚNÍ ===" "$NC"
|
||||||
printf 'Datum: %s\n' "$(date '+%Y-%m-%d %H:%M:%S')"
|
printf 'Datum: %s\n' "$(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
printf 'Režim: %s\n' "$([ "$DRY_RUN" = "1" ] && echo "DRY-RUN" || echo "SKUTEČNÝ MAZÁNÍ")"
|
printf 'Režim: %s\n' "$([ "$DRY_RUN" = "1" ] && echo "DRY-RUN" || echo "SKUTEČNÝ MAZÁNÍ")"
|
||||||
printf '\n'
|
printf '\n'
|
||||||
}
|
}
|
||||||
|
|
||||||
# === DANGLING IMAGES ===
|
# === DANGLING IMAGES ===
|
||||||
plan_dangling() {
|
plan_dangling() {
|
||||||
if [ "$DO_DANGLING" = "0" ]; then return; fi
|
if [ "$DO_DANGLING" = "0" ]; then return; fi
|
||||||
printf '%s● Dangling images (<none>:<none>):%s\n' "$BLUE" "$NC"
|
printf '%s Dangling images (<none>:<none>):%s\n' "$BLUE" "$NC"
|
||||||
|
|
||||||
local pure_dangling=0
|
local pure_dangling=0
|
||||||
local stopped_holding=0
|
local stopped_holding=0
|
||||||
|
|
||||||
while IFS=$'\t' read -r id size; do
|
while IFS=$'\t' read -r id size; do
|
||||||
[ -z "$id" ] && continue
|
[ -z "$id" ] && continue
|
||||||
# Kolik kontejnerů (včetně zastavených) na tento image ukazuje?
|
# Kolik kontejnerů (včetně zastavených) na tento image ukazuje?
|
||||||
local holders
|
local holders
|
||||||
holders=$(docker ps -a --filter "ancestor=$id" --format '{{.Names}}\t{{.Status}}' 2>/dev/null | head -3)
|
holders=$(docker ps -a --filter "ancestor=$id" --format '{{.Names}}\t{{.Status}}' 2>/dev/null | head -3)
|
||||||
if [ -n "$holders" ]; then
|
if [ -n "$holders" ]; then
|
||||||
stopped_holding=$((stopped_holding + 1))
|
stopped_holding=$((stopped_holding + 1))
|
||||||
printf ' %s %s ⚠ drží zastavený kontejner: %s\n' \
|
printf ' %s %s drží zastavený kontejner: %s\n' \
|
||||||
"$id" "$size" "$(echo "$holders" | head -1 | awk '{print $1}')"
|
"$id" "$size" "$(echo "$holders" | head -1 | awk '{print $1}')"
|
||||||
else
|
else
|
||||||
pure_dangling=$((pure_dangling + 1))
|
pure_dangling=$((pure_dangling + 1))
|
||||||
printf ' %s %s\n' "$id" "$size"
|
printf ' %s %s\n' "$id" "$size"
|
||||||
fi
|
fi
|
||||||
done < <(docker images -f "dangling=true" --format '{{.ID}}\t{{.Size}}')
|
done < <(docker images -f "dangling=true" --format '{{.ID}}\t{{.Size}}')
|
||||||
|
|
||||||
if [ "$pure_dangling" -eq 0 ] && [ "$stopped_holding" -eq 0 ]; then
|
if [ "$pure_dangling" -eq 0 ] && [ "$stopped_holding" -eq 0 ]; then
|
||||||
printf ' (žádné)\n'
|
printf ' (žádné)\n'
|
||||||
else
|
else
|
||||||
printf ' -> %d čistě dangling, %d drží zastavené kontejnery (docker image prune -f je nesmaže)\n' \
|
printf ' -> %d čistě dangling, %d drží zastavené kontejnery (docker image prune -f je nesmaže)\n' \
|
||||||
"$pure_dangling" "$stopped_holding"
|
"$pure_dangling" "$stopped_holding"
|
||||||
if [ "$pure_dangling" -gt 0 ]; then
|
if [ "$pure_dangling" -gt 0 ]; then
|
||||||
PLANNED_TOTAL=$((PLANNED_TOTAL + pure_dangling))
|
PLANNED_TOTAL=$((PLANNED_TOTAL + pure_dangling))
|
||||||
PLANNED_ITEMS+=("dangling-images:$pure_dangling")
|
PLANNED_ITEMS+=("dangling-images:$pure_dangling")
|
||||||
fi
|
fi
|
||||||
if [ "$stopped_holding" -gt 0 ]; then
|
if [ "$stopped_holding" -gt 0 ]; then
|
||||||
PLANNED_ITEMS+=("dangling-with-stopped-containers:$stopped_holding")
|
PLANNED_ITEMS+=("dangling-with-stopped-containers:$stopped_holding")
|
||||||
printf ' 💡 Pro smazání těchto images je nutné nejprve odstranit zastavené kontejnery (--stopped-containers)\n'
|
printf ' Pro smazání těchto images je nutné nejprve odstranit zastavené kontejnery (--stopped-containers)\n'
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# === BUILD CACHE ===
|
# === BUILD CACHE ===
|
||||||
plan_build_cache() {
|
plan_build_cache() {
|
||||||
if [ "$DO_BUILDER" = "0" ]; then return; fi
|
if [ "$DO_BUILDER" = "0" ]; then return; fi
|
||||||
printf '%s● Build cache:%s\n' "$BLUE" "$NC"
|
printf '%s Build cache:%s\n' "$BLUE" "$NC"
|
||||||
|
|
||||||
local size
|
local size
|
||||||
size=$(docker system df --format '{{.Type}}\t{{.Size}}' 2>/dev/null \
|
size=$(docker system df --format '{{.Type}}\t{{.Size}}' 2>/dev/null \
|
||||||
| awk -F'\t' '$1 == "Build Cache" {print $2}')
|
| awk -F'\t' '$1 == "Build Cache" {print $2}')
|
||||||
if [ -z "$size" ]; then
|
if [ -z "$size" ]; then
|
||||||
size="0B"
|
size="0B"
|
||||||
fi
|
fi
|
||||||
printf ' Build cache: %s\n' "$size"
|
printf ' Build cache: %s\n' "$size"
|
||||||
|
|
||||||
if [ "$size" = "0B" ]; then
|
if [ "$size" = "0B" ]; then
|
||||||
printf ' (žádný ke smazání)\n'
|
printf ' (žádný ke smazání)\n'
|
||||||
else
|
else
|
||||||
PLANNED_TOTAL=$((PLANNED_TOTAL + 1))
|
PLANNED_TOTAL=$((PLANNED_TOTAL + 1))
|
||||||
PLANNED_ITEMS+=("build-cache:$size")
|
PLANNED_ITEMS+=("build-cache:$size")
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# === ORPHANED NETWORKS ===
|
# === ORPHANED NETWORKS ===
|
||||||
plan_networks() {
|
plan_networks() {
|
||||||
if [ "$DO_NETWORKS" = "0" ]; then return; fi
|
if [ "$DO_NETWORKS" = "0" ]; then return; fi
|
||||||
printf '%s● Opuštěné Docker sítě:%s\n' "$BLUE" "$NC"
|
printf '%s Opuštěné Docker sítě:%s\n' "$BLUE" "$NC"
|
||||||
|
|
||||||
local count=0
|
local count=0
|
||||||
# Najdi sítě, které NEpoužívá žádný kontejner
|
# Najdi sítě, které NEpoužívá žádný kontejner
|
||||||
while IFS= read -r net; do
|
while IFS= read -r net; do
|
||||||
[ -z "$net" ] && continue
|
[ -z "$net" ] && continue
|
||||||
# Vynech výchozí systémové
|
# Vynech výchozí systémové
|
||||||
case "$net" in
|
case "$net" in
|
||||||
bridge|host|none|docker_gwbridge|docker-desktop*) continue ;;
|
bridge|host|none|docker_gwbridge|docker-desktop*) continue ;;
|
||||||
esac
|
esac
|
||||||
# Používá ji nějaký kontejner?
|
# Používá ji nějaký kontejner?
|
||||||
if ! docker ps --format '{{.Networks}}' 2>/dev/null \
|
if ! docker ps --format '{{.Networks}}' 2>/dev/null \
|
||||||
| grep -qw "$net"; then
|
| grep -qw "$net"; then
|
||||||
printf ' %s\n' "$net"
|
printf ' %s\n' "$net"
|
||||||
count=$((count + 1))
|
count=$((count + 1))
|
||||||
fi
|
fi
|
||||||
done < <(docker network ls --format '{{.Name}}')
|
done < <(docker network ls --format '{{.Name}}')
|
||||||
|
|
||||||
if [ "$count" -eq 0 ]; then
|
if [ "$count" -eq 0 ]; then
|
||||||
printf ' (žádné opuštěné)\n'
|
printf ' (žádné opuštěné)\n'
|
||||||
else
|
else
|
||||||
printf ' -> %d opuštěných sítí\n' "$count"
|
printf ' -> %d opuštěných sítí\n' "$count"
|
||||||
PLANNED_TOTAL=$((PLANNED_TOTAL + count))
|
PLANNED_TOTAL=$((PLANNED_TOTAL + count))
|
||||||
PLANNED_ITEMS+=("networks:$count")
|
PLANNED_ITEMS+=("networks:$count")
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# === TMP FILES ===
|
# === TMP FILES ===
|
||||||
plan_tmp() {
|
plan_tmp() {
|
||||||
if [ "$DO_TMP" = "0" ]; then return; fi
|
if [ "$DO_TMP" = "0" ]; then return; fi
|
||||||
printf '%s● Dočasné soubory:%s\n' "$BLUE" "$NC"
|
printf '%s Dočasné soubory:%s\n' "$BLUE" "$NC"
|
||||||
|
|
||||||
local total_bytes=0
|
local total_bytes=0
|
||||||
local paths=()
|
local paths=()
|
||||||
|
|
||||||
# /tmp/docker-* (logy docker-update atd., starší než 7 dní)
|
# /tmp/docker-* (logy docker-update atd., starší než 7 dní)
|
||||||
if [ -d /tmp ]; then
|
if [ -d /tmp ]; then
|
||||||
while IFS= read -r f; do
|
while IFS= read -r f; do
|
||||||
[ -f "$f" ] || continue
|
[ -f "$f" ] || continue
|
||||||
sz=$(stat -f '%z' "$f")
|
sz=$(stat -f '%z' "$f")
|
||||||
total_bytes=$((total_bytes + sz))
|
total_bytes=$((total_bytes + sz))
|
||||||
paths+=("$f")
|
paths+=("$f")
|
||||||
done < <(/usr/bin/find /tmp -maxdepth 1 -name 'docker-*' -type f -mtime +7 2>/dev/null)
|
done < <(/usr/bin/find /tmp -maxdepth 1 -name 'docker-*' -type f -mtime +7 2>/dev/null)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Docker cache adresáře
|
# Docker cache adresáře
|
||||||
[ -d "$HOME/.docker/buildx" ] && paths+=("$HOME/.docker/buildx")
|
[ -d "$HOME/.docker/buildx" ] && paths+=("$HOME/.docker/buildx")
|
||||||
[ -d "$HOME/.cache/docker" ] && paths+=("$HOME/.cache/docker")
|
[ -d "$HOME/.cache/docker" ] && paths+=("$HOME/.cache/docker")
|
||||||
|
|
||||||
if [ "${#paths[@]}" -eq 0 ]; then
|
if [ "${#paths[@]}" -eq 0 ]; then
|
||||||
printf ' (žádné)\n'
|
printf ' (žádné)\n'
|
||||||
else
|
else
|
||||||
for p in "${paths[@]}"; do
|
for p in "${paths[@]}"; do
|
||||||
printf ' %s\n' "$p"
|
printf ' %s\n' "$p"
|
||||||
done
|
done
|
||||||
printf ' -> %d položek, cca %d B\n' "${#paths[@]}" "$total_bytes"
|
printf ' -> %d položek, cca %d B\n' "${#paths[@]}" "$total_bytes"
|
||||||
PLANNED_TOTAL=$((PLANNED_TOTAL + ${#paths[@]}))
|
PLANNED_TOTAL=$((PLANNED_TOTAL + ${#paths[@]}))
|
||||||
PLANNED_ITEMS+=("tmp-files:${#paths[@]}")
|
PLANNED_ITEMS+=("tmp-files:${#paths[@]}")
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
confirm() {
|
confirm() {
|
||||||
if [ "$AUTO" = "1" ]; then
|
if [ "$AUTO" = "1" ]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
if [ "$DRY_RUN" = "1" ]; then
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
printf '\n%sPokračovat? [y/N]%s ' "$YELLOW" "$NC"
|
printf '\n%sPokračovat? [y/N]%s ' "$YELLOW" "$NC"
|
||||||
read -r ans
|
read -r ans
|
||||||
case "$ans" in
|
case "$ans" in
|
||||||
y|Y|yes|YES) return 0 ;;
|
y|Y|yes|YES) return 0 ;;
|
||||||
*) printf 'Zrušeno.\n'; exit 1 ;;
|
*) printf 'Zrušeno.\n'; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# === STOPPED CONTAINERS ===
|
# === STOPPED CONTAINERS ===
|
||||||
plan_stopped() {
|
plan_stopped() {
|
||||||
if [ "$DO_STOPPED" = "0" ]; then return; fi
|
if [ "$DO_STOPPED" = "0" ]; then return; fi
|
||||||
printf '%s● Zastavené kontejnery:%s\n' "$BLUE" "$NC"
|
printf '%s Zastavené kontejnery:%s\n' "$BLUE" "$NC"
|
||||||
|
|
||||||
local count=0
|
local count=0
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
[ -z "$line" ] && continue
|
[ -z "$line" ] && continue
|
||||||
# Formát: NAME STATUS
|
# Formát: NAME STATUS
|
||||||
printf ' %s\n' "$line"
|
printf ' %s\n' "$line"
|
||||||
count=$((count + 1))
|
count=$((count + 1))
|
||||||
done < <(docker ps -a --filter "status=exited" --format '{{.Names}}\t{{.Status}}' 2>/dev/null)
|
done < <(docker ps -a --filter "status=exited" --format '{{.Names}}\t{{.Status}}' 2>/dev/null)
|
||||||
|
|
||||||
if [ "$count" -eq 0 ]; then
|
if [ "$count" -eq 0 ]; then
|
||||||
printf ' (žádné)\n'
|
printf ' (žádné)\n'
|
||||||
else
|
else
|
||||||
printf ' -> %d zastavených kontejnerů\n' "$count"
|
printf ' -> %d zastavených kontejnerů\n' "$count"
|
||||||
PLANNED_TOTAL=$((PLANNED_TOTAL + count))
|
PLANNED_TOTAL=$((PLANNED_TOTAL + count))
|
||||||
PLANNED_ITEMS+=("stopped-containers:$count")
|
PLANNED_ITEMS+=("stopped-containers:$count")
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Spustit plánování
|
# Spustit plánování
|
||||||
@@ -266,14 +266,14 @@ plan_tmp
|
|||||||
|
|
||||||
printf '\n%sCelkem plánovaných akcí: %d%s\n' "$BLUE" "$PLANNED_TOTAL" "$NC"
|
printf '\n%sCelkem plánovaných akcí: %d%s\n' "$BLUE" "$PLANNED_TOTAL" "$NC"
|
||||||
if [ "$PLANNED_TOTAL" -eq 0 ]; then
|
if [ "$PLANNED_TOTAL" -eq 0 ]; then
|
||||||
log "Není co čistit."
|
log "Není co čistit."
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Bez potvrzení v dry-run režimu
|
# Bez potvrzení v dry-run režimu
|
||||||
if [ "$DRY_RUN" = "1" ]; then
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
printf '\n%sDRY-RUN: žádná akce neprovedena%s\n' "$YELLOW" "$NC"
|
printf '\n%sDRY-RUN: žádná akce neprovedena%s\n' "$YELLOW" "$NC"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
confirm
|
confirm
|
||||||
@@ -283,39 +283,39 @@ log ""
|
|||||||
log "=== PROVÁDÍM ČIŠTĚNÍ ==="
|
log "=== PROVÁDÍM ČIŠTĚNÍ ==="
|
||||||
|
|
||||||
if [ "$DO_DANGLING" = "1" ]; then
|
if [ "$DO_DANGLING" = "1" ]; then
|
||||||
log "docker image prune -f (dangling only)"
|
log "docker image prune -f (dangling only)"
|
||||||
docker image prune -f 2>&1 || warn "dangling prune selhal"
|
docker image prune -f 2>&1 || warn "dangling prune selhal"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$DO_BUILDER" = "1" ]; then
|
if [ "$DO_BUILDER" = "1" ]; then
|
||||||
log "docker builder prune -f"
|
log "docker builder prune -f"
|
||||||
docker builder prune -f 2>&1 || warn "builder prune selhal"
|
docker builder prune -f 2>&1 || warn "builder prune selhal"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$DO_NETWORKS" = "1" ]; then
|
if [ "$DO_NETWORKS" = "1" ]; then
|
||||||
log "docker network prune -f (filtrováno na opuštěné)"
|
log "docker network prune -f (filtrováno na opuštěné)"
|
||||||
# docker network prune umí vyfiltrovat, ale my jsme již identifikovali konkrétní
|
# docker network prune umí vyfiltrovat, ale my jsme již identifikovali konkrétní
|
||||||
while IFS= read -r net; do
|
while IFS= read -r net; do
|
||||||
case "$net" in bridge|host|none|docker_gwbridge|docker-desktop*) continue ;; esac
|
case "$net" in bridge|host|none|docker_gwbridge|docker-desktop*) continue ;; esac
|
||||||
if ! docker ps --format '{{.Networks}}' 2>/dev/null | grep -qw "$net"; then
|
if ! docker ps --format '{{.Networks}}' 2>/dev/null | grep -qw "$net"; then
|
||||||
log " rm network: $net"
|
log " rm network: $net"
|
||||||
docker network rm "$net" 2>&1 || warn " rm $net selhal"
|
docker network rm "$net" 2>&1 || warn " rm $net selhal"
|
||||||
fi
|
fi
|
||||||
done < <(docker network ls --format '{{.Name}}')
|
done < <(docker network ls --format '{{.Name}}')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$DO_TMP" = "1" ]; then
|
if [ "$DO_TMP" = "1" ]; then
|
||||||
log "mazání starých /tmp/docker-* (≥7 dní)"
|
log "mazání starých /tmp/docker-* (7 dní)"
|
||||||
# -print -delete musí být v tomto pořadí; -delete sám o sobě na macOS BSD find někdy selže
|
# -print -delete musí být v tomto pořadí; -delete sám o sobě na macOS BSD find někdy selže
|
||||||
/usr/bin/find /tmp -maxdepth 1 -name 'docker-*' -type f -mtime +7 -print 2>&1 | while read -r f; do
|
/usr/bin/find /tmp -maxdepth 1 -name 'docker-*' -type f -mtime +7 -print 2>&1 | while read -r f; do
|
||||||
log " rm: $f"
|
log " rm: $f"
|
||||||
/bin/rm -f "$f" 2>&1 || warn " rm $f selhal"
|
/bin/rm -f "$f" 2>&1 || warn " rm $f selhal"
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$DO_STOPPED" = "1" ]; then
|
if [ "$DO_STOPPED" = "1" ]; then
|
||||||
log "docker container prune -f (zastavené kontejnery)"
|
log "docker container prune -f (zastavené kontejnery)"
|
||||||
docker container prune -f 2>&1 || warn "container prune selhal"
|
docker container prune -f 2>&1 || warn "container prune selhal"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log ""
|
log ""
|
||||||
|
|||||||
+191
-191
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# docker-restore.sh — bezpečná obnova Docker projektů z tar.zst zálohy
|
# docker-restore.sh -- bezpečná obnova Docker projektů z tar.zst zálohy
|
||||||
#
|
#
|
||||||
# - zobrazí dostupné zálohy v $BACKUP_ROOT
|
# - zobrazí dostupné zálohy v $BACKUP_ROOT
|
||||||
# - vyžádá potvrzení (nebo --auto)
|
# - vyžádá potvrzení (nebo --auto)
|
||||||
@@ -11,12 +11,12 @@
|
|||||||
# - spustí kontejnery a ověří zdraví
|
# - spustí kontejnery a ověří zdraví
|
||||||
#
|
#
|
||||||
# Použití:
|
# Použití:
|
||||||
# ./docker-restore.sh # interaktivně: vybere zálohu + projekty
|
# ./docker-restore.sh # interaktivně: vybere zálohu + projekty
|
||||||
# ./docker-restore.sh --list # jen seznam dostupných záloh
|
# ./docker-restore.sh --list # jen seznam dostupných záloh
|
||||||
# ./docker-restore.sh 2026-07-29_19-21-00 # konkrétní záloha, interaktivní výběr projektů
|
# ./docker-restore.sh 2026-07-29_19-21-00 # konkrétní záloha, interaktivní výběr projektů
|
||||||
# ./docker-restore.sh 2026-07-29_19-21-00 nginx vaultwarden # konkrétní záloha + projekty
|
# ./docker-restore.sh 2026-07-29_19-21-00 nginx vaultwarden # konkrétní záloha + projekty
|
||||||
# ./docker-restore.sh --auto 2026-07-29_19-21-00 nginx # bez ptaní
|
# ./docker-restore.sh --auto 2026-07-29_19-21-00 nginx # bez ptaní
|
||||||
# ./docker-restore.sh --help # nápověda
|
# ./docker-restore.sh --help # nápověda
|
||||||
#
|
#
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -30,30 +30,30 @@ TS_ARG=""
|
|||||||
PROJECTS_RAW=()
|
PROJECTS_RAW=()
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'
|
sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'
|
||||||
}
|
}
|
||||||
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--help|-h) usage; exit 0 ;;
|
--help|-h) usage; exit 0 ;;
|
||||||
--auto|-y) AUTO=1 ;;
|
--auto|-y) AUTO=1 ;;
|
||||||
--list|-l) LIST_ONLY=1 ;;
|
--list|-l) LIST_ONLY=1 ;;
|
||||||
--*) echo "Unknown flag: $arg" >&2; exit 1 ;;
|
--*) echo "Unknown flag: $arg" >&2; exit 1 ;;
|
||||||
*)
|
*)
|
||||||
if [ -z "$TS_ARG" ]; then
|
if [ -z "$TS_ARG" ]; then
|
||||||
TS_ARG="$arg"
|
TS_ARG="$arg"
|
||||||
else
|
else
|
||||||
PROJECTS_RAW+=("$arg")
|
PROJECTS_RAW+=("$arg")
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Barevné výstupy
|
# Barevné výstupy
|
||||||
if [ -t 1 ]; then
|
if [ -t 1 ]; then
|
||||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
|
||||||
else
|
else
|
||||||
RED=''; GREEN=''; YELLOW=''; BLUE=''; NC=''
|
RED=''; GREEN=''; YELLOW=''; BLUE=''; NC=''
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log() { printf '%s[%s]%s %s\n' "$GREEN" "$(date '+%H:%M:%S')" "$NC" "$*"; }
|
log() { printf '%s[%s]%s %s\n' "$GREEN" "$(date '+%H:%M:%S')" "$NC" "$*"; }
|
||||||
@@ -61,147 +61,147 @@ warn() { printf '%s[%s]%s %s\n' "$YELLOW" "$(date '+%H:%M:%S')" "$NC" "$*"; }
|
|||||||
err() { printf '%s[%s]%s %s\n' "$RED" "$(date '+%H:%M:%S')" "$NC" "$*"; }
|
err() { printf '%s[%s]%s %s\n' "$RED" "$(date '+%H:%M:%S')" "$NC" "$*"; }
|
||||||
|
|
||||||
confirm() {
|
confirm() {
|
||||||
if [ "$AUTO" = "1" ]; then return 0; fi
|
if [ "$AUTO" = "1" ]; then return 0; fi
|
||||||
printf '%s%s%s ' "$YELLOW" "$1" "$NC"
|
printf '%s%s%s ' "$YELLOW" "$1" "$NC"
|
||||||
read -r ans
|
read -r ans
|
||||||
case "$ans" in
|
case "$ans" in
|
||||||
y|Y|yes|YES) return 0 ;;
|
y|Y|yes|YES) return 0 ;;
|
||||||
*) printf 'Zrušeno.\n'; exit 1 ;;
|
*) printf 'Zrušeno.\n'; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# Kontrola
|
# Kontrola
|
||||||
if ! docker info >/dev/null 2>&1; then
|
if ! docker info >/dev/null 2>&1; then
|
||||||
err "Docker není dostupný."
|
err "Docker není dostupný."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ ! -d "$DOCKERY" ]; then
|
if [ ! -d "$DOCKERY" ]; then
|
||||||
err "Dockery adresář neexistuje: $DOCKERY"
|
err "Dockery adresář neexistuje: $DOCKERY"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if [ ! -d "$BACKUP_ROOT" ]; then
|
if [ ! -d "$BACKUP_ROOT" ]; then
|
||||||
err "Backup adresář neexistuje: $BACKUP_ROOT"
|
err "Backup adresář neexistuje: $BACKUP_ROOT"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# === Seznam dostupných záloh ===
|
# === Seznam dostupných záloh ===
|
||||||
list_backups() {
|
list_backups() {
|
||||||
printf '%s%s%s\n' "$BLUE" "=== DOSTUPNÉ ZÁLOHY ===" "$NC"
|
printf '%s%s%s\n' "$BLUE" "=== DOSTUPNÉ ZÁLOHY ===" "$NC"
|
||||||
found=0
|
found=0
|
||||||
while IFS= read -r d; do
|
while IFS= read -r d; do
|
||||||
[ -d "$d" ] || continue
|
[ -d "$d" ] || continue
|
||||||
ts=$(basename "$d")
|
ts=$(basename "$d")
|
||||||
archives=$(/bin/ls "$d"/*.tar.zst 2>/dev/null | /usr/bin/wc -l | /usr/bin/tr -d ' ')
|
archives=$(/bin/ls "$d"/*.tar.zst 2>/dev/null | /usr/bin/wc -l | /usr/bin/tr -d ' ')
|
||||||
size=$(/usr/bin/du -sh "$d" 2>/dev/null | /usr/bin/awk '{print $1}')
|
size=$(/usr/bin/du -sh "$d" 2>/dev/null | /usr/bin/awk '{print $1}')
|
||||||
printf ' %s %s archivů %s\n' "$ts" "$archives" "$size"
|
printf ' %s %s archivů %s\n' "$ts" "$archives" "$size"
|
||||||
found=$((found + 1))
|
found=$((found + 1))
|
||||||
done < <(/usr/bin/find "$BACKUP_ROOT" -maxdepth 1 -mindepth 1 -type d \
|
done < <(/usr/bin/find "$BACKUP_ROOT" -maxdepth 1 -mindepth 1 -type d \
|
||||||
| /usr/bin/sort -r)
|
| /usr/bin/sort -r)
|
||||||
if [ "$found" -eq 0 ]; then
|
if [ "$found" -eq 0 ]; then
|
||||||
printf ' (žádné)\n'
|
printf ' (žádné)\n'
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Zobrazit seznam, pokud --list
|
# Zobrazit seznam, pokud --list
|
||||||
if [ "$LIST_ONLY" = "1" ]; then
|
if [ "$LIST_ONLY" = "1" ]; then
|
||||||
list_backups
|
list_backups
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# === Vyber zálohu ===
|
# === Vyber zálohu ===
|
||||||
if [ -z "$TS_ARG" ]; then
|
if [ -z "$TS_ARG" ]; then
|
||||||
list_backups
|
list_backups
|
||||||
printf '\n%sZadejte timestamp zálohy (např. 2026-07-29_19-21-00):%s ' "$YELLOW" "$NC"
|
printf '\n%sZadejte timestamp zálohy (např. 2026-07-29_19-21-00):%s ' "$YELLOW" "$NC"
|
||||||
read -r TS_ARG
|
read -r TS_ARG
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BACKUP_DIR="$BACKUP_ROOT/$TS_ARG"
|
BACKUP_DIR="$BACKUP_ROOT/$TS_ARG"
|
||||||
if [ ! -d "$BACKUP_DIR" ]; then
|
if [ ! -d "$BACKUP_DIR" ]; then
|
||||||
err "Záloha neexistuje: $BACKUP_DIR"
|
err "Záloha neexistuje: $BACKUP_DIR"
|
||||||
list_backups
|
list_backups
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Vybraná záloha: $BACKUP_DIR"
|
log "Vybraná záloha: $BACKUP_DIR"
|
||||||
log "Archivy v záloze:"
|
log "Archivy v záloze:"
|
||||||
/bin/ls -1 "$BACKUP_DIR"/*.tar.zst 2>/dev/null | /usr/bin/sed 's/^/ /' || {
|
/bin/ls -1 "$BACKUP_DIR"/*.tar.zst 2>/dev/null | /usr/bin/sed 's/^/ /' || {
|
||||||
err "Záloha neobsahuje žádné archivy"
|
err "Záloha neobsahuje žádné archivy"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# === Detekce projektů v záloze ===
|
# === Detekce projektů v záloze ===
|
||||||
DETECTED=()
|
DETECTED=()
|
||||||
for archive in "$BACKUP_DIR"/*.tar.zst; do
|
for archive in "$BACKUP_DIR"/*.tar.zst; do
|
||||||
name=$(basename "$archive" .tar.zst)
|
name=$(basename "$archive" .tar.zst)
|
||||||
# Rozlišení: project archive vs volume archive
|
# Rozlišení: project archive vs volume archive
|
||||||
# Project: <project>.tar.zst v Dockery/
|
# Project: <project>.tar.zst v Dockery/
|
||||||
# Volume: <volname>.tar.zst v named volumes
|
# Volume: <volname>.tar.zst v named volumes
|
||||||
if [ -d "$DOCKERY/$name" ] && compgen -G "$DOCKERY/$name/docker-compose.y*ml" > /dev/null; then
|
if [ -d "$DOCKERY/$name" ] && compgen -G "$DOCKERY/$name/docker-compose.y*ml" > /dev/null; then
|
||||||
DETECTED+=("project:$name")
|
DETECTED+=("project:$name")
|
||||||
else
|
else
|
||||||
# Může to být volume; docker volume ls to ověří
|
# Může to být volume; docker volume ls to ověří
|
||||||
if docker volume inspect "$name" >/dev/null 2>&1; then
|
if docker volume inspect "$name" >/dev/null 2>&1; then
|
||||||
DETECTED+=("volume:$name")
|
DETECTED+=("volume:$name")
|
||||||
else
|
else
|
||||||
DETECTED+=("unknown:$name")
|
DETECTED+=("unknown:$name")
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
log ""
|
log ""
|
||||||
log "Položky v záloze:"
|
log "Položky v záloze:"
|
||||||
for d in "${DETECTED[@]}"; do printf ' %s\n' "$d"; done
|
for d in "${DETECTED[@]}"; do printf ' %s\n' "$d"; done
|
||||||
|
|
||||||
# === Vyber projekty k obnově ===
|
# === Vyber projekty k obnově ===
|
||||||
if [ "${#PROJECTS_RAW[@]}" -eq 0 ]; then
|
if [ "${#PROJECTS_RAW[@]}" -eq 0 ]; then
|
||||||
PROJECTS=()
|
PROJECTS=()
|
||||||
for d in "${DETECTED[@]}"; do
|
for d in "${DETECTED[@]}"; do
|
||||||
type=${d%%:*}
|
type=${d%%:*}
|
||||||
name=${d#*:}
|
name=${d#*:}
|
||||||
if [ "$type" = "project" ]; then
|
if [ "$type" = "project" ]; then
|
||||||
PROJECTS+=("$name")
|
PROJECTS+=("$name")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "${#PROJECTS[@]}" -eq 0 ]; then
|
if [ "${#PROJECTS[@]}" -eq 0 ]; then
|
||||||
err "V záloze nebyly nalezeny žádné projekty k obnově"
|
err "V záloze nebyly nalezeny žádné projekty k obnově"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
printf '\n%sDostupné projekty v této záloze:%s\n' "$BLUE" "$NC"
|
printf '\n%sDostupné projekty v této záloze:%s\n' "$BLUE" "$NC"
|
||||||
for i in "${!PROJECTS[@]}"; do
|
for i in "${!PROJECTS[@]}"; do
|
||||||
printf ' %d) %s\n' $((i+1)) "${PROJECTS[$i]}"
|
printf ' %d) %s\n' $((i+1)) "${PROJECTS[$i]}"
|
||||||
done
|
done
|
||||||
printf '%sVyberte projekty (např. 1 3 nebo "all"):%s ' "$YELLOW" "$NC"
|
printf '%sVyberte projekty (např. 1 3 nebo "all"):%s ' "$YELLOW" "$NC"
|
||||||
read -r sel
|
read -r sel
|
||||||
case "$sel" in
|
case "$sel" in
|
||||||
all|"") SELECTED=("${PROJECTS[@]}") ;;
|
all|"") SELECTED=("${PROJECTS[@]}") ;;
|
||||||
*)
|
*)
|
||||||
SELECTED=()
|
SELECTED=()
|
||||||
for n in $sel; do
|
for n in $sel; do
|
||||||
idx=$((n - 1))
|
idx=$((n - 1))
|
||||||
if [ "$idx" -ge 0 ] && [ "$idx" -lt "${#PROJECTS[@]}" ]; then
|
if [ "$idx" -ge 0 ] && [ "$idx" -lt "${#PROJECTS[@]}" ]; then
|
||||||
SELECTED+=("${PROJECTS[$idx]}")
|
SELECTED+=("${PROJECTS[$idx]}")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
else
|
else
|
||||||
SELECTED=("${PROJECTS_RAW[@]}")
|
SELECTED=("${PROJECTS_RAW[@]}")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${#SELECTED[@]}" -eq 0 ]; then
|
if [ "${#SELECTED[@]}" -eq 0 ]; then
|
||||||
err "Nebyly vybrány žádné projekty"
|
err "Nebyly vybrány žádné projekty"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# === Plán obnovy ===
|
# === Plán obnovy ===
|
||||||
TS_NOW="$(date +%Y-%m-%d_%H-%M-%S)"
|
TS_NOW="$(date +%Y-%m-%d_%H-%M-%S)"
|
||||||
|
|
||||||
printf '\n%s%s%s\n' "$BLUE" "=== PLÁN OBNOVY ===" "$NC"
|
printf '\n%s%s%s\n' "$BLUE" "=== PLÁN OBNOVY ===" "$NC"
|
||||||
printf 'Záloha: %s\n' "$BACKUP_DIR"
|
printf 'Záloha: %s\n' "$BACKUP_DIR"
|
||||||
printf 'Projekty: %s\n' "${SELECTED[*]}"
|
printf 'Projekty: %s\n' "${SELECTED[*]}"
|
||||||
printf 'Pre-restore: %s/_pre-restore-%s/\n' "$DOCKERY" "$TS_NOW"
|
printf 'Pre-restore: %s/_pre-restore-%s/\n' "$DOCKERY" "$TS_NOW"
|
||||||
printf '\n'
|
printf '\n'
|
||||||
|
|
||||||
confirm "Pokračovat v obnově? (PŘEPSOU se současná data projektů!) [y/N]"
|
confirm "Pokračovat v obnově? (PŘEPSOU se současná data projektů!) [y/N]"
|
||||||
@@ -210,34 +210,34 @@ confirm "Pokračovat v obnově? (PŘEPSOU se současná data projektů!) [y/N]"
|
|||||||
log ""
|
log ""
|
||||||
log "=== STOP: zastavuji kontejnery vybraných projektů ==="
|
log "=== STOP: zastavuji kontejnery vybraných projektů ==="
|
||||||
for project in "${SELECTED[@]}"; do
|
for project in "${SELECTED[@]}"; do
|
||||||
cd "$DOCKERY/$project"
|
cd "$DOCKERY/$project"
|
||||||
if docker compose ps -q 2>/dev/null | grep -q .; then
|
if docker compose ps -q 2>/dev/null | grep -q .; then
|
||||||
log " [$project] docker compose stop"
|
log " [$project] docker compose stop"
|
||||||
docker compose stop 2>&1 || warn " [$project] stop selhal"
|
docker compose stop 2>&1 || warn " [$project] stop selhal"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# === Move-aside současných dat ===
|
# === Move-aside současných dat ===
|
||||||
log ""
|
log ""
|
||||||
log "=== MOVE-ASIDE: přesouvám současná data do _pre-restore-$TS_NOW/ ==="
|
log "=== MOVE-ASIDE: přesouvám současná data do _pre-restore-$TS_NOW/ ==="
|
||||||
for project in "${SELECTED[@]}"; do
|
for project in "${SELECTED[@]}"; do
|
||||||
SRC="$DOCKERY/$project"
|
SRC="$DOCKERY/$project"
|
||||||
DEST="$DOCKERY/_pre-restore-$TS_NOW/$project"
|
DEST="$DOCKERY/_pre-restore-$TS_NOW/$project"
|
||||||
mkdir -p "$DEST"
|
mkdir -p "$DEST"
|
||||||
# Najdi velké podadresáře a přesuň je (NEVYMAŽ!)
|
# Najdi velké podadresáře a přesuň je (NEVYMAŽ!)
|
||||||
for sub in $(/bin/ls -A "$SRC" 2>/dev/null); do
|
for sub in $(/bin/ls -A "$SRC" 2>/dev/null); do
|
||||||
# Přeskoč .DS_Store, samotný _pre-restore-*, docker-compose soubory
|
# Přeskoč .DS_Store, samotný _pre-restore-*, docker-compose soubory
|
||||||
case "$sub" in
|
case "$sub" in
|
||||||
_pre-restore-*) continue ;;
|
_pre-restore-*) continue ;;
|
||||||
docker-compose.y*ml) continue ;;
|
docker-compose.y*ml) continue ;;
|
||||||
.env|.env.*) continue ;; # .env necháme — je to runtime konfigurace
|
.env|.env.*) continue ;; # .env necháme -- je to runtime konfigurace
|
||||||
.DS_Store) continue ;;
|
.DS_Store) continue ;;
|
||||||
esac
|
esac
|
||||||
if [ -e "$SRC/$sub" ]; then
|
if [ -e "$SRC/$sub" ]; then
|
||||||
log " [$project] mv $sub → _pre-restore-$TS_NOW/$project/"
|
log " [$project] mv $sub -> _pre-restore-$TS_NOW/$project/"
|
||||||
mv "$SRC/$sub" "$DEST/$sub" 2>&1 || warn " mv $sub selhal"
|
mv "$SRC/$sub" "$DEST/$sub" 2>&1 || warn " mv $sub selhal"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
# === Restore ze zálohy ===
|
# === Restore ze zálohy ===
|
||||||
@@ -245,50 +245,50 @@ log ""
|
|||||||
log "=== RESTORE: extrahuji ze zálohy ==="
|
log "=== RESTORE: extrahuji ze zálohy ==="
|
||||||
FAILED=()
|
FAILED=()
|
||||||
for project in "${SELECTED[@]}"; do
|
for project in "${SELECTED[@]}"; do
|
||||||
ARCHIVE="$BACKUP_DIR/${project}.tar.zst"
|
ARCHIVE="$BACKUP_DIR/${project}.tar.zst"
|
||||||
if [ ! -f "$ARCHIVE" ]; then
|
if [ ! -f "$ARCHIVE" ]; then
|
||||||
warn "[$project] archiv neexistuje: $ARCHIVE (přeskakuji)"
|
warn "[$project] archiv neexistuje: $ARCHIVE (přeskakuji)"
|
||||||
FAILED+=("$project:no-archive")
|
FAILED+=("$project:no-archive")
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log " [$project] extrahuji do $DOCKERY/$project/"
|
log " [$project] extrahuji do $DOCKERY/$project/"
|
||||||
# Archiv má cesty jako Volumes/SSD-data/Dockery/<project>/...
|
# Archiv má cesty jako Volumes/SSD-data/Dockery/<project>/...
|
||||||
# Extrahujeme s --strip-components=3, abychom získali přímo <project>/...
|
# Extrahujeme s --strip-components=3, abychom získali přímo <project>/...
|
||||||
cd "$DOCKERY"
|
cd "$DOCKERY"
|
||||||
if /opt/homebrew/bin/zstd -d -c "$ARCHIVE" \
|
if /opt/homebrew/bin/zstd -d -c "$ARCHIVE" \
|
||||||
| /usr/bin/tar --strip-components=3 -xf - 2>>"$BACKUP_DIR/.restore-errors-$TS_NOW"; then
|
| /usr/bin/tar --strip-components=3 -xf - 2>>"$BACKUP_DIR/.restore-errors-$TS_NOW"; then
|
||||||
log " OK"
|
log " OK"
|
||||||
else
|
else
|
||||||
err " extrakce selhala pro $project"
|
err " extrakce selhala pro $project"
|
||||||
FAILED+=("$project:extract-failed")
|
FAILED+=("$project:extract-failed")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# === Vyčistit AppleDouble _* (prevence MariaDB/PG smyček) ===
|
# === Vyčistit AppleDouble _* (prevence MariaDB/PG smyček) ===
|
||||||
log ""
|
log ""
|
||||||
log "=== CLEAN APPLE DOUBLE: mažu ._* před startem ==="
|
log "=== CLEAN APPLE DOUBLE: mažu ._* před startem ==="
|
||||||
for project in "${SELECTED[@]}"; do
|
for project in "${SELECTED[@]}"; do
|
||||||
count=$(/usr/bin/find "$DOCKERY/$project" -name '._*' -type f 2>/dev/null | /usr/bin/wc -l | /usr/bin/tr -d ' ')
|
count=$(/usr/bin/find "$DOCKERY/$project" -name '._*' -type f 2>/dev/null | /usr/bin/wc -l | /usr/bin/tr -d ' ')
|
||||||
if [ "$count" -gt 0 ]; then
|
if [ "$count" -gt 0 ]; then
|
||||||
log " [$project] nalezeno $count ._* souborů, mažu"
|
log " [$project] nalezeno $count ._* souborů, mažu"
|
||||||
/usr/bin/find "$DOCKERY/$project" -name '._*' -type f -delete 2>/dev/null || true
|
/usr/bin/find "$DOCKERY/$project" -name '._*' -type f -delete 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
# .DS_Store
|
# .DS_Store
|
||||||
dscount=$(/usr/bin/find "$DOCKERY/$project" -name '.DS_Store' -type f 2>/dev/null | /usr/bin/wc -l | /usr/bin/tr -d ' ')
|
dscount=$(/usr/bin/find "$DOCKERY/$project" -name '.DS_Store' -type f 2>/dev/null | /usr/bin/wc -l | /usr/bin/tr -d ' ')
|
||||||
if [ "$dscount" -gt 0 ]; then
|
if [ "$dscount" -gt 0 ]; then
|
||||||
log " [$project] nalezeno $dscount .DS_Store, mažu"
|
log " [$project] nalezeno $dscount .DS_Store, mažu"
|
||||||
/usr/bin/find "$DOCKERY/$project" -name '.DS_Store' -type f -delete 2>/dev/null || true
|
/usr/bin/find "$DOCKERY/$project" -name '.DS_Store' -type f -delete 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# === Up kontejnerů ===
|
# === Up kontejnerů ===
|
||||||
log ""
|
log ""
|
||||||
log "=== UP: spouštím kontejnery ==="
|
log "=== UP: spouštím kontejnery ==="
|
||||||
for project in "${SELECTED[@]}"; do
|
for project in "${SELECTED[@]}"; do
|
||||||
cd "$DOCKERY/$project"
|
cd "$DOCKERY/$project"
|
||||||
log " [$project] docker compose up -d"
|
log " [$project] docker compose up -d"
|
||||||
docker compose up -d 2>&1 | /usr/bin/head -10 || warn " [$project] up -d selhal"
|
docker compose up -d 2>&1 | /usr/bin/head -10 || warn " [$project] up -d selhal"
|
||||||
done
|
done
|
||||||
|
|
||||||
# === Health check ===
|
# === Health check ===
|
||||||
@@ -298,45 +298,45 @@ sleep 10
|
|||||||
elapsed=10
|
elapsed=10
|
||||||
all_healthy=1
|
all_healthy=1
|
||||||
while [ "$elapsed" -lt "$HEALTH_TIMEOUT" ]; do
|
while [ "$elapsed" -lt "$HEALTH_TIMEOUT" ]; do
|
||||||
sleep 5
|
sleep 5
|
||||||
elapsed=$((elapsed + 5))
|
elapsed=$((elapsed + 5))
|
||||||
for project in "${SELECTED[@]}"; do
|
for project in "${SELECTED[@]}"; do
|
||||||
for c in $(docker compose -f "$DOCKERY/$project/docker-compose.yml" ps -a --format '{{.Names}}' 2>/dev/null); do
|
for c in $(docker compose -f "$DOCKERY/$project/docker-compose.yml" ps -a --format '{{.Names}}' 2>/dev/null); do
|
||||||
status=$(docker inspect --format='{{.State.Status}}' "$c" 2>/dev/null)
|
status=$(docker inspect --format='{{.State.Status}}' "$c" 2>/dev/null)
|
||||||
health=$(docker inspect --format='{{.State.Health.Status}}' "$c" 2>/dev/null)
|
health=$(docker inspect --format='{{.State.Health.Status}}' "$c" 2>/dev/null)
|
||||||
if [ "$status" != "running" ]; then
|
if [ "$status" != "running" ]; then
|
||||||
log " [$project/$c] state=$status health=$health"
|
log " [$project/$c] state=$status health=$health"
|
||||||
all_healthy=0
|
all_healthy=0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
# Pokud všechny kontejnery running a žádné health check nehlásí starting/unhealthy, ukončíme
|
# Pokud všechny kontejnery running a žádné health check nehlásí starting/unhealthy, ukončíme
|
||||||
starting=$(docker ps --format '{{.Names}}' \
|
starting=$(docker ps --format '{{.Names}}' \
|
||||||
| while read -r c; do docker inspect --format='{{.State.Health.Status}}' "$c" 2>/dev/null; done \
|
| while read -r c; do docker inspect --format='{{.State.Health.Status}}' "$c" 2>/dev/null; done \
|
||||||
| /usr/bin/grep -c '^starting$' || true)
|
| /usr/bin/grep -c '^starting$' || true)
|
||||||
if [ "$starting" = "0" ]; then
|
if [ "$starting" = "0" ]; then
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# === Report ===
|
# === Report ===
|
||||||
log ""
|
log ""
|
||||||
log "======================================"
|
log "======================================"
|
||||||
log " SHRNUTÍ OBNOVY"
|
log " SHRNUTÍ OBNOVY"
|
||||||
log "======================================"
|
log "======================================"
|
||||||
log "Obnoveno:"
|
log "Obnoveno:"
|
||||||
for p in "${SELECTED[@]}"; do log " ✓ $p"; done
|
for p in "${SELECTED[@]}"; do log " [OK] $p"; done
|
||||||
if [ "${#FAILED[@]}" -gt 0 ]; then
|
if [ "${#FAILED[@]}" -gt 0 ]; then
|
||||||
err "Selhalo:"
|
err "Selhalo:"
|
||||||
for f in "${FAILED[@]}"; do err " ✗ $f"; done
|
for f in "${FAILED[@]}"; do err " [FAIL] $f"; done
|
||||||
fi
|
fi
|
||||||
log ""
|
log ""
|
||||||
log "Současná data jsou v: $DOCKERY/_pre-restore-$TS_NOW/"
|
log "Současná data jsou v: $DOCKERY/_pre-restore-$TS_NOW/"
|
||||||
log "Po ověření můžete tento adresář ručně smazat:"
|
log "Po ověření můžete tento adresář ručně smazat:"
|
||||||
log " rm -rf $DOCKERY/_pre-restore-$TS_NOW"
|
log " rm -rf $DOCKERY/_pre-restore-$TS_NOW"
|
||||||
log ""
|
log ""
|
||||||
if [ "$all_healthy" = "1" ]; then
|
if [ "$all_healthy" = "1" ]; then
|
||||||
log "Všechny kontejnery běží."
|
log "Všechny kontejnery běží."
|
||||||
else
|
else
|
||||||
warn "Některé kontejnery nejsou v pořádku — zkontrolujte logy."
|
warn "Některé kontejnery nejsou v pořádku -- zkontrolujte logy."
|
||||||
fi
|
fi
|
||||||
+99
-99
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
#
|
#
|
||||||
# docker-update.sh — bezpečná aktualizace Docker Compose projektů
|
# docker-update.sh -- bezpečná aktualizace Docker Compose projektů
|
||||||
#
|
#
|
||||||
# - zastaví všechny kontejnery,
|
# - zastaví všechny kontejnery,
|
||||||
# - stáhne nové image (docker compose pull),
|
# - stáhne nové image (docker compose pull),
|
||||||
@@ -8,36 +8,36 @@
|
|||||||
# - spustí kontejnery z nových image,
|
# - spustí kontejnery z nových image,
|
||||||
# - zkontroluje health checks,
|
# - zkontroluje health checks,
|
||||||
# - ponechá staré image pro případný rollback (docker image prune -f na konci,
|
# - ponechá staré image pro případný rollback (docker image prune -f na konci,
|
||||||
# ale jen dangling, ne všechno).
|
# ale jen dangling, ne všechno).
|
||||||
#
|
#
|
||||||
# Použití:
|
# Použití:
|
||||||
# ./docker-update.sh # aktualizuje všechny projekty v <docker-projects-root>
|
# ./docker-update.sh # aktualizuje všechny projekty v <docker-projects-root>
|
||||||
# ./docker-update.sh nginx mattermost # aktualizuje vybrané projekty
|
# ./docker-update.sh nginx mattermost # aktualizuje vybrané projekty
|
||||||
# ./docker-update.sh --help # nápověda
|
# ./docker-update.sh --help # nápověda
|
||||||
#
|
#
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'
|
sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'
|
||||||
}
|
}
|
||||||
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
--help|-h) usage; exit 0 ;;
|
--help|-h) usage; exit 0 ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
DOCKERY="${DOCKERY:-<docker-projects-root>}"
|
DOCKERY="${DOCKERY:-<docker-projects-root>}"
|
||||||
LOG="${LOG:-/tmp/docker-update-$(date +%Y-%m-%d_%H-%M).log}"
|
LOG="${LOG:-/tmp/docker-update-$(date +%Y-%m-%d_%H-%M).log}"
|
||||||
PRUNE_DANGLING="${PRUNE_DANGLING:-1}" # 1 = docker image prune -f (dangling only)
|
PRUNE_DANGLING="${PRUNE_DANGLING:-1}" # 1 = docker image prune -f (dangling only)
|
||||||
HEALTH_TIMEOUT="${HEALTH_TIMEOUT:-60}" # sekund na health check po startu
|
HEALTH_TIMEOUT="${HEALTH_TIMEOUT:-60}" # sekund na health check po startu
|
||||||
|
|
||||||
# Barevné výstupy (pokud terminál umí)
|
# Barevné výstupy (pokud terminál umí)
|
||||||
if [ -t 1 ]; then
|
if [ -t 1 ]; then
|
||||||
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||||
else
|
else
|
||||||
RED=''; GREEN=''; YELLOW=''; NC=''
|
RED=''; GREEN=''; YELLOW=''; NC=''
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log() { printf '%s[%s]%s %s\n' "$GREEN" "$(date '+%H:%M:%S')" "$NC" "$*" | tee -a "$LOG"; }
|
log() { printf '%s[%s]%s %s\n' "$GREEN" "$(date '+%H:%M:%S')" "$NC" "$*" | tee -a "$LOG"; }
|
||||||
@@ -46,26 +46,26 @@ err() { printf '%s[%s]%s %s\n' "$RED" "$(date '+%H:%M:%S')" "$NC" "$*" | tee -a
|
|||||||
|
|
||||||
# Kontrola, že docker běží
|
# Kontrola, že docker běží
|
||||||
if ! docker info >/dev/null 2>&1; then
|
if ! docker info >/dev/null 2>&1; then
|
||||||
err "Docker není dostupný. Spusť Docker Desktop a zkus znovu."
|
err "Docker není dostupný. Spusť Docker Desktop a zkus znovu."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Najdi projekty
|
# Najdi projekty
|
||||||
if [ "$#" -gt 0 ]; then
|
if [ "$#" -gt 0 ]; then
|
||||||
PROJECTS=("$@")
|
PROJECTS=("$@")
|
||||||
else
|
else
|
||||||
PROJECTS=()
|
PROJECTS=()
|
||||||
for d in "$DOCKERY"/*/; do
|
for d in "$DOCKERY"/*/; do
|
||||||
[ -d "$d" ] || continue
|
[ -d "$d" ] || continue
|
||||||
if compgen -G "$d/docker-compose.y*ml" > /dev/null; then
|
if compgen -G "$d/docker-compose.y*ml" > /dev/null; then
|
||||||
PROJECTS+=("$(basename "$d")")
|
PROJECTS+=("$(basename "$d")")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${#PROJECTS[@]}" -eq 0 ]; then
|
if [ "${#PROJECTS[@]}" -eq 0 ]; then
|
||||||
err "Nebyly nalezeny žádné Docker Compose projekty v $DOCKERY"
|
err "Nebyly nalezeny žádné Docker Compose projekty v $DOCKERY"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Nalezeno ${#PROJECTS[@]} projektů: ${PROJECTS[*]}"
|
log "Nalezeno ${#PROJECTS[@]} projektů: ${PROJECTS[*]}"
|
||||||
@@ -74,108 +74,108 @@ log "Nalezeno ${#PROJECTS[@]} projektů: ${PROJECTS[*]}"
|
|||||||
log "=== PRE-FLIGHT ==="
|
log "=== PRE-FLIGHT ==="
|
||||||
BEFORE_RUNNING=$(docker ps --format '{{.Names}}')
|
BEFORE_RUNNING=$(docker ps --format '{{.Names}}')
|
||||||
log "Běžící kontejnery před aktualizací:"
|
log "Běžící kontejnery před aktualizací:"
|
||||||
printf ' %s\n' $BEFORE_RUNNING
|
printf ' %s\n' $BEFORE_RUNNING
|
||||||
|
|
||||||
UPDATED=()
|
UPDATED=()
|
||||||
SKIPPED=()
|
SKIPPED=()
|
||||||
FAILED=()
|
FAILED=()
|
||||||
|
|
||||||
for project in "${PROJECTS[@]}"; do
|
for project in "${PROJECTS[@]}"; do
|
||||||
cd "$DOCKERY/$project"
|
cd "$DOCKERY/$project"
|
||||||
if ! compgen -G "docker-compose.y*ml" > /dev/null; then
|
if ! compgen -G "docker-compose.y*ml" > /dev/null; then
|
||||||
warn "[$project] nemá docker-compose soubor, přeskakuji"
|
warn "[$project] nemá docker-compose soubor, přeskakuji"
|
||||||
SKIPPED+=("$project")
|
SKIPPED+=("$project")
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log ""
|
log ""
|
||||||
log "=== [$project] ==="
|
log "=== [$project] ==="
|
||||||
|
|
||||||
# Zaznamenej aktuální image ID
|
# Zaznamenej aktuální image ID
|
||||||
BEFORE_DIGESTS=$(docker compose ps -q 2>/dev/null \
|
BEFORE_DIGESTS=$(docker compose ps -q 2>/dev/null \
|
||||||
| xargs -I{} docker inspect --format='{{.Image}}' {} 2>/dev/null \
|
| xargs -I{} docker inspect --format='{{.Image}}' {} 2>/dev/null \
|
||||||
| sort -u)
|
| sort -u)
|
||||||
|
|
||||||
# Pull
|
# Pull
|
||||||
log "[$project] docker compose pull"
|
log "[$project] docker compose pull"
|
||||||
if ! docker compose pull 2>&1 | tee -a "$LOG"; then
|
if ! docker compose pull 2>&1 | tee -a "$LOG"; then
|
||||||
err "[$project] pull selhal"
|
err "[$project] pull selhal"
|
||||||
FAILED+=("$project")
|
FAILED+=("$project")
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Ověř, že se image změnil
|
# Ověř, že se image změnil
|
||||||
AFTER_DIGESTS=$(docker compose config --images 2>/dev/null \
|
AFTER_DIGESTS=$(docker compose config --images 2>/dev/null \
|
||||||
| xargs -I{} docker image inspect --format='{{index .RepoDigests 0}}' "{}" 2>/dev/null \
|
| xargs -I{} docker image inspect --format='{{index .RepoDigests 0}}' "{}" 2>/dev/null \
|
||||||
| sort -u)
|
| sort -u)
|
||||||
|
|
||||||
CHANGED=0
|
CHANGED=0
|
||||||
for d in $AFTER_DIGESTS; do
|
for d in $AFTER_DIGESTS; do
|
||||||
if ! grep -qx "$d" <<<"$BEFORE_DIGESTS"; then
|
if ! grep -qx "$d" <<<"$BEFORE_DIGESTS"; then
|
||||||
CHANGED=1
|
CHANGED=1
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "$CHANGED" -eq 0 ]; then
|
if [ "$CHANGED" -eq 0 ]; then
|
||||||
log "[$project] žádná změna, přeskakuji up -d"
|
log "[$project] žádná změna, přeskakuji up -d"
|
||||||
SKIPPED+=("$project")
|
SKIPPED+=("$project")
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Up -d (vždy background kvůli shell guardu)
|
# Up -d (vždy background kvůli shell guardu)
|
||||||
log "[$project] docker compose up -d"
|
log "[$project] docker compose up -d"
|
||||||
if ! docker compose up -d 2>&1 | tee -a "$LOG"; then
|
if ! docker compose up -d 2>&1 | tee -a "$LOG"; then
|
||||||
err "[$project] up -d selhal"
|
err "[$project] up -d selhal"
|
||||||
FAILED+=("$project")
|
FAILED+=("$project")
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Počkej na health check
|
# Počkej na health check
|
||||||
log "[$project] čekám na health check (max ${HEALTH_TIMEOUT}s)"
|
log "[$project] čekám na health check (max ${HEALTH_TIMEOUT}s)"
|
||||||
elapsed=0
|
elapsed=0
|
||||||
healthy=0
|
healthy=0
|
||||||
while [ "$elapsed" -lt "$HEALTH_TIMEOUT" ]; do
|
while [ "$elapsed" -lt "$HEALTH_TIMEOUT" ]; do
|
||||||
sleep 5
|
sleep 5
|
||||||
elapsed=$((elapsed + 5))
|
elapsed=$((elapsed + 5))
|
||||||
unhealthy=$(docker compose ps 2>/dev/null \
|
unhealthy=$(docker compose ps 2>/dev/null \
|
||||||
| tail -n +2 \
|
| tail -n +2 \
|
||||||
| awk '{print $NF}' \
|
| awk '{print $NF}' \
|
||||||
| grep -vE 'healthy|Up' || true)
|
| grep -vE 'healthy|Up' || true)
|
||||||
if [ -z "$unhealthy" ]; then
|
if [ -z "$unhealthy" ]; then
|
||||||
healthy=1
|
healthy=1
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [ "$healthy" -eq 1 ]; then
|
if [ "$healthy" -eq 1 ]; then
|
||||||
log "[$project] OK"
|
log "[$project] OK"
|
||||||
UPDATED+=("$project")
|
UPDATED+=("$project")
|
||||||
else
|
else
|
||||||
warn "[$project] běží, ale ne všechny health checks prošly (zkontroluj ručně)"
|
warn "[$project] běží, ale ne všechny health checks prošly (zkontroluj ručně)"
|
||||||
UPDATED+=("$project:health_unknown")
|
UPDATED+=("$project:health_unknown")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Prune dangling images (bezpečné)
|
# Prune dangling images (bezpečné)
|
||||||
if [ "$PRUNE_DANGLING" = "1" ]; then
|
if [ "$PRUNE_DANGLING" = "1" ]; then
|
||||||
log ""
|
log ""
|
||||||
log "=== DOCKER IMAGE PRUNE -f (dangling only) ==="
|
log "=== DOCKER IMAGE PRUNE -f (dangling only) ==="
|
||||||
docker image prune -f 2>&1 | tee -a "$LOG" || true
|
docker image prune -f 2>&1 | tee -a "$LOG" || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Report
|
# Report
|
||||||
log ""
|
log ""
|
||||||
log "======================================"
|
log "======================================"
|
||||||
log " SHRNUTÍ"
|
log " SHRNUTÍ"
|
||||||
log "======================================"
|
log "======================================"
|
||||||
log "Aktualizováno: ${#UPDATED[@]} projekt(ů)"
|
log "Aktualizováno: ${#UPDATED[@]} projekt(ů)"
|
||||||
for p in "${UPDATED[@]:-}"; do [ -n "$p" ] && log " ✓ $p"; done
|
for p in "${UPDATED[@]:-}"; do [ -n "$p" ] && log " [OK] $p"; done
|
||||||
log "Přeskočeno (beze změny): ${#SKIPPED[@]}"
|
log "Přeskočeno (beze změny): ${#SKIPPED[@]}"
|
||||||
for p in "${SKIPPED[@]:-}"; do [ -n "$p" ] && log " - $p"; done
|
for p in "${SKIPPED[@]:-}"; do [ -n "$p" ] && log " - $p"; done
|
||||||
if [ "${#FAILED[@]}" -gt 0 ]; then
|
if [ "${#FAILED[@]}" -gt 0 ]; then
|
||||||
log "Selhalo: ${#FAILED[@]}"
|
log "Selhalo: ${#FAILED[@]}"
|
||||||
for p in "${FAILED[@]}"; do err " ✗ $p"; done
|
for p in "${FAILED[@]}"; do err " [FAIL] $p"; done
|
||||||
fi
|
fi
|
||||||
log ""
|
log ""
|
||||||
log "Log: $LOG"
|
log "Log: $LOG"
|
||||||
+9
-9
@@ -10,35 +10,35 @@ echo ""
|
|||||||
|
|
||||||
# Kontrola Homebrew
|
# Kontrola Homebrew
|
||||||
if ! command -v brew >/dev/null 2>&1; then
|
if ! command -v brew >/dev/null 2>&1; then
|
||||||
echo "Homebrew není nainstalovaný."
|
echo "Homebrew není nainstalovaný."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "→ Aktualizace Homebrew metadata..."
|
echo "-> Aktualizace Homebrew metadata..."
|
||||||
brew update
|
brew update
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "→ Upgrade formulae..."
|
echo "-> Upgrade formulae..."
|
||||||
brew upgrade
|
brew upgrade
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "→ Upgrade casks..."
|
echo "-> Upgrade casks..."
|
||||||
brew upgrade --cask
|
brew upgrade --cask
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "→ Cleanup..."
|
echo "-> Cleanup..."
|
||||||
brew cleanup
|
brew cleanup
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "→ Autoremove..."
|
echo "-> Autoremove..."
|
||||||
brew autoremove
|
brew autoremove
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "→ Health check..."
|
echo "-> Health check..."
|
||||||
brew doctor
|
brew doctor
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "→ Outdated packages:"
|
echo "-> Outdated packages:"
|
||||||
brew outdated || true
|
brew outdated || true
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
Reference in New Issue
Block a user