Это третья и последняя часть в серии из трех частей, где мы создаем Thoupformation Tempate для того, что мне нравится думать как довольно типичная среда: одно виртуальное частное облако, разбитое на две подчинки и включает в себя два экземпляра. Пока мы покрыли …
- Часть 1: Мы настроем VPC и подсету
- Часть 2: Мы настроем некоторые ведра, группы и привилегии, а также наши случаи
В этой последней части мы поместили наши резервные работы на место и завершаем шаблон. Мы рассмотрели много синтаксиса CloudFormation и действительно использовали этот инструмент, после этой последней части в серии у нас будет все необходимое для поддержки вашего следующего проекта. Если вы хотите просмотреть полный шаблон, Он доступен на GitHub Анкет
Настройка Lambda для снимка (и удержания)
Что, когда и как сделать резервное копирование ваших экземпляров и данных, является сложным предметом и охватывает много мест. Для этого проекта мы собираемся иметь дело только с минимумом: выполнение ночных снимков ваших изображений и сохранение этих снимков в соответствии с графиком. Это получит надежное изображение хранилища вашего сервера во время резервного копирования, но может не гарантировать, что ваши файлы базы данных находятся в согласованном состоянии. Мы не проходим процесс здесь, но я советую настроить задание на вашем сервере базы данных, которая записывает резервную копию базы данных на диск до Задача снимка работает. Если вы когда -нибудь восстановите снимок, у вас будет постоянная резервная копия базы данных, вместе с ним.
Мы будем использовать Amazon Lambda Служба для выполнения снимка и отбраковывать старые изображения снимков. Поскольку сценарий Lambda работает независимо от наших экземпляров, мы можем быть уверены, что он всегда будет работать вовремя, и мы можем контролировать успех или неудачу резервного работы самостоятельно: нам не нужно предоставлять административный доступ к нашим случаям в порядке. выполнить или контролировать процесс резервного копирования. Также очень приятно, что мы можем получить резервную работу и начать все, запустив все из нашего сценария CloudFormation.
Ежедневная работа снимка
Первое, что нам нужно сделать, — это настроить новую роль для нашей работы резервного копирования, эта роль будет иметь возможность управлять снимками и записывать журналы для облачной информации (чтобы мы могли отслеживать эти резервные задания).
TutorialLambdaBackupRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyName: TutorialLambdaBackupRolePolicy
PolicyDocument:
Statement:
- Effect: Allow
Action:
- ec2:CreateTags
- ec2:CreateSnapshot
- ec2:DeleteSnapshot
- ec2:Describe*
- ec2:ModifySnapshotAttribute
- ec2:ResetSnapshotAttribute
- xray:PutTraceSegments
- xray:PutTelemetryRecords
- xray:GetSamplingRules
- xray:GetSamplingTargets
- xray:GetSamplingStatisticSummaries
- logs:*
Resource: "*"
Мы видели все эти вещи раньше: мы создаем новую роль, и мы связываем службу AWS (в данном случае Lambda) и Предполагаемое действие, позволяя тому, кто мы назначаем роль, способность «принимать» ее. Затем мы создаем новую политику для роли, которая позволяет тому, кто принимает эту роль, доступ к управлению снимками для нашей учетной записи, а также о возможности записать в журналы CloudFormation и Amazon рентген (отслеживание) сервис Анкет
Далее мы создаем наш процесс лямбда, я сейчас оставлю код И мы рассмотрим это дальше. Есть лучшие способы получить сценарий резервного копирования в лямбду Но все они включают в себя добавление много сложности к этой статье. Когда у вас есть время и энергия, вы можете изучить это самостоятельно (или, может быть, я напишу еще одну статью). В любом случае, наличие встроенного сценария работает на данный момент.
TutorialCreateBackupLambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: ebs-snapshots-create
Description: Create EBS Snapshots
Handler: index.handler
Role: !GetAtt TutorialLambdaBackupRole.Arn
Environment:
Variables:
REGIONS: !Ref AWS::Region
Runtime: python3.6
Timeout: 90
TracingConfig:
Mode: Active
Code:
ZipFile: |
...
code ellided for clarity
...
Мы используем AWS:: Lambda:: function Ресурс, чтобы предоставить нашу новую функцию Lambda, мы присваиваем ему имя, описание и рассказываем, как вызвать себя. Мы собираемся написать резервную работу в Питон Потому что он делает код простым, и почти все знают Python. ; - И затем сам код Python.
import os
from datetime import datetime, timedelta
import boto3
def handler(event, context):
ec2_client = boto3.client("ec2")
env_regions = os.getenv("REGIONS", None)
today = datetime.today()
total_created = 0
if env_regions:
regions = ec2_client.describe_regions(RegionNames = env_regions.split(","))
else:
return total_created
# loop through all of our regions
for region in regions.get("Regions", []):
regionName = region["RegionName"]
print("Checking for volumes in region %s" % (regionName))
ec2_client = boto3.client("ec2", region_name = region["RegionName"])
# query for volumes with matching tags that are in use
result = ec2_client.describe_volumes(Filters = [
{"Name": "status", "Values": ["in-use"]}
])
if not result:
print("No matching EBS volumes with tag 'Client' = '%s' and tag 'Project' = '%s' in region %s" % (clientTag, projectTag, regionName))
return total_created
for volume in result["Volumes"]:
volume_name = volume["VolumeId"]
volume_description = "Created by ebs-snapshots-create"
# check the volume for matching tags
if "Tags" in volume:
for tag in volume["Tags"]:
if tag["Key"] == "Name":
volume_name = tag["Value"]
elif tag["Key"] == "Description":
volume_description = tag["Value"]
# daily
print("Creating snapshot for EBS volume %s" % (volume["VolumeId"]))
result = ec2_client.create_snapshot(
VolumeId = volume["VolumeId"],
Description = volume_description
)
ec2_resource = boto3.resource("ec2", region_name = region["RegionName"])
snapshot_resource = ec2_resource.Snapshot(result["SnapshotId"])
snapshot_resource.create_tags(Tags = [
{"Key": "Name", "Value": volume_name},
{"Key": "Description", "Value": volume_description},
{"Key": "Interval", "Value": "Daily"}
])
total_created += 1
# quarterly
if today.day == 1 and today.month % 4 == 0:
print("Creating quarterly snapshot for EBS volume %s" % (volume["VolumeId"]))
result = ec2_client.create_snapshot(
VolumeId = volume["VolumeId"],
Description = volume_description
)
ec2_resource = boto3.resource("ec2", region_name = region["RegionName"])
snapshot_resource = ec2_resource.Snapshot(result["SnapshotId"])
snapshot_resource.create_tags(Tags = [
{"Key": "Name", "Value": volume_name},
{"Key": "Description", "Value": volume_description},
{"Key": "Interval", "Value": "Quarterly"}
])
total_created += 1
# annual
if today.day == 31 and today.month == 12:
print("Creating annual snapshot for EBS volume %s" % (volume["VolumeId"]))
result = ec2_client.create_snapshot(
VolumeId = volume["VolumeId"],
Description = volume_description
)
ec2_resource = boto3.resource("ec2", region_name = region["RegionName"])
snapshot_resource = ec2_resource.Snapshot(result["SnapshotId"])
snapshot_resource.create_tags(Tags = [
{"Key": "Name", "Value": volume_name},
{"Key": "Description", "Value": volume_description},
{"Key": "Interval", "Value": "Annual"},
])
total_created += 1
return total_created
У вас есть только так много символов, доступных для встроенного сценария Lambda, поэтому я сведен к минимуму комментарии. Сценарий прост, и код (ради ясности) не делает ничего сложного; Это должно быть легко следовать. По сути, он проверяет все регионы, которые мы предоставили в Регионы переменная среды А потом он проверяет этот регион на наличие экземпляров. Когда он находит один, он получает ручку на всех его объемах, а затем снимки каждый.
- Сначала создается ежедневный снимок, скрипт всегда делает этот снимок
- Если это первый день первого месяца квартала, то также сделан ежеквартальный снимок
- Если это последний день года, то мы делаем ежегодный снимок
Для каждого снимка создал наше сценарий устанавливает имя снимка, чтобы соответствовать имени тома и описание «Создано EBS-Snapshot-Create» (имя нашей функции Lambda). Он также устанавливает теги «клиент» и «проект» на каждом снимке, чтобы соответствовать значениям переменной среды.
Сценарий отслеживает, сколько снимков он создает, и возвращает это число, когда функция заканчивается. По пути мы также Печать Данные для стандарта: стандартное выход, и возвращаемое значение будет собрано и зарегистрировано при запуске скрипта.
Ежедневная работа по удержанию снимка
Мы не хотим накапливаться снимками неопределенно, давайте не будем забывать, что нас обвиняют в их хранении. ;-) Эта следующая работа обеспечит нашу политику удержания.
TutorialDeleteBackupLambda:
Type: AWS::Lambda::Function
Properties:
FunctionName: ebs-snapshots-delete
Description: Delete Old EBS Snapshots
Handler: index.handler
Role: !GetAtt TutorialLambdaBackupRole.Arn
Environment:
Variables:
REGIONS: !Ref AWS::Region
Runtime: python3.6
Timeout: 90
TracingConfig:
Mode: Active
Code:
ZipFile: |
...
code elided for clarity
...
Это почти точно так же, как последняя работа, помимо названия и описания. Реальная разница заключается в сценарии для функции Lambda. Сценарий рассчитывает дату отсечения для наших ежедневных снимков: семь дней. По мере того, как скрипт проверяет снимки для объемов, он проверит теги, которые мы разместили на снимках в задании резервного копирования, определяет «интервал». Ежедневные снимки старше нашей даты отсечения будут удалены, оставляя только семь самых последних ежедневных снимков.
Мы делаем то же самое для снимков квартала; Мы рассчитываем данные об сокращении, а затем проверяем все снимки, помеченные «интервалом» значением «квартального». Все снимки после отсечения удалены, оставляя нас с четырьмя последними снимками квартала.
Ежегодные снимки, которые мы оставляем как есть, мы будем начислять их по цене раз в год навсегда. Вы никогда не знаете, когда кто -то захочет выкопать старые, исторические данные.
import os
from datetime import datetime, timedelta
from dateutil import relativedelta
import boto3
def handler(event, context):
ec2_client = boto3.client("ec2")
date_format = "%Y-%m-%dT%H:%M:%S.%fZ"
daily_cutoff = (datetime.utcnow() - timedelta(days = 7)).strftime(date_format)
env_regions = os.getenv("REGIONS", None)
today = datetime.today()
total_deleted = 0
if env_regions:
regions = ec2_client.describe_regions(RegionNames = env_regions.split(","))
else:
return total_created
for region in regions.get("Regions", []):
regionName = region["RegionName"]
print("Checking for snapshots in region %s" % (regionName))
# query for Daily snapshots with matching tags
result = ec2_client.describe_snapshots(Filters = [
{"Name": "tag:Interval", "Values": ["Daily"]}
])
if not result:
print("No matching Daily snapshots with tag 'Client' = '%s' and tag 'Project' = '%s' in region %s" % (clientTag, projectTag, regionName))
return total_deleted
for snapshot in result["Snapshots"]:
snapshot_time = snapshot["StartTime"].strftime(date_format)
snapshot_id = snapshot["SnapshotId"]
if daily_cutoff > snapshot_time:
snapshot_name = ""
if "Tags" in snapshot:
for tag in snapshot["Tags"]:
if tag["Key"] == "Name":
snapshot_name = tag["Value"]
print("Deleting Daily EBS snapshot ID = %s, Name = %s" % (snapshot_id, snapshot_name))
ec2_client.delete_snapshot(SnapshotId = snapshot_id)
total_deleted += 1
# query for quarterly snapshots with matching tags
if today.day == 1 and today.month % 4 == 1:
quarterly_cutoff_raw = datetime.utcnow() - relativedelta.relativedelta(months = 15) # keep five quarters
quarterly_cutoff = quarterly_cutoff_raw.strftime(date_format)
result = ec2_client.describe_snapshots(Filters = [
{"Name": "tag:Interval", "Values": ["Quarterly"]}
])
if not result:
print("No matching Quarterly snapshots with tag 'Client' = '%s' and tag 'Project' = '%s' in region %s" % (clientTag, projectTag, regionName))
return total_deleted
for snapshot in result["Snapshots"]:
snapshot_time = snapshot["StartTime"].strftime(date_format)
snapshot_id = snapshot["SnapshotId"]
if quarterly_cutoff > snapshot_time:
snapshot_name = ""
if "Tags" in snapshot:
for tag in snapshot["Tags"]:
if tag["Key"] == "Name":
snapshot_name = tag["Value"]
print("Deleting Quarterly EBS snapshot ID = %s, Name = %s" % (snapshot_id, snapshot_name))
ec2_client.delete_snapshot(SnapshotId = snapshot_id)
total_deleted += 1
return total_deleted
С этими двумя функциями, добавленными в наш шаблон облачной информации, мы почти готовы к работе. Все, что нам нужно сделать, это звонить на эти работы каждую ночь.
Правила, чтобы вызвать нашу работу
Мы хотим выполнять нашу работу каждую ночь, сначала резервная работа, а затем работа по удержанию, которая отбирает старые снимки. К счастью для нас CloudWatch позволит вам создать «Правило», которое запускается по графику , мы можем создать свое собственное правило, которое начнет наши резервные работы. AWS:: Events:: Правило Ресурс может быть использован, чтобы сделать именно это.
TutorialPerformBackupRule:
Type: AWS::Events::Rule
Properties:
Description: Triggers our backup lambda script
ScheduleExpression: cron(30 4 * * ? *)
State: ENABLED
Targets:
- Arn: !GetAtt TutorialCreateBackupLambda.Arn
Id: TutorialBackupRule
TutorialDeleteBackupRule:
Type: AWS::Events::Rule
Properties:
Description: Triggers our backup delete lambda script
ScheduleExpression: cron(45 4 * * ? *)
State: ENABLED
Targets:
- Arn: !GetAtt TutorialDeleteBackupLambda.Arn
Id: TutorialBackupRule
Эти две задания используют знакомые Синтаксис стиля Крона , мы резервные копии в 4:00, а затем удаляем старые снимки в 4:45 каждый день. Каждое правило имеет указатель на Arn Функции Lambda это вызывает.
При всей нашей работе мы должны предоставить наши правила разрешения для вызова наших функций Lambda с помощью AWS:: Lambda:: разрешение ресурс.
TutorialPermissionCreate:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt TutorialCreateBackupLambda.Arn
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
SourceArn: !GetAtt TutorialPerformBackupRule.Arn
TutorialPermissionDelete:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt TutorialDeleteBackupLambda.Arn
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
SourceArn: !GetAtt TutorialDeleteBackupRule.Arn
Добавляем Invokefunction Разрешение на наши два триггера Cloudfront, чтобы им было разрешено вызывать наши функции Lambda. При этом все позаботятся о наших резервных копиях.
Шаблон завершается: вывод
Последний бит любого шаблона CloudFront предоставляется выходные данные, эти данные будут предоставлены в качестве объекта JSON. Вы можете предоставить данные Thiss для другого процесса, который дважды проверяет, что ресурсы были созданы или выполняют дополнительные задачи. Вот наш выход вывода …
Outputs:
WebServer:
Description: A reference to the web server
Value: !Ref TutorialWebServer
DatabaseServer:
Description: A reference to the database server
Value: !Ref TutorialDatabaseServer
BackupS3Bucket:
Description: S3 Bucket used to backup data
Value: !Sub |
https://${TutorialBackupS3Bucket.DomainName}\
Здесь мы возвращаем ссылки на наши два экземпляра сервера и наше ведро S3. В этом разделе мы могли бы перечислить больше элементов, возможно, наша ссылка на наш VPC. На данный момент мы оставим это коротким и по сути.
Вот, как будто! Это последний произведение в серии! Вы можете моделировать на шаблоне, который мы собрали здесь, и начать предоставлять ресурсы для облачной информации с немного большей скоростью и уверенностью. ;-D
Оригинал: «https://dev.to/cmiles74/provision-pragmatically-and-programmatically-with-cloudformation-backup-jobs-and-wrap-up-4plp»