Архивы в DE-workflow: log rotation, streaming backups, split
Финальный урок модуля. Сборник production-готовых паттернов: rotation старых логов, streaming compression БД-дампов, разрезание больших архивов на части, integration с rsync для backup pipelines.
Pattern 1: Архивирование старых логов
DE-сервер копит логи Airflow, Postgres, Nginx. Если их не ротировать — диск кончается. Стандартный подход: ежедневный cron-job, который архивирует логи старше N дней и удаляет оригиналы.
#!/bin/bash
# rotate-logs.sh — ежедневная архивация логов старше 7 дней
set -euo pipefail
LOG_DIR="/var/log/airflow"
ARCHIVE_DIR="/var/log/archive"
DAYS=7
DATE=$(date +%Y-%m-%d)
mkdir -p "$ARCHIVE_DIR"
# Найти .log файлы старше 7 дней
OLD_LOGS=$(find "$LOG_DIR" -name '*.log' -mtime +$DAYS)
if [ -z "$OLD_LOGS" ]; then
echo "No old logs to archive"
exit 0
fi
# Архивировать в один tar.zst
echo "$OLD_LOGS" | tar --zstd -czvf "$ARCHIVE_DIR/airflow-logs-$DATE.tar.zst" -T -
# После успешного создания архива — удалить оригиналы
if [ -f "$ARCHIVE_DIR/airflow-logs-$DATE.tar.zst" ]; then
echo "$OLD_LOGS" | xargs rm -f
echo "Archived $(echo "$OLD_LOGS" | wc -l) files to $ARCHIVE_DIR/airflow-logs-$DATE.tar.zst"
fi
# Удалить архивы старше 90 дней
find "$ARCHIVE_DIR" -name 'airflow-logs-*.tar.zst' -mtime +90 -delete
-T - — список файлов из stdin. find ... | tar -T - — известный pattern.
Запуск через cron (модуль 16):
# /etc/cron.d/airflow-log-rotation
0 3 * * * root /opt/scripts/rotate-logs.sh > /var/log/log-rotation.log 2>&1
В реальности на production обычно используют logrotate — system utility для этого (модуль 15 покажет). Но понимать механику через tar — must.
Pattern 2: Streaming compression БД-дампов
Постгресовый dump 100GB БД в файл — нужно 100GB места для temporary, потом сжатие, потом удаление. Streaming compression — обходимся без temp:
# Простой dump + zstd
pg_dump --format=plain dbname | zstd -3 > /backup/dbname-$(date +%F).sql.zst
# С verbose progress (pv — pipe viewer)
pg_dump --format=plain dbname | pv | zstd -3 > /backup/dbname-$(date +%F).sql.zst
# Или
pg_dump --format=plain dbname | pv -p -t -e -r -s 100G | zstd -3 > backup.sql.zst
# -p прогресс, -t время, -e ETA, -r скорость, -s ожидаемый размер
Восстановление:
# Decompress + restore в один шаг
zstdcat /backup/dbname-2026-05-13.sql.zst | psql dbname
# Или
zstd -d -c /backup/dbname-2026-05-13.sql.zst | psql dbname
Без temp файла. Stream идёт прямо из pg_dump через zstd на диск (или через сеть). Это позволяет работать с БД больше доступного disk space.
Custom format для большой БД
# pg_dump --format=custom — бинарный, уже compressed
pg_dump --format=custom -Z 6 dbname > /backup/dbname-$(date +%F).dump
# -Z 6 — gzip compression level 6 (по умолчанию)
# Restore
pg_restore -d dbname /backup/dbname-2026-05-13.dump
# Параллельное восстановление (быстрее)
pg_restore -d dbname -j 4 /backup/dbname-2026-05-13.dump
Custom format уже сжат, не нужно дополнительно gzip. Также поддерживает selective restore (только конкретные таблицы).
Pattern 3: split — разрезание больших файлов
Иногда нужно разрезать огромный архив на части — для передачи через email (limit 20MB), для upload на cloud storage с size limit, для parallel download.
# Разрезать на куски по 100MB
split -b 100M huge-archive.tar.gz part_
# Результат: part_aa, part_ab, part_ac, ...
# С numeric suffix вместо alphabetic
split -b 100M -d huge-archive.tar.gz part_
# Результат: part_00, part_01, part_02, ...
# С suffix length 3 цифры
split -b 100M -d -a 3 huge-archive.tar.gz part_
# С дополнительным suffix
split -b 100M --additional-suffix=.bin huge-archive.tar.gz part_
# part_aa.bin, part_ab.bin, ...
Воссоединение — просто cat:
# Собрать обратно
cat part_* > huge-archive.tar.gz
# Проверить, что результат идентичен оригиналу
md5sum original.tar.gz huge-archive.tar.gz
# Хэши должны совпадать
Split по строкам
# Разрезать CSV на куски по 1M строк
split -l 1000000 huge.csv chunk_
# chunk_aa, chunk_ab, ...
# Сохранить header в каждом куске (стандартный split этого не умеет)
HEADER=$(head -1 huge.csv)
tail -n +2 huge.csv | split -l 1000000 - chunk_
for f in chunk_*; do
(echo "$HEADER"; cat "$f") > "${f}.csv"
done
DE use case: обрабатывать огромный CSV параллельно (Spark, parallel awk).
Docker volumes и bind mounts: архивы и persistence
Pattern 4: tar + rsync для backup pipelines
Комбинировать архивирование с network transfer:
# Сразу streaming на remote — без temp файла
tar czf - /var/data | ssh backup-server "cat > /backups/data-$(date +%F).tar.gz"
# То же через rsync (с прогрессом)
tar czf - /var/data | pv | ssh backup-server "cat > /backups/data-$(date +%F).tar.gz"
# Альтернатива — tar + rsync для incremental
tar czf /tmp/data.tar.gz /var/data
rsync -avhP /tmp/data.tar.gz backup-server:/backups/
rm /tmp/data.tar.gz
Streaming через ssh быстрее для one-shot. rsync лучше для incremental (если только часть архива изменилась — но изменение в архиве обычно меняет весь файл, так что incremental не помогает для архивов).
Backup pipeline с rotation
#!/bin/bash
# nightly-backup.sh
set -euo pipefail
SRC="/var/data/lake"
BACKUP_HOST="[email protected]"
BACKUP_DIR="/backups/lake"
DATE=$(date +%Y-%m-%d)
LOCAL_TMP="/tmp/backup-$DATE.tar.zst"
# Создать архив локально
tar --zstd -cf "$LOCAL_TMP" "$SRC"
# Передать на NAS
rsync -avhP "$LOCAL_TMP" "$BACKUP_HOST:$BACKUP_DIR/"
# Удалить local tmp
rm "$LOCAL_TMP"
# Rotate на NAS — хранить только последние 30
ssh "$BACKUP_HOST" "ls -t $BACKUP_DIR/*.tar.zst | tail -n +31 | xargs rm -f"
# Notification
curl -s -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"Backup $DATE complete\"}" \
"$SLACK_WEBHOOK"
Direct streaming (без local tmp)
Если local disk space ограничен:
tar --zstd -cf - "$SRC" | ssh "$BACKUP_HOST" "cat > $BACKUP_DIR/data-$DATE.tar.zst"
-cf - означает «write to stdout». Полезно когда вы тестируете backup pipeline на маленьком сервере.
Pattern 5: Параллельная обработка ZIP-файлов
Получили 100 partner-data ZIP файлов, нужно распаковать все:
# Последовательно (медленно)
for z in *.zip; do
unzip "$z" -d /tmp/extracted/
done
# Параллельно с xargs
ls *.zip | xargs -P 8 -I {} unzip -q -o {} -d /tmp/extracted/
# -P 8 — 8 параллельных процессов
# -q — quiet (не спамить)
# -o — overwrite без вопросов
# Через GNU parallel (если установлен)
parallel -j 8 unzip -q -o {} -d /tmp/extracted/ ::: *.zip
DE-сценарий: monthly data dumps от партнёра в виде 30 daily ZIP файлов. Распаковать все за минуты, не часы.
Pattern 6: Verify integrity
Архивы могут портиться при передаче. Стандартная практика — checksums:
# Создать архив + checksum
tar czvf backup.tar.gz /data
sha256sum backup.tar.gz > backup.tar.gz.sha256
# На receiving стороне
sha256sum -c backup.tar.gz.sha256
# Должно вывести: backup.tar.gz: OK
# Если что-то изменилось — FAILED
Bash-функция для backup с verification:
backup_with_verify() {
local src="$1"
local dst="$2"
tar czf "$dst" "$src"
local checksum=$(sha256sum "$dst" | awk '{print $1}')
echo "$checksum $dst" > "$dst.sha256"
# Verify
if sha256sum -c "$dst.sha256" > /dev/null; then
echo "OK: $dst (sha256: $checksum)"
else
echo "FAIL: $dst checksum mismatch!" >&2
return 1
fi
}
backup_with_verify /home/levo/data /backup/data-$(date +%F).tar.gz
Pattern 7: Selective restore из tar
Иногда нужно достать один файл из huge tar:
# Список файлов
tar tvf big-archive.tar.gz | head -30
# Извлечь конкретный
tar xvf big-archive.tar.gz path/to/specific-file.csv
# Извлечь по паттерну
tar xvf big-archive.tar.gz --wildcards '*.csv'
# Только в stdout (не сохранять)
tar xOf big-archive.tar.gz path/to/file.csv | head
# -O — extract to stdout
DE use case: бэкап на 100GB, нужно достать конкретный CSV — не нужно распаковывать всё.
Pattern 8: Incremental backup с tar
tar сам по себе не делает incremental — он копирует всё. Для incremental используют --listed-incremental:
# Full backup (level 0)
tar --listed-incremental=/var/backup/snapshot.snar \
-czf /backup/full.tar.gz /data
# Incremental (level 1) — только изменения с last full
tar --listed-incremental=/var/backup/snapshot.snar \
-czf /backup/inc-$(date +%F).tar.gz /data
Файл snapshot.snar — это база, которая запоминает inode и timestamps. На основе её tar определяет, что изменилось.
В реальности — это сложно в обращении (restore требует full + все incrementals по порядку). Большинство DE предпочитают rsync с --link-dest (модуль 12 урок 03) или specialized tools (BorgBackup, restic).
Pattern 9: Encrypted backup
Backup с шифрованием для compliance (GDPR, HIPAA):
# tar + gpg
tar czf - /sensitive/data | gpg -c -o backup.tar.gz.gpg
# -c symmetric encryption (запросит пароль)
# Восстановление
gpg -d backup.tar.gz.gpg | tar xzf -
# С key вместо password (production-ready)
gpg --import recipient-public.key
tar czf - /sensitive/data | gpg -e -r recipient@email > backup.tar.gz.gpg
# Decrypt
gpg -d backup.tar.gz.gpg | tar xzf -
GPG (GnuPG) — стандарт криптографии для UNIX. Альтернатива — age (современнее, проще API).
# age — современная альтернатива
sudo apt install age
# или
brew install age
# Сгенерировать пару ключей
age-keygen -o key.txt
# Encrypt
tar czf - /data | age -r $(grep public key.txt | awk '{print $4}') > backup.tar.gz.age
# Decrypt
age -d -i key.txt backup.tar.gz.age | tar xzf -
Целостный DE-backup пример
Финальный production-готовый скрипт:
#!/bin/bash
# production-backup.sh
set -euo pipefail
SRC="/var/data"
BACKUP_HOST="[email protected]"
BACKUP_DIR="/backups/data"
DATE=$(date +%Y-%m-%d)
SLACK_WEBHOOK="${SLACK_WEBHOOK_URL:?Set SLACK_WEBHOOK_URL}"
RETENTION_DAYS=30
# Trap для cleanup и notify on failure
trap 'curl -s -X POST -H "Content-type: application/json" --data "{\"text\":\"BACKUP FAILED $DATE on $(hostname)\"}" "$SLACK_WEBHOOK"' ERR
echo "[$(date -Iseconds)] Starting backup for $DATE"
# 1. Создать и compression на лету, прямо на NAS
tar --zstd -cf - "$SRC" | \
ssh "$BACKUP_HOST" "cat > $BACKUP_DIR/data-$DATE.tar.zst"
# 2. Verify checksum
EXPECTED=$(tar --zstd -cf - "$SRC" | sha256sum | awk '{print $1}')
ACTUAL=$(ssh "$BACKUP_HOST" "sha256sum $BACKUP_DIR/data-$DATE.tar.zst" | awk '{print $1}')
if [ "$EXPECTED" != "$ACTUAL" ]; then
echo "Checksum mismatch! Expected $EXPECTED, got $ACTUAL" >&2
exit 1
fi
# 3. Rotation
ssh "$BACKUP_HOST" "find $BACKUP_DIR -name 'data-*.tar.zst' -mtime +$RETENTION_DAYS -delete"
# 4. Notify success
SIZE=$(ssh "$BACKUP_HOST" "du -h $BACKUP_DIR/data-$DATE.tar.zst | awk '{print \$1}'")
curl -s -X POST -H "Content-type: application/json" \
--data "{\"text\":\"Backup $DATE: $SIZE on $BACKUP_HOST\"}" \
"$SLACK_WEBHOOK"
echo "[$(date -Iseconds)] Backup complete: $SIZE"
Все patterns в одном: streaming, verification, rotation, error handling, notifications.
Попробуй сам
# 1. Streaming compression
echo "Generating test data..."
seq 1 1000000 > /tmp/test-data.txt
ls -lh /tmp/test-data.txt
# Streaming через pipe (без temp)
cat /tmp/test-data.txt | gzip > /tmp/test-data.txt.gz
ls -lh /tmp/test-data.txt.gz
# 2. Split
split -b 1M /tmp/test-data.txt /tmp/chunk_
ls /tmp/chunk_*
# Reassemble
cat /tmp/chunk_* > /tmp/reassembled.txt
md5sum /tmp/test-data.txt /tmp/reassembled.txt
# Хэши совпадают
# 3. Selective extract
mkdir /tmp/dir-for-tar
echo "a" > /tmp/dir-for-tar/file1.txt
echo "b" > /tmp/dir-for-tar/file2.txt
echo "c" > /tmp/dir-for-tar/file3.txt
tar czvf /tmp/multi.tar.gz /tmp/dir-for-tar
tar tvf /tmp/multi.tar.gz
# Only file2
tar xOvf /tmp/multi.tar.gz tmp/dir-for-tar/file2.txt
# 4. Checksum verification
tar czf /tmp/data.tar.gz /tmp/dir-for-tar
sha256sum /tmp/data.tar.gz > /tmp/data.tar.gz.sha256
# Verify
sha256sum -c /tmp/data.tar.gz.sha256
# Modify
echo "tampered" >> /tmp/data.tar.gz
sha256sum -c /tmp/data.tar.gz.sha256 || echo "Tampering detected!"
# 5. Cleanup
rm -rf /tmp/test-data.txt /tmp/test-data.txt.gz /tmp/chunk_* /tmp/multi.tar.gz /tmp/dir-for-tar /tmp/reassembled.txt /tmp/data.tar.gz /tmp/data.tar.gz.sha256
Cross-link: предыдущий урок 03 — zip. Модуль 11 — rsync, который часто комбинируется с tar для backup. Модуль 15 — cron для регулярных backup-задач. Модуль 17 — production-bash с error handling.