Fargateで起動しているECSクラスタのタスクを特定の時間に起動・停止

以前、[EC2インスタンスを特定の時間に起動・停止](https://qiita.com/ot-nemoto/items/0e148bed48bd0d0900f2)について投稿しました。

こちらではEC2インスタンスと、Auto Scaling環境のEC2インスタンスを、営業時間以外に停止する方法です。

こちらのAuto Scaling環境でのEC2インスタンスは、ECSクラスタを想定したものだったのですが、今回はFargateで構成されたECSクラスタ場合です。

コスト削減を目的とした場合、EC2インスタンスで構成されたECSクラスタは、そもそもEC2インスタンス自体を停止しなければなりませんでした。

ただ、ECSクラスタのインスタンスはAuto Scaling環境下で動いているいるので、EC2インスタンス自体を終了させても、ゾンビのように何度も立ち上がります。
そのため、Auto Scalingの `Desired Capacity`（希望するキャパシティ）を 0 にすることで、EC2インスタンスを停止させていました。

それと異なり、Fargateでは停止するインスタンスは存在しません。
また、[Fargateの料金体系](https://aws.amazon.com/jp/fargate/pricing/)を見ると、

**Amazon ECS の場合、AWS Fargate の料金は、コンテナイメージのダウンロード (docker pull) を開始した時点から Amazon ECS タスク が終了するまでに使用された vCPU およびメモリリソースに基づいて計算され、最も近い秒数に切り上げられます。1 分の最低料金が適用されます。**

とされています。

そのため、Fargate環境では、ECSクラスタのサービス設定にある `Desired Count` を変えることでタスクを停止させます。
逆に単純です。

### AWS-Cli

タスクの停止

```sh
aws ecs update-service \
  --service $service_name \
  --cluster $cluster_name \
  --desired-count 0
```

タスクの起動

```sh
aws ecs update-service \
  --service $service_name \
  --cluster $cluster_name \
  --desired-count ${desired_count:-1}
```

### Lambda

タスクの停止

```javascript
'use strict';

var AWS = require('aws-sdk');

module.exports.handler = function(event, context) {
  var ecs = new AWS.ECS();

  ecs.listClusters({}, function(err, data) {
    if (err) { console.log(err, err.stack); return }

    data.clusterArns.forEach(function(clusterArn) {

      var cluster = clusterArn.split("/")[1];
      ecs.listServices({cluster: cluster}, function(err, data) {
        if (err) { console.log(err, err.stack); return }

        var service = clusterArn.split("/")[1];
        ecs.updateService({cluster: cluster, service: service, desiredCount: 0}, function(err, data) {
          if (err) console.log(err, err.stack);
          else     console.log(data);
        });
      });
    });
  });
};
```

タスクの起動

```javascript
'use strict';

var AWS = require('aws-sdk');

module.exports.handler = function(event, context) {
  var ecs = new AWS.ECS();

  ecs.listClusters({}, function(err, data) {
    if (err) { console.log(err, err.stack); return }

    data.clusterArns.forEach(function(clusterArn) {

      var cluster = clusterArn.split("/")[1];
      ecs.listServices({cluster: cluster}, function(err, data) {
        if (err) { console.log(err, err.stack); return }

        var service = clusterArn.split("/")[1];
        ecs.updateService({cluster: cluster, service: service, desiredCount: 1}, function(err, data) {
          if (err) console.log(err, err.stack);
          else     console.log(data);
        });
      });
    });
  });
};
```

但し、上記の場合では、対象アカウントのECSクラスタがすべて対象になってしまうので、クラスタに特定のタグを付け、対象のECSクラスタを絞るなど、対象となるECSクラスタのサービスを選別させる必要があれば、個別に対応が必要です。

また起動・停止させるクラスタやサービスが分かっているのであれば、ここまで複雑にする必要はなく、

```javascript
'use strict';

var AWS = require('aws-sdk');

module.exports.handler = function(event, context) {
  var ecs = new AWS.ECS();
  ecs.updateService({cluster: CLUSTER_NAME, service: SERVICE_NAME, desiredCount: 0}, function(err, data) {
    if (err) console.log(err, err.stack);
    else     console.log(data);
  });
};
```

停止ならこれでOK。（起動の場合は、desiredCountを希望のタスク数に）

Lambda関数を作成したら、CloudWatch Eventsから特定の時間に起動させるだけです。