Высокая доступность приложения-службы Windows с несколькими азами в AWS

#c# #amazon-web-services #windows-services

#c# #amazon-веб-сервисы #windows-службы

Вопрос:

У меня есть C#.NET приложение-служба Windows, работающее на Windows EC2 в AWS. Каков наилучший шаблон для обеспечения высокой доступности с использованием Multi-AZ (например, если зона доступности выйдет из строя, как 2-я служба Windows узнает о запуске?)

Ответ №1:

Если служба Windows работает <15 минут, преобразуйте ее в лямбда-выражение — таким образом, она будет масштабироваться в миллисекундах, платите только за используемое время выполнения (не 24×7), забудьте об исправлениях. Если нет, вы можете рассмотреть пакетную службу или, наконец, использовать EC2s.

Вы захотите автоматизировать развертывание всего в AWS с помощью AWS SDK, AWS CLI, AWS CDK или Cloud Formation.

CloudFormation проще всего настроить, в приведенном здесь примере создается один EC2 в ASG с минимальным и максимальным значением, равным 1. Это означает, что если экземпляр EC2 выйдет из строя, ASG обнаружит это (это называется событием масштаба), и поскольку минимальное значение равно 1, он запустит другой экземпляр.

Я включил в UserData пример загрузки скрипта powershell, который может установить вашу службу, это занимает слишком много времени, я бы порекомендовал вам создать AMI и использовать его в шаблоне CloudFormation (CFN). Я бы рекомендовал вам загрузить этот шаблон CFN и попробовать завершить экземпляр ad, через минуту ASG запустит новый. Единственный способ — удалить ASG, чтобы удалить этот единственный экземпляр!

Замените «YourWindowsService» именем вашей службы и укажите VPC:

 AWSTemplateFormatVersion: 2010-09-09
Description: YourWindowsService deployment script

Parameters:
  Environment:
    Description: The environment we deploy to
    Type: String
    Default: 'nonprod'
    AllowedValues: ['nonprod','prod']
  KeyPairName:
    Description: >-
      Mandatory. Enter a Public/private key pair. If you do not have one in this region,
      please create it before continuing
    Type: 'AWS::EC2::KeyPair::KeyName'
    Default: YourKeyPairName
  Subnet1ID:
    Description: 'ID of the subnet 1 for auto scaling group'
    Type: 'AWS::EC2::Subnet::Id'
  Subnet2ID:
    Description: 'ID of the subnet 2 for auto scaling group'
    Type: 'AWS::EC2::Subnet::Id'
  RemoteAccessCIDR:
    AllowedPattern: >-
      ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/x
    Description: Allowed CIDR block for external SSH access to YourWindowsServices IIS WS
    Type: String
    Default: 0.0.0.0/0
  AppName:
    Type: String
    Description: Application Name
    Default: "YourWindowsService"
  InstanceProfile:
    Type: String
    Default: YourIAMProvisioningProfileOrEc2FullAccess
  AMI:
    Type: 'AWS::EC2::Image::Id'


Mappings:
  EnvironmentToInstanceType:    
    nonprod:
      instanceType: t3.medium
    prod:
      instanceType: t3.large

  EnvironmentToVPC:
    nonprod:
      VPC: vpc-................
    prod:
      VPC: something.cant.be.null

  DisableTerminate:
    prod:
      YesorNo: 'false'
    nonprod:
      YesorNo: 'false'

Conditions:
  CreateNonProdResources: !Equals [ !Ref Environment, prod ]
  CreateProdResources: !Equals [ !Ref Environment, nonprod ]

Resources:
  YourWindowsServiceMainLogGroup:
    Type: 'AWS::Logs::LogGroup'
  SSHMetricFilter:
    Type: 'AWS::Logs::MetricFilter'
    Properties:
      LogGroupName: !Ref YourWindowsServiceMainLogGroup
      FilterPattern: ON FROM USER PWD
      MetricTransformations:
        - MetricName: SSHCommandCount
          MetricValue: 1
          MetricNamespace: !Join
            - /
            - - AWSQuickStart
              - !Ref 'AWS::StackName'
  YourWindowsServiceAutoScalingGroup:
    Type: 'AWS::AutoScaling::AutoScalingGroup'
    Properties:
      LaunchConfigurationName: !Ref YourWindowsServiceLaunchConfiguration
      AutoScalingGroupName:  !Join
            - '.'
            - - !Ref 'AWS::StackName'
              - 'ASG'
      VPCZoneIdentifier:
        - !Ref Subnet1ID
        - !Ref Subnet2ID
      MinSize: 1
      MaxSize: 1
      Cooldown: '300'
      DesiredCapacity: 1
      Tags:
        - Key: Name
          Value: YourWindowsPCName
          PropagateAtLaunch: 'true'
          
  YourWindowsServiceLaunchConfiguration:
    Type: 'AWS::AutoScaling::LaunchConfiguration'
    Properties:
      AssociatePublicIpAddress: 'false'
      PlacementTenancy: default
      KeyName: !Ref KeyPairName
      ImageId: !Ref AMI

      SecurityGroups:
        - Fn::If: [CreateNonProdResources, !Ref YourWindowsServiceNonProdSecurityGroup, !Ref "AWS::NoValue"]
        - Fn::If: [CreateProdResources, !Ref YourWindowsServiceProdSecurityGroup, !Ref "AWS::NoValue"]

      IamInstanceProfile: !Ref InstanceProfile
      InstanceType: !FindInMap [EnvironmentToInstanceType, !Ref 'Environment', instanceType]
      UserData:
        !Base64 |
          <powershell>
          #setup an install folder
          $path = "C:temp"
          If(!(test-path $path))
          {
            New-Item -ItemType Directory -Force -Path $path
          }
          cd $path

          #using S3 (ie the AWS API) fetch the powershell install script and execute it
          $S3BucketName = "a-unique-bucket"
          $bootstrap = "install-env.ps1"
          $script = ($path   $bootstrap)
          Set-DefaultAWSRegion -Region ap-southeast-2
          Copy-S3Object -BucketName $S3BucketName -key $bootstrap -LocalFile ($path   $bootstrap)
          amp; $script
          </powershell>
          <persist>true</persist>
          <runAsLocalSystem>true</runAsLocalSystem>

  YourWindowsServiceNonProdSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Condition: CreateNonProdResources
    Properties:
      GroupDescription: Enables access to YourWindowsService
      GroupName: !Join
            - '.'
            - - !Ref 'AWS::StackName'
              - 'SG'
      VpcId: !FindInMap [EnvironmentToVPC, !Ref 'Environment', VPC]
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '22'
          ToPort: '22'
          CidrIp: !Ref RemoteAccessCIDR
        - IpProtocol: icmp
          FromPort: '-1'
          ToPort: '-1'
          CidrIp: !Ref RemoteAccessCIDR
        - IpProtocol: tcp
          FromPort: '80'
          ToPort: '80'
          CidrIp: !Ref RemoteAccessCIDR
        - IpProtocol: tcp
          FromPort: '3389'
          ToPort: '3389'
          CidrIp: !Ref RemoteAccessCIDR
        - IpProtocol: udp
          FromPort: '3389'
          ToPort: '3389'
          CidrIp: !Ref RemoteAccessCIDR

  YourWindowsServiceProdSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Condition: CreateProdResources
    Properties:
      GroupDescription: Enables access to YourWindowsService
      GroupName: !Join
            - '.'
            - - !Ref 'AWS::StackName'
              - 'SG'
      VpcId: !FindInMap [EnvironmentToVPC, !Ref 'Environment', VPC]
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '22'
          ToPort: '22'
          CidrIp: !Ref RemoteAccessCIDR
        - IpProtocol: icmp
          FromPort: '-1'
          ToPort: '-1'
          CidrIp: !Ref RemoteAccessCIDR
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '80'
          ToPort: '80'
          CidrIp: !Ref RemoteAccessCIDR
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '3389'
          ToPort: '3389'
          CidrIp: !Ref RemoteAccessCIDR
        - IpProtocol: udp
          FromPort: '3389'
          ToPort: '3389'
          CidrIp: !Ref RemoteAccessCIDR
 

Комментарии:

1. Я бы дважды проверил, нужно ли мне использовать Lambda для замены моей службы Windows. Экземпляр EC2 может быть более дешевым вариантом, если мой код выполняется как рабочая служба. Например, если я использую выделенный лямбда-код объемом 2 ГБ памяти 24×7 при циклическом выполнении, это обойдется мне примерно в 86 долларов в месяц, в то время как EC2 t3a.small будет стоить 13,5 долларов в месяц. Однако Lambda отлично подходит для выполнения на основе событий.

Ответ №2:

Вы можете использовать механизм блокировки, чтобы предотвратить одновременную работу обоих экземпляров. Redis может быть хорошим вариантом, используя https://github.com/samcook/RedLock.net

Ответ №3:

Зона доступности в AWS — это группа центров обработки данных, необычно, что все центры обработки данных в AZ могут отключаться одновременно. Если вы согласны с тем, чтобы ваш экземпляр EC2 Windows оставался в single AZ, вы можете настроить автоматическое восстановление на вашем экземпляре EC2. Это восстановит ваш экземпляр EC2 на работоспособном хосте в случае повреждения существующего экземпляра EC2 или сервера.

На случай, если вы действительно хотите перейти через AZS в случае маловероятного отключения уровня AZ. Вы можете настроить автоматическое масштабирование EC2 с несколькими выбранными AZs. Вы можете сохранить минимальное и максимальное количество экземпляров равным 1. Таким образом, вы настраиваете автоматическое масштабирование для управления экземплярами EC2 в AZs, указывая при этом, чтобы он не создавал более 1 экземпляра одновременно. Если ваш экземпляр в существующей AZ становится недоступным и если вся AZ, в которой запущен ваш экземпляр, становится недоступной, служба автоматического масштабирования запустит ваш единственный экземпляр в одном из других AZ. Однако для службы Windows это перебор. Этот шаблон имеет смысл для нескольких веб-серверов, стоящих за средством балансировки нагрузки приложений. Как я уже сказал, AWS AZ — это группа из нескольких центров обработки данных, поэтому автоматического восстановления более чем достаточно для одного экземпляра EC2.