如何使用卷影副本进行备份

Tom*_*lak 11 backup windows-server-2003 vss

计划是创建一个相当大的 I/O 密集型卷的卷影副本。它有 350GB,包含一个基于文件系统的全文索引,该索引组织在数百个文件夹和数十万个需要处于一致状态才能成功恢复的小文件中。

当前索引器已停止,备份任务运行,然后索引器重新启动。这会导致索引在备份期间数小时不可用。我想通过卷影副本进行一致的备份,理想情况下根本不必停止索引器。

因此,我为该卷打开了 Shadow Copy,并将其配置为每晚制作一次快照到不同的卷。

现在我有点不知所措 - 我怎样才能访问整个卷影副本,以便我可以进行备份?我设想了一个只读驱动器,其中包含上次快照时的文件,但也许事情完全不同。

操作系统为Windows Server 2003 SP2,备份软件为CommVault Galaxy 7.0。


编辑:请注意 - 同时 - 已经创建了两个答案,它们以脚本的形式实现了必要的功能:

Joh*_*mer 11

因此,本着重新发明轮子的精神,我向您展示了 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
Run Code Online (Sandbox Code Playgroud)

这是脚本:

# 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"
Run Code Online (Sandbox Code Playgroud)

以下是要使用的 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
  2. 视窗 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
  3. 视窗 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

  • 顺便说一句......我能够将其作为我们备份解决方案的一部分,使用 Arcserve 作为穷人的开放文件备份。这比为代理许可证为每台服务器支付 800 美元要好。如果有人感兴趣,我会在这里发布。 (2认同)

Tom*_*lak 9

所以……我一直在研究一个小的 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
05/03/2010 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 完成

这是脚本本身。通常的免责声明适用:该软件按原样提供,我不提供任何保证,使用风险自负,如果出现问题,唯一应归咎于您自己。不过,我已经对它进行了彻底的测试,对我来说效果很好。请随时通过下面的评论通知我任何错误。

vshadow.exe -q                - List all shadow copies in the system
vshadow.exe -p {volume list}  - Manages persistent shadow copies
vshadow.exe -el={SnapID},dir  - Expose the shadow copy as a mount point
vshadow.exe -ds={SnapID}      - Deletes this shadow copy

我希望这对某人有帮助。按照cc-by-sa随意使用它。我所要求的只是您将指向此处的链接完好无损。


小智 6

  1. 使用该命令vssadmin list shadows列出所有可用的卷影副本。你会得到这样的输出......
C:\> vssadmin 列表阴影
vssadmin 1.1 - 卷影复制服务管理命令行工具
(C) 版权所有 2001 Microsoft Corp.

卷影副本集 ID 的内容:{b6f6fb45-bedd-4b77-8f51-14292ee921f3}
   创建时包含 1 个卷影副本:2016 年 9 月 25 日下午 12:14:23
      卷影副本 ID:{321930d4-0442-4cc6-b2aa-ec47f21d0eb1}
         原始卷:(C:)\\?\Volume{ad1dd231-1200-11de-b1df-806e6f6e6963}\
         卷影复制卷:\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy68
         始发机:joshweb.josh.com
         服务机器:joshweb.josh.com
         提供程序:“Microsoft 软件卷影复制提供程序 1.0”
         类型:ClientAccessible
         属性:持久性、客户端可访问、无自动发布、无编写器、差异化

卷影副本集 ID 的内容:{c4fd8646-57b3-4b39-be75-47dc8e7f881d}
   创建时包含 1 个卷影副本:2016 年 8 月 25 日上午 7:00:18
      卷影副本 ID:{fa5da100-5d90-493c-89b1-5c27874a23c6}
         原始卷:(E:)\\?\卷{4ec17949-12b6-11de-8872-00235428b661}\
         卷影复制卷:\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy3
         始发机:joshweb.josh.com
         服务机器:joshweb.josh.com
         提供程序:“Microsoft 软件卷影复制提供程序 1.0”
         类型:ClientAccessible
         属性:持久性、客户端可访问、无自动发布、无编写器、差异化

C:\
  1. 请注意Shadow Copy Volume您想要的卷影副本的名称(最容易到剪贴板)。

  2. 挂载卷影副本

在 Windows 2003 上...

如果您还没有2003资源工具包工具,则需要下载它。

输入命令...

链接 c:\shadow \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy69\

...c:\shadow您希望卷影副本出现的路径是您在\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy69上面复制的名称。请注意,您必须在卷影副本名称的末尾添加一个反斜杠!

在 Windows 2008 及更高版本上...

输入命令...

mklink c:\shadow \\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy69\

...c:\shadow您希望卷影副本出现的路径是您在\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy69上面复制的名称。请注意,您必须在卷影副本名称的末尾添加一个反斜杠!

  1. 使用您想要的任何工具(包括 Windows 资源管理器或XCOPY)从c:\shadow.