Это третья и последняя часть в серии из трех частей, где мы создаем 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»