AWS 设计高可用程序架构——ECS Cloudformation

潘璞瑜
2023-12-01

依赖:本文需要了解AWS 架构设计基础知识

AWS Fargate

AWS Fargate 是可与 Amazon ECS 结合使用的技术,使您在运行容器时不必管理 Amazon EC2 实例的服务器或集群。使用 Fargate,您不必再预配置、配置或扩展虚拟机集群即可运行容器。这样一来,您就无需再选择服务器类型、确定扩展集群的时间和优化集群打包。

AWS ECS

Amazon Elastic Container Service(Amazon ECS)是一个高度可扩展的快速容器管理服务。您可以使用它来运行、停止和管理集群上的容器。使用 Amazon ECS,您的容器是在用于运行单个任务或服务内任务的任务定义中定义的。在此上下文中,服务是一种配置,您可以使用它在集群中同时运行和维护指定数量的任务。您可以在由 AWS Fargate 托管的无服务器基础设施上运行任务和服务。或者,为了更好地控制您的基础设施,您可以在托管的 Amazon EC2 实例集群上运行任务和服务。

创建IAM角色

附加策略

AmazonECSTaskExecutionRolePolicy
AmazonSSMFullAccess
EC2InstanceProfileForImageBuilderECRContainerBuilds
AmazonECS_FullAccess
AmazonS3FullAccess
AmazonSNSFullAccess
CloudWatchFullAccess

信任关系

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": [
                    "ecs-tasks.amazonaws.com",
                    "apigateway.amazonaws.com",
                    "ecs.amazonaws.com"
                ]
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

创建ECR

AWSTemplateFormatVersion: 2010-09-09
Parameters:
  Environment:
        Type: String
        Default: DEV
  EnvironmentName:
    Type: String
    Default: d
  CustomerName:
    Description: The name of the customer
    Type: String
    #TODO:
    Default: your-company-name
  ProjectName:
    Description: The name of the project
    Type: String
    #TODO:
    Default: your-project-name
Resources:
  Repository:
    Type: AWS::ECR::Repository
    Properties:
      RepositoryName:
        !Join [ "-", [ !Ref CustomerName, !Ref ProjectName, your-Repository-name, !Ref EnvironmentName, "ecr", ], ]
      Tags:
        - Key: ApplName
          Value: your-app-name

创建Cluster

AWSTemplateFormatVersion: 2010-09-09
Parameters:
  Environment:
    Type: String
    Default: DEV
  EnvironmentName:
    Type: String
    Default: d
  CustomerName:
    Description: The name of the customer
    Type: String
    #TODO:
    Default:  your-company-name
  ProjectName:
    Description: The name of the project
    Type: String
    #TODO:
    Default: your-project-name
  ContainerInsights:
    Type: String
    AllowedValues:
      - enabled
      - disabled
    #HACK:Development environment is disabled, production environment is enabled
    Default: disabled
Resources:
  Cluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterSettings:
        - Name: containerInsights
          Value: !Ref ContainerInsights
      ClusterName: !Join [ '-', [ !Ref CustomerName, !Ref ProjectName, !Ref EnvironmentName, 'cluster'] ]
      Tags:
        - Key: ApplName
          Value: your-app-name

创建ECS

AWSTemplateFormatVersion: 2010-09-09
Parameters:
  Environment:
    Type: String
    Default: DEV
  EnvironmentName:
    Type: String
    AllowedValues:
    Default: d
  SubProjectName:
    Description: The name of the sub project
    Type: String
    #TODO:
    Default: your-subsystem-name
  CustomerName:
    Description: The name of the customer
    Type: String
    #TODO:
    Default: your-company-name
  ProjectName:
    Description: The name of the project
    Type: String
    #TODO:
    Default: your-project-name
  ProjectType:
    Type: String
    #TODO:
    Default: your-domain-name
  HostedZoneName:
    Description: The name of route53 hosted
    Type: String
    #TODO:
    Default: dev.xxx.cn.
  ServiceSubnetIds:
    Description: The subnet ids of the ECS service
    Type: List<AWS::EC2::Subnet::Id>
    #TODO:
    Default: subnet-xxxxxxx,subnet-xxxxxxx
  VPCID:
    Type: AWS::EC2::VPC::Id
    #TODO:
    Default: vpc-xxxxxxxx
  FargateSecurityGroups:
    Description: security groups for fargate
    Type: List<AWS::EC2::SecurityGroup::Id>
    #TODO:
    Default: sg-xxxxxxxx
  S3BktName:
    Type: String
    Default: ""
  Cpu:
    Type: Number
    Default: 512
  Memory:
    Type: Number
    Default: 1024
  CFDomain:
    Type: String
    #TODO:
    Default: "https://www.xxxx.com.cn/"
  ALBSubnetsIds:
    Description: The subnet ids of the ALB
    Type:
      List<AWS::EC2::Subnet::Id>
    #TODO: 
    Default: subnet-xxxxxxx, subnet-xxxxxxxx
  AlbSecurityGroups:
    Description: Security group for ALB
    Type:
      List<AWS::EC2::SecurityGroup::Id>
      #TODO:
    Default: sg-xxxxxxxx
  AutoScalingRoleARN:
    Type: String
    Default: AWSServiceRoleForApplicationAutoScaling_xxxxxxx
  Certificate:
    Type:
      String
      #TODO:
    Default: xxxxx-xxxx-xxxx-xxxx-xxxxxxxxx
Resources:
  LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub '/ecs/${CustomerName}-${ProjectName}-${SubProjectName}-${EnvironmentName}-task'
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      Tags:
        - Key: ApplName
          Value: your-app-name
      Family:
        !Join [ "-", [ !Ref CustomerName, !Ref ProjectName, !Ref SubProjectName, !Ref EnvironmentName, "task", ], ]
      Cpu: !Ref Cpu
      Memory: !Ref Memory
      NetworkMode: awsvpc
      ExecutionRoleArn: !Sub arn:aws-cn:iam::${AWS::AccountId}:role/${CustomerName}-${ProjectName}-ecs-task-role-${EnvironmentName}-iamr
      TaskRoleArn: !Sub arn:aws-cn:iam::${AWS::AccountId}:role/${CustomerName}-${ProjectName}-ecs-task-role-${EnvironmentName}-iamr
      ContainerDefinitions:
        - Name:
            !Join [ "-", [ !Ref CustomerName, !Ref ProjectName, !Ref SubProjectName,  !Ref EnvironmentName, "container", ],  ]
          Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com.cn/${CustomerName}-${ProjectName}-${SubProjectName}-${EnvironmentName}-ecr:latest
          PortMappings:
            - ContainerPort: 80
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-region: !Ref 'AWS::Region'
              awslogs-group:  !Ref LogGroup
              awslogs-stream-prefix: ecs
          Environment:
            - Name: ECS_ROLE_ARN
              Value: !Sub arn:aws-cn:iam::${AWS::AccountId}:role/${CustomerName}-${ProjectName}-ecs-task-role-${EnvironmentName}-iamr
            - Name: S3_BKT_NAME
              Value: !Ref S3BktName
            - Name: CF_DOMAIN
              Value: !Ref CFDomain
      RequiresCompatibilities:
        - FARGATE
  Service:
    Type: AWS::ECS::Service
    DependsOn:
      - LoadBalancerListenerHTTP
      - LoadBalancerListenerHTTPS
    Properties:
      Tags:
        - Key: ApplName
          Value: your-app-name
      PropagateTags: TASK_DEFINITION
      ServiceName:
        !Join [ "-", [ !Ref CustomerName, !Ref ProjectName, !Ref SubProjectName, !Ref EnvironmentName, "service", ], ]
      Cluster: !Sub arn:aws-cn:ecs:${AWS::Region}:${AWS::AccountId}:cluster/${CustomerName}-${ProjectName}-${EnvironmentName}-cluster
      TaskDefinition: !Ref TaskDefinition
      DeploymentConfiguration:
        MinimumHealthyPercent: 100
        MaximumPercent: 200
        #CircuitBreaker Use
        DeploymentCircuitBreaker:
          Enable: True
          Rollback: True
      DesiredCount: 1
      LaunchType: FARGATE
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: DISABLED
          Subnets: !Ref ServiceSubnetIds
          SecurityGroups: !Ref FargateSecurityGroups
      LoadBalancers:
        - ContainerName: !Join [ "-",  [ !Ref CustomerName, !Ref ProjectName, !Ref SubProjectName, !Ref EnvironmentName, "container", ], ]
          ContainerPort: 80
          TargetGroupArn: !Ref TargetGroup
  AutoScalingTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Properties:
      MinCapacity: 1
      MaxCapacity: 5
      ResourceId: !Sub service/${CustomerName}-${ProjectName}-${EnvironmentName}-cluster/${Service.Name}
      ScalableDimension: ecs:service:DesiredCount
      ServiceNamespace: ecs
      RoleARN: !Sub arn:aws-cn:iam::${AWS::AccountId}:role/aws-service-role/ecs.application-autoscaling.amazonaws.com/${AutoScalingRoleARN}
  AutoScalingPolicyCPU:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    DependsOn: AutoScalingTarget
    Properties:
      PolicyName:
        !Join [ "-", [ !Ref CustomerName, !Ref ProjectName, !Ref SubProjectName, !Ref EnvironmentName, "autoscaling-policy-cpu", ], ]
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref AutoScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        PredefinedMetricSpecification:
          PredefinedMetricType: ECSServiceAverageCPUUtilization
        ScaleInCooldown: 10
        ScaleOutCooldown: 10
        TargetValue: 50
  AutoScalingPolicyMemory:
    Type: AWS::ApplicationAutoScaling::ScalingPolicy
    DependsOn: AutoScalingTarget
    Properties:
      PolicyName: !Join [ "-", [ !Ref CustomerName, !Ref ProjectName, !Ref SubProjectName, !Ref EnvironmentName, "autoscaling-policy-mem", ], ]
      PolicyType: TargetTrackingScaling
      ScalingTargetId: !Ref AutoScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        PredefinedMetricSpecification:
          PredefinedMetricType: ECSServiceAverageMemoryUtilization
        ScaleInCooldown: 10
        ScaleOutCooldown: 10
        TargetValue: 50
  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Join [ "-", [ !Ref CustomerName, !Ref ProjectName, !Ref SubProjectName, !Ref EnvironmentName, "group", ], ]
      HealthCheckEnabled: true
      HealthCheckIntervalSeconds: 60
      HealthCheckPath: /healthcheck
      HealthCheckPort: 80
      HealthCheckProtocol: HTTP
      Port: 80
      Protocol: HTTP
      HealthCheckTimeoutSeconds: 30
      HealthyThresholdCount: 3
      Tags:
        - Key: ApplName
          Value: your-app-name
      TargetType: ip
      VpcId: !Ref VPCID
  LoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Tags:
        - Key: ApplName
          Value: your-app-name
      Name: !Join [ "-", [ !Ref CustomerName, !Ref ProjectName, !Ref SubProjectName, !Ref EnvironmentName, "alb", ], ]
      Subnets: !Ref ALBSubnetsIds
      SecurityGroups: !Ref AlbSecurityGroups
  LoadBalancerListenerHTTP:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref LoadBalancer
      Port: 80
      Protocol: HTTP
      DefaultActions:
        - Type: "redirect"
          RedirectConfig:
            Protocol: "HTTPS"
            Port: 443
            Host: "#{host}"
            Path: "/#{path}"
            Query: "#{query}"
            StatusCode: "HTTP_301"
  LoadBalancerListenerHTTPS:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref TargetGroup
          Type: forward
      LoadBalancerArn: !Ref LoadBalancer
      Port: 443
      Protocol: HTTPS
      SslPolicy: ELBSecurityPolicy-TLS-1-2-2017-01
      Certificates:
        - CertificateArn: !Sub arn:aws-cn:acm:${AWS::Region}:${AWS::AccountId}:certificate/${Certificate}
  Rount53DNSRecord:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneName: !Ref HostedZoneName
      Comment: subsystem DNS.
      Name: !Sub ${ProjectName}${ProjectType}.${HostedZoneName}
      Type: A
      AliasTarget:
        HostedZoneId: !GetAtt "LoadBalancer.CanonicalHostedZoneID"
        DNSName: !GetAtt "LoadBalancer.DNSName"

 类似资料: