このブログ記事では、PowerShell とネイティブの Microsoft SQL Server バックアップを使用して、オンプレミス SQL Server データベースを Amazon EC2 インスタンスに移行する方法について説明します。
原則として、オンプレミス SQL Server データベースを Amazon RDS に移行するために、できる限りのことを行うのがよいでしょう。この方法の詳細については、「Amazon RDS ユーザーガイド」、特にガイド内の SQL Server に関するトピックを参照してください。しかし、Amazon RDS がソリューションとして実行できない場合があります。だからこそ、あなたはこのブログ記事を読んでいるのかもしれません。
AWS では、オンプレミス SQL Server データベースを AWS に移行するためのたくさんの方法を用意しています。同種のデータベース移行には、ネイティブツールを使用することをお勧めします。異種間での移行、恐らく Amazon Aurora か Amazon RDS for PostgreSQL に移行する場合には、AWS Database Migration Service を使うのがよいでしょう。それ以外の場合は、ネイティブレプリケーションのオプションを使用してください。
この記事で説明するシナリオは、DBA チームが SQL .bak ファイルを Amazon S3 にアップロードするというケースです。カットオーバー時のダウンタイム中にチームが PowerShell ブートストラップスクリプトを実行すると、データベースが AWS で起動し実行します。
普通なら、非アクティブまたは低トラフィックの間に移行を実行したいと考えるでしょう。ダウンタイムとセキュリティを決定する際に考慮するべき重要なことを、以下に示します。ダウンタイムを最小限に抑える方法はいくつかあります。
- ネットワーク帯域幅 – AWS Direct Connect や VPN などを使っていますか? .Bak ファイルの Amazon S3 アップロードをシミュレートして、アップロード時間を見積もることをお勧めします。
- 圧縮 – データベースバックアップの圧縮もオプションに含めることができます。これで、CPU の使用量はやや多くなりますが、転送されるデータ量が少なくなるため、移行時間が短縮されます。
- バックアップサイズ – バックアップサイズが、アップロードの完了時間と成功率をもっと良くするために、バックアップをより小さな幅のかたまりにストライプする必要があるかどうかを決定することがあります。
- バックアップをストライピングすると、バックアップが完了するまでの時間が大幅に短縮される可能性があります。
- 個々のストライプファイルを 5 GB 未満に保つと、マルチパートアップロード機能を使用せずに済みます。詳細については、Amazon S3 ドキュメントにあるこのトピックを参照してください。Amazon S3 の単一ファイルの最大ファイルサイズは 5 TB です。したがって、データベースがそれより大きい場合は、複数のファイルにストライピングする必要があります。
- 並列 S3 アップロードの場合は、分割ファイルを並行してアップロードすることで移行時間を短縮できるサードパーティのオプションを検討することもできます。ソフトウェアエンジニアの André Rocha Agostinho 氏が、C# についての優れた記事、「Using C# Parallel to copy files to S3」を書いています。
- 暗号化 – いくつかのオプションがあります。
- AWS KMS の暗号化を使用してバックアップとアップロードを実行してから、同じ暗号を使用してダウンロードし復元することができます。AWS KMS の暗号化を用いると、ファイルが格納されている S3 バケットに誰かがアクセスしても、データベースを復元することはできません。AWS KMS を使って S3 バケット全体を暗号化することで、セキュリティをさらに高度にすることも可能です。
- 別の方法として、バックアップ用に SQL Server に MEDIAPASSWORD の値を含めることです。これは暗号化ほど安全ではなく、間もなく Microsoft の推奨ではなくなります。
考慮すべきもう 1 つの重要な点は、宛先 EC2 インスタンスクラスと Amazon EBS ジオメトリです。Microsoft SQL Server インスタンスの場合、Amazon RDS ドキュメントの DB インスタンスクラスに関するトピックで、要件を満たす推奨される DB インスタンスクラスを参照できます。推奨される EBS ボリュームタイプは、プロビジョンド IOPS SSD (io1) です。これは、ミッションクリティカルな低いレイテンシーまたは高いスループットのワークロードに対して、最高のパフォーマンスの SSD ボリュームを提供します。
この例では、Adventureworks データベースのストライプバックアップを作成します。優れたブログ記事が、「SQL CAT New Whitepaper: Data Compression: Strategy, Capacity Planning and Best Practices by Sanjay Mishra, et al.」にあります。データベースのバックアップをストライプ化する最適なファイル数を決定する方法について述べています。後で、Amazon S3 にバッチアップロードを行う方法を解説します。
データベースバックアップを AWS に取り込む
データベースを AWS にバックアップするには、まずストライプバックアップを作成します。
ストライプバックアップを作成する
次の PowerShell コマンドを使用して、データベースをバックアップします。
#Load required assemblies
[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended") | Out-Null
[Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo") | Out-Null
[Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoEnum") | Out-Null
$server = New-Object ("Microsoft.SqlServer.Management.Smo.Server") $dbInstance
#Allow time for backup
$server.ConnectionContext.StatementTimeout = 0
#Backup Database
$MachineName = (Get-WmiObject -Class Win32_ComputerSystem -Property Name).Name
$myarr=@($RelocateData,$RelocateLog)
$backupfile1 = "D:\Backup\AdventureWorks1.bak"
$backupfile2 = "D:\Backup\AdventureWorks2.bak"
$backupfile3 = "D:\Backup\Adventureworks3.bak"
$dbname = 'AdventureWorks'
Backup-SqlDatabase
-ServerInstance MachineName
-Database $dbname
-BackupFile $backupfile1,$backupfile2,$backupfile3
別のアプローチは、Amazon S3 に直接バックアップし、S3から直接復元します。この方法では、ローカルディスクへのバックアップ、S3 へのコピー、S3 へのダウンロード、および復元を行わずに済みます。Amazon S3 との直接バックアップと復元は、同じブロックの読み取り回数を減らすことで、移行時間を短縮します。
ストライプバックアップを Amazon S3 にアップロードする
次に、AWS Tools for Windows PowerShell をインストールした PC を使用して、ストライプバックアップを Amazon S3 にアップロードします。詳細は、Amazon PowerShell のウェブサイトを参照してください。認証されたユーザーで AWS アカウントに接続し、次のコマンドを実行してデータベースを Amazon S3 にバックアップします。必ずバックアップをプライベートバケットにアップロードし、ファイルを暗号化してください。
これを行うための前提条件は、次のとおりです。
- アクセス可能な S3 バケットにアップロードしたバックアップ
- AWS アカウントへのアクセスが必要な IAM ユーザー
Write-S3Object
-BucketName sql-backups-demo
-Folder “D:\Backup”
-KeyPrefix “/SQLServer/Backups”
-Region 'us-east-1'
-ServerSideEncryption AES256
Amazon S3 への正常なアップロードを確認するには、次のコマンドを使用します。
Get-S3Object
-BucketName sql-backups-demo
-KeyPrefix “/SQLServer/Backups”
-Region 'us-east-1'
正しいポリシーで IAM ロールをプロビジョニングする
次に、IAM ロール、SQLMigration
を作成し、EC2 インスタンスがバックアップを保持する S3 バケットにアクセスできるようにします。
セキュリティを強化するため、このタスクが読み取り専用のポリシー権限を実行する IAM ロールを与えます。AmazonS3ReadOnlyAccess
。
EC2 インスタンスのブートストラップ
次に、AWS Tools for Windows PowerShell をインストールした PC を使用して、EC2 インスタンスをブートストラップします。詳細は、Amazon PowerShell のウェブサイトを参照してください。承認されたユーザーを使用して AWS アカウントに接続し、次のコマンドを実行して次の操作を行います。
- EC2 インスタンスのプロビジョニング
- SQL と SSMS をインストールする
- Amazon S3 にアップロードした Adventureworks データベースを復元する
これを行うための前提条件は、次のとおりです。
- AWS アカウントへのアクセスが必要な IAM ユーザー
私のスクリプトは Amazon S3 から必要に応じて保存され、ダウンロードされます。
EC2 インスタンスのプロビジョニング
EC2 インスタンスのプロビジョニング後の PowerShell スクリプト。そのインスタンスが起動すると、スクリプトをダウンロードし SQL をインストールすることによって、インスタンスがブートストラップします。最後に、Adventureworks データベースを復元します。
$userDataString = @"
<powershell>
#Initialise New Volume
Initialize-Disk -Number 1 -PartitionStyle GPT
New-Partition -DiskNumber 1 -AssignDriveLetter -UseMaximumSize
Format-volume -DriveLetter D
#Bootstrap SQL Installation
Copy-S3Object -BucketName sql-backups-demo -Key scripts/Install-SQL-short.ps1 -LocalFile 'D:\\Install-SQL-short.ps1' -Region 'us-east-1'
D:\\Install-SQL-short.ps1
#Restore AdventureWorks from S3
Copy-S3Object -BucketName sql-backups-demo -Key scripts/Restore-AdventureWorks.ps1 -LocalFile 'D:\\Restore-AdventureWorks.ps1' -Region 'us-east-1'
D:\\Restore-AdventureWorks.ps1
</powershell>"@
$EncodeUserData = [System.Text.Encoding]::UTF8.GetBytes($userDataString)
$userData = [System.Convert]::ToBase64String($EncodeUserData)
#Get Latest AMI
$latestamiid = ((Get-EC2Image -Filter @{"Name"="platform";"Value"="windows"} | where {$_.name -like "*2016*english*full*base*"} | sort -Property CreationDate -Descending)[0]).imageid
$reservation = New-EC2Instance -ImageId $latestamiid -MinCount 1 -MaxCount 1 -InstanceType m4.large -KeyName "Prototyping(Virigina).pem" -SecurityGroup default -AvailabilityZone us-east-1a -Region us-east-1 -InstanceProfile_Name UA-Role-Demo -UserData $userData
#Get the instanceID of the running instance
$InstanceId = (Get-ec2instance -Filter @{Name = "reservation-id"; Values = $reservation.ReservationId}).RunningInstance[0].InstanceID
#Create New Volume for SQL Installation
$newvolumnid = New-EC2Volume -Size 100 -AvailabilityZone us-east-1a -VolumeType gp2 -Region us-east-1
#Check Volume is OK Before Launching Instance
$volumecontinue = $true
while($volumecontinue)
{
$volumnestatus = Get-EC2VolumeStatus -VolumeId $newvolumnid.VolumeId
if ($volumnestatus.VolumeStatus.Status -ne "ok")
{
Start-Sleep -Milliseconds 500
}
else
{
$volumecontinue = $false
}
}
Check Instance is Running before Mounting
$continue = $true
while($continue)
{
$status = Get-EC2InstanceStatus -InstanceId $InstanceId
if ($status.InstanceState.Name -eq "running")
{
Add-EC2Volume -InstanceId $InstanceId -VolumeId $newvolumnid.VolumeId -Device xvdf
$continue = $false
}
else
{
Start-Sleep -Milliseconds 500
}
}
SQL と SSMS をインストールする
このブログの記事では、SQL インストールをブートストラップする方法についても説明します。ご希望であれば、SQL Server で設定済みの Amazon Machine Image (AMI) を入手できます。ただし、インストールを完全に制御したい場合もあるかもしれません。
<powershell>
#Initialise New Volume
Initialize-Disk -Number 1 -PartitionStyle GPT
New-Partition -DiskNumber 1 -AssignDriveLetter -UseMaximumSize
Format-volume -DriveLetter D
#Bootstrap SQL Installation
Copy-S3Object
-BucketName sql-backups-demo
-Key scripts/Install-SQL-short.ps1
-LocalFile 'D:\\Install-SQL-short.ps1'
-Region 'us-east-1'
D:\\Install-SQL-short.ps1
#Restore AdventureWorks from S3
Copy-S3Object
-BucketName sql-backups-demo
-Key scripts/Restore-AdventureWorks.ps1
-LocalFile 'D:\\Restore-AdventureWorks.ps1'
-Region 'us-east-1'
D:\\Restore-AdventureWorks.ps1
</powershell>
この例では、ConfigurationFile.ini ファイルを使用して、サイレントインストールを実行しています。このアプローチの詳細は Microsoft documentation を参照してください。
SQL インストールのための PowerShell は以下の通りです。Microsoft から直接インストールできます。ConfigurationFile.ini
を自分のニーズに合わせ、Amazon S3 からダウンロードしました。
#Helper Functions
function Create-Folder {
Param ([string]$path)
if ((Test-Path $path) -eq $false)
{
Write-Host $path +' doesn''t exist.Creating now..'
New-Item -ItemType 'directory' -Path $path
}
}
function Download-File{
Param ([string]$src, [string] $dst)
(New-Object System.Net.WebClient).DownloadFile($src,$dst)
#Invoke-WebRequest $src -OutFile $dst
}
$setupFolder = 'D:\installs'
Create-Folder $setupFolder
Create-Folder $setupFolder'\sqlbi'
Create-Folder $setupFolder'\sqlbi\datasets'
Create-Folder $setupFolder'\sqlbi\installations'
$setupFolder = Join-Path -Path $setupFolder -ChildPath '\sqlbi\installations'
(Get-Content $setupFolder'\ConfigurationFile.ini').replace('USERNAMETBR', $env:computername+'\'+$env:username) | Set-Content $setupFolder\ConfigurationFile_local.ini
Copy-S3Object -BucketName sql-backups-demo -Key scripts/ConfigurationFile.ini -LocalFile $setupFolder'\ConfigurationFile.ini' -Region 'us-east-1'
Download-File 'https://go.microsoft.com/fwlink/?LinkID=799012' $setupFolder\SQLServer2016-SSEI-Expr.exe
Download-File 'https://download.microsoft.com/download/3/1/D/31D734E0-BFE8-4C33-A9DE-2392808ADEE6/SSMS-Setup-ENU.exe' $setupFolder\SSMS-Setup-ENU.exe
(Get-Content $setupFolder'\ConfigurationFile.ini').replace('USERNAMETBR', $env:computername+'\'+$env:username) | Set-Content $setupFolder\ConfigurationFile_local.ini
$localconfigfilepath = $setupFolder + '\ConfigurationFile_local.ini'
$argulist1 = '/ConfigurationFile=' + $localconfigfilepath
$argulist2 = '/MediaPath=' + $setupFolder
Write-Host 'Installing SQL Server..'
Start-Process -FilePath $setupFolder\SQLServer2016-SSEI-Expr.exe -ArgumentList $argulist1, $argulist2, '/IAcceptSqlServerLicenseTerms' -Wait
Write-Host 'Installing SSMS..'
Start-Process -FilePath $setupFolder\SSMS-Setup-ENU.exe -ArgumentList '/install','/passive' -Wait
Add-PSSnapin SqlServerCmdletSnapin* -ErrorAction SilentlyContinue
Import-Module SQLPS -WarningAction SilentlyContinue
Write-Host 'Installation completed.'
Amazon S3 にアップロードした Adventureworks データベースを復元する
SQL のインストールが完了したら、復元を実行する前に、SQL Server サービスが実行中であることを確認するクエリを実行します。Brenton Blawat 氏による優れた PowerShell スクリプトでは、一定期間にわたってサービスを使用する前に、そのサービスのクエリを続ける方法を解説しています。
PowerShell コマンドは、次のとおりです。
unction Restore-Database
{
# Create Data locations
$path = "D:\DATA\"
If(!(test-path $path))
{
New-Item -ItemType Directory -Force -Path $path
}
#Load Assemblies Required for Restore
[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended") | Out-Null
[Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo") | Out-Null
[Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoEnum") | Out-Null
#Create server object and set timeout to 0 to avoid restore time out
$server = New-Object ("Microsoft.SqlServer.Management.Smo.Server") $dbInstance
$server.ConnectionContext.StatementTimeout = 0
$MachineName = (Get-WmiObject -Class Win32_ComputerSystem -Property Name).Name + '\SQLEXPRESS
$RelocateData = New-Object Microsoft.SqlServer.Management.Smo.RelocateFile("AdventureWorks_Data", "D:\DATA\AdventureWorks_Data.mdf")
$RelocateLog = New-Object Microsoft.SqlServer.Management.Smo.RelocateFile("AdventureWorks_Log", "D:\DATA\AdventureWorks_Log.ldf")
$file = New-Object Microsoft.SqlServer.Management.Smo.RelocateFile($RelocateData,$RelocateLog)
$myarr=@($RelocateData,$RelocateLog)
#Restore Database
$backupfile1 = "D:\Backups\Adventureworks1.bak"
$backupfile2 = "D:\Backups\Adventureworks2.bak"
$backupfile3 = "D:\Backups\Adventureworks3.bak"
Restore-SqlDatabase -ServerInstance $MachineName -Database AdventureWorks -BackupFile $backupfile1,$backupfile2,$backupfile3 -RelocateFile $myarr
}
# This Function Will Query A Dependancy Service and wait until the timer expires OR for the service to start.
function QueryService { param($Service,$timer1)
$success = ""
write-host "Waiting on $Service Service..."
# Create a for loop to INC a timer Every Second
for ($b=1; $b -lt $timer1; $b++) {
{
.......(完全なスクリプトについては、Brenton Blawat 氏の元のコードを参照してください)
}
# Start-Sleep is available for the write-progress.Its value is in seconds.
start-sleep 1
}
# The script will now stop as the above loop has meet its time criteria and the success is not set to yes.time has expired.
if ($success -ne "yes") {
# Stop the Script
BREAK
}
Else
{
#Restore Database
Restore-Database
}
}
$region = "us-east-1"
# The name of your S3 Bucket
$bucket = "sql-backups-demo"
# The folder in your bucket to copy, including trailing slash.Leave blank to copy the entire bucket
$keyPrefix = "SQLServer/Backups/"
# Create Download locations
$path = "D:\Backups\"
If(!(test-path $path))
{
New-Item -ItemType Directory -Force -Path $path
}
#Download Database from S3
$objects = Get-S3Object -BucketName $bucket -KeyPrefix $keyPrefix -Region $region
foreach($object in $objects) {
$localFileName = $object.Key -replace $keyPrefix, ''
if ($localFileName -ne '') {
$localFilePath = Join-Path $path $localFileName
Copy-S3Object -BucketName $bucket -Key $object.Key -LocalFile $localFilePath -Region $region
}
}
#Begin Restore Process, check if SQL is up and running first give it 2 hours to standup
QueryService 'MSSQL$SQLEXPRESS' "7200"
まとめ
このブログ記事では、SQL Server で EC2 インスタンスを起動しブートストラップする方法を解説しました。インストールが完了したら、Amazon S3 から SQL Server にバックアップをダウンロードし復元する方法もお話ししています。データベースの復元を実行しようとする前に、サービスが実行されていて、それが待機していることを確認する方法についても、簡単に説明しました。Adventureworks データベースのサイズはそれほど必要ではありませんが、データベースバックアップをストライプしました。その結果、Amazon S3 へのアップロード用のオンプレミスファイルのバックアップ時間を短縮し、ブートストラップ中に Amazon EC2 インスタンスにダウンロードできました。
ここでは、SQL Server データベースを AWS に移行する方法や、本番環境へのカットオーバーを実行する方法についてのアイデアを、簡単ですがご紹介できたかと思います。
下の欄で、ご意見やご質問をお待ちしています。
著者について
Stefan Minhas はアマゾン ウェブ サービスのシニアコンサルタントです。 Global Microsoft Specialty Practice に関する Subject Matter Expert で、プロフェッショナルサービスコミュニティ全体で専門技術を開発し成長できるように指導と技術支援を行っています。