pve-pbs-restore/pbs-restore.sh
시골약사 070a23ff74 초기 커밋: PBS 백업 복구 스크립트
 주요 기능:
- PBS (Proxmox Backup Server)에서 VM/Container 백업 복구
- 대화형 모드로 쉬운 백업 선택 및 복구
- 백업 목록 조회 및 VMID별 검색
- VM 및 Container 타입 자동 감지
- VMID 중복 확인 및 다른 VMID로 복구 (클론)
- 스토리지 선택 가능
- 안전한 확인 절차 포함

🛠️ 포함 파일:
- pbs-restore.sh: PBS 백업 복구 스크립트
- README.md: 상세한 사용 가이드, 시나리오 및 문제 해결

📦 사용 방법:
# 대화형 모드 (권장)
curl -fsSL https://git.0bin.in/thug0bin/pve-pbs-restore/raw/branch/main/pbs-restore.sh | bash

# 백업 목록 보기
./pbs-restore.sh -l -s pbs-backup -v 100

# 비대화형 복구
./pbs-restore.sh -r -V "pbs-backup:backup/vm-100-2025_01_28-10_00_00.vma.zst" -t 100 -T vm

🔧 주요 기능:
- qmrestore 및 pct restore 명령어 활용
- 컬러 출력으로 가독성 향상
- Volume ID 자동 파싱
- 백업 날짜 및 크기 표시
- 복구 전 확인 단계
- Root 권한 및 Proxmox VE 환경 자동 확인

🎯 사용 사례:
- 삭제된 VM/Container 복구
- VM 클론 생성 (테스트 환경)
- 특정 시점으로 롤백
- 재해 복구 (Disaster Recovery)
- 자동화 스크립트에 통합

💡 특징:
- 3가지 모드: 대화형, 목록, 복구
- VMID 가용성 자동 확인
- 다중 PBS 스토리지 지원
- 추가 복구 옵션 지원 (--unique, --force 등)
- 상세한 에러 메시지

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 09:00:07 +00:00

495 lines
13 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
set -euo pipefail
# Proxmox VE - PBS 백업 복구 스크립트
# PBS에서 VM/Container 백업을 쉽게 복구
# 색상 정의
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
NC='\033[0m' # No Color
# 로그 함수
log_info() { echo -e "${BLUE}${NC} $1"; }
log_success() { echo -e "${GREEN}${NC} $1"; }
log_warning() { echo -e "${YELLOW}${NC} $1"; }
log_error() { echo -e "${RED}${NC} $1"; }
log_step() { echo -e "${CYAN}${NC} $1"; }
# 배너 출력
print_banner() {
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " PBS 백업 복구 스크립트"
echo " Proxmox Backup Server Restore Tool"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
}
# Root 권한 확인
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "이 스크립트는 root 권한이 필요합니다."
exit 1
fi
}
# Proxmox VE 환경 확인
check_proxmox() {
if ! command -v qmrestore &> /dev/null && ! command -v pct &> /dev/null; then
log_error "Proxmox VE가 설치되어 있지 않습니다."
exit 1
fi
log_success "Proxmox VE 환경 확인 완료"
}
# PBS 스토리지 목록 가져오기
list_pbs_storages() {
log_step "사용 가능한 PBS 스토리지 목록:"
echo ""
pvesm status --type pbs 2>/dev/null | tail -n +2 | awk '{print " • " $1}'
echo ""
}
# PBS 스토리지에서 백업 목록 가져오기
list_backups() {
local storage="$1"
log_step "PBS 스토리지 '${storage}'의 백업 목록을 가져오는 중..."
echo ""
# 백업 목록 가져오기
local backups=$(pvesm list "$storage" 2>/dev/null | grep -E '\.vma|\.tar' || true)
if [[ -z "$backups" ]]; then
log_warning "백업을 찾을 수 없습니다."
return 1
fi
# 백업 목록 파싱 및 출력
echo "$backups" | while IFS= read -r line; do
local volid=$(echo "$line" | awk '{print $1}')
local type=$(echo "$line" | awk '{print $2}')
local size=$(echo "$line" | awk '{print $3}')
# VMID 추출
local vmid=$(echo "$volid" | grep -oP 'vm-\K[0-9]+' || echo "N/A")
if [[ "$vmid" == "N/A" ]]; then
vmid=$(echo "$volid" | grep -oP 'ct-\K[0-9]+' || echo "N/A")
fi
# 날짜 추출 시도
local date=$(echo "$volid" | grep -oP '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z' || echo "")
echo -e "${CYAN}VMID:${NC} $vmid ${MAGENTA}Type:${NC} $type ${YELLOW}Size:${NC} $size"
echo " Volume ID: $volid"
if [[ -n "$date" ]]; then
echo " Date: $date"
fi
echo ""
done
}
# 특정 VMID의 백업 목록 가져오기
list_backups_by_vmid() {
local storage="$1"
local vmid="$2"
log_step "VMID ${vmid}의 백업 목록을 검색 중..."
echo ""
local backups=$(pvesm list "$storage" 2>/dev/null | grep -E "vm-${vmid}-|ct-${vmid}-" || true)
if [[ -z "$backups" ]]; then
log_warning "VMID ${vmid}의 백업을 찾을 수 없습니다."
return 1
fi
local count=0
declare -g -A BACKUP_LIST
echo "$backups" | while IFS= read -r line; do
((count++))
local volid=$(echo "$line" | awk '{print $1}')
local type=$(echo "$line" | awk '{print $2}')
local size=$(echo "$line" | awk '{print $3}')
local date=$(echo "$volid" | grep -oP '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z' || echo "Unknown")
BACKUP_LIST[$count]="$volid"
echo -e "${GREEN}[$count]${NC} ${CYAN}Date:${NC} $date ${MAGENTA}Type:${NC} $type ${YELLOW}Size:${NC} $size"
echo " Volume ID: $volid"
echo ""
done
return 0
}
# 백업 검색 (대화형)
search_backups_interactive() {
list_pbs_storages
read -p "PBS 스토리지 이름을 입력하세요: " PBS_STORAGE
if ! pvesm status --storage "$PBS_STORAGE" &> /dev/null; then
log_error "스토리지 '${PBS_STORAGE}'를 찾을 수 없습니다."
exit 1
fi
echo ""
read -p "복구할 VMID를 입력하세요 (모두 보려면 Enter): " SEARCH_VMID
echo ""
if [[ -n "$SEARCH_VMID" ]]; then
list_backups_by_vmid "$PBS_STORAGE" "$SEARCH_VMID"
else
list_backups "$PBS_STORAGE"
fi
}
# 백업 복구 (VM)
restore_vm() {
local volid="$1"
local target_vmid="$2"
local storage="$3"
local options="$4"
log_step "VM 백업 복구 시작..."
log_info "Volume ID: $volid"
log_info "Target VMID: $target_vmid"
log_info "Storage: $storage"
# 기본 복구 명령어
local cmd="qmrestore '$volid' $target_vmid"
# 스토리지 지정 (선택사항)
if [[ -n "$storage" ]]; then
cmd+=" --storage $storage"
fi
# 추가 옵션
if [[ -n "$options" ]]; then
cmd+=" $options"
fi
log_info "실행 명령어: $cmd"
echo ""
# 확인
read -p "복구를 진행하시겠습니까? (y/N): " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
log_warning "복구가 취소되었습니다."
exit 0
fi
# 복구 실행
echo ""
log_step "복구 진행 중..."
if eval "$cmd"; then
log_success "VM 복구가 완료되었습니다!"
log_info "VMID: $target_vmid"
return 0
else
log_error "VM 복구에 실패했습니다."
return 1
fi
}
# 백업 복구 (Container)
restore_container() {
local volid="$1"
local target_vmid="$2"
local storage="$3"
local options="$4"
log_step "Container 백업 복구 시작..."
log_info "Volume ID: $volid"
log_info "Target VMID: $target_vmid"
log_info "Storage: $storage"
# 기본 복구 명령어
local cmd="pct restore $target_vmid '$volid'"
# 스토리지 지정 (선택사항)
if [[ -n "$storage" ]]; then
cmd+=" --storage $storage"
fi
# 추가 옵션
if [[ -n "$options" ]]; then
cmd+=" $options"
fi
log_info "실행 명령어: $cmd"
echo ""
# 확인
read -p "복구를 진행하시겠습니까? (y/N): " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
log_warning "복구가 취소되었습니다."
exit 0
fi
# 복구 실행
echo ""
log_step "복구 진행 중..."
if eval "$cmd"; then
log_success "Container 복구가 완료되었습니다!"
log_info "VMID: $target_vmid"
return 0
else
log_error "Container 복구에 실패했습니다."
return 1
fi
}
# VMID 사용 가능 여부 확인
check_vmid_available() {
local vmid="$1"
if qm status "$vmid" &> /dev/null || pct status "$vmid" &> /dev/null; then
log_error "VMID ${vmid}는 이미 사용 중입니다."
log_warning "다른 VMID를 사용하거나 기존 VM/CT를 삭제하세요."
return 1
fi
return 0
}
# 스토리지 목록 출력
list_storages() {
log_step "사용 가능한 스토리지 목록:"
echo ""
pvesm status | tail -n +2 | awk '{print " • " $1 " (" $2 ")"}'
echo ""
}
# 대화형 모드
interactive_mode() {
echo ""
log_info "PBS 백업 복구 - 대화형 모드"
echo ""
# PBS 스토리지 선택
list_pbs_storages
read -p "PBS 스토리지 이름: " PBS_STORAGE
if ! pvesm status --storage "$PBS_STORAGE" &> /dev/null; then
log_error "스토리지 '${PBS_STORAGE}'를 찾을 수 없습니다."
exit 1
fi
# VMID 입력
echo ""
read -p "복구할 원본 VMID: " SOURCE_VMID
# 백업 목록 표시
echo ""
if ! list_backups_by_vmid "$PBS_STORAGE" "$SOURCE_VMID"; then
exit 1
fi
# 백업 선택
read -p "복구할 백업 번호를 선택하세요: " backup_num
# 백업 목록 다시 생성 (서브셸 문제 해결)
local count=0
declare -A BACKUP_LIST_LOCAL
while IFS= read -r line; do
((count++))
local volid=$(echo "$line" | awk '{print $1}')
BACKUP_LIST_LOCAL[$count]="$volid"
done < <(pvesm list "$PBS_STORAGE" 2>/dev/null | grep -E "vm-${SOURCE_VMID}-|ct-${SOURCE_VMID}-")
SELECTED_VOLID="${BACKUP_LIST_LOCAL[$backup_num]}"
if [[ -z "$SELECTED_VOLID" ]]; then
log_error "잘못된 선택입니다."
exit 1
fi
# 타겟 VMID 입력
echo ""
read -p "복구할 타겟 VMID (기본값: ${SOURCE_VMID}): " TARGET_VMID
TARGET_VMID=${TARGET_VMID:-$SOURCE_VMID}
# VMID 사용 가능 여부 확인
if ! check_vmid_available "$TARGET_VMID"; then
read -p "새로운 VMID를 입력하세요: " TARGET_VMID
if ! check_vmid_available "$TARGET_VMID"; then
exit 1
fi
fi
# 스토리지 선택
echo ""
list_storages
read -p "복구할 스토리지 (Enter로 원본 스토리지 사용): " TARGET_STORAGE
# VM 타입 판단
echo ""
if [[ "$SELECTED_VOLID" =~ "vm-" ]]; then
log_info "VM 백업으로 감지되었습니다."
BACKUP_TYPE="vm"
elif [[ "$SELECTED_VOLID" =~ "ct-" ]]; then
log_info "Container 백업으로 감지되었습니다."
BACKUP_TYPE="ct"
else
read -p "백업 타입 (vm/ct): " BACKUP_TYPE
fi
# 추가 옵션
echo ""
log_info "추가 옵션 (선택사항)"
read -p "추가 옵션 (예: --force): " EXTRA_OPTIONS
# 복구 실행
echo ""
if [[ "$BACKUP_TYPE" == "vm" ]]; then
restore_vm "$SELECTED_VOLID" "$TARGET_VMID" "$TARGET_STORAGE" "$EXTRA_OPTIONS"
else
restore_container "$SELECTED_VOLID" "$TARGET_VMID" "$TARGET_STORAGE" "$EXTRA_OPTIONS"
fi
}
# 사용법
usage() {
cat << EOF
사용법: $0 [옵션]
대화형 모드 (권장):
$0
리스트 모드:
$0 -l -s PBS_STORAGE [-v VMID]
복구 모드:
$0 -r -V VOLID -t TARGET_VMID [-S STORAGE] [-T TYPE]
옵션:
-l 백업 목록 보기
-r 백업 복구
-s STORAGE PBS 스토리지 이름
-v VMID 검색할 VMID
-V VOLID 복구할 Volume ID
-t VMID 타겟 VMID
-S STORAGE 복구할 스토리지
-T TYPE 백업 타입 (vm 또는 ct)
-o OPTIONS 추가 옵션
-h 도움말 표시
예시:
# 대화형 모드
$0
# PBS 스토리지의 모든 백업 보기
$0 -l -s pbs-backup
# 특정 VMID의 백업 보기
$0 -l -s pbs-backup -v 100
# VM 백업 복구
$0 -r -V "pbs-backup:backup/vm-100-2025_01_28-10_00_00.vma.zst" \\
-t 100 -T vm
# Container 복구 (다른 VMID로)
$0 -r -V "pbs-backup:backup/ct-200-2025_01_28-10_00_00.tar.zst" \\
-t 201 -S local-lvm -T ct
EOF
exit 0
}
# 메인 함수
main() {
print_banner
check_root
check_proxmox
# 옵션 없으면 대화형 모드
if [[ $# -eq 0 ]]; then
interactive_mode
exit 0
fi
local MODE=""
local PBS_STORAGE=""
local VMID=""
local VOLID=""
local TARGET_VMID=""
local TARGET_STORAGE=""
local BACKUP_TYPE=""
local EXTRA_OPTIONS=""
# 옵션 파싱
while getopts "lrs:v:V:t:S:T:o:h" opt; do
case $opt in
l) MODE="list" ;;
r) MODE="restore" ;;
s) PBS_STORAGE="$OPTARG" ;;
v) VMID="$OPTARG" ;;
V) VOLID="$OPTARG" ;;
t) TARGET_VMID="$OPTARG" ;;
S) TARGET_STORAGE="$OPTARG" ;;
T) BACKUP_TYPE="$OPTARG" ;;
o) EXTRA_OPTIONS="$OPTARG" ;;
h) usage ;;
*) usage ;;
esac
done
# 모드별 실행
case "$MODE" in
list)
if [[ -z "$PBS_STORAGE" ]]; then
log_error "PBS 스토리지를 지정하세요 (-s 옵션)"
exit 1
fi
if [[ -n "$VMID" ]]; then
list_backups_by_vmid "$PBS_STORAGE" "$VMID"
else
list_backups "$PBS_STORAGE"
fi
;;
restore)
if [[ -z "$VOLID" ]] || [[ -z "$TARGET_VMID" ]]; then
log_error "Volume ID(-V)와 타겟 VMID(-t)를 지정하세요"
exit 1
fi
if ! check_vmid_available "$TARGET_VMID"; then
exit 1
fi
# 타입 자동 감지
if [[ -z "$BACKUP_TYPE" ]]; then
if [[ "$VOLID" =~ "vm-" ]]; then
BACKUP_TYPE="vm"
elif [[ "$VOLID" =~ "ct-" ]]; then
BACKUP_TYPE="ct"
else
log_error "백업 타입을 지정하세요 (-T vm 또는 -T ct)"
exit 1
fi
fi
if [[ "$BACKUP_TYPE" == "vm" ]]; then
restore_vm "$VOLID" "$TARGET_VMID" "$TARGET_STORAGE" "$EXTRA_OPTIONS"
else
restore_container "$VOLID" "$TARGET_VMID" "$TARGET_STORAGE" "$EXTRA_OPTIONS"
fi
;;
*)
usage
;;
esac
}
# 스크립트 실행
main "$@"