Noční dump databáze do S3 je jeden řádek cronu, který pošle výstup dumpovacího nástroje rourou do nahrávání: pg_dump -Fc | aws s3 cp - s3://db-dumps/app/$(date +%F).dump. Ten řádek funguje a obsahuje také nejčastější způsob, jak tyhle úlohy tiše selhávají - když dump v půlce zemře, nahrávání přesto uspěje a ty každou noc ukládáš useknutý soubor až do dne, kdy budeš nějaký potřebovat. Tento článek staví úlohu správně pro PostgreSQL, MySQL a MongoDB: kde ji spouštět, jak udržet hesla mimo seznam procesů, jak zajistit, že se uchovávají jen kompletní dumpy, šifrování, retenci bez lifecycle pravidel a test obnovy, který ti řekne, jestli něco z toho funguje.
Kde úloha běží#
Dumpovací nástroj se k databázi připojuje jako každý jiný klient, takže úloha může běžet na libovolném stroji, který dosáhne na host a port databáze a má nainstalované správné klientské nástroje. Možnosti, zhruba v pořadí podle preference:
- Malý Linuxový VDS nebo server, který už provozuješ, s cronem a klientskými balíčky. Obvyklá odpověď.
- Aplikační server, pokud je to plnohodnotný stroj, kam můžeš nainstalovat
postgresql-clienta nastavit úlohu v cronu. - Tvůj vlastní počítač nebo domácí server, který má tu výhodu, že je na jiném místě než databáze i bucket.
Spravované řady databází často nedávají shell na samotném stroji s databází, což je v pořádku: dumpovat přes síť je běžný postup. Na RE:NODE jsou řady PostgreSQL, MySQL a MongoDB dostupné na hostu a portu tarifu s vygenerovanými přihlašovacími údaji a mají vlastní sloty pro zálohy v panelu. Ty používej pro rychlé návraty zpět; dump v S3 je kopie, která přežije smazaný server, a přenositelný soubor, který načteš kdekoli.
Na verzi klienta záleží. pg_dump musí mít stejnou hlavní verzi jako server nebo novější - starší pg_dump odmítne dumpovat novější server. mysqldump z MySQL 8.4 dumpuje server 8.4 čistě; mysqldump z MariaDB proti MySQL 8.4 většinou funguje, ale není to nástroj, kterému bys měl pro zálohu věřit. mongodump pochází z balíčku MongoDB Database Tools, verzovaného zvlášť od serveru; jakékoli nedávné vydání podporuje současné servery.
Přihlašovací údaje bez úniku#
Hesla na příkazovém řádku vidí přes ps každý uživatel na stroji a končí v historii shellu. Každý nástroj má alternativu v podobě souboru.
| Nástroj | Soubor s údaji | Formát |
|---|---|---|
pg_dump | ~/.pgpass (režim 600) | host:port:dbname:user:password |
mysqldump | --defaults-extra-file=/etc/backup/my.cnf | sekce [client] s user, password, host, port |
mongodump | --config=/etc/backup/mongo.yaml | klíče uri: a password: |
| AWS CLI | ~/.aws/credentials a ~/.aws/config | profil s klíči a endpointem |
[client]host = db.example.netport = 3306user = backuppassword = s3cret-generated-value--defaults-extra-file musí být první volba na příkazovém řádku, jinak ji mysqldump ignoruje. Každý z těchto souborů nastav tak, aby ho mohl číst jen uživatel, pod kterým úloha běží (chmod 600).
Pokud to jde, dej dumpu vlastního databázového uživatele. Uživatel pro zálohy potřebuje právo čtení, ne zápisu: v PostgreSQL je to role pg_read_all_data (PostgreSQL 14 a novější); v MySQL SELECT, SHOW VIEW, TRIGGER, EVENT a LOCK TABLES na databázi plus globálně PROCESS, aby se zabránilo varování o tablespaces. Pak uniklý přihlašovací údaj k zálohám nemůže nic změnit. Role a oprávnění v PostgreSQL obsahují podrobnosti pro Postgres.
Na straně bucketu jednou nastav profil AWS CLI s endpointem, path-style a nastavením kompatibility kontrolních součtů, jak popisuje úložiště S3 s AWS CLI:
[profile store]region = us-east-1endpoint_url = https://s3.example.comrequest_checksum_calculation = when_requiredresponse_checksum_validation = when_requireds3 = addressing_style = pathPostgreSQL#
Použij formát custom (-Fc). Je komprimovaný a pg_restore z něj umí obnovit jedinou tabulku nebo schéma, vypsat jeho obsah a obnovovat paralelně. Prostý SQL dump jde přehrát jen celý.
$ pg_dump -h db.example.net -p 5432 -U backup -d app -Fc --no-owner \ | aws s3 cp - "s3://db-dumps/pg/app/$(date -u +%F_%H%M).dump" --profile storepg_dump pořizuje konzistentní snímek uvnitř jedné transakce, takže dump odráží jediný okamžik, i když aplikace dál zapisuje. Zapisující neblokuje. --no-owner vynechá příkazy o vlastnictví, takže se soubor dá obnovit do databáze, jejíž uživatel se jmenuje jinak - užitečné, když je cílem obnovy nový server s vygenerovanými údaji. Role a další objekty platné pro celý cluster v souboru z pg_dump vůbec nejsou; pokud je potřebuješ, zachytí je pg_dumpall --globals-only, i když na spravované řadě obvykle jediného aplikačního uživatele vytvoříš znovu ručně. pg_dump a pg_restore prochází každý přepínač.
MySQL#
$ mysqldump --defaults-extra-file=/etc/backup/my.cnf \ --single-transaction --routines --triggers --events --hex-blob \ --set-gtid-purged=OFF app \ | zstd -q -T0 \ | aws s3 cp - "s3://db-dumps/mysql/app/$(date -u +%F_%H%M).sql.zst" --profile storeK čemu který přepínač slouží:
--single-transactiondumpuje tabulky InnoDB z jednoho konzistentního snímku bez jejich zamykání. U tabulek MyISAM nedělá nic, ty bez zámku konzistentní nejsou - další důvod nemít žádné.--routines --triggers --eventszahrnou uložené procedury, triggery a naplánované události. Triggery jsou zapnuté ve výchozím stavu; zbylé dvě ne, a dump bez nich obnoví databázi, které tiše chybí logika.--hex-blobzapisuje binární sloupce v hexadecimálním tvaru, který přežije jakékoli zacházení se znakovou sadou na cestě zpátky.--set-gtid-purged=OFFnechá příkazy GTID mimo dump, takže se importuje do serveru, který není součástí stejného nastavení replikace.
Pozor: mysqlpump, paralelní alternativa, kterou doporučují některé starší návody, byl v MySQL 8.4 odstraněn. mysqldump zůstává a pro cokoli nad několik gigabajtů jsou rychlejší nástroje pro dump z MySQL Shell. Záloha a obnova pomocí mysqldump pokrývá obnovu a velké dumpy.
MongoDB#
$ mongodump --config=/etc/backup/mongo.yaml --archive --gzip \ | aws s3 cp - "s3://db-dumps/mongo/app/$(date -u +%F_%H%M).archive.gz" --profile storeuri: mongodb://backup@db.example.net:27017/app?authSource=adminpassword: s3cret-generated-value--archive bez názvu souboru zapisuje jediný proud archivu na stdout a --gzip ho komprimuje. Obnova čte tentýž proud: aws s3 cp s3://... - | mongorestore --archive --gzip --drop. Na samostatném serveru není mongodump snímkem k určitému časovému bodu - dokumenty zapsané během dlouhého dumpu v něm mohou, ale nemusí být. --oplog to řeší, ale funguje jen proti replica setu. U většiny malých aplikací je dump v nejklidnější hodinu dostatečně konzistentní; pokud ten tvůj ne, zastav zápisy nebo dumpuj z repliky. mongodump a mongorestore obsahuje zbytek.
Valkey a SQL Server
Dva další enginy chtějí jiný přístup. Valkey drží data v paměti a ukládá je na disk samo, takže „dump“ je kopie jeho snímku. valkey-cli (nebo redis-cli, který mluví stejným protokolem) ho umí stáhnout přes síť: valkey-cli -h db.example.net -p 6379 --user default --pass "$PW" --rdb /tmp/dump.rdb požádá server o čerstvý soubor RDB a zapíše ho lokálně, připravený k nahrání. Většina lidí cache nezálohuje vůbec - pokud jde přestavět ze skutečné databáze, její ztráta stojí pár pomalých minut - ale Valkey, které drží relace nebo fronty, si noční kopii zaslouží. --pass na příkazovém řádku se objeví v ps; redis-cli místo toho čte heslo z proměnné prostředí REDISCLI_AUTH a valkey-cli --help vypíše proměnnou, kterou respektuje tvá verze.
SQL Server to dělá obráceně: BACKUP DATABASE zapíše soubor .bak na vlastní disk serveru a Express nemá SQL Server Agent, který by to plánoval. Obvyklý postup je naplánované volání sqlcmd z jiného stroje, které zálohu spustí, a pak stažení souboru. Záloha a obnova SQL Serveru to pokrývá a krok nahrání na konci je stejný aws s3 cp jako všude jinde.
Roura, která nahraje půl dumpu#
Tady je past z úvodu podrobně. V rouře A | B shell hlásí návratový kód B. Pokud pg_dump ztratí spojení po 300 MB z 1 GB dumpu, skončí s chybou, roura se zavře, aws s3 cp - vidí normální konec vstupu, nahraje 300 MB a skončí s kódem 0. Úloha „uspěla“. A může takhle uspět každou noc.
set -o pipefail způsobí, že skript si toho všimne - roura teď selže, pokud selhala kterákoli fáze - ale useknutý objekt už je v bucketu a nic v jeho názvu to neříká. Dva spolehlivé vzory:
Nahrát pod dočasný klíč, při úspěchu povýšit.
TMP="s3://db-dumps/_incoming/app-$$.dump"FINAL="s3://db-dumps/pg/app/$(date -u +%F_%H%M).dump"if pg_dump ... -Fc | aws s3 cp - "$TMP" --profile store; then aws s3 mv "$TMP" "$FINAL" --profile storeelse aws s3 rm "$TMP" --profile store exit 1fiS nastaveným pipefail vidí if selhání kterékoli fáze. Neúspěšný běh nezanechá nic pod finálním prefixem a cokoli, co uvízne v _incoming/, je viditelným znamením problému. mv je kopie na straně serveru následovaná smazáním, takže data nenahrává dvakrát.
Nejdřív dumpovat na lokální disk. Zapiš do souboru, zkontroluj návratový kód, ověř soubor (pg_restore --list dump.file > /dev/null přečte celý obsah archivu a na useknutém souboru ve formátu custom selže) a teprve potom nahraj. Vyžaduje to volný lokální disk o velikosti komprimovaného dumpu a výměnou za to dává nejsilnější kontrolu.
U proudů nad zhruba 50 GB AWS CLI potřebuje navíc --expected-size s odhadem velikosti v bajtech, aby zvolilo dost velké části a zůstalo pod limitem 10 000 částí multipartu. Pod tím jsou výchozí hodnoty v pořádku.
Šifrování#
Dump databáze je nejcitlivější soubor, jaký vlastníš: každý uživatel, každý hash, každá adresa. Pokud někdy unikne klíče k bucketu, uniknou s nimi i dumpy. Šifruj před nahráním, aby úložiště drželo jen šifrovaný text.
age je na to nejjednodušší nástroj. Vygeneruj pár klíčů jednou na stroji, který není záložním strojem, soukromý klíč drž offline a do úlohy dej jen veřejný klíč:
$ age-keygen -o backup-key.txt # on your own machine; keep this file safe$ pg_dump ... -Fc | age -r age1qz...publickey... \ | aws s3 cp - "s3://db-dumps/pg/app/$(date -u +%F).dump.age" --profile storeZáložní stroj umí šifrovat, ale ne dešifrovat, takže útočník, který převezme záložní stroj, nepřečte staré dumpy z bucketu. Obnova: aws s3 cp s3://.../file.dump.age - | age -d -i backup-key.txt | pg_restore .... gpg --symmetric také funguje, ale vyžaduje na záložním stroji heslo, čímž tuto vlastnost ztrácí. Ať zvolíš cokoli, dešifrovací klíč ulož někam, kde ho ještě budeš mít po zániku serveru - správce hesel, vytištěný v zásuvce - jinak jsou šifrované dumpy jen šum.
Retence bez lifecycle pravidel#
Pojmenuj klíče tak, aby se řadily podle data - pg/app/2026-10-08_0400.dump - a mazej podle názvu. Nespoléhej se na filtry typu --min-age v synchronizačních nástrojích; používají časy změny, které ne vždy znamenají „kdy byla záloha pořízena“.
Schéma děda-otec-syn drží týden denních, měsíc týdenních a rok měsíčních záloh se třemi prefixy. V neděli zkopíruj dump dané noci do weekly/; prvního do monthly/. Kopie na straně serveru nestojí žádné nahrávání:
#!/usr/bin/env bashset -euo pipefailP="--profile store"B="s3://db-dumps/pg/app"TODAY=$(date -u +%F)LATEST=$(aws s3 ls "$B/" $P | awk '{print $4}' | grep "^$TODAY" | sort | tail -n1)[ -n "$LATEST" ] || exit 1if [ "$(date -u +%u)" = 7 ]; then aws s3 cp "$B/$LATEST" "$B/weekly/$LATEST" $P; fiif [ "$(date -u +%d)" = 01 ]; then aws s3 cp "$B/$LATEST" "$B/monthly/$LATEST" $P; fiprune() { # prune <prefix> <days> local cutoff; cutoff=$(date -u -d "$2 days ago" +%F) { aws s3 ls "$1/" $P || true; } | awk '{print $4}' | while read -r k; do if [ -n "$k" ] && [[ "${k:0:10}" < "$cutoff" ]]; then aws s3 rm "$1/$k" $P; fi done}prune "$B" 7prune "$B/weekly" 35prune "$B/monthly" 366aws s3 ls na prefixu vypisuje objekty ve čtvrtém sloupci a složky jako řádky PRE, které kontrola prázdnoty přeskočí. Také skončí s chybou, když v prefixu zatím nic není - první týden, dokud neexistuje žádná týdenní kopie - a proto je to volání obaleno || true; bez toho by pipefail zastavil skript hned při prvním běhu. Spouštěj dump ve 04:00 a tohle ve 04:30 z cronu a na konec každého přidej ping dead-man's-switch, abys se dozvěděl o noci, kdy nic neběží. Výrazy cron vysvětleně popisují syntaxi plánu.
Velikost bucketu odvoď z pravidel. Sedm denních, pět týdenních a dvanáct měsíčních záloh je 24 dumpů; při komprimovaném dumpu o 400 MB to dělá zhruba 10 GB a růst databáze zvětšuje velikost úměrně.
Test obnovy#
Záloha, kterou nikdo neobnovil, je hypotéza. Jednou měsíčně, nebo po jakékoli změně úlohy:
- Stáhni nejnovější dump a dešifruj ho.
- Obnov do pomocné databáze - ideální je lokální kontejner Docker:
docker run --rm -e POSTGRES_PASSWORD=x -p 5433:5432 postgres:17. - Spusť dotaz, který funguje jen na skutečných datech: počty řádků v hlavních tabulkách, časová značka nejnovější objednávky.
- Poznamenej si, jak dlouho to trvalo. To je tvoje skutečná doba obnovy a bývá delší, než lidé odhadují.
Zálohy a obnovy databází a testování obnovy dřív, než ji potřebuješ z toho dělají rutinu.
FAQ#
Jak často mám databázi dumpovat?
Denně pokryje většinu malých aplikací. Interval je nejvíc dat, o která můžeš přijít, takže e-shop přijímající objednávky celý den může chtít dumpy každých pár hodin plus zálohy z panelu mezi nimi. Nad to je dalším krokem průběžné archivování (WAL pro Postgres, binlogy pro MySQL), což je jiná úloha.
Mám dumpy před nahráním komprimovat?
Ano, pokud formát už komprimovaný není. Formát custom v PostgreSQL je komprimovaný; mongodump --gzip také. Prostý mysqldump je text a pomocí zstd nebo gzip se zmenší pětkrát až desetkrát.
Můžu dumpovat, když aplikace běží?
Ano. pg_dump a mysqldump --single-transaction čtou konzistentní snímek bez blokování zápisů. I tak je spouštěj v klidné hodiny; dump čte každý řádek a soupeří se skutečnými dotazy o disk a CPU.
Stačí jedna kopie v S3?
Je to dobrá druhá kopie, ne jediná. Úložiště RE:NODE například drží jednu kopii na NVMe na jednom místě bez replikace. Zachovej i zálohy z panelu a jednou měsíčně stáhni dump někam úplně jinam.
Jak poznám, že úloha pořád běží?
Nech ji na konci každého úspěšného běhu pingnout monitorovací službu a upozorni se, když ping chybí. Čas od času také zkontroluj velikost nejnovějšího dumpu: náhlý pokles na pár kilobajtů znamená, že je něco špatně, i když každý krok „uspěl“.




Komentáře
Zcela anonymně: žádný účet, žádný e-mail, žádné cookie. Ukládáme jen jméno, které napíšeš, text a čas - nic dalšího. Počet odkazů je omezený a značkování se nevykresluje.