Рубрики
Uncategorized

Предоставление прагматически и программно с CloudFormation: Часть 3, Задача резервного копирования и завершение

Это третья и последняя часть в серии из трех частей, где мы создаем Thoupformation Tempate … Tagged с облачной информацией, EC2, AWS, DevOps.

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