Windows-Server-2003

如何使用卷影複製進行備份

  • September 25, 2016

計劃是創建一個相當大的、I/O 繁重的捲的捲影副本。它有 350GB,包含一個基於文件系統的全文索引,該索引組織在數百個文件夾和數十萬個需要處於一致狀態才能成功還原的小文件中。

目前索引器停止,備份任務執行,然后索引器重新啟動。這會導致索引在備份期間數小時不可用。我想通過卷影副本進行一致的備份,理想情況下根本不必停止索引器。

因此,我為該卷打開了卷影複製,並將其配置為每晚製作一次快照到不同的捲。

現在我有點不知所措 - 我怎樣才能訪問整個卷影副本,以便我可以進行備份?我設想一個只讀驅動器包含上次快照時的文件,但也許事情完全不同。

作業系統為 Windows Server 2003 SP2,備份軟體為 CommVault Galaxy 7.0。


編輯:請注意 - 同時 - 已經創建了兩個答案,它們以腳本的形式實現了必要的功能:

所以……我一直在研究一個小的 VBScript,它可以:

  • 拍攝持久的 VSS 快照
  • 將它們掛載到一個文件夾(然後您可以從中備份文件)
  • 解除安裝 VSS 快照

它依賴於vshadow.exe文件),它是Microsoft 提供的Volume Shadow Copy Service SDK 7.2的一部分。我一直在使用這個版本:“ VSHADOW.EXE 2.2 - 卷影複製範例客戶端,版權所有 (C) 2005 Microsoft Corporation。

基本上,它是對這四個 vshadow 命令的一個簡潔的包裝:

vshadow.exe -q - 列出系統中的所有捲影副本
vshadow.exe -p {volume list} - 管理持久的捲影副本
vshadow.exe -el={SnapID},dir - 將捲影副本公開為掛載點
vshadow.exe -ds={SnapID} - 刪除這個卷影副本

這是它的幫助螢幕:

VSS 快照創建/安裝工具

用法:
cscript /nologo VssSnapshot.vbs /target:path { /volume:X | /解除安裝} [/調試]

/volume - 要快照的捲的驅動器號
/target - 將快照掛載到的路徑(絕對或相對)
/debug - 切換調試輸出

例子:
cscript /nologo VssSnapshot.vbs /target:C:\Backup\DriveD /volume:D
cscript /nologo VssSnapshot.vbs /target:C:\Backup\DriveD /unmount

提示:在拍攝新快照之前無需解除安裝。

這裡有一些範例輸出:

C:\VssSnapshot>cscript /nologo VssSnapshot.vbs /target:MountPoints\E /volume:E
05/03/2010 17:13:04 準備 VSS 掛載點...
05/03/2010 17:13:04 掛載點準備在:C:\VssSnapshot\MountPoints\E
05/03/2010 17:13:04 為卷創建 VSS 快照:E
2010 年 5 月 3 日 17:13:08 使用 ID 創建快照:{4ed3a907-c66f-4b20-bda0-9dcda3b667ec}
05/03/2010 17:13:08 VSS 快照掛載成功
05/03/2010 17:13:08 完成

C:\VssSnapshot>cscript /nologo VssSnapshot.vbs /target:MountPoints\E /unmount
05/03/2010 17:13:35 準備 VSS 掛載點...
05/03/2010 17:13:36 無事可做
05/03/2010 17:13:36 完成

這是腳本本身。通常的免責聲明適用:軟體按原樣提供,我不做任何保證,使用風險自負,如果出現問題,唯一的責任就是你自己。不過,我已經對其進行了非常徹底的測試,並且對我來說效果很好。隨時通過下面的評論通知我任何錯誤。

''# VssSnapshot.vbs
''# http://serverfault.com/questions/119120/how-to-use-a-volume-shadow-copy-to-make-backups/119592#119592
Option Explicit

Dim fso: Set fso = CreateObject("Scripting.FileSystemObject")

''# -- MAIN SCRIPT -------------------------------------------
Dim args, snapshotId, targetPath, success
Set args = WScript.Arguments.Named
CheckEnvironment

Log "preparing VSS mount point..."
targetPath = PrepareVssMountPoint(args("target"))

If args.Exists("unmount") Then
 Log "nothing else to do"
ElseIf targetPath <> vbEmpty Then
 Log "mount point prepared at: " & targetPath
 Log "creating VSS snapshot for volume: " & args("volume")
 snapshotId = CreateVssSnapshot(args("volume"))

 If snapshotId <> vbEmpty Then
   Log "snapshot created with ID: " & snapshotId
   success = MountVssSnapshot(snapshotId, targetPath)
   If success Then
     Log "VSS snapshot mounted sucessfully"
   Else
     Die "failed to mount snapshot"
   End If
 Else
   Die "failed to create snapshot"
 End If
Else
 Die "failed to prepare mount point"
End If

Log "finished"

''# -- FUNCTIONS ---------------------------------------------
Function PrepareVssMountPoint(target) ''# As String
 Dim cmd, result, outArray
 Dim path, snapshot, snapshotId
 Dim re, matches, match

 PrepareVssMountPoint = VbEmpty
 target = fso.GetAbsolutePathName(target)

 If Not fso.FolderExists(fso.GetParentFolderName(target)) Then 
   Die "Invalid mount point: " & target
 End If

 ''# create or unmount (=delete existing snapshot) mountpoint
 If Not fso.FolderExists(target) Then
   If Not args.Exists("unmount") Then fso.CreateFolder target
 Else
   Set re = New RegExp
   re.MultiLine = False
   re.Pattern = "- Exposed locally as: ([^\r\n]*)"

   cmd = "vshadow -q"
   result = RunCommand(cmd, false)
   outarray = Split(result, "*")

   For Each snapshot In outArray
     snapshotId = ParseSnapshotId(snapshot)
     If snapshotId <> vbEmpty Then
       Set matches = re.Execute(snapshot)
       If matches.Count = 1 Then
         path = Trim(matches(0).SubMatches(0))
         If fso.GetAbsolutePathName(path) = target Then
           cmd = "vshadow -ds=" & snapshotId
           RunCommand cmd, true
           Exit For
         End If
       End If
     End If
   Next

   If args.Exists("unmount") Then fso.DeleteFolder target
 End If

 PrepareVssMountPoint = target
End Function

Function CreateVssSnapshot(volume) ''# As String
 Dim cmd, result

 If Not fso.DriveExists(volume) Then
   Die "Drive " & volume & " does not exist."
 End If

 cmd = "vshadow -p " & Replace(UCase(volume), ":", "") & ":"
 result = RunCommand(cmd, false)
 CreateVssSnapshot = ParseSnapshotId(result)
End Function

Function MountVssSnapshot(snapshotId, target) ''# As Boolean
 Dim cmd, result

 If fso.FolderExists(targetPath) Then
   cmd = "vshadow -el=" & snapshotId & "," & targetPath
   result = RunCommand(cmd, true)
 Else
   Die "Mountpoint does not exist: " & target
 End If

 MountVssSnapshot = (result = "0")
End Function

Function ParseSnapshotId(output) ''# As String
 Dim re, matches, match

 Set re = New RegExp
 re.Pattern = "SNAPSHOT ID = (\{[^}]{36}\})"
 Set matches = re.Execute(output)

 If matches.Count = 1 Then
   ParseSnapshotId = matches(0).SubMatches(0)
 Else
   ParseSnapshotId = vbEmpty
 End If
End Function

Function RunCommand(cmd, exitCodeOnly) ''# As String
 Dim shell, process, output

 Dbg "Running: " & cmd

 Set shell = CreateObject("WScript.Shell")

 On Error Resume Next
 Set process = Shell.Exec(cmd)
 If Err.Number <> 0 Then
   Die Hex(Err.Number) & " - " & Err.Description
 End If
 On Error GoTo 0

 Do While process.Status = 0
   WScript.Sleep 100
 Loop
 output = Process.StdOut.ReadAll

 If process.ExitCode = 0 Then 
   Dbg "OK"
   Dbg output
 Else
   Dbg "Failed with ERRORLEVEL " & process.ExitCode
   Dbg output
   If Not process.StdErr.AtEndOfStream Then 
     Dbg process.StdErr.ReadAll
   End If
 End If  

 If exitCodeOnly Then
   Runcommand = process.ExitCode
 Else
   RunCommand = output
 End If
End Function

Sub CheckEnvironment
 Dim argsOk

 If LCase(fso.GetFileName(WScript.FullName)) <> "cscript.exe" Then
   Say "Please execute me on the command line via cscript.exe!"
   Die ""
 End If

 argsOk = args.Exists("target")
 argsOk = argsOk And (args.Exists("volume") Or args.Exists("unmount"))

 If Not argsOk Then
   Say "VSS Snapshot Create/Mount Tool" & vbNewLine & _
       vbNewLine & _
       "Usage: " & vbNewLine & _
       "cscript /nologo " & fso.GetFileName(WScript.ScriptFullName) & _
         " /target:path { /volume:X | /unmount } [/debug]" & _
       vbNewLine & vbNewLine & _
       "/volume  - drive letter of the volume to snapshot" & _
       vbNewLine & _
       "/target  - the path (absolute or relative) to mount the snapshot to" & _
       vbNewLine & _
       "/debug   - swich on debug output" & _
       vbNewLine & vbNewLine & _
       "Examples: " & vbNewLine & _
       "cscript /nologo " & fso.GetFileName(WScript.ScriptFullName) & _
         " /target:C:\Backup\DriveD /volume:D" &  vbNewLine & _
       "cscript /nologo " & fso.GetFileName(WScript.ScriptFullName) & _
         " /target:C:\Backup\DriveD /unmount" & _
       vbNewLine & vbNewLine & _
       "Hint: No need to unmount before taking a new snapshot." & vbNewLine

   Die ""
 End If
End Sub

Sub Say(message)
 If message <> "" Then WScript.Echo message
End Sub

Sub Log(message)
 Say FormatDateTime(Now()) & " " & message
End Sub

Sub Dbg(message)
 If args.Exists("debug") Then 
   Say String(75, "-")
   Say "DEBUG: " & message
 End If
End Sub

Sub Die(message)
 If message <> "" Then Say "FATAL ERROR: " & message
 WScript.Quit 1
End Sub

我希望這對某人有所幫助。隨意按照cc-by-sa使用它。我只要求您保留指向此處的連結完好無損。

因此,本著重新發明輪子的精神,我向您展示了 Tomalak 的出色腳本(見上文),但完全用Powershell重寫!我這樣做的主要原因是宣傳 Powershell 的強大功能,但也因為我整個人都鄙視 vbscript。

它主要是功能相同的功能,但由於各種原因,我確實實現了一些不同的東西。調試輸出肯定更詳細。

需要注意的一件非常重要的事情是,此版本會檢測作業系統版本和位數,並呼叫適當版本的 vshadow.exe。我在下面提供了一個圖表,以顯示要使用的 vshadow.exe 版本、獲取它們的位置以及命名它們的名稱。


以下是使用資訊:

VssSnapshot.ps1

Description:
 Create, mount or delete a Volume Shadow Copy Service (VSS) Shadow Copy (snapshot)

Usage:
 VssSnapshot.ps1 Create -Target <Path> -Volume <Volume> [-Debug]
 VssSnapshot.ps1 Delete -Target <Path> [-Debug]

Paremeters:
 Create  - Create a snapshot for the specified volume and mount it at the specified target
 Delete  - Unmount and delete the snapshot mounted at the specified target
 -Target - The path (quoted string) of the snapshot mount point
 -Volume - The volume (drive letter) to snapshot
 -Debug  - Enable debug output (optional)

Examples:
 VssSnapshot.ps1 Create -Target D:\Backup\DriveC -Volume C
 - Create a snapshot of volume C and mount it at "D:\Backup\DriveC"

 VssSnapshot.ps1 Delete -Target D:\Backup\DriveC
 - Unmount and delete a snapshot mounted at "D:\Backup\DriveC"

Advanced:
 VssSnapshot.ps1 create -t "c:\vss mount\c" -v C -d
 - Create a snapshot of volume C and mount it at "C:\Vss Mount\C"
 - example mounts snapshot on source volume (C: --> C:)
 - example uses shortform parameter names
 - example uses quoted paths with whitespace
 - example includes debug output

這是腳本:

# VssSnapshot.ps1
# http://serverfault.com/questions/119120/how-to-use-a-volume-shadow-copy-to-make-backups/119592#119592

Param ([String]$Action, [String]$Target, [String]$Volume, [Switch]$Debug)
$ScriptCommandLine = $MyInvocation.Line
$vshadowPath = "."

# Functions
Function Check-Environment {
 Write-Dbg "Checking environment..."

 $UsageMsg = @'
VssSnapshot

Description:
 Create, mount or delete a Volume Shadow Copy Service (VSS) Shadow Copy (snapshot)

Usage:
 VssSnapshot.ps1 Create -Target <Path> -Volume <Volume> [-Debug]
 VssSnapshot.ps1 Delete -Target <Path> [-Debug]

Paremeters:
 Create  - Create a snapshot for the specified volume and mount it at the specified target
 Delete  - Unmount and delete the snapshot mounted at the specified target
 -Target - The path (quoted string) of the snapshot mount point
 -Volume - The volume (drive letter) to snapshot
 -Debug  - Enable debug output (optional)

Examples:
 VssSnapshot.ps1 Create -Target D:\Backup\DriveC -Volume C
 - Create a snapshot of volume C and mount it at "D:\Backup\DriveC"

 VssSnapshot.ps1 Delete -Target D:\Backup\DriveC
 - Unmount and delete a snapshot mounted at "D:\Backup\DriveC"

Advanced:
 VssSnapshot.ps1 create -t "c:\vss mount\c" -v C -d
 - Create a snapshot of volume C and mount it at "C:\Vss Mount\C"
 - example mounts snapshot on source volume (C: --> C:)
 - example uses shortform parameter names
 - example uses quoted paths with whitespace
 - example includes debug output
'@

 If ($Action -eq "Create" -And ($Target -And $Volume)) {
   $Script:Volume = (Get-PSDrive | Where-Object {$_.Name -eq ($Volume).Substring(0,1)}).Root
   If ($Volume -ne "") {
     Write-Dbg "Verified volume: $Volume"
   } Else {
     Write-Dbg "Cannot find the specified volume"
     Exit-Script "Cannot find the specified volume"
   }
   Write-Dbg "Argument check passed"
 } ElseIf ($Action -eq "Delete" -And $Target ) {
   Write-Dbg "Argument check passed"
 } Else {
   Write-Dbg "Invalid arguments: $ScriptCommandLine"
   Exit-Script "Invalid arguments`n`n$UsageMsg"
 }


 $WinVer = ((Get-WmiObject Win32_OperatingSystem).Version).Substring(0,3)
   Switch ($WinVer) {
   "5.2" {
     $vshadowExe = "vshadow_2003"
     $WinBit = ((Get-WmiObject Win32_Processor)[0]).AddressWidth
   }
   "6.0" {
     $vshadowExe = "vshadow_2008"
     $WinBit = (Get-WmiObject Win32_OperatingSystem).OSArchitecture
   }
   "6.1" {
     $vshadowExe = "vshadow_2008R2"
     $WinBit = (Get-WmiObject Win32_OperatingSystem).OSArchitecture
   }
   Default {
     Write-Dbg "Unable to determine OS version"
     Exit-Script "Unable to determine OS version"
   }
 }

 Switch ($WinBit) {
   {($_ -eq "32") -or ($_ -eq "32-bit")} {$vshadowExe += "_x86.exe"}
   {($_ -eq "64") -or ($_ -eq "64-bit")} {$vshadowExe += "_x64.exe"}
   Default {
     Write-Dbg "Unable to determine OS bitness"
     Exit-Script "Unable to determine OS bitness"
   }
 }

 $Script:vshadowExePath = Join-Path $vshadowPath $vshadowExe
 If (Test-Path $vshadowExePath) {
   Write-Dbg "Verified vshadow.exe: $vshadowExePath"
 } Else {
   Write-Dbg "Cannot find vshadow.exe: $vshadowExePath"
   Exit-Script "Cannot find vshadow.exe"
 }

 Write-Dbg "Environment ready"
}

Function Prepare-Target {
 Write-Log "Preparing target..."
 Write-Dbg "Preparing target $Target"


 If (!(Test-Path (Split-Path $Target -Parent))) {
 Write-Dbg "Target parent does not exist"
 Exit-Script "Invalid target $Target"
 }
 If ((Test-Path $Target)) {
   Write-Dbg "Target already exists"
   If (@(Get-ChildItem $Target).Count -eq 0) {
     Write-Dbg "Target is empty"
   } Else {
     Write-Dbg "Target is not empty"
     Exit-Script "Target contains files/folders"
   }
 } Else {
   Write-Dbg "Target does not exist. Prompting user..."
   $PromptYes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", "Create target folder"
   $PromptNo = New-Object System.Management.Automation.Host.ChoiceDescription "&No", "Do not create target folder"
   $PromptOptions = [System.Management.Automation.Host.ChoiceDescription[]]($PromptYes, $PromptNo)
   $PromptResult = $Host.UI.PromptForChoice("Create folder", "The target folder `"$target`" does not exist.`nWould you like to create the folder?", $PromptOptions, 0) 
   Switch ($PromptResult) {
     0 {
       Write-Dbg "User Accepted. Creating target..."
       $Null = New-Item -Path (Split-Path $Target -Parent) -Name (Split-Path $Target -Leaf) -ItemType "Directory"
     }
     1 {
       Write-Dbg "User declined. Exiting..."
       Exit-Script "Target does not exist"
     }
   }
 }
 Write-Log "Target ""$Target"" ready"
 Write-Dbg """$Target"" ready"
}

Function Create-Snapshot {
 Write-Log "Creating snapshot..."
 Write-Dbg "Creating snapshot of $Volume"
 $Cmd = "$vshadowExePath -p $Volume"
 $CmdResult = Run-Command $Cmd -AsString

 Write-Dbg "Snapshot created successfully"

 $SnapshotID = $CmdResult -Match 'SNAPSHOT ID = (\{[^}]{36}\})'
 If ($SnapshotID) {
   $SnapshotID = $Matches[1]
   Write-Dbg "SnapshotID: $SnapshotID"
   Write-Log "Snapshot $SnapshotID created"
 } Else {
   Write-Dbg "Unable to determine SnapshotID"
   Exit-Script "Unable to determine SnapshotID"
 }

 Return $SnapshotID
}

Function Mount-Snapshot ($SnapshotID) {
 Write-Log "Mounting snapshot..."
 Write-Dbg "Mounting $SnapshotID at ""$Target"""

 $Cmd = "$vshadowExePath `"-el=$SnapshotId,$Target`"" #Must use escaped quotes because Invoke-Expression gets all weird about curly braces
 $CmdResult = Run-Command $Cmd

 Write-Log "Snapshot $SnapshotID mounted at target ""$Target"""
 Write-Dbg "$SnapshotID mounted at ""$Target"""
}

Function Delete-Snapshot {
 Write-Log "Deleting snapshot..."
 Write-Dbg "Deleting snapshot at target ""$Target"""

 $SnapshotID = Get-SnapshotIdbyTarget

 $Cmd = "$vshadowExePath `"-ds=$SnapshotId`""
 $CmdResult = Run-Command $Cmd

 Write-Log "Snapshot $SnapshotID deleted at target ""$Target"""
 Write-Dbg "$SnapshotID deleted at ""$Target"""
}

Function Get-SnapshotIdbyTarget {
 Write-Dbg "Finding SnapshotID for $Target"

 $Cmd = "$vshadowExePath -q"
 $CmdResult = Run-Command $Cmd -AsString

 $TargetRegEx = '(?i)' + $Target.Replace('\','\\') + '\\?\r'
 $Snapshots = ($CmdResult.Split('*')) -Match $TargetRegEx | Out-String

 If ($Snapshots) {
   $Null = $Snapshots -Match '(\{[^}]{36}\})'
   $SnapshotID = $Matches[0]
 } Else {
   Write-Dbg "Unable to determine SnapshotID for target $Target"
   Exit-Script "Unable to determine SnapshotID"
 }  

 Write-Dbg "SnapshotID: $SnapshotID"

 Return $SnapshotID
}

Function Run-Command ([String]$Cmd, [Switch]$AsString=$False, [Switch]$AsArray=$False) {
 Write-Dbg "Running: $Cmd"

 $CmdOutputArray = Invoke-Expression $Cmd
 $CmdOutputString = $CmdOutputArray | Out-String
 $CmdErrorCode = $LASTEXITCODE

 If ($CmdErrorCode -eq 0 ) {
   Write-Dbg "Command successful. Exit code: $CmdErrorCode"
   Write-Dbg $CmdOutputString
 } Else {
   Write-Dbg "Command failed. Exit code: $CmdErrorCode"
   Write-Dbg $CmdOutputString
   Exit-Script "Command failed. Exit code: $CmdErrorCode"
 }

 If (!($AsString -or $AsArray)) {
   Return $CmdErrorCode
 } ElseIf ($AsString) {
   Return $CmdOutputString
 } ElseIf ($AsArray) {
   Return $CmdOutputArray
 }
}

Function Write-Msg ([String]$Message) {
 If ($Message -ne "") {
   Write-Host $Message
 }
}

Function Write-Log ([String]$Message) {
 Write-Msg "[$(Get-Date -Format G)] $Message"
}

Function Write-Dbg ([String]$Message) {
 If ($Debug) {
   Write-Msg ("-" * 80)
   Write-Msg "[DEBUG] $Message"
   Write-Msg ("-" * 80)
 }
}

Function Exit-Script ([String]$Message) {
 If ($Message -ne "") {
   Write-Msg "`n[FATAL ERROR] $Message`n"
 }
 Exit 1
}

# Main
Write-Log "VssSnapshot started"
Check-Environment

Switch ($Action) {
 "Create" {
   Prepare-Target
   $SnapshotID = Create-Snapshot
   Mount-Snapshot $SnapshotID
 }
 "Delete" {
   Delete-Snapshot
 }
}

Write-Log "VssSnapshot finished"

以下是要使用的 vshadow.exe 版本:

  1. 視窗 2003/2003R2
  • 卷影複製服務 SDK 7.2

  • x86: C:\Program Files\Microsoft\VSSSDK72\TestApps\vshadow\bin\release-server\vshadow.exe

    • 重命名為:vshadow_2003_x86.exe
  • x64:我無法找到適用於 Windows 2003 x64 的 x64 版本的 vshadow.exe

  1. 視窗 2008
  • 適用於 Windows Server 2008 和 .NET Framework 3.5 的 Windows SDK

  • x86: C:\Program Files\Microsoft SDKs\Windows\v6.1\Bin\vsstools\vshadow.exe

    • 重命名為:vshadow_2008_x86.exe
  • x64: C:\Program Files\Microsoft SDKs\Windows\v6.1\Bin\x64\vsstools\vshadow.exe

    • 重命名為:vshadow_2008_x64.exe
  1. 視窗 2008R2
  • 適用於 Windows 7 和 .NET Framework 4 的 Microsoft Windows SDK

  • x86: C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\vsstools\vshadow.exe

    • 重命名為:vshadow_2008R2_x86.exe
  • x64: C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\x64\vsstools\vshadow.exe

    • 重命名為:vshadow_2008R2_x64.exe

引用自:https://serverfault.com/questions/119120