#!/bin/bash # # PBS 자동 복구 스크립트 - VM 103 → 203 # 작성일: 2025-11-21 # 용도: PBS 등록 → VM 103 백업 자동 복구 → VM 203으로 설치 # set -e # 색상 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' # PBS 서버 설정 (자동 입력) PBS_SERVER="100.64.0.10" PBS_PORT="8007" PBS_USERNAME="0bin@pbs" PBS_PASSWORD="@Trajet6640" PBS_DATASTORE="PBS-DVA" PBS_FINGERPRINT="24:42:c6:0f:a8:1b:93:32:32:44:84:be:6a:c5:71:97:e4:4d:61:fc:a4:48:12:0c:97:3b:9f:1f:cc:b2:54:e8" PBS_STORAGE_NAME="PBS-Auto" # 고정 복구 설정 SOURCE_VMID="103" # 백업 소스 VM ID TARGET_VMID="203" # 복구할 VM ID TARGET_STORAGE="local-lvm" # 저장 스토리지 BACKUP_TYPE="vm" # 백업 타입 (vm 고정) LATEST_SNAPSHOT="" # 최신 스냅샷 (자동 조회) # 로그 함수 log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } log_step() { echo -e "${CYAN}╰─► ${NC}$1"; } # 배너 print_banner() { clear echo -e "${MAGENTA}" cat << "EOF" ╔═══════════════════════════════════════════════════════════════╗ ║ ║ ║ PBS 자동 복구 스크립트 ║ ║ VM 103 → VM 203 자동 복구 ║ ║ ║ ╚═══════════════════════════════════════════════════════════════╝ EOF echo -e "${NC}" echo "" } # Proxmox 환경 확인 check_proxmox() { if [ ! -f /etc/pve/storage.cfg ]; then log_error "Proxmox VE가 설치되어 있지 않습니다." exit 1 fi log_success "Proxmox VE 환경 확인 완료" } # PBS 스토리지 등록 register_pbs() { log_step "PBS 스토리지 등록 중..." # 이미 등록되어 있는지 확인 if pvesm status | grep -q "^${PBS_STORAGE_NAME} "; then log_warning "PBS 스토리지가 이미 등록되어 있습니다." log_info "기존 PBS 스토리지 제거 중..." pvesm remove "${PBS_STORAGE_NAME}" 2>/dev/null || true sleep 1 fi # PBS 등록 log_info "PBS 서버 등록 중..." pvesm add pbs "${PBS_STORAGE_NAME}" \ --server "${PBS_SERVER}" \ --port "${PBS_PORT}" \ --datastore "${PBS_DATASTORE}" \ --username "${PBS_USERNAME}" \ --password "${PBS_PASSWORD}" \ --fingerprint "${PBS_FINGERPRINT}" \ --namespace "PQ" 2>&1 if [ $? -eq 0 ]; then log_success "PBS 스토리지 등록 완료!" else log_error "PBS 스토리지 등록 실패" return 1 fi # 연결 테스트 log_info "PBS 연결 테스트 중..." if pvesm status -storage "${PBS_STORAGE_NAME}" &>/dev/null; then log_success "PBS 연결 성공!" echo "" pvesm status -storage "${PBS_STORAGE_NAME}" echo "" else log_error "PBS 연결 테스트 실패" return 1 fi } # PBS API 인증 pbs_login() { log_info "PBS API 인증 중..." curl -k -s -X POST "https://${PBS_SERVER}:${PBS_PORT}/api2/json/access/ticket" \ -d "username=${PBS_USERNAME}" \ -d "password=${PBS_PASSWORD}" > /tmp/pbs_auth.json if [ ! -s /tmp/pbs_auth.json ]; then log_error "PBS API 응답 없음. 서버 연결을 확인하세요." return 1 fi PBS_TICKET=$(python3 -c 'import json; data=json.load(open("/tmp/pbs_auth.json")); print(data["data"]["ticket"])' 2>/dev/null) PBS_CSRF=$(python3 -c 'import json; data=json.load(open("/tmp/pbs_auth.json")); print(data["data"]["CSRFPreventionToken"])' 2>/dev/null) if [ -z "$PBS_TICKET" ]; then log_error "PBS API 인증 실패. 응답:" cat /tmp/pbs_auth.json | head -5 return 1 fi log_success "PBS API 인증 완료" } # VM 103 백업 확인 check_backup_exists() { log_step "VM ${SOURCE_VMID} 백업 확인 중..." curl -k -s -X GET "https://${PBS_SERVER}:${PBS_PORT}/api2/json/admin/datastore/${PBS_DATASTORE}/groups?ns=PQ" \ -H "Cookie: PBSAuthCookie=${PBS_TICKET}" \ -H "CSRFPreventionToken: ${PBS_CSRF}" > /tmp/pbs_groups.json # VM 103 백업이 있는지 확인 local has_backup=$(python3 << PYEOF import sys, json try: with open('/tmp/pbs_groups.json', 'r') as f: data = json.load(f) groups = data.get("data", []) for group in groups: if group.get("backup-type") == "vm" and group.get("backup-id") == "${SOURCE_VMID}": backup_count = group.get("backup-count", 0) last_backup = group.get("last-backup", 0) from datetime import datetime if last_backup > 0: last_str = datetime.fromtimestamp(last_backup).strftime("%Y-%m-%d %H:%M") else: last_str = "N/A" print(f"VM {group.get('backup-id')} - {backup_count}개 백업 (최근: {last_str})") sys.exit(0) print("") sys.exit(1) except Exception as e: print("") sys.exit(1) PYEOF ) if [ $? -eq 0 ]; then log_success "백업 발견: ${has_backup}" return 0 else log_error "VM ${SOURCE_VMID} 백업을 찾을 수 없습니다" return 1 fi } # 최신 스냅샷 조회 get_latest_snapshot() { log_info "최신 백업 조회 중..." curl -k -s -X GET "https://${PBS_SERVER}:${PBS_PORT}/api2/json/admin/datastore/${PBS_DATASTORE}/snapshots?backup-type=${BACKUP_TYPE}&backup-id=${SOURCE_VMID}&ns=PQ" \ -H "Cookie: PBSAuthCookie=${PBS_TICKET}" \ -H "CSRFPreventionToken: ${PBS_CSRF}" > /tmp/pbs_snapshots.json LATEST_SNAPSHOT=$(python3 << 'PYEOF' import sys, json try: with open('/tmp/pbs_snapshots.json', 'r') as f: data = json.load(f) snapshots = data.get("data", []) if not snapshots: print("") sys.exit(1) # 가장 최근 백업 선택 latest = max(snapshots, key=lambda x: x.get("backup-time", 0)) backup_time = latest.get("backup-time", 0) from datetime import datetime backup_time_str = datetime.utcfromtimestamp(backup_time).strftime("%Y-%m-%dT%H:%M:%SZ") print(backup_time_str) except Exception as e: print("", file=sys.stderr) sys.exit(1) PYEOF ) if [ -z "$LATEST_SNAPSHOT" ]; then log_error "최신 백업을 찾을 수 없습니다" return 1 fi log_success "최신 백업: ${LATEST_SNAPSHOT}" return 0 } # 스토리지 확인 check_storage() { log_step "저장 스토리지 확인 중..." if pvesm status -storage "${TARGET_STORAGE}" &>/dev/null; then # VM 이미지를 저장할 수 있는지 확인 if pvesm status -storage "${TARGET_STORAGE}" -content images &>/dev/null; then log_success "스토리지 '${TARGET_STORAGE}' 확인 완료" # 스토리지 정보 표시 local storage_info=$(pvesm status -storage "${TARGET_STORAGE}" 2>/dev/null | tail -n +2) local total_mb=$(echo "$storage_info" | awk '{print $4}') local avail_mb=$(echo "$storage_info" | awk '{print $6}') local usage_pct=$(echo "$storage_info" | awk '{print $7}') if [ -n "$total_mb" ]; then local total_gb=$(echo "scale=2; $total_mb / 1024 / 1024" | bc 2>/dev/null || echo "N/A") local avail_gb=$(echo "scale=2; $avail_mb / 1024 / 1024" | bc 2>/dev/null || echo "N/A") log_info "스토리지 정보: ${total_gb}GB 총용량 | ${avail_gb}GB 여유 | ${usage_pct} 사용률" fi return 0 else log_error "스토리지 '${TARGET_STORAGE}'는 VM 이미지를 저장할 수 없습니다" return 1 fi else log_error "스토리지 '${TARGET_STORAGE}'를 찾을 수 없습니다" return 1 fi } # 복구 설정 확인 show_restore_config() { echo "" log_info "복구 설정:" echo " 백업 소스: VM ${SOURCE_VMID}" echo " 백업 시점: ${LATEST_SNAPSHOT}" echo " 복구 ID: VM ${TARGET_VMID}" echo " 저장 위치: ${TARGET_STORAGE}" echo "" } # VM 복구 restore_backup() { log_step "백업 복구 중... (시간이 걸릴 수 있습니다)" echo "" # 백업 경로 구성 BACKUP_PATH="${PBS_STORAGE_NAME}:backup/${BACKUP_TYPE}/${SOURCE_VMID}/${LATEST_SNAPSHOT}" log_info "백업 경로: ${BACKUP_PATH}" # 기존 VM 확인 if qm status ${TARGET_VMID} 2>/dev/null; then log_warning "VM ${TARGET_VMID}가 이미 존재합니다" log_info "기존 VM 삭제 중..." qm stop ${TARGET_VMID} --skiplock 2>/dev/null || true sleep 2 qm destroy ${TARGET_VMID} --purge --skiplock 2>/dev/null || true sleep 2 log_success "기존 VM 삭제 완료" fi # 복구 실행 log_info "VM 복구 시작..." echo "" qmrestore "${BACKUP_PATH}" ${TARGET_VMID} \ --storage ${TARGET_STORAGE} \ --unique 1 2>&1 | tee /tmp/pbs-restore.log if [ ${PIPESTATUS[0]} -eq 0 ]; then echo "" log_success "VM 복구 완료!" return 0 else echo "" log_error "VM 복구 실패. 로그: /tmp/pbs-restore.log" return 1 fi } # VM 자동 시작 (선택) start_vm() { echo "" log_info "VM ${TARGET_VMID}를 바로 시작하시겠습니까?" echo -e "${YELLOW}10초 내 입력하세요 (y/N):${NC} " if read -t 10 start_confirm 2>/dev/null; then : else start_confirm="N" fi echo "" if [[ "$start_confirm" =~ ^[Yy]$ ]]; then log_info "VM 시작 중..." qm start ${TARGET_VMID} if [ $? -eq 0 ]; then log_success "VM 시작 완료!" sleep 3 log_info "상태 확인 중..." qm status ${TARGET_VMID} else log_error "시작 실패" return 1 fi else log_info "VM을 수동으로 시작하세요: qm start ${TARGET_VMID}" fi } # 완료 메시지 print_summary() { echo "" echo -e "${GREEN}" cat << "EOF" ╔═══════════════════════════════════════════════════════════════╗ ║ ║ ║ ✅ 복구 완료! ║ ║ ║ ╚═══════════════════════════════════════════════════════════════╝ EOF echo -e "${NC}" echo "" log_info "복구 정보:" echo " 백업 소스: VM ${SOURCE_VMID}" echo " 백업 시점: ${LATEST_SNAPSHOT}" echo " 복구 ID: VM ${TARGET_VMID}" echo " 스토리지: ${TARGET_STORAGE}" echo "" log_info "Proxmox Web UI:" local pve_ip=$(hostname -I | awk '{print $1}') echo " https://${pve_ip}:8006" echo "" log_info "유용한 명령어:" echo " VM 상태: qm status ${TARGET_VMID}" echo " VM 시작: qm start ${TARGET_VMID}" echo " VM 중지: qm stop ${TARGET_VMID}" echo " VM 정보: qm config ${TARGET_VMID}" echo "" log_info "PBS 스토리지:" echo " 이름: ${PBS_STORAGE_NAME}" echo " 제거: pvesm remove ${PBS_STORAGE_NAME}" echo "" } # 메인 실행 main() { print_banner # Proxmox 환경 확인 check_proxmox echo "" # PBS 스토리지 등록 register_pbs || exit 1 echo "" # PBS API 인증 pbs_login || exit 1 echo "" # VM 103 백업 확인 check_backup_exists || exit 1 echo "" # 최신 스냅샷 조회 get_latest_snapshot || exit 1 echo "" # 스토리지 확인 check_storage || exit 1 echo "" # 복구 설정 표시 show_restore_config # 자동 복구 시작 (3초 대기) log_warning "3초 후 자동으로 복구를 시작합니다..." sleep 3 echo "" # 백업 복구 restore_backup || exit 1 echo "" # VM 시작 (선택) start_vm echo "" # 완료 메시지 print_summary } # 도움말 if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "PBS 자동 복구 스크립트 - VM 103 → VM 203" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "사용법: $0" echo "" echo "이 스크립트는:" echo " 1. PBS 스토리지를 자동 등록" echo " 2. VM 103 백업 자동 조회" echo " 3. VM 203으로 자동 복구" echo "" echo "고정 설정:" echo " 소스 VM ID: 103" echo " 복구 VM ID: 203" echo " 스토리지: local-lvm" echo " PBS 서버: 100.64.0.10" echo "" echo "실행 예시:" echo " bash $0" echo "" echo "사전 준비:" echo " - Proxmox VE 설치 완료" echo " - PBS 서버 (100.64.0.10) 접근 가능" echo " - python3 설치 (보통 기본 설치됨)" echo "" echo "모든 설정이 자동화되어 있어 사용자 입력이 최소화됩니다!" echo "" exit 0 fi # Root 권한 확인 if [ "$EUID" -ne 0 ]; then log_error "이 스크립트는 root 권한이 필요합니다." log_info "다음 명령으로 실행하세요: sudo $0" exit 1 fi # 스크립트 실행 main