インスタント スナップショットを使用して 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 ロールは必要ありません。ロールが付与されているプロジェクトであれば、どのプロジェクトでも選択できます。
      • プロジェクトを作成する: プロジェクトを作成するには、resourcemanager.projects.create 権限を含むプロジェクト作成者ロール(roles/resourcemanager.projectCreator)が必要です。詳しくは、ロールを付与する方法をご覧ください。

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

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

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

      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 にアタッチできます。

クリーンアップ

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

プロジェクトを削除する

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

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

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

次のステップ