Резервное копирование из сервера AWS EC2 в хранилище S3

Опубликовано: 30.12.2016
Автор: Виталий Бочкарев
Поддержать автора статьи по этой ссылке

Для тех кто пользуется облачным сервисом AWS (Amazon Web Services) я предлагаю следующую модель резервного копирования:
1. Создание локальных резервных копий в определенную папку сервера в сервисе EC2 (архивирование ZIP, процедуры SQL Backup и т.п.).
2. Копирование содержимого папки бэкапа в хранилище сервиса S3 (Powershell скрипт с использованием команд AWS CLI).
3. Проверка содержимого облачного хранилища S3 на предмет устаревших архивных копий (Powershell скрипт с использованием команд AWS CLI).

Ниже приведен Powershell скрипт, который использует команды AWS CLI (необходимо установить эти утилиты с сайта AWS). Этот скрипт получает входные данные: регион AWS, имя букета S3, имя служебного профиля (пользователя), пароль и секретную фразу служебного пользователя, папку с локальными резервными копиями, время жизни резервных копий в облаке. Скрипт подключается к указанному букету в облаке S3 и переносит содержимое локальной папки резервных копий в папку с именем сервера в облачном хранилище (используется фильтр по расширению файлов *.bak). После переноса всех файлов скрипт сравнивает даты создания файлов в облаке с текущей датой и удаляет файлы, у которых возраст больше заданного в начальных условиях.

# Input data
$ServerName = $env:computername
$S3Region = "eu-central-1"
$S3BucketName = "my-backup-bucket"
$S3Profile = "my_backup"
$S3ProfileAccessKey = "ABCD1234ABCD1234ADCB"
$S3ProfileSecretKey = "abcd1234abcd1234abcd1234abcd1234abcd1234abc"
$LocalFolder = "C:\SQLBackup"
$KeepAge = 14

# Calculated data
$Destination = "s3://" + $S3BucketName + "/" + $ServerName
$CurrentDate = Get-Date
$ScriptFolder = $MyInvocation.MyCommand.Path.SubString(0,($MyInvocation.MyCommand.Path.Length - `
 $MyInvocation.MyCommand.Name.Length))
$LogFile = $ScriptFolder + (Get-Date -format yyyy_MM_dd) + "_" + `
 $MyInvocation.MyCommand.Name.SubString(0,($MyInvocation.MyCommand.Name.Length - 4)) + ".log"

# Log-file creation
Out-File -FilePath $LogFile

Function Write-ScriptLog {
 Param(
   [CmdletBinding()]
   [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
   [String]$Message,
   [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
   [String]$LogFile
 )
 Process {
   $LogMessage = Get-Date -uformat "%d.%m.%Y %H:%M:%S"
   $LogMessage += "`t"
   $LogMessage += $Message
   $LogMessage | Out-File -FilePath $LogFile -Append
 }
}

Write-ScriptLog -LogFile $LogFile -Message ($MyInvocation.MyCommand.Name + " started")
Write-ScriptLog -LogFile $LogFile -Message ("=== Input data ===")
Write-ScriptLog -LogFile $LogFile -Message ("Server Name = $ServerName")
Write-ScriptLog -LogFile $LogFile -Message ("S3 Region = $S3Region")
Write-ScriptLog -LogFile $LogFile -Message ("S3 Bucket Name = $S3BucketName")
Write-ScriptLog -LogFile $LogFile -Message ("S3 Profile = $S3Profile")
Write-ScriptLog -LogFile $LogFile -Message ("Local Folder = $LocalFolder")
Write-ScriptLog -LogFile $LogFile -Message ("Keep Age = $KeepAge")

# S3 profile creation
# Run it once to create service profile for windows account which executes the script
#Set-AWSCredentials -AccessKey $S3ProfileAccessKey -SecretKey $S3ProfileSecretKey -StoreAs $S3Profile

# Initializing S3 profile
Set-DefaultAWSRegion -Region $S3Region
Initialize-AWSDefaults -ProfileName $S3Profile

# Get local backup files
Write-ScriptLog -LogFile $LogFile -Message ("=== Processing local files ===")
$LocalFiles = Get-ChildItem $LocalFolder -Filter *.bak
Write-ScriptLog -LogFile $LogFile -Message ($LocalFiles.Count.ToString() + `
 " files are found for moving to AWS S3")


# Move local backup files to S3 bucket
ForEach ($LocalFile in $LocalFiles) {
 $FileName = $LocalFile.Name
 Write-ScriptLog -LogFile $LogFile -Message ($LocalFile.Name + " will be moved to AWS S3")
 Try {
   Write-S3Object -BucketName $S3BucketName -Key ($ServerName + "/" + $FileName) `
     -File ($LocalFile.DirectoryName + "\" + $LocalFile.Name) -StoredCredentials $S3Profile `
     -Region $S3Region
   Remove-Item -Path ($LocalFile.DirectoryName + "\" + $LocalFile.Name)
   Write-ScriptLog -LogFile $LogFile -Message ($LocalFile.Name + " is moved to $Destination/$FileName")
 }
 Catch {
   $ErrorMessage = $_.Exception.Message
   Write-ScriptLog -LogFile $LogFile -Message ("Error: " + $ErrorMessage)
 }
}

# Get S3 bucket files
Write-ScriptLog -LogFile $LogFile -Message ("=== Processing remote files ===")
$S3Objects = Get-S3Object -BucketName $S3BucketName -KeyPrefix $ServerName -StoredCredentials $S3Profile `
 -Region $S3Region
Write-ScriptLog -LogFile $LogFile -Message (($S3Objects.Count - 1).ToString() + `
 " files are found in AWS S3")

# Deleting old files from S3 bucket
ForEach ($S3Object in $S3Objects) {
 # Get child objects of the server folder
 If  ($S3Object.Key.Length -gt ($ServerName.Length + 1)) {
   $S3ObjectAge = (Get-Date) - $S3Object.LastModified
   # Get out-of-date object
   If ($S3ObjectAge.Days -gt $KeepAge) {
     Write-ScriptLog -LogFile $LogFile -Message ($S3Object.Key.Substring(($ServerName.Length + 1)) + `
       " is out of date (" + $S3ObjectAge.Days.ToString() + " days old) and will be deleted")
     Remove-S3Object -BucketName $S3BucketName -Key $S3Object.Key -StoredCredentials $S3Profile `
       -Region $S3Region -Force
     Write-ScriptLog -LogFile $LogFile -Message ($S3Object.Key.Substring(($ServerName.Length + 1)) + `
       " is deleted from AWS S3")
   }
 }
}

Write-ScriptLog -LogFile $LogFile -Message ($MyInvocation.MyCommand.Name + " stopped")

Этот скрипт нужно поставить в расписание Windows на ежедневный запуск после выполнения локальных операций резервного копирования. Для выполнения скрипта рекомендуется использовать командный файл, приведенный ниже.

powershell.exe "%~DP0Process-S3Backup.ps1"

Скрипт рекомендуется запускать от имени SYSTEM, поэтому в теле скрипта есть закомментированный раздел S3 profile creation, который создает профиль служебного пользователя AWS в текущем профиле пользователя Windows. То есть для создания профиля AWS в профиле SYSTEM нужно раскомментировать строку Set-AWSCredentials -AccessKey $S3ProfileAccessKey -SecretKey $S3ProfileSecretKey -StoreAs $S3Profile и запустить однократно скрипт через расписание Windows от имени SYSTEM, после чего закомментировать строку.

Помимо указанных скриптов необходимо так же настроить безопасность на букете S3. Для этого нужно знать ARN служебного пользователя (можно посмотреть в профиле пользователя в разделе IAM). Политика доступа к букету S3 настраивается в свойствах букета в разделе Permissions - Edit bucket policy.

{
 "Version": "2016-12-17",
 "Id": "Policy1481272123456",
 "Statement": [
   {
     "Sid": "Stmt1481272123456",
     "Effect": "Allow",
     "Principal": {
       "AWS": "arn:aws:iam::444222123456:user/my_backup"
     },
     "Action": "s3:*",
     "Resource": [
       "arn:aws:s3:::my-backup-bucket",
       "arn:aws:s3:::my-backup-bucket/*"
     ]
   }
 ]
}