This commit is contained in:
2026-01-06 22:12:07 +05:00
commit 12f57baa26
13 changed files with 3110 additions and 0 deletions

View File

@@ -0,0 +1,383 @@
#!/bin/bash
#
# Скрипт бэкапа Gitea из Docker
# Основан на рекомендациях: https://docs.gitea.com/administration/backup-and-restore
#
# Использует официальную команду `gitea dump` для создания консистентного бэкапа.
# Для PostgreSQL дополнительно делает нативный дамп БД (рекомендовано Gitea).
#
# База данных: PostgreSQL
# Запускать на исходном сервере (192.168.0.38)
#
set -e
# ============================================
# НАСТРОЙКИ - ОТРЕДАКТИРУЙТЕ ПОД ВАШУ КОНФИГУРАЦИЮ
# ============================================
# Имя Docker контейнера Gitea
# Для docker-compose обычно: gitea-server-1, gitea-gitea-1 или gitea_server_1
GITEA_CONTAINER="${GITEA_CONTAINER:-gitea-server-1}"
# Имя контейнера PostgreSQL
# Для docker-compose: gitea-db-1, gitea_db_1
DB_CONTAINER="${DB_CONTAINER:-gitea-db-1}"
# Credentials для PostgreSQL
DB_USER="${DB_USER:-gitea}"
DB_NAME="${DB_NAME:-gitea}"
DB_PASSWORD="${DB_PASSWORD:-}"
# Директория для бэкапа
BACKUP_DIR="${BACKUP_DIR:-/tmp/gitea-backup-$(date +%Y%m%d-%H%M%S)}"
# Формат архива: zip или tar.gz
ARCHIVE_TYPE="${ARCHIVE_TYPE:-zip}"
# ============================================
# ПУТИ GITEA (автоопределяются, но можно переопределить)
# ============================================
# Для rootless образа (docker.gitea.com/gitea:*-rootless):
# - Конфиг: /etc/gitea/app.ini
# - Бинарник: /usr/local/bin/gitea
# - Данные: /var/lib/gitea
# Для обычного образа (gitea/gitea:*):
# - Конфиг: /data/gitea/conf/app.ini
# - Бинарник: /app/gitea/gitea или /usr/local/bin/gitea
# - Данные: /data
GITEA_CONFIG="${GITEA_CONFIG:-}"
GITEA_BIN="${GITEA_BIN:-}"
# Rootless режим (автоопределяется)
ROOTLESS="${ROOTLESS:-auto}"
# ============================================
# ФУНКЦИИ
# ============================================
log() {
echo -e "\033[1;32m[$(date '+%Y-%m-%d %H:%M:%S')]\033[0m $1"
}
warn() {
echo -e "\033[1;33m[WARN]\033[0m $1"
}
error() {
echo -e "\033[1;31m[ERROR]\033[0m $1" >&2
exit 1
}
check_container() {
if ! docker ps --format '{{.Names}}' | grep -q "^${1}$"; then
if docker ps -a --format '{{.Names}}' | grep -q "^${1}$"; then
warn "Контейнер '$1' существует, но не запущен"
return 1
else
error "Контейнер '$1' не найден"
fi
fi
return 0
}
detect_gitea_paths() {
log "Автоопределение путей и режима Gitea..."
# Определяем rootless режим по образу
if [ "$ROOTLESS" = "auto" ]; then
IMAGE=$(docker inspect "$GITEA_CONTAINER" --format '{{.Config.Image}}' 2>/dev/null || echo "")
if echo "$IMAGE" | grep -q "rootless"; then
ROOTLESS="true"
log " Обнаружен rootless образ: $IMAGE"
else
ROOTLESS="false"
log " Обнаружен стандартный образ: $IMAGE"
fi
fi
# Пробуем найти app.ini (порядок зависит от режима)
if [ -z "$GITEA_CONFIG" ]; then
if [ "$ROOTLESS" = "true" ]; then
CONFIG_PATHS="/etc/gitea/app.ini /var/lib/gitea/custom/conf/app.ini /data/gitea/conf/app.ini"
else
CONFIG_PATHS="/data/gitea/conf/app.ini /etc/gitea/app.ini /app/gitea/custom/conf/app.ini"
fi
for config_path in $CONFIG_PATHS; do
if docker exec "$GITEA_CONTAINER" test -f "$config_path" 2>/dev/null; then
GITEA_CONFIG="$config_path"
log " Найден конфиг: $GITEA_CONFIG"
break
fi
done
fi
# Пробуем найти бинарник gitea
if [ -z "$GITEA_BIN" ]; then
if [ "$ROOTLESS" = "true" ]; then
BIN_PATHS="/usr/local/bin/gitea /usr/bin/gitea /app/gitea/gitea"
else
BIN_PATHS="/app/gitea/gitea /usr/local/bin/gitea /usr/bin/gitea"
fi
for bin_path in $BIN_PATHS; do
if docker exec "$GITEA_CONTAINER" test -x "$bin_path" 2>/dev/null; then
GITEA_BIN="$bin_path"
log " Найден бинарник: $GITEA_BIN"
break
fi
done
fi
# Устанавливаем дефолты если не найдено
GITEA_CONFIG="${GITEA_CONFIG:-/etc/gitea/app.ini}"
GITEA_BIN="${GITEA_BIN:-/usr/local/bin/gitea}"
}
# ============================================
# ОСНОВНОЙ СКРИПТ
# ============================================
log "=========================================="
log " Бэкап Gitea (официальный метод)"
log "=========================================="
log ""
log "Контейнер: $GITEA_CONTAINER"
log "БД контейнер: $DB_CONTAINER (PostgreSQL)"
log "Директория бэкапа: $BACKUP_DIR"
log ""
# Создаём директорию бэкапа
mkdir -p "$BACKUP_DIR"
# Проверяем наличие контейнера
if ! check_container "$GITEA_CONTAINER"; then
error "Контейнер Gitea должен быть запущен для определения путей"
fi
# Автоопределение путей
detect_gitea_paths
# Получаем версию Gitea
GITEA_VERSION=$(docker exec "$GITEA_CONTAINER" "$GITEA_BIN" --version 2>/dev/null | head -1 || echo "unknown")
log "Версия Gitea: $GITEA_VERSION"
echo "$GITEA_VERSION" > "$BACKUP_DIR/gitea-version.txt"
# Сохраняем информацию о Docker
log "Сохранение информации о Docker конфигурации..."
docker inspect "$GITEA_CONTAINER" > "$BACKUP_DIR/docker-inspect.json" 2>/dev/null || true
docker inspect "$GITEA_CONTAINER" --format '{{json .Mounts}}' 2>/dev/null | jq '.' > "$BACKUP_DIR/docker-mounts.json" 2>/dev/null || true
# ============================================
# ШАГ 1: Нативный дамп PostgreSQL
# Рекомендация Gitea: использовать нативные инструменты
# ============================================
log "Создание нативного дампа PostgreSQL..."
if [ -n "$DB_CONTAINER" ]; then
if [ -n "$DB_PASSWORD" ]; then
docker exec -e PGPASSWORD="$DB_PASSWORD" "$DB_CONTAINER" \
pg_dump -U "$DB_USER" -d "$DB_NAME" --no-owner --no-acl \
> "$BACKUP_DIR/gitea-db-native.sql" 2>/dev/null
else
docker exec "$DB_CONTAINER" \
pg_dump -U "$DB_USER" -d "$DB_NAME" --no-owner --no-acl \
> "$BACKUP_DIR/gitea-db-native.sql" 2>/dev/null
fi
log " ✓ PostgreSQL дамп создан: gitea-db-native.sql"
else
warn "DB_CONTAINER не указан, пропускаем нативный дамп"
fi
# ============================================
# ШАГ 2: Остановка Gitea (рекомендуется для консистентности)
# ============================================
log "Остановка Gitea для консистентного бэкапа..."
GITEA_WAS_RUNNING=false
if docker ps --format '{{.Names}}' | grep -q "^${GITEA_CONTAINER}$"; then
GITEA_WAS_RUNNING=true
docker stop "$GITEA_CONTAINER" >/dev/null
log " ✓ Gitea остановлена"
sleep 2
fi
# ============================================
# ШАГ 3: Создание бэкапа с помощью `gitea dump`
# Официальный метод согласно документации
# ============================================
log "Создание бэкапа через 'gitea dump'..."
# Временно запускаем контейнер для выполнения dump
docker start "$GITEA_CONTAINER" >/dev/null 2>&1 || true
sleep 3
# Определяем параметры для gitea dump
DUMP_ARGS="-c $GITEA_CONFIG"
DUMP_ARGS="$DUMP_ARGS --type $ARCHIVE_TYPE"
# Для rootless временная директория должна быть доступна пользователю git
if [ "$ROOTLESS" = "true" ]; then
DUMP_ARGS="$DUMP_ARGS --tempdir /var/lib/gitea"
DUMP_SEARCH_PATHS="/var/lib/gitea /tmp /home/git"
else
DUMP_ARGS="$DUMP_ARGS --tempdir /tmp"
DUMP_SEARCH_PATHS="/tmp /app/gitea /data"
fi
# Пропускаем встроенный дамп БД (уже сделали нативный)
if [ -f "$BACKUP_DIR/gitea-db-native.sql" ]; then
DUMP_ARGS="$DUMP_ARGS --skip-db"
log " (пропуск встроенного дампа БД - используем нативный)"
fi
# Выполняем gitea dump
log " Выполнение: gitea dump $DUMP_ARGS"
if [ "$ROOTLESS" = "true" ]; then
docker exec "$GITEA_CONTAINER" "$GITEA_BIN" dump $DUMP_ARGS 2>&1 | tee "$BACKUP_DIR/dump.log" || {
warn "gitea dump завершился с ошибкой, проверьте dump.log"
}
else
docker exec -u git "$GITEA_CONTAINER" "$GITEA_BIN" dump $DUMP_ARGS 2>&1 | tee "$BACKUP_DIR/dump.log" || {
warn "Повторная попытка без флага -u git..."
docker exec "$GITEA_CONTAINER" "$GITEA_BIN" dump $DUMP_ARGS 2>&1 | tee "$BACKUP_DIR/dump.log"
}
fi
# Находим созданный архив
log " Поиск архива..."
DUMP_FILE=""
for search_path in $DUMP_SEARCH_PATHS; do
DUMP_FILE=$(docker exec "$GITEA_CONTAINER" sh -c "ls -t ${search_path}/gitea-dump-*.${ARCHIVE_TYPE} 2>/dev/null | head -1" || true)
if [ -n "$DUMP_FILE" ] && [ "$" != "" ]; then
break
fi
done
# Если не нашли, ищем везде
if [ -z "$DUMP_FILE" ]; then
DUMP_FILE=$(docker exec "$GITEA_CONTAINER" find / -name "gitea-dump-*.${ARCHIVE_TYPE}" -type f 2>/dev/null | head -1 || true)
fi
if [ -n "$DUMP_FILE" ] && [ "$DUMP_FILE" != "" ]; then
log " Найден архив: $DUMP_FILE"
docker cp "$GITEA_CONTAINER:$DUMP_FILE" "$BACKUP_DIR/gitea-dump.${ARCHIVE_TYPE}"
log " ✓ Архив скопирован: gitea-dump.${ARCHIVE_TYPE}"
# Удаляем архив из контейнера
docker exec "$GITEA_CONTAINER" rm -f "$DUMP_FILE" 2>/dev/null || true
else
warn "Архив gitea dump не найден, делаем ручное копирование..."
# Fallback: ручное копирование данных
log " Копирование данных вручную..."
if [ "$ROOTLESS" = "true" ]; then
docker cp "$GITEA_CONTAINER:/var/lib/gitea" "$BACKUP_DIR/data" 2>/dev/null || \
docker cp "$GITEA_CONTAINER:/data" "$BACKUP_DIR/data" 2>/dev/null || \
warn " Не удалось скопировать данные"
else
docker cp "$GITEA_CONTAINER:/data" "$BACKUP_DIR/data" 2>/dev/null || \
docker cp "$GITEA_CONTAINER:/var/lib/gitea" "$BACKUP_DIR/data" 2>/dev/null || \
warn " Не удалось скопировать данные"
fi
fi
# ============================================
# ШАГ 4: Копирование конфигурации отдельно
# ============================================
log "Копирование конфигурации..."
docker cp "$GITEA_CONTAINER:$GITEA_CONFIG" "$BACKUP_DIR/app.ini" 2>/dev/null && \
log " ✓ app.ini скопирован" || \
warn " Не удалось скопировать app.ini (возDUMP_FILEможно включён в архив)"
# ============================================
# ШАГ 5: Возврат в исходное состояние
# ============================================
if [ "$GITEA_WAS_RUNNING" = true ]; then
log "Перезапуск Gitea..."
docker restart "$GITEA_CONTAINER" >/dev/null
log " ✓ Gitea запущена"
else
log "Остановка Gitea (была остановлена до бэкапа)..."
docker stop "$GITEA_CONTAINER" >/dev/null 2>&1 || true
fi
# ============================================
# ШАГ 6: Поиск docker-compose
# ============================================
log "Поиск docker-compose файлов..."
COMPOSE_FILES=$(find /home /opt /root /srv -maxdepth 4 \
\( -name "docker-compose*.yml" -o -name "docker-compose*.yaml" -o -name "compose*.yml" -o -name "compose*.yaml" \) \
2>/dev/null | xargs grep -l -i gitea 2>/dev/null || true)
if [ -n "$COMPOSE_FILES" ]; then
mkdir -p "$BACKUP_DIR/compose"
for f in $COMPOSE_FILES; do
cp "$f" "$BACKUP_DIR/compose/" 2>/dev/null || true
done
log " ✓ Docker Compose файлы скопированы"
fi
# ============================================
# ШАГ 7: Создание итогового архива
# ============================================
log "Создание итогового архива..."
ARCHIVE_NAME="gitea-backup-$(date +%Y%m%d-%H%M%S).tar.gz"
cd /tmp
tar -czf "$ARCHIVE_NAME" "$(basename $BACKUP_DIR)"
log ""
log "=========================================="
log " ✅ Бэкап успешно завершён!"
log "=========================================="
log ""
log "Содержимое бэкапа:"
ls -lh "$BACKUP_DIR"
log ""
log "Итоговый архив:"
ls -lh "/tmp/$ARCHIVE_NAME"
log ""
log "Файлы:"
log " Директория: $BACKUP_DIR"
log " Архив: /tmp/$ARCHIVE_NAME"
log ""
log "Следующий шаг - скопируйте архив на новый сервер:"
log " scp /tmp/$ARCHIVE_NAME root@NEW_SERVER_IP:/tmp/"
log ""
# Создаём файл с инструкцией
cat > "$BACKUP_DIR/RESTORE_INFO.txt" << EOF
Gitea Backup Information
========================
Created: $(date)
Gitea Version: $GITEA_VERSION
Database: PostgreSQL
Contents:
- gitea-dump.${ARCHIVE_TYPE} - Official Gitea dump (repos, data, config)
- gitea-db-native.sql - Native PostgreSQL dump (recommended for restore)
- app.ini - Configuration file
- gitea-version.txt - Version information
- docker-inspect.json - Docker container configuration
- docker-mounts.json - Docker volume mounts
$([ -d "$BACKUP_DIR/compose" ] && echo "- compose/ - Docker Compose files")
Restore Instructions:
1. Extract: tar -xzf $ARCHIVE_NAME
2. Restore PostgreSQL: psql -U gitea -d gitea < gitea-db-native.sql
3. Extract gitea-dump.${ARCHIVE_TYPE} and restore files
4. Update app.ini with new paths/domains
5. Start Gitea
For detailed instructions see: https://docs.gitea.com/administration/backup-and-restore
EOF
log "Информация о восстановлении сохранена в RESTORE_INFO.txt"