简体   繁体   English

安排 AWS ECS Fargate 启动和停止任务

[英]Schedule AWS ECS Fargate to Start and STOP Tasks

Per AWS documentation they say: " Fargate supports scheduling tasks in response to CloudWatch Events. You can easily launch and stop Fargate tasks that you only need to run at certain times "根据 AWS 文档,他们说:“ Fargate 支持调度任务以响应 CloudWatch Events。您可以轻松启动和停止只需要在特定时间运行的 Fargate 任务

Start of Fargate Tasks can easily be enabled from CloudWatch Events or ECS Task Scheduler.可以从CloudWatch Events 或 ECS Task Scheduler 轻松启用 Fargate 任务的启动。

But STOP of Fargate Tasks I cannot find.但是我找不到 Fargate 任务的 STOP。 Is it possible that need to use Lambda and not native Fargate feature to stop tasks?是否可能需要使用 Lambda 而不是本机 Fargate 功能来停止任务?

My goal is to run ECS Container between 8ap-5pm Only!我的目标是只在晚上 8 点到下午 5 点之间运行 ECS 容器!

answer by @Francesco Grotta is right. @Francesco Grotta 的回答是对的。 By we can create the following resources, to trigger this action on schedule:通过我们可以创建以下资源,以按时触发此操作:

  • A lambda function, to start or stop by update the ECS service DesiredCount .一个 lambda 函数,通过更新 ECS 服务DesiredCount来启动或停止。
  • A scheduled CloudWatch Events, to start the ECS tasks.计划的 CloudWatch Events,用于启动 ECS 任务。
  • A scheduled CloudWatch Events, to stop the ECS tasks.计划的 CloudWatch Events,用于停止 ECS 任务。

Lambda Function that will start or stop the ECS service based on the input from CloudWatch Events:将根据来自 CloudWatch Events 的输入启动或停止 ECS 服务的 Lambda 函数:

    if(event.status == 'stop'){
        var params = {
            cluster: process.env.ECS_CLUSTER,
            service: process.env.ECS_SERVICE_NAME,
            desiredCount: 0
        };
    }
    else{
        var params = {
            cluster: process.env.ECS_CLUSTER,
            service: process.env.ECS_SERVICE_NAME,
            desiredCount: 1
        };
    }

    var ecs = new AWS.ECS();
    ecs.updateService(params, function (err, data) {
        if (err) console.log(err, err.stack); // an error occurred
        else console.log(data);           // successful response
    });

In Cloudformation template, create resources that will invoke the Lambda Function on a schedule:在 Cloudformation 模板中,创建将按计划调用 Lambda 函数的资源:

  StartEcsLambdaSchedule:
    Type: AWS::Events::Rule
    Properties:
      Description: >
        A schedule for the Lambda function to start ECS service during office hours.
      ScheduleExpression: !Ref StartEcsLambdaScheduleExpression
      State: ENABLED
      Targets:
        - Arn: !Sub ${EcsTaskScheduleLambdaFunction.Arn}
          Id: StartEcsLambdaScheduleV1
          Input: '{"status": "start"}'

  StopEcsLambdaSchedule:
    Type: AWS::Events::Rule
    Properties:
      Description: >
        A schedule for the Lambda function to stop ECS service after office hours.
      ScheduleExpression: !Ref StopEcsLambdaScheduleExpression
      State: ENABLED
      Targets:
        - Arn: !Sub ${EcsTaskScheduleLambdaFunction.Arn}
          Id: StopEcsLambdaScheduleV1
          Input: '{"status": "stop"}'

I've taken the code @Mr3381 proposed and made some improvements:我采用了@Mr3381 提出的代码并进行了一些改进:

interface EventLambda {
  status: string,
  services: string[]
}

interface UpdateServiceParams {
  cluster: string
  service: string
  desiredCount: number
}

// Load AWS SDK for Node.js
import AWS from 'aws-sdk';

export const handler = async (event: EventLambda): Promise<string[]> => {
  const ecs = new AWS.ECS({region: 'sa-east-1'});
  const promises = new Array<Promise<any>>();
  const desiredCount = event.status == 'start' ? 1 : 0

  event.services.forEach(service => {
      var params: UpdateServiceParams = {
        cluster: process.env.ECS_CLUSTER!,
        service,
        desiredCount
      };
      promises.push(updateService(ecs, params, desiredCount))
    }
  )
  return Promise.all(promises)
};

function updateService(ecs: AWS.ECS, params: UpdateServiceParams, desiredCount: number): Promise<string> {
  return new Promise((resolve, reject) => {
    ecs.updateService(params, function(err, data) {
      if (err) {
        console.log(err, err.stack); // An error occurred
        resolve(`${params.service} not updated`);
      }
      else {
        console.log(data); // Successful response
        resolve(`${params.service} updated => Desired count: ${desiredCount}`)
      }
    });
  })
}

It's now in TypeScript and supports an array of services as an input.它现在在 TypeScript 中并支持将一系列服务作为输入。

There's only the need to transpile it to JavaScript to be able to run on AWS.只需将其转换为 JavaScript 即可在 AWS 上运行。

Make sure to have the required permissions to execute the function and update your services.确保拥有执行函数和更新服务所需的权限。 This can be achieved by attaching the policies describeServices and updateServices from ECS to your Lambda function IAM Role.这可以通过将 ECS 中的策略describeServicesupdateServices附加到您的 Lambda 函数 IAM 角色来实现。

I believe this is the best solution for your office hours problem.我相信这是您办公时间问题的最佳解决方案。

Just schedule a local cron job in the EC2 instance with the below command to update desired count according the timestamp and you are good to go.只需使用以下命令在 EC2 实例中安排本地 cron 作业,即可根据时间戳更新所需的计数,您就可以开始了。

$ aws ecs update-service --cluster <cluster-name> --service <service-name> --desired-count x

Do let me know in case this helped you.如果这对您有帮助,请告诉我。

It's the same to Start, but you need specify "Desired task" to 0. My issue is that the Scheduler need the Task Definition, so if I upgrade it, I need change also the Scheduler.与开始相同,但您需要将“所需任务”指定为 0。我的问题是调度程序需要任务定义,所以如果我升级它,我还需要更改调度程序。 Exists some service, lambda or project to handle it?是否存在一些服务、lambda 或项目来处理它? My need is to stop a Cluster of Fargate container.我的需要是停止 Fargate 容器集群。

The following will find out the all the qa, uat or dev clusters (Provided they have the string qa, uat or dev in their name) and then set the number of tasks for all the services under them to 0. Just cron this script and create another cron for --desired-count 1 if you want to start all non-prod environments.下面将找出所有的 qa、uat 或 dev 集群(前提是它们的名称中有字符串 qa、uat 或 dev),然后将它们下所有服务的任务数设置为 0。只需 cron 这个脚本和如果要启动所有非生产环境,请为--desired-count 1创建另一个 cron。 Create /tmp/ecs-stop.log file to use the script as is.创建 /tmp/ecs-stop.log 文件以按原样使用脚本。 Make sure that the file is writable and the script is executable for cron to be able to run the script and write to the log file.确保文件是可写的,并且脚本对于 cron 是可执行的,以便能够运行脚本并写入日志文件。

#!/bin/bash
export AWS_PAGER="" # Doesnt ask user to press q for paginated outputs
export AWS_PROFILE=default
d=$(date +%Y-%m-%d)
for cluster in $(/usr/local/bin/aws ecs list-clusters --output text | awk '{print $2}' | egrep -i 'qa|uat|dev'  )
do 
    echo -e "Cluster ==> $cluster"
    for service in $(/usr/local/bin/aws ecs list-services --cluster $cluster --output text | awk '{print $2}')
    do
        echo "Service ==> Stopping $service..."
        /usr/local/bin/aws ecs update-service --cluster $cluster --service $service --desired-count 0 > /dev/null 2>&1
    done
echo
done
echo "Stopped all non-prod ECS services."
echo "The script ran on $d" >> /tmp/ecs-stop.log

This is my solution which is highly based on a great AWS employee (Alfredo J).这是我的解决方案,它高度基于一位出色的 AWS 员工 (Alfredo J)。

First, you need to create a Lambda function and add this Python script:首先,您需要创建一个Lambda函数并添加以下 Python 脚本:

import json
import boto3
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

client = boto3.client('ecs')

def lambda_handler(event, context):
    cluster = event["cluster"]
    service_names = event["service_names"]
    service_desired_count = int(event["service_desired_count"])

    for service_name in service_names.split(","):
        response = client.update_service(
            cluster=cluster,
            service=service_name,
            desiredCount=service_desired_count
            )

        logger.info("Updated {0} service in {1} cluster with desire count set to {2} tasks".format(service_name, cluster, service_desired_count))
        
    return {
        'statusCode': 200,
        'new_desired_count': service_desired_count
    }

The script expects the following variables in a JSON format:该脚本需要 JSON 格式的以下变量:

{
  "cluster": "clusterName",
  "service_names": "service1,service2",
  "service_desired_count": "0"
}

Where:在哪里:

  • cluster is the name of the cluster you want to modify. cluster是您要修改的集群的名称。
  • service_names is an array for the collection of services. service_names是一个用于服务集合的数组。
  • service_desired_count is the number of desired services. service_desired_count是所需服务的数量。 0 is to stop the service/s, any other number is to start the service/s. 0 是停止服务/秒,任何其他数字是启动服务/秒。

After everything is created you need to create some rules in Amazon EventBridge (formerly, CloudWatch Events).创建完所有内容后,您需要在 Amazon EventBridge(以前称为 CloudWatch Events)中创建一些规则。 Here, you define the event you want to trigger based on the schedule that you expect.在这里,您可以根据预期的计划定义要触发的事件。

If something fails, you need to double-check that the created IAM role has the required policies like: ecs:UpdateService .如果出现问题,您需要仔细检查创建的 IAM 角色是否具有所需的策略,例如: ecs:UpdateService You can check this from the logs.您可以从日志中检查这一点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM