【问题标题】:creating a 'Target' for a cloudwatch event rule via cloudformation for a fargate launchtype task通过 cloudformation 为 fargate 启动类型任务创建 cloudwatch 事件规则的“目标”
【发布时间】:2019-02-11 23:46:18
【问题描述】:

我正在尝试在我的 CloudFormation 模板中创建一个计划任务(CloudWatch 事件规则),该模板将具有以下 EcsParameters:

EcsParameters:
        LaunchType: FARGATE
        NetworkConfiguration: 
          AwsVpcConfiguration:
            AssignPublicIp: !Ref PublicIpAssignment
            SecurityGroups:
              - !Ref EcsSecurityGroups
            Subnets:
              - !Ref SubnetName
        TaskCount: 1
        TaskDefinitionArn: !Ref TaskDefinitionOne

我的 ECS 集群是在 Fargate 而不是 EC2 上启动的,并且我没有运行服务(用例不需要长时间运行的进程,直接从事件规则中调度任务。)

每当我运行此模板(使用LaunchTypeNetworkConfiguration)时,堆栈创建都会失败,并出现以下错误:

遇到不支持的属性 NetworkConfiguration


作为替代方案,我也尝试从 AWS CLI 启动计划任务,但似乎网络配置和启动类型选项在那里也不可用:

参数验证失败: Targets[0].EcsParameters 中的未知参数:“LaunchType”,必须是以下之一:TaskDefinitionArn、TaskCount


根据 AWS 文档本身的 this page,我应该能够在 TargetsProperties 资源的 EcsParameters 部分中指定 LaunchTypeNetworkConfiguration

有什么我可以尝试的方法吗?

【问题讨论】:

    标签: amazon-web-services amazon-ecs aws-fargate


    【解决方案1】:

    经过一天的研究,AWS 似乎仍未通过 CloudFormation 发布对此的支持。 但是,这里有一个替代方法,它通过 cli 上的 aws events put-targets 命令起作用。

    此方法对于旧版本的 cli 失败。运行此更新:pip install awscli --upgrade --user 这是我现在使用的版本:aws-cli/1.16.9 Python/2.7.15 Darwin/17.7.0 botocore/1.11.9

    使用aws events put-targets --rule <value> --targets <value> 命令。确保您已经在集群上定义了规则。如果没有,您可以使用aws events put-rule cmd 执行此操作。请参阅AWS docs for put-rulefor put-targets

    文档中的规则示例如下:

    aws events put-rule --name "DailyLambdaFunction" --schedule-expression "cron(0 9 * * ? *)"
    

    对我有用的 put-targets 命令是这样的:

    aws events put-targets --rule cli-RS-rule --targets '{"Arn": "arn:aws:ecs:1234/cluster/clustername","EcsParameters": {"LaunchType": "FARGATE","NetworkConfiguration": {"awsvpcConfiguration": {"AssignPublicIp": "ENABLED", "SecurityGroups": [ "sg-id1233" ], "Subnets": [ "subnet-1234" ] }},"TaskCount": 1,"TaskDefinitionArn": "arn:aws:ecs:1234:task-definition/taskdef"},"Id": "sampleID111","RoleArn": "arn:aws:iam:1234:role/eventrole"}'
    

    【讨论】:

    • 我遇到了同样的问题。但我认为您的解决方法会弄乱 CloudFormation 堆栈上的更新操作。
    • @gcv 我对 CloudFormation 比较陌生,你能解释一下这可能会导致 CF 堆栈出现问题吗?
    • 当您使用 CloudFormation 在 AWS 中创建某些内容时,之后从 CF 外部对其进行修改通常会从 CF 的角度将其搞砸。它通常不会出错,但会停止应用您从 CF 对同一对象所做的更新。
    【解决方案2】:

    CloudFormation 尚未赶上将 Fargate 任务作为 CloudWatch 事件规则的直接目标运行所需的参数。同时,您可以通过让规则以运行 Fargate 任务的 Lambda 函数为目标来实现相同的结果。

    为此,事件规则需要对 Lambda 函数具有 lambda:InvokeFunction 权限,并且 Lambda 函数需要对适当资源具有 ecs:RunTaskiam:PassRole 权限(除了 AWSLambdaBasicExecutionRole 中的常用日志权限)。

    编辑:这是一个示例 CF 模板,它显示了我在说什么。 (它是从我们使用的东西拼凑和简化的,所以没有经过测试,但希望能说明这个过程。)

    Parameters:
      #ClusterName
      #Subnets
      #SecurityGroups
      #CronExpression
      #TaskDefinitionArn
      #TaskRoleArn
      #ExecutionRoleArn
    
    Resources:
      FargateLauncherRole:
        Type: AWS::IAM::Role
        Properties:
          RoleName: !Sub ${AWS::StackName}-FargateLauncher-${AWS::Region}
          AssumeRolePolicyDocument:
            Statement:
              -
                Effect: Allow
                Principal:
                  Service: lambda.amazonaws.com
                Action: sts:AssumeRole
          ManagedPolicyArns:
            - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
          Path: /
    
      FargateLauncherPolicy:
        Type: AWS::IAM::Policy
        Properties:
          PolicyName: !Sub ${AWS::StackName}-FargateLauncher-${AWS::Region}
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              -
                Sid: RunTaskAccess
                Effect: Allow
                Action:
                  - ecs:RunTask
                Resource: '*'
              -
                Sid: PassRoleAccess
                Effect: Allow
                Action:
                  - iam:PassRole
                Resource:
                  # whatever you have defined in your TaskDefinition, if any
                  - !Ref TaskRoleArn
                  - !Ref ExecutionRoleArn
          Roles:
            - !Ref FargateLauncherRole
    
      FargateLauncher:
        Type: AWS::Lambda::Function
        DependsOn: FargateLauncherPolicy
        Properties:
          Environment:
            Variables:
              CLUSTER_NAME: !Ref ClusterName
              SUBNETS: !Ref Subnets
              SECURITY_GROUPS: !Ref SecurityGroups
          Handler: index.handler
          Role: !GetAtt FargateLauncherRole.Arn
          Runtime: python3.6
          Code:
            ZipFile: |
              from os import getenv
              from boto3 import client
              ecs = client('ecs')
    
              def handler(event, context):
                ecs.run_task(
                  cluster=getenv('CLUSTER_NAME'),
                  launchType='FARGATE',
                  taskDefinition=event.get('taskDefinition'),
                  count=1,
                  platformVersion='LATEST',
                  networkConfiguration={'awsvpcConfiguration': {
                    'subnets': getenv('SUBNETS').split(','),
                    'securityGroups': getenv('SECURITY_GROUPS').split(','),
                    'assignPublicIp': 'DISABLED'
                  }})
    
      Schedule:
        Type: AWS::Events::Rule
        Properties:
          ScheduleExpression: !Sub "cron(${CronExpression})"
          State: ENABLED
          Targets:
            -
              Id: fargate-launcher
              Arn: !GetAtt FargateLauncher.Arn
              Input: !Sub |
                {
                  "taskDefinition": "${TaskDefinitionArn}"
                }
    
      InvokePermission:
        Type: AWS::Lambda::Permission
        Properties:
          FunctionName: !Ref FargateLauncher
          Action: lambda:InvokeFunction
          Principal: events.amazonaws.com
          SourceArn: !GetAtt Schedule.Arn
    

    我在我的集​​群堆栈中定义了 Lambda 函数,其中我已经有 ClusterNameSubnetsSecurityGroups 参数,并且可以将它们直接传递给 Lambda 环境。然后可以在一个或多个单独的堆栈中定义计划和调用权限,通过 Lambda 函数的输入为每个任务传递 TaskDefinition。这样,您可以在每个集群中拥有一个 Lambda,但可以根据需要使用尽可能多的不同任务。您还可以将自定义命令字符串和/或其他容器覆盖添加到 Lambda 输入,这些可以通过 run_taskoverrides 参数传递。

    编辑#2: 下面是一个可以放入 CF 模板的 Fargate TaskDefinition 示例:

    TaskDefinition:
      Type: AWS::ECS::TaskDefinition
      Properties:
        Family: !Ref Family
        Cpu: !Ref Cpu
        Memory: !Ref Memory
        NetworkMode: awsvpc
        ExecutionRoleArn: !Ref ExecutionRoleArn
        TaskRoleArn: !Ref TaskRoleArn
        RequiresCompatibilities:
          - FARGATE
        ContainerDefinitions:
          - Name: !Ref ContainerName
            Essential: true
            Image: !Ref Image
            LogConfiguration:
              LogDriver: awslogs
              Options:
                awslogs-group: !Ref LogGroup
                awslogs-region: !Ref AWS::Region
                awslogs-stream-prefix: !Ref LogPrefix
    

    【讨论】:

    • 由于我是在运行定时任务,所以我不需要手动设置如何运行任务。 Cloudwatch 规则负责该部分;我坚持的是如何首先使用 awsvpc config 在 ecs 本身上获取 fargate 任务定义
    • 我建议使用触发 Lambda 函数启动 ECS 任务的 CloudWatch 事件规则(计划)。没有什么是手动的,一切都在 CloudFormation 的控制之下。与其他建议的答案一样,添加 Fargate 事件目标不需要额外的单独 cli 步骤。我同意@gcv 的观点,最好避免在 CloudFormation 之外添加/修改/删除应该由 CF 管理的内容。
    • 您能告诉我您在哪里创建了任务定义吗?是通过 ECS 仪表板手动设置的,还是使用 CF 模板来设置任务定义?
    • 另外,由于您使用了 lambda 函数,您是否遇到过由于 至少一次 SLA 导致 lambda 函数运行不止一次的问题?问是因为我想知道这是否会导致任务有时运行不止一次。
    • 我的任务定义在另一个 CF 模板中,或者您可以将它放在同一个模板中。 Fargate 需要几个属性,我将添加到答案中。回复:lambda 函数运行不止一次:是的,我认为它可能会发生,但我还没有注意到它。您应该确保正在运行的任务是幂等的。如果这对于作为事件目标的常规 EC2 任务来说也是一个问题,我想知道。我遇到了他们 not 运行的问题,例如如果服务器资源不可用,但没有注意到它们多次运行。
    【解决方案3】:

    尽管 AWS 尚未在今天(2019 年 7 月 15 日)之前更新文档,但它的工作方式与初始海报中描述的一样。

    【讨论】:

    • 太棒了,你是对的!即使它没有记录,它也可以工作。希望它最终能按原样进入文档。我确实注意到它确实应该是AwsVpcConfiguration,因为最初的海报有它。它不应该是AwsvpcConfiguration(带有小写的“v”),即使它在 AWS::ECS::Service (docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/…) 的 NetworkConfiguration 下是这样显示的。
    猜你喜欢
    • 1970-01-01
    • 2019-08-06
    • 1970-01-01
    • 1970-01-01
    • 2017-12-30
    • 2018-03-23
    • 1970-01-01
    • 2020-11-27
    • 1970-01-01
    相关资源
    最近更新 更多