AWS ECS Create Scheduled Tasks (cron) via Cloudfor

2019-03-24 18:53发布

问题:

We want to create ScheduledTasks in AWS ECS via CloudFormation. Is there a programmatic way to create via boto or cloudformation?

回答1:

In order to define a scheduled ECS task in CloudFormation, you need to define a "AWS::Events::Rule" resource with an ECS task as a target.

"TaskSchedule": {
  "Type": "AWS::Events::Rule",
  "Properties": {
    "Description": "dump data every workday at 10",
    "Name": "dump-data",
    "ScheduleExpression": "cron(0 10 ? * MON-FRI *)",
    "State": "ENABLED",
    "Targets": [
      {
        "Id": "dump-data-ecs-task",
        "RoleArn": {
          "Fn::GetAtt": ["TaskSchedulerRole", "Arn"]
        },
        "EcsParameters": {
          "TaskDefinitionArn": {
            "Ref": "TaskDefinition"
          },
          "TaskCount": 1
        },
        "Arn": {
          "Fn::GetAtt": ["EcsCluster", "Arn"]
        }
      }
    ]
  }
}


回答2:

Troposphere now allows to define ECS scheduled task, you need three resources for that,

  1. the task definition:

    from troposphere.ecs import (
        ContainerDefinition,
        TaskDefinition,
    )
    
    scheduled_worker_task_definition = TaskDefinition(
        "ScheduledWorkerTask",
        template=template,
        ContainerDefinitions=[
            ContainerDefinition(
                Name="ScheduledWorker",
                Cpu=200,
                Memory=300,
                Essential=True,
                Image="<image>",
                EntryPoint=['<entry_point>']
            ),  
        ],  
    )
    
  2. The role to run the task:

    from troposphere import iam
    
    run_task_role = iam.Role(
        "RunTaskRole",
        template=template,
        AssumeRolePolicyDocument=dict(Statement=[dict(
            Effect="Allow",
            Principal=dict(Service=["events.amazonaws.com"]),
            Action=["sts:AssumeRole"],
        )]),
        Path="/",
        Policies=[
            iam.Policy(
                PolicyName="RunTaskPolicy",
                PolicyDocument=dict(
                    Statement=[dict(
                        Effect="Allow",
                        Action=[
                            "ecs:RunTask",
                        ],
                        Resource=["*"],
                        Condition=dict(
                            ArnEquals={
                                "ecs:cluster": GetAtt(cluster, "Arn"),
                            }
                        )
                    )],
                ),
            ),
        ],
    )
    
  3. The event rule that schedules the task:

    from troposphere.events import Rule, Target, EcsParameters
    
    Rule(
        "SchedulingRule",
        template=template,
        Description="My schedule task rule",
        State="ENABLED",
        ScheduleExpression="rate(30 minutes)",
        Targets=[
            Target(
                Id="ScheduledWorkerTaskDefinitionTarget",
                RoleArn=GetAtt(run_task_role, "Arn"),
                Arn=GetAtt(cluster, "Arn"),
                EcsParameters=EcsParameters(
                    TaskCount=1,
                    TaskDefinitionArn=Ref(scheduled_worker_task_definition),
                ),
            ),
        ]
    )
    

Where the JSON output looks like:

    ...
    "ScheduledWorkerTask": {
        "Condition": "Deploy",
        "Properties": {
            "ContainerDefinitions": [
                {
                    "Cpu": 200,
                    "EntryPoint": [
                        "<entry_point>"
                    ],
                    "Essential": "true",
                    "Image": {
                        "Fn::Join": [
                            "",
                            [
                                {
                                    "Ref": "AWS::AccountId"
                                },
                                ".dkr.ecr.",
                                {
                                    "Ref": "AWS::Region"
                                },
                                ".amazonaws.com/",
                                {
                                    "Ref": "ApplicationRepository"
                                },
                                ":",
                                "<image>"
                            ]
                        ]
                    },
                    "Memory": 300,
                    "Name": "ScheduledWorker"
                }
            ]
        },
        "Type": "AWS::ECS::TaskDefinition"
    },

    "SchedulingRule": {
        "Properties": {
            "Description": "My schedule task rule",
            "ScheduleExpression": "rate(30 minutes)",
            "State": "ENABLED",
            "Targets": [
                {
                    "Arn": {
                        "Fn::GetAtt": [
                            "Cluster",
                            "Arn"
                        ]
                    },
                    "EcsParameters": {
                        "TaskCount": 1,
                        "TaskDefinitionArn": {
                            "Ref": "ScheduledWorkerTask"
                        }
                    },
                    "Id": "ScheduledWorkerTaskDefinitionTarget",
                    "RoleArn": {
                        "Fn::GetAtt": [
                            "RunTaskRole",
                            "Arn"
                        ]
                    }
                }
            ]
        },
        "Type": "AWS::Events::Rule"
    },

    "RunTaskRole": {
        "Properties": {
            "AssumeRolePolicyDocument": {
                "Statement": [
                    {
                        "Action": [
                            "sts:AssumeRole"
                        ],
                        "Effect": "Allow",
                        "Principal": {
                            "Service": [
                                "events.amazonaws.com"
                            ]
                        }
                    }
                ]
            },
            "Path": "/",
            "Policies": [
                {
                    "PolicyDocument": {
                        "Statement": [
                            {
                                "Action": [
                                    "ecs:RunTask"
                                ],
                                "Condition": {
                                    "ArnEquals": {
                                        "ecs:cluster": {
                                            "Fn::GetAtt": [
                                                "Cluster",
                                                "Arn"
                                            ]
                                        }
                                    }
                                },
                                "Effect": "Allow",
                                "Resource": [
                                    "*"
                                ]
                            }
                        ]
                    },
                    "PolicyName": "RunTaskPolicy"
                }
            ]
        },
        "Type": "AWS::IAM::Role"
    },
    ...

More info at https://jeanphix.github.io/2017/10/04/ecs-scheduled-cron-task-with-cloudformation/



回答3:

You can do this with both boto and cloudformation. If you do it with cloudformation, you would need to use a Lambda-backed custom resource. And you can do it with Troposphere as well.

Troposphere Example:

# Read in the code
fname = "lambda_code.py"
try:
  with open(fname) as target:
    code = target.readlines()
except Exception as e:
  print(e)

# Create the custom resource.
CustomResource = template.add_resource(Function(
  "CustomResource",
  Description="Creates and Runs a Lambda function.",
  FunctionName="myFunctionName",
  Code=Code(
    ZipFile=Join("",code)
  ),
  Runtime="python2.7",
  Role=GetAtt("lambaRole","Arn"),
  Handler="index.decide_action",
  MemorySize='128',
  Timeout='10'
))      

As for the creating of tasks in boto, check out the Boto3 Documentation.

Additional Resources:

  • Cloudformation Custom Resources
  • AWS Lambda-backed Custom Resources
  • Lambda Troposphere Example