создание 'Target' для правила события cloudwatch через cloudformation для задачи типа запуска fargate

Я пытаюсь создать запланированное задание (правило событий CloudWatch) в своем шаблоне CloudFormation, которое будет иметь следующие EcsParameters:

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

Мой ECS CLuster запускается на Fargate, а не на EC2, и у меня НЕ работает служба (сценарий использования не требует длительного процесса, непосредственно планируя задачи из правил событий).

Всякий раз, когда я запускаю этот шаблон (с LaunchType и NetworkConfiguration) создание стека терпит неудачу с этой ошибкой:

Обнаружено неподдерживаемое свойство NetworkConfiguration


В качестве альтернативы я также попытался запустить запланированное задание из интерфейса командной строки AWS, но кажется, что параметры конфигурации сети и типа запуска там также недоступны:

Ошибка проверки параметра: неизвестный параметр в Targets [0].EcsParameters: "LaunchType", должен быть одним из: TaskDefinitionArn, TaskCount


Согласно этой странице на самой AWS документации, я должен быть в состоянии определить LaunchType и NetworkConfiguration в моем EcsParameters разделе в Targets в Properties этого AWS::Events::Rule ресурса.

Могу ли я попробовать что-нибудь, что может сработать?

Ответ 1

CloudFormation еще не ознакомился с параметрами, необходимыми для запуска задачи Fargate в качестве прямой цели правила событий CloudWatch. В то же время, вы можете достичь того же результата, назначив целевому правилу функцию Lambda, которая запускает задачу Fargate.

Чтобы это работало, для правила событий потребуется разрешение lambda:InvokeFunction для функции Lambda, а для функции Lambda потребуется ecs:RunTask и iam: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 в моем стеке кластера, где у меня уже есть ClusterName, Subnets и SecurityGroups параметров, и может передавать их непосредственно в окружающую среду Lambda. Разрешение расписания и вызова затем может быть определено в одном или нескольких отдельных стеках, передавая TaskDefinition для каждой задачи через входные данные для функции Lambda. Таким образом, вы можете иметь одну лямбду на кластер, но использовать столько разных задач, сколько необходимо. Вы также можете добавить пользовательскую строку команды и/или другие переопределения контейнеров к входу Lambda, которые можно передать через параметр overrides run_task.

Редактирование # 2: Вот пример Fargate TaskDefinition, который может входить в шаблон CF:

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

Ответ 2

После дня исследования, похоже, AWS до сих пор не выпустил поддержку для этого, хотя CloudFormation. Тем не менее, вот альтернатива, которая работала через команду aws events put-targets в 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 для put-rule и для put-target.

Пример правила из документации приведен ниже:

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

Команда put-target, которая работала для меня, такова:

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"}'

Ответ 3

Несмотря на то, что на сегодняшний день AWS не обновила документацию (15 июля 2019 г.), она работает так, как описано в первоначальном постере.