Cloud/GCP

[GCP] 스냅샷으로 신규 VM 만들기: 콘솔(수동) & CLI/쉘(자동화) 가이드

달빛궁전- 2025. 6. 1. 10:21

 

GCP Compute Engine(GCE)는 VM의 현 상태를 저장 후 복원할 수 있는 스냅샷(snapshot)기능을 제공합니다.
스냅샷은 영구 디스크의 백업입니다.
스냅샷의 주요 용도는 
운영 중인 VM을 특정 스냅샷 시점으로 복구할 수 있습니다.
스냅샷을 사용하여 신규 디스크를 만들고, 이를 바탕으로 새로운 VM(GCE) 인스턴스를 생성할 수 있습니다.
데이터 복구, 데이터 전송, 또는 프로젝트의 다른 리소스에서 데이터에 접근하는 데 사용됩니다.

GCP에서는 GUI(콘솔)를 통해 클릭 몇 번으로 이 작업을 수행할 수 있습니다.
CLI(gcloud+shell script)로도 동일한 설정이 가능합니다.
특히 대량의 작업을 수행해야 할 경우, CLI 방식이 훨씬 유리합니다. 이 글에서는 두 가지 방식을 모두 상세히 다루겠습니다.
  1. GUI 진행

    스냅샷부터 생성해보겠습니다.

    Google Cloud Console에 접속하여 'Compute Engine' -> '스냅샷' 메뉴로 이동한 후, '스냅샷 만들기'를 클릭합니다.
    이름설정, 소스는 “디스크" , 대상이 될 VM의 disk를 선택합니다.
    해당 스냅샷으로 신규 디스크→ VM을 생성하기에 “표준스냅샷”으로 선택합니다.


    스냅샷이 생성된 것을 확인합니다.

    해당 스냅샷으로 VM을 생성해 보겠습니다.
    GCP에서는 스냅샷을 이용해 VM을 생성하는 두 가지 주요 방안이 있습니다. 
    방법 1: 스냅샷에서 디스크를 먼저 만들고, VM 생성 시 해당 디스크를 연결.


    방법 2: VM 생성 과정에서 부팅 디스크로 스냅샷을 직접 선택 (권장). 이 방법은 GCP가 자동으로 디스크 생성 및 연결을 처리해줍니다.
    'Compute Engine' -> 'VM 인스턴스' -> '인스턴스 만들기'로 이동합니다.
    '운영체제 및 스토리지' 에서  부팅 디스크 '변경'을 클릭합니다.
    상단 탭에서 '스냅샷'을 선택합니다.
    필요한 경우 디스크 유형, 크기 등을 조정한 후 나머지 VM 설정을 완료하고 인스턴스를 생성합니다.

  2. CLI (gcloud+shell script) 방안

    한두 대의 VM을 생성하는 경우 GUI가 직관적이지만, 다수의 VM을 빠르게 생성해야 하거나 반복적인 작업을 자동화해야 할 때 CLI 방식이 매우 효과적입니다.

    이 문서에서 제공하는 쉘 스크립트는 대화형으로 진행되며, GCP 프로젝트 ID, 사용할 스냅샷, Zone 등의 정보를 입력받아 새 디스크를 생성하고, 해당 디스크를 사용하여 새 VM 인스턴스를 만듭니다.

    네트워크, 태그, 지역 등 VM 생성 시 필요한 여러 설정값에 대해 기본값이 제안되며, Enter 키를 누르면 기본값이 사용되도록 하여 편의성을 높였습니다.
    스크립트 실행 후, 생성된 VM에 바로 접속할 수 있는 gcloud compute ssh 명령어도 출력해줍니다.

    스크립트 주요 입력 항목 
    1. GCP 프로젝트 ID: 기본 프로젝트 ID가 있다면 자동으로 제안됩니다.

    2. 사용할 스냅샷 선택: 프로젝트 내 사용 가능한 스냅샷 목록이 최신 생성일 순으로 표시되며, 번호로 선택합니다 
    3. 리소스 Zone: 디스크와 인스턴스를 생성할 Zone을 입력합니다 (예: asia-northeast3-a).

    4. 새 디스크 정보:
    디스크 이름 (예: test-disk).
    디스크 유형 (예: pd-standard, pd-ssd, pd-balanced).
    디스크 크기 (비워두면 스냅샷 크기를 따름).

    5. 새 인스턴스 정보:
    인스턴스 이름 (예: restore-vm).
    인스턴스 머신 유형 (예: e2-medium, e2-micro).

    6. 네트워크 정보:
    네트워크 이름 (예: default 또는 사용자 정의 네트워크 ix-dr-test-seoul).
    서브넷 이름 (선택 사항, 예: seoul-prd).

    7. 서비스 계정 및 액세스 범위 (선택 사항):
    서비스 계정 이메일
    액세스 범위 (기본값: https://www.googleapis.com/auth/cloud-platform )

    8. 태그 및 레이블 (선택 사항):
    인스턴스 태그 (쉼표로 구분, 예: http-server,allow-ssh).
    인스턴스 레이블 (KEY=VALUE 형식, 쉼표로 구분, 예: env=dev,owner=user).

    9. 부팅 디스크 자동 삭제: 인스턴스 삭제 시 부팅 디스크 자동 삭제 여부 (yes/no).

    최종 확인 및 실행: 모든 입력 값을 지정한 후, 최종 설정값을 확인하고 진행 여부를 결정합니다.

    스크립트 내부 주요 gcloud 명령어:
    스냅샷 목록 조회: gcloud compute snapshots list 
    디스크 생성: gcloud compute disks create "[NEW_DISK_NAME]" --source-snapshot="[SOURCE_SNAPSHOT_NAME]"
    인스턴스 생성: gcloud compute instances create "[INSTANCE_NAME]" --disk="name=[NEW_DISK_NAME],boot=yes,auto-delete=[AUTO_DELETE_DISK]" 
     



    - 모든 입력 값을 지정 후 최종 진행여부 확인


    - 최종 진행 후 완료화면


    - GUI에서 생성된 VM, Disk 확인



  • shell script 내용
    #!/bin/bash
    
    "=============================================================="
    echo "이 스크립트는 GCP 프로젝트, 스냅샷, Zone 등의 정보를 입력받아"
    echo "새 디스크를 생성하고, 해당 디스크를 사용하여 새 VM 인스턴스를 만듭니다."
    echo "기본값이 제안되는 경우, Enter 키를 누르면 기본값이 사용됩니다."
    echo "
    echo 2025.05 Choi SeongGi Create
    --------------------------------------------------------------"
    set -e # 오류 발생 시 스크립트 즉시 중단
    
    echo "========================================================================"
    echo " 스냅샷 선택 후 디스크 생성 및 새 인스턴스 생성 (대화형)"
    echo "========================================================================"
    # (이하 스크립트 설명 동일)
    echo "------------------------------------------------------------------------"
    echo ""
    
    # --- 1. 프로젝트 ID 입력 ---
    DEFAULT_PROJECT_ID=$(gcloud config get-value project 2>/dev/null)
    if [[ -n "$DEFAULT_PROJECT_ID" ]]; then
      read -p "1. GCP 프로젝트 ID를 입력하세요 [${DEFAULT_PROJECT_ID}]: " PROJECT_ID
      PROJECT_ID="${PROJECT_ID:-$DEFAULT_PROJECT_ID}"
    else
      read -p "1. GCP 프로젝트 ID를 입력하세요: " PROJECT_ID
      if [[ -z "$PROJECT_ID" ]]; then
        echo "오류: 프로젝트 ID는 필수 입력 항목입니다."
        exit 1
      fi
    fi
    PROJECT_FLAG="--project=${PROJECT_ID}"
    echo "  > 프로젝트 ID: ${PROJECT_ID}"
    echo ""
    
    # --- 2. 사용 가능한 스냅샷 목록 표시 및 선택 ---
    echo "2. 프로젝트 '${PROJECT_ID}'에서 사용 가능한 스냅샷 목록을 가져오는 중..."
    
    SNAPSHOT_TEMP_FILE=$(mktemp)
    if ! gcloud compute snapshots list ${PROJECT_FLAG} \
        --format="value(name,creationTimestamp.date('%Y-%m-%d %H:%M:%S %Z'),sourceDisk.basename(),diskSizeGb)" \
        --sort-by "~creationTimestamp" > "${SNAPSHOT_TEMP_FILE}"; then
        echo "  오류: 스냅샷 목록을 가져오는 데 실패했습니다."
        rm -f "${SNAPSHOT_TEMP_FILE}"
        exit 1
    fi
    
    declare -a SNAPSHOT_NAMES
    declare -a SNAPSHOT_DISPLAY_LINES
    
    line_num=0
    while IFS=$'\t' read -r name timestamp sourcedisk size || [[ -n "$name" ]]; do
        if [[ -z "$name" ]]; then
            continue
        fi
        line_num=$((line_num + 1))
        SNAPSHOT_NAMES+=("$name")
        SNAPSHOT_DISPLAY_LINES+=("$(printf "%-5s %-40s %-25s %-20s %-10s" "${line_num}." "$name" "$timestamp" "$sourcedisk" "$size")")
    done < "${SNAPSHOT_TEMP_FILE}"
    rm -f "${SNAPSHOT_TEMP_FILE}"
    
    if [ ${#SNAPSHOT_NAMES[@]} -eq 0 ]; then
      echo "  오류: 프로젝트 '${PROJECT_ID}'에서 사용 가능한 스냅샷을 찾을 수 없습니다."
      exit 1
    fi
    
    echo ""
    echo "다음은 사용 가능한 스냅샷 목록입니다 (최신 생성일 순):"
    echo "-----------------------------------------------------------------------------------------------------"
    printf "%-5s %-40s %-25s %-20s %-10s\n" "번호" "스냅샷 이름" "생성일시" "소스 디스크" "크기(GB)"
    echo "-----------------------------------------------------------------------------------------------------"
    for display_line in "${SNAPSHOT_DISPLAY_LINES[@]}"; do
      echo "$display_line"
    done
    echo "-----------------------------------------------------------------------------------------------------"
    
    choice="" # 또는 그냥 다음 줄에서 바로 read -p 사용
    while true; do
      read -p "사용할 스냅샷의 번호를 입력하세요: " choice
      if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le "${#SNAPSHOT_NAMES[@]}" ]; then
        SOURCE_SNAPSHOT_NAME="${SNAPSHOT_NAMES[$((choice-1))]}"
        break
      else
        echo "잘못된 선택입니다. 1과 ${#SNAPSHOT_NAMES[@]} 사이의 숫자를 입력해주세요."
      fi
    done
    echo "  > 선택된 스냅샷: ${SOURCE_SNAPSHOT_NAME}"
    echo ""
    
    # --- 3. 리소스 Zone 입력 (디스크 및 인스턴스) ---
    DEFAULT_ZONE="asia-northeast3-a"
    read -p "3. 디스크와 인스턴스를 생성할 Zone을 입력하세요 (예: asia-northeast3-a) [${DEFAULT_ZONE}]: " RESOURCE_ZONE
    RESOURCE_ZONE="${RESOURCE_ZONE:-$DEFAULT_ZONE}"
    if [[ -z "$RESOURCE_ZONE" ]]; then
      echo "오류: Zone은 필수 입력 항목입니다."
      exit 1
    fi
    echo "  > 리소스 Zone: ${RESOURCE_ZONE}"
    echo ""
    
    # --- 4. 새 디스크 정보 입력 ---
    DEFAULT_DISK_NAME="disk-from-${SOURCE_SNAPSHOT_NAME,,}"
    read -p "4a. 새로 생성할 디스크의 이름을 입력하세요 [${DEFAULT_DISK_NAME}]: " NEW_DISK_NAME
    NEW_DISK_NAME="${NEW_DISK_NAME:-$DEFAULT_DISK_NAME}"
    
    DEFAULT_DISK_TYPE="pd-standard"
    read -p "4b. 디스크 유형을 입력하세요 (예: pd-standard, pd-ssd, pd-balanced) [${DEFAULT_DISK_TYPE}]: " DISK_TYPE
    DISK_TYPE="${DISK_TYPE:-$DEFAULT_DISK_TYPE}"
    
    read -p "4c. 디스크 크기를 지정하시겠습니까? (예: 50GB. 스냅샷 크기를 따르려면 비워두세요): " DISK_SIZE
    echo "  > 새 디스크 이름: ${NEW_DISK_NAME}"
    echo "  > 디스크 유형    : ${DISK_TYPE}"
    [[ -n "${DISK_SIZE}" ]] && echo "  > 디스크 크기    : ${DISK_SIZE}"
    echo ""
    
    # --- 5. 새 인스턴스 정보 입력 ---
    DEFAULT_INSTANCE_NAME="instance-from-${SOURCE_SNAPSHOT_NAME,,}"
    read -p "5a. 새로 생성할 인스턴스의 이름을 입력하세요 [${DEFAULT_INSTANCE_NAME}]: " INSTANCE_NAME
    INSTANCE_NAME="${INSTANCE_NAME:-$DEFAULT_INSTANCE_NAME}"
    
    DEFAULT_MACHINE_TYPE="e2-medium"
    read -p "5b. 인스턴스 머신 유형을 입력하세요 (예: e2-medium, n1-standard-1) [${DEFAULT_MACHINE_TYPE}]: " MACHINE_TYPE
    MACHINE_TYPE="${MACHINE_TYPE:-$DEFAULT_MACHINE_TYPE}"
    echo "  > 새 인스턴스 이름: ${INSTANCE_NAME}"
    echo "  > 머신 유형      : ${MACHINE_TYPE}"
    echo ""
    
    # --- 6. 네트워크 정보 입력 ---
    DEFAULT_NETWORK_NAME="default"
    read -p "6a. 사용할 네트워크 이름을 입력하세요 [${DEFAULT_NETWORK_NAME}]: " NETWORK_NAME
    NETWORK_NAME="${NETWORK_NAME:-$DEFAULT_NETWORK_NAME}"
    
    read -p "6b. 특정 서브넷을 사용하려면 서브넷 이름을 입력하세요 (기본값 사용 시 비워둠): " SUBNET_NAME
    echo "  > 네트워크 이름: ${NETWORK_NAME}"
    [[ -n "${SUBNET_NAME}" ]] && echo "  > 서브넷 이름  : ${SUBNET_NAME}"
    echo ""
    
    # --- 7. (선택) 서비스 계정 및 액세스 범위 ---
    read -p "7a. 특정 서비스 계정을 사용하려면 이메일을 입력하세요 (기본값 사용 시 비워둠): " SERVICE_ACCOUNT_EMAIL
    
    DEFAULT_SCOPES="https://www.googleapis.com/auth/cloud-platform"
    read -p "7b. 인스턴스 액세스 범위를 입력하세요 (예: compute-rw,storage-ro 또는 전체) [${DEFAULT_SCOPES}]: " SCOPES
    SCOPES="${SCOPES:-$DEFAULT_SCOPES}"
    [[ -n "${SERVICE_ACCOUNT_EMAIL}" ]] && echo "  > 서비스 계정: ${SERVICE_ACCOUNT_EMAIL}"
    echo "  > 액세스 범위: ${SCOPES}"
    echo ""
    
    # --- 8. (선택) 태그 및 레이블 ---
    read -p "8a. 인스턴스에 적용할 태그를 쉼표로 구분하여 입력하세요 (예: http-server,allow-ssh): " INSTANCE_TAGS
    read -p "8b. 인스턴스에 적용할 레이블을 KEY=VALUE 형식으로 쉼표로 구분하여 입력하세요 (예: env=dev,owner=user): " INSTANCE_LABELS
    [[ -n "${INSTANCE_TAGS}" ]] && echo "  > 태그    : ${INSTANCE_TAGS}"
    [[ -n "${INSTANCE_LABELS}" ]] && echo "  > 레이블  : ${INSTANCE_LABELS}"
    echo ""
    
    # --- 9. (선택) 부팅 후 자동 삭제 여부 ---
    read -p "9. 인스턴스 삭제 시 부팅 디스크를 자동으로 함께 삭제하시겠습니까? (yes/no) [yes]: " AUTO_DELETE_DISK
    AUTO_DELETE_DISK="${AUTO_DELETE_DISK:-yes}"
    echo "  > 부팅 디스크 자동 삭제: ${AUTO_DELETE_DISK}"
    echo ""
    
    # --- 입력값 확인 ---
    # (이하 동일)
    echo "------------------------------------------------------------------------"
    echo "입력된 설정값으로 다음 작업을 진행합니다:"
    echo "  1. 프로젝트 ID       : ${PROJECT_ID}"
    echo "  2. 선택된 스냅샷     : ${SOURCE_SNAPSHOT_NAME}"
    echo "  3. 리소스 Zone       : ${RESOURCE_ZONE}"
    echo "  4. 새 디스크 이름    : ${NEW_DISK_NAME}"
    echo "     디스크 유형       : ${DISK_TYPE}"
    [[ -n "${DISK_SIZE}" ]] && echo "     디스크 크기       : ${DISK_SIZE}"
    echo "  5. 새 인스턴스 이름  : ${INSTANCE_NAME}"
    echo "     머신 유형         : ${MACHINE_TYPE}"
    echo "  6. 네트워크          : ${NETWORK_NAME}"
    [[ -n "${SUBNET_NAME}" ]] && echo "     서브넷            : ${SUBNET_NAME}"
    [[ -n "${SERVICE_ACCOUNT_EMAIL}" ]] && echo "  7. 서비스 계정       : ${SERVICE_ACCOUNT_EMAIL}"
    echo "     액세스 범위       : ${SCOPES}"
    [[ -n "${INSTANCE_TAGS}" ]] && echo "  8. 태그              : ${INSTANCE_TAGS}"
    [[ -n "${INSTANCE_LABELS}" ]] && echo "     레이블            : ${INSTANCE_LABELS}"
    echo "  9. 부팅 디스크 자동 삭제: ${AUTO_DELETE_DISK}"
    echo "------------------------------------------------------------------------"
    read -p "위 설정으로 진행하시겠습니까? (yes/no): " CONFIRM
    if [[ "${CONFIRM,,}" != "yes" ]]; then
      echo "작업이 취소되었습니다."
      exit 0
    fi
    echo ""
    
    # --- 작업 실행 ---
    # (이하 동일)
    echo "작업 1: 스냅샷 '${SOURCE_SNAPSHOT_NAME}'으로부터 새 디스크 '${NEW_DISK_NAME}'을(를) 생성합니다..."
    DISK_CREATE_CMD="gcloud compute disks create \"${NEW_DISK_NAME}\" \
        --source-snapshot=\"${SOURCE_SNAPSHOT_NAME}\" \
        --zone=\"${RESOURCE_ZONE}\" \
        --type=\"${DISK_TYPE}\" \
        ${PROJECT_FLAG}"
    if [[ -n "${DISK_SIZE}" ]]; then
      DISK_CREATE_CMD="${DISK_CREATE_CMD} --size=\"${DISK_SIZE}\""
    fi
    
    if eval "${DISK_CREATE_CMD}"; then
      echo "  성공: 디스크 '${NEW_DISK_NAME}'이(가) 성공적으로 생성되었습니다."
    else
      echo "  오류: 디스크 '${NEW_DISK_NAME}' 생성에 실패했습니다."
      exit 1
    fi
    echo ""
    
    echo "작업 2: 생성된 디스크 '${NEW_DISK_NAME}'을(를) 사용하여 새 인스턴스 '${INSTANCE_NAME}'을(를) 생성합니다..."
    INSTANCE_CREATE_CMD="gcloud compute instances create \"${INSTANCE_NAME}\" \
        --zone=\"${RESOURCE_ZONE}\" \
        --machine-type=\"${MACHINE_TYPE}\" \
        --disk=\"name=${NEW_DISK_NAME},boot=yes,auto-delete=${AUTO_DELETE_DISK}\" \
        --network=\"${NETWORK_NAME}\" \
        ${PROJECT_FLAG}"
    
    if [[ -n "${SUBNET_NAME}" ]]; then
      INSTANCE_CREATE_CMD="${INSTANCE_CREATE_CMD} --subnet=\"${SUBNET_NAME}\""
    fi
    if [[ -n "${SERVICE_ACCOUNT_EMAIL}" ]]; then
      INSTANCE_CREATE_CMD="${INSTANCE_CREATE_CMD} --service-account=\"${SERVICE_ACCOUNT_EMAIL}\""
    fi
    if [[ -n "${SCOPES}" ]]; then
      INSTANCE_CREATE_CMD="${INSTANCE_CREATE_CMD} --scopes=\"${SCOPES}\""
    fi
    if [[ -n "${INSTANCE_TAGS}" ]]; then
      INSTANCE_CREATE_CMD="${INSTANCE_CREATE_CMD} --tags=\"${INSTANCE_TAGS}\""
    fi
    if [[ -n "${INSTANCE_LABELS}" ]]; then
      INSTANCE_CREATE_CMD="${INSTANCE_CREATE_CMD} --labels=\"${INSTANCE_LABELS}\""
    fi
    
    if eval "${INSTANCE_CREATE_CMD}"; then
      echo "  성공: 인스턴스 '${INSTANCE_NAME}'이(가) 성공적으로 생성되었습니다."
    else
      echo "  오류: 인스턴스 '${INSTANCE_NAME}' 생성에 실패했습니다."
      exit 1
    fi
    echo ""
    
    echo "========================================================================"
    echo "완료: 모든 작업이 성공적으로 완료되었습니다."
    echo "========================================================================"
    echo "  생성된 디스크: ${NEW_DISK_NAME} (Zone: ${RESOURCE_ZONE})"
    echo "  생성된 인스턴스: ${INSTANCE_NAME} (Zone: ${RESOURCE_ZONE})"
    echo "  인스턴스 접속: gcloud compute ssh \"${INSTANCE_NAME}\" --zone \"${RESOURCE_ZONE}\" ${PROJECT_FLAG}"
    echo ""