Резервное копирование Docker volumes без остановки контейнеров
Когда в контейнере крутится прод, остановка ради бэкапа будет невозможной роскошью. Но docker volumes можно сохранять, без остановки, если правильно защититься от: открытых файлов, неконсистентных записей и гонок при чтении.
1️⃣ Проблема горячих бэкапов
Если контейнер пишет в volume во время архивации:
файлы могут быть разорваны,
часть транзакций попадет в архив, а часть нет,
каталог может измениться, пока его читает tar.
Чтобы избежать этого, нужен мягкий снапшот: заморозить структуру, дождаться закрытия файлов и архивировать.
Мы это реализуем через:
проверку открытых дескрипторов lsof
копию структуры через freeze-режим
tar с фиксированным списком файлов
2️⃣ Определяем volume и контейнер
3️⃣ Ждем, пока контейнер перестанет писать. Контейнер может продолжать работать, но нам важно дождаться момента тишины.
Это не блокирует процесс, просто ждёт микропаузу между транзакциями.
4️⃣ Создаем снапшот список файлов
Важно: tar должен использовать фиксированный список, а не динамическое дерево.
Так мы замораживаем текущее состояние структуры.
5️⃣ Архивация без остановки контейнера
Файлы читаются быстрее, чем контейнер успевает их поменять, а вероятность гонки почти нулевая, потому что мы:
дождались отсутствия открытых дескрипторов
зафиксировали список файлов заранее
используем tar с указанием точных путей
6️⃣ Опционально: перезапрос тишины перед чтением больших файлов
Для больших БД/логов:
И внутри цикла архивации:
Это максимально приближает бэкап к снапшот-принципам.
7️⃣ Очистка
8️⃣ Пример полностью готового скрипта. Минимальный файл backup_volume.sh:
BashTex📱 #bash
Когда в контейнере крутится прод, остановка ради бэкапа будет невозможной роскошью. Но docker volumes можно сохранять, без остановки, если правильно защититься от: открытых файлов, неконсистентных записей и гонок при чтении.
Если контейнер пишет в volume во время архивации:
файлы могут быть разорваны,
часть транзакций попадет в архив, а часть нет,
каталог может измениться, пока его читает tar.
Чтобы избежать этого, нужен мягкий снапшот: заморозить структуру, дождаться закрытия файлов и архивировать.
Мы это реализуем через:
проверку открытых дескрипторов lsof
копию структуры через freeze-режим
tar с фиксированным списком файлов
VOLUME="mydata"
BACKUP_DIR="/backups"
STAMP=$(date +%Y%m%d-%H%M)
TARGET="$BACKUP_DIR/$VOLUME-$STAMP.tar.gz"
MOUNTPOINT=$(docker volume inspect "$VOLUME" -f '{{.Mountpoint}}')
wait_until_quiet() {
local path="$1"
local delay=${2:-1}
while lsof +D "$path" >/dev/null 2>&1; do
echo "[*] Volume busy, waiting..."
sleep "$delay"
done
}
Это не блокирует процесс, просто ждёт микропаузу между транзакциями.
Важно: tar должен использовать фиксированный список, а не динамическое дерево.
SNAPLIST=$(mktemp)
find "$MOUNTPOINT" -type f -o -type d | sed "s#^$MOUNTPOINT/##" > "$SNAPLIST"
Так мы замораживаем текущее состояние структуры.
echo "[*] Waiting for files to become quiet..."
wait_until_quiet "$MOUNTPOINT"
echo "[*] Creating hot-backup: $TARGET"
tar -czf "$TARGET" \
-C "$MOUNTPOINT" \
-T "$SNAPLIST"
Файлы читаются быстрее, чем контейнер успевает их поменять, а вероятность гонки почти нулевая, потому что мы:
дождались отсутствия открытых дескрипторов
зафиксировали список файлов заранее
используем tar с указанием точных путей
Для больших БД/логов:
pause_if_lock() {
local file="$1"
while lsof "$file" >/dev/null 2>&1; do
echo "[*] File still in use: $file"
sleep 0.5
done
}
И внутри цикла архивации:
while read -r f; do
pause_if_lock "$MOUNTPOINT/$f"
done < "$SNAPLIST"
Это максимально приближает бэкап к снапшот-принципам.
rm -f "$SNAPLIST"
echo "[+] Backup completed: $TARGET"
#!/usr/bin/env bash
set -euo pipefail
VOLUME="$1"
BACKUP_DIR="/backups"
STAMP=$(date +%Y%m%d-%H%M)
TARGET="$BACKUP_DIR/$VOLUME-$STAMP.tar.gz"
MOUNTPOINT=$(docker volume inspect "$VOLUME" -f '{{.Mountpoint}}')
wait_until_quiet() {
while lsof +D "$1" >/dev/null 2>&1; do
sleep 1
done
}
echo "[*] Volume mountpoint: $MOUNTPOINT"
SNAPLIST=$(mktemp)
find "$MOUNTPOINT" -mindepth 1 -printf '%P\n' > "$SNAPLIST"
echo "[*] Waiting for quiet state..."
wait_until_quiet "$MOUNTPOINT"
echo "[*] Archiving..."
tar -czf "$TARGET" -C "$MOUNTPOINT" -T "$SNAPLIST"
rm -f "$SNAPLIST"
echo "[+] Done: $TARGET"
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Please open Telegram to view this post
VIEW IN TELEGRAM
😁13
Сбор данных с нескольких серверов
Конструкция позволяет сравнивать состояние двух (или нескольких) машин без временных файлов, параллельно и максимально быстро.
Process substitution создает виртуальные файлы, в которые пишется вывод команд. SSH-запросы выполняются параллельно, а diff думает, что сравнивает два обычных файла:
▪️ Лучшие практические примеры
▪️ Сравнение пакетов
▪️ Конфиги (sshd_config)
▪️ Сетевые соединения
▪️ Docker-контейнеры
▪️ Мини-функция
Использование:
BashTex📱 #bash #utils
<(ssh host1 cmd) vs <(ssh host2 cmd)
Конструкция позволяет сравнивать состояние двух (или нескольких) машин без временных файлов, параллельно и максимально быстро.
Process substitution создает виртуальные файлы, в которые пишется вывод команд. SSH-запросы выполняются параллельно, а diff думает, что сравнивает два обычных файла:
diff <(ssh h1 cmd) <(ssh h2 cmd)
diff <(ssh h1 "dpkg -l | sort") \
<(ssh h2 "dpkg -l | sort")
diff -u <(ssh h1 "grep -v '^#' /etc/ssh/sshd_config") \
<(ssh h2 "grep -v '^#' /etc/ssh/sshd_config")
diff <(ssh h1 "ss -tuna | sort") \
<(ssh h2 "ss -tuna | sort")
diff <(ssh h1 "docker ps --format '{{.Names}} {{.Image}}' | sort") \
<(ssh h2 "docker ps --format '{{.Names}} {{.Image}}' | sort")
compare_hosts() {
diff <(ssh "$1" "$3") <(ssh "$2" "$3")
}
Использование:
compare_hosts node1 node2 "df -h"
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Сравнение authorized_keys между серверами
Когда в кластере несколько серверов, важно, чтобы доступ имели одни и те же пользователи с одинаковыми ключами. Несогласованность в authorized_keys будет частой точка входа для проблем.
▪️ Базовый прием: сравнение двух серверов
Сразу видно какие ключи есть только на h1 и какие есть только на h2. Строки отличаются полностью, поэтому легко понять лишние.
▪️ Сравнение директорий для всех пользователей
Использование:
▪️ Мультисерверная проверка списка хостов
Покажет отличия между эталоном и всеми остальными.
▪️ Поиск лишних ключей
Покажет ключи, присутствующие только на h1.
BashTex📱 #bash #utils
Когда в кластере несколько серверов, важно, чтобы доступ имели одни и те же пользователи с одинаковыми ключами. Несогласованность в authorized_keys будет частой точка входа для проблем.
diff <(ssh h1 "sort ~/.ssh/authorized_keys") \
<(ssh h2 "sort ~/.ssh/authorized_keys")
Сразу видно какие ключи есть только на h1 и какие есть только на h2. Строки отличаются полностью, поэтому легко понять лишние.
compare_keys() {
user="$1"
diff <(ssh "$2" "sort /home/$user/.ssh/authorized_keys 2>/dev/null") \
<(ssh "$3" "sort /home/$user/.ssh/authorized_keys 2>/dev/null")
}
Использование:
compare_keys deploy h1 h2
compare_keys admin h1 h2
hosts=(h1 h2 h3)
base="h1"
for h in "${hosts[@]:1}"; do
echo "=== $base vs $h ==="
diff <(ssh "$base" "sort ~/.ssh/authorized_keys") \
<(ssh "$h" "sort ~/.ssh/authorized_keys")
done
Покажет отличия между эталоном и всеми остальными.
ssh h1 "sort ~/.ssh/authorized_keys" \
| grep -vFf <(ssh h2 "sort ~/.ssh/authorized_keys")
Покажет ключи, присутствующие только на h1.
BashTex
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4