インスタント スナップショットを使用して SQL Server をバックアップする

SQL Server データベース ファイルが複数のディスクに分散している場合は、アプリケーションとデータの整合性を維持しながら、すべてのディスクを同時にバックアップできます。Compute Engine のインスタント スナップショットの整合性グループを使用すると、ディスク グループ全体でデータをバックアップできるため、この正確なポイントインタイム バックアップ を実現できます。

このチュートリアルでは、Compute Engine スナップショットと、SQL Server 2022 以降で使用できる Transact-SQL(T-SQL)スナップショット機能を使用して SQL Server データベースをバックアップする方法について説明します。 このソリューションは、Windows と Linux の両方のデプロイをサポートし、本番環境のワークロードへのパフォーマンスの影響を最小限に抑えます。

仕組み

ワークフローは、コンピューティング インスタンスで実行されるスクリプトによって管理される次の主なステップで構成されます。

  1. データベースをフリーズする: スクリプトは、ターゲット データベースのすべての書き込みオペレーションを 一時停止する T-SQL コマンドを SQL Server に送信します。これにより、データベース ファイルがバックアップ用に一貫した状態になります。
  2. インスタント スナップショットを作成する: データベースがフリーズしている間に、データベースのデータファイルとログファイルが保存されている ディスクのインスタント スナップショットのグループを作成します。これは、ハードウェアまたはサービスレベルのスナップショット メカニズムを使用するのと同じです。 Google Cloud
  3. 記録して解凍する: スクリプトは、 バックアップ メタデータを記録する別の T-SQL コマンドを SQL Server に送信します。このコマンドは、インスタント スナップショットの整合性 グループを指す小さなバックアップ ファイルを作成し、msdbバックアップ履歴に記録します。完了すると、 SQL Server はデータベースを自動的に解凍し、通常のオペレーションを再開します。

通常、このプロセス全体は 1 秒以内に完了するため、データベースの書き込みフリーズの期間を最小限に抑えることができます。フリーズ中は、データの読み取りはできますが、書き込みはできません。データベースの SUSPEND_FOR_SNAPSHOT_BACKUP=OFF を設定すると、フリーズ状態を手動でキャンセルできます。

目標

このチュートリアルでは、次のタスクを完了する方法について説明します。

  • 2 つのデータディスクを使用して SQL Server インスタンスを作成します。
  • データファイルとログファイルが別々のディスクにある新しいデータベースを作成します。
  • SQL Server を実行している VM のすべてのディスクの整合性グループを作成します。
  • ディスク グループのインスタント スナップショットを作成します。
  • インスタント スナップショットから新しいディスクを作成します。

費用

このドキュメントでは、課金対象である次のコンポーネントを使用します。 Google Cloud

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。

新規の Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。

始める前に

  1. このチュートリアルでは Google Cloud プロジェクトが必要です。新しいプロジェクトを作成するか、既存のプロジェクトを選択します。

    1. コンソールのプロジェクト セレクタページで、プロジェクトを選択または作成します。 Google Cloud Google Cloud

      プロジェクトを選択または作成するために必要なロール

      • プロジェクトを選択する: プロジェクトの選択には特定の IAM ロールは必要ありません。ロールが付与されているプロジェクトを選択できます。
      • プロジェクトを作成する: プロジェクトを作成するには、プロジェクト作成者ロール (roles/resourcemanager.projectCreator)が必要です。これには resourcemanager.projects.create 権限が含まれています。詳しくは、ロールを付与する方法をご覧ください。

      プロジェクト セレクタに移動

    2. プロジェクトで課金が有効になっていることを確認します Google Cloud 。

    3. コンソールで Cloud Shell をアクティブにします。 Google Cloud

      Cloud Shell をアクティブにする

  2. Microsoft SQL Server 2022 以降がインストールされ、実行されていることを確認します。

必要な権限

標準の読み取りアクセス権に加えて、SQL Server 管理者がターゲット データベースに対する ALTER DATABASE 権限を付与していることを確認してください。

インスタンスを作成してスナップショットを作成するために必要な権限を取得するには、プロジェクトに関する次の IAM ロールを付与するよう管理者に依頼してください。

  • インスタンスの管理: compute.instanceAdmin.v1
  • スナップショットの作成: compute.storageAdmin

ロールの付与については、プロジェクト、フォルダ、組織へのアクセス権の管理をご覧ください。

必要な権限は、カスタム ロールや他の事前定義 ロールから取得することもできます。

Compute Engine SQL インスタンスを作成する

SQL Server インスタンスを作成します。

  1. Google Cloud コンソールで、[Cloud Shell をアクティブにする] Cloud Shell をアクティブにします。 ボタンをクリックして Cloud Shell を開きます。

    コンソールに移動 Google Cloud

  2. SQL Server インスタンスを作成します。次のコマンドを貼り付けます。

  REGION=REGION
  ZONE=$REGION-a
  VM_NAME=VM_NAME
  gcloud compute instances create $VM_NAME \
    --boot-disk-auto-delete \
    --boot-disk-size 100 \
    --boot-disk-type hyperdisk-balanced \
    --image-family sql-std-2022-win-2025 \
    --image-project windows-sql-cloud \
    --machine-type c4-highmem-4 \
    --zone $ZONE \
    --network-interface subnet=default \
    --tags sql-server-instant-snapshot \
    --scopes=cloud-platform,service-control,service-management,monitoring-write,logging-write,storage-rw \
    --create-disk=device-name=$VM_NAME-data-disk1,mode=rw,name=$VM_NAME-data-disk1,size=100,type=hyperdisk-balanced \
    --create-disk=device-name=$VM_NAME-data-disk2,mode=rw,name=$VM_NAME-data-disk2,size=100,type=hyperdisk-balanced

次のように置き換えます。

  • リージョン: 新しいインスタンスがデプロイされるリージョン。
  • VM_NAME: 新しい SQL Server インスタンスの名前。

ディスクの整合性グループを作成する

  1. 整合性グループを作成します。

    gcloud compute resource-policies create disk-consistency-group $VM_NAME-snap-grp \
        --region=$REGION
    
  2. VM のディスクを整合性グループに追加します。

    gcloud compute disks add-resource-policies $VM_NAME \
        --zone=$ZONE \
        --resource-policies=$VM_NAME-snap-grp
    
    gcloud compute disks add-resource-policies $VM_NAME-data-disk1 \
        --zone=$ZONE \
        --resource-policies=$VM_NAME-snap-grp
    
    gcloud compute disks add-resource-policies $VM_NAME-data-disk2 \
        --zone=$ZONE \
        --resource-policies=$VM_NAME-snap-grp
    

新しいデータベースを作成する

  1. VM インスタンスに ユーザー名とパスワードを作成します。
  2. リモート デスクトップを使用して VM に接続し 前のステップで作成したユーザー名とパスワードを使用してログインします。
  3. [スタート] ボタンを右クリックするか Win+X を押して、[ターミナル(管理者)] をクリックします。
  4. 特権昇格を確認するプロンプトが表示されたら [はい] をクリックします。
  5. 開いたターミナル ウィンドウで次の PowerShell スクリプトを実行します。このスクリプトは、データディスクを初期化し、64 KB のブロックサイズでディスクをフォーマットして、ドライブ文字を割り当てます。

    $availableDisks = Get-PhysicalDisk -CanPool $True
    $diskLetters = [char[]] (68..72) | Where-Object { !(Get-PSDrive $_ -ErrorAction SilentlyContinue) }
    $diskCount = 0
    foreach ($disk in $availableDisks) {
        $diskLetter = $diskLetters[$diskCount]
        $diskLabel = "$diskLetter-DataDisk"
        Initialize-Disk -Number $disk.DeviceId -PartitionStyle MBR -PassThru
    
        New-Partition -DiskNumber $disk.DeviceId -UseMaximumSize `
        -DriveLetter $diskLetter | Format-Volume -FileSystem NTFS `
        -NewFileSystemLabel $diskLabel -AllocationUnitSize 65536 -Confirm:$false
    
        New-Item -ItemType Directory -Path "$($diskLetter):\MSSQL"
        $diskCount = $diskCount +1
    }
    
  6. SQL Server Management Studio(SSMS)を開きます。管理者として実行します。

  7. [サーバーに接続] ダイアログで、サーバー名が localhost に設定されていることを確認し、[接続] を選択します。

  8. ファイル メニューで、現在の接続で [File] > [New] > [Query] を選択します。

  9. 次のコードを新しく開いたクエリ ウィンドウにコピーします。

    USE Master;
    GO
    CREATE DATABASE SnapBackupDB
    ON PRIMARY
    (
        NAME = 'SnapBackupDB_Data',
        FILENAME = 'D:\MSSQL\SnapBackupDB.mdf',
        SIZE = 500MB,
        MAXSIZE = UNLIMITED,
        FILEGROWTH = 64MB
    )
    LOG ON
    (
        NAME = 'SnapBackupDB_Log',
        FILENAME = 'E:\MSSQL\SnapBackupDB_log.ldf',
        SIZE = 250MB,
        MAXSIZE = 2GB,
        FILEGROWTH = 64MB
    );
    

スナップショット スクリプトをデプロイする

このスクリプトは、SQL Server を実行している VM に接続されたディスクのアプリケーション整合性スナップショットを作成するために必要なプロセス全体を自動化します。

  1. 同じリモート デスクトップ セッションを使用して、メモ帳を開きます。
  2. 次のスクリプトの内容をコピーして、開いたメモ帳ウィンドウに貼り付けます。
  3. ファイルを c:\scriptstake-snapshot.ps1 として保存します。

    # Import Modules
    Import-Module -Name SQLPS
    
    # Set variables
    $formattedTimestamp = Get-Date -Format 'yyyyMMdd-HHmmss'
    $backupLocation = 'D:\MSSQL\Backup'
    $dbName = 'SnapBackupDB'
    $bkmFile = Join-Path -Path $backupLocation  -ChildPath ("${dbName}_${formattedTimestamp}.bkm")
    $sqlServer = 'localhost'
    
    # SQL Commands
    $suspendDbCmd = "ALTER DATABASE [$dbName] SET SUSPEND_FOR_SNAPSHOT_BACKUP=ON WITH NO_WAIT;"
    $backupMetadataCmd = "BACKUP DATABASE [$dbName] TO DISK='$bkmFile' WITH METADATA_ONLY, FORMAT;"
    $unsuspendDbCmd = "ALTER DATABASE [$dbName] SET SUSPEND_FOR_SNAPSHOT_BACKUP=OFF;"
    
    # --- Helper Function for SQL Execution ---
    function Invoke-MySqlCommand {
        param(
            [Parameter(Mandatory=$true)]
            [string]$CommandText,
            [Parameter(Mandatory=$true)]
            [System.Data.SqlClient.SqlConnection]$SqlConnection
        )
        Write-Host "Executing SQL Command: $($CommandText)"
        try {
            $Command = New-Object System.Data.SqlClient.SqlCommand($CommandText, $SqlConnection)
            $Result = $Command.ExecuteNonQuery()
            return $true
        }
        catch {
            Write-Host "Error executing SQL Command: $($CommandText)`n$($_.Exception.Message)" -ForegroundColor Red
            return $false
        }
    }
    
    $sqlConn = New-Object System.Data.SqlClient.SqlConnection
    $sqlConn.ConnectionString = "server='$sqlServer';database='$dbName';Integrated Security=True;"
    $databaseSuspended = $false
    
    if (-not(Test-Path $backupLocation)) {
        New-Item -Path $backupLocation -ItemType directory -Force
    }
    
    try {
        $instanceName = (Invoke-RestMethod -Headers @{"Metadata-Flavor"="Google"} -Uri "http://metadata.google.internal/computeMetadata/v1/instance/name")
        $vmConfigString = (gcloud compute instances list --filter="name=('$instanceName')" --format=json --quiet)
        if ($LASTEXITCODE -ne 0) {
            Write-Host "Error querying gcloud for VM instances (exit code: $LASTEXITCODE). Exiting." -ForegroundColor Red
            exit 1
        }
        $vmConfig = $vmConfigString | ConvertFrom-Json
    
        # Open SQL Server connection
        $sqlConn.Open()
    
        # Suspend Database
        Write-Host "Suspending database '$dbName' for snapshot..."
        if (-not (Invoke-MySqlCommand -CommandText $suspendDbCmd -SqlConnection $sqlConn)) {
            Write-Host "Failed to suspend database. Exiting." -ForegroundColor Red
            exit 1
        }
        $databaseSuspended = $true
    
        # Take a disk snapshot
        try {
            $consistencyGroupListStr = (gcloud compute resource-policies list --filter='name:*-snap-grp' --format=json --quiet)
            if ($LASTEXITCODE -ne 0) { throw "gcloud resource-policies list failed (exit code: $LASTEXITCODE)." }
            $consistencyGroupList = $consistencyGroupListStr | ConvertFrom-Json
    
            if (@($consistencyGroupList).Count -eq 0) { throw "No consistency group found matching '*-snap-grp'." }
            if (@($consistencyGroupList).Count -gt 1) { Write-Warning "More than one consistency group found, using the first one." }
    
            $consistencyGroupSelfLink = $consistencyGroupList[0].selfLink
            $zoneName = Split-Path $vmConfig[0].zone -Leaf
            $snapshotName = "$($vmConfig.Name)-${formattedTimestamp}"
    
            $snapshotCmd = "gcloud compute instant-snapshot-groups create $snapshotName --source-consistency-group=$consistencyGroupSelfLink --zone=$zoneName --quiet"
            Invoke-Expression $snapshotCmd
            if ($LASTEXITCODE -ne 0) { throw "gcloud compute instant-snapshot-groups create failed (exit code: $LASTEXITCODE)." }
            Write-Host "Instant snapshot group created successfully."
        }
        catch {
            Write-Host "Error during snapshot creation: $($_.Exception.Message)" -ForegroundColor Red
            exit 1 # Exit after handling in finally
        }
    
        # Backup database metadata
        if (-not (Invoke-MySqlCommand -CommandText $backupMetadataCmd -SqlConnection $sqlConn)) {
            Write-Host "Failed to backup database metadata." -ForegroundColor Red
            exit 1
        }
    
        # Unsuspend Database
        Write-Host "Unsuspending database '$dbName'..."
        if (-not (Invoke-MySqlCommand -CommandText $unsuspendDbCmd -SqlConnection $sqlConn)) {
            Write-Host "Failed to unsuspend database after successful snapshot and metadata backup. Manual intervention may be required." -ForegroundColor Red
            exit 1
        }
        $databaseSuspended = $false # Successfully unsuspended
    
    }
    catch {
        Write-Host "An unhandled error occurred: $($_.Exception.Message)" -ForegroundColor Red
        exit 1
    }
    finally {
        if ($databaseSuspended -and ($sqlConn.State -eq [System.Data.ConnectionState]::Open)) {
            if (-not (Invoke-MySqlCommand -CommandText $unsuspendDbCmd -SqlConnection $sqlConn)) {
                Write-Host "Failed to unsuspend database in finally block. Manual intervention may be required." -ForegroundColor Red
            }
        }
    
        # Ensure SQL connection is closed
        if ($sqlConn.State -eq [System.Data.ConnectionState]::Open) {
            $sqlConn.Close()
        }
    }
    
    
  4. [スタート] ボタンを右クリックするか Win+X を押して、[ターミナル(管理者)] をクリックします。

  5. 特権昇格を確認するプロンプトが表示されたら [はい] をクリックします。

  6. 次のコマンドを実行してスクリプトを実行します。

        C:\scripts\take-snapshot.ps1
    

インスタント スナップショットが作成されたことを確認する

Cloud Shell で以下のコマンドを実行します。

  1. スクリプトでスナップショットが作成されたことを確認します。

        gcloud compute instant-snapshots list --filter="name:$VM_NAME*"
    
  2. インスタント スナップショット グループを一覧表示します。

        gcloud compute instant-snapshot-groups list --zones $ZONE
    
  3. インスタント スナップショット グループの詳細を表示します。

        gcloud compute instant-snapshot-groups describe  \
        INSTANT_SNAPSHOT_NAME --zone=$ZONE
    
  4. selfLink 値をコピーして、次のセクションで使用します。

インスタント スナップショットを新しいディスクに復元する

コンソール ウィンドウで次のコマンドを実行します。 Google Cloud

  1. インスタント スナップショットの整合性グループからディスクを作成します。

        gcloud compute disks bulk create --source-instant-snapshot-group \
        INSTANT_SNAPSHOT_NAME_URL \
        --source-instant-snapshot-group-region $REGION --zone=$ZONE
    
  2. ゾーン内のディスクを一覧表示して、スナップショットが復元されたことを確認します。

        gcloud compute disks list --zones=$ZONE --filter="name:$VM_NAME-*"
    
  3. 新しく作成したディスクから新しい VM を作成します。

    REGION=REGION
    ZONE=$REGION-a
    VM_NAME=VM_NAME
    gcloud compute instances create $VM_NAME \
        --machine-type c4-highmem-4 \
        --zone $ZONE \
        --network-interface subnet=default \
        --tags sql-server-instant-snapshot \
        --disk=name=BOOT_DISK_NAME,boot=yes,auto-delete=no \
        --disk=name=DATA_DISK_1_NAME,mode=rw,auto-delete=no \
        --disk=name=DATA_DISK_2_NAME,mode=rw,auto-delete=no
    

次のように置き換えます。

  • リージョン: 新しいインスタンスがデプロイされるリージョン。
  • VM_NAME: 新しい SQL インスタンスの名前。
  • BOOT_DISK_NAME: 前のステップで作成したブートディスクの名前。
  • DATA_DISK_1_NAME: 前の ステップで作成した最初のデータディスクの名前。
  • DATA_DISK_2_NAME: 前のステップ で作成した 2 番目のデータディスクの名前。

これで、ディスクを新しい VM または既存の VM に接続できます。

クリーンアップ

このチュートリアルで使用したリソースについて Google Cloud アカウントに課金されないようにするには、プロジェクトを削除します。

プロジェクトの削除

  1. コンソールで [**リソースの管理**] ページに移動します。 Google Cloud

    [リソースの管理] に移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、 [Shut down] をクリックしてプロジェクトを削除します。

次のステップ