Amazon ECSのDockerコンテナをLambdaでAuto Scalingに連携させる

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

ども、大瀧です。
ECS ServiceのLambda連携、2本目行きます!前回の記事では、Lambdaを利用してECS Serviceにオートスケーリングのような動きをさせました。今回は、Auto Scalingのインスタンス増減にECS Serviceのコンテナが追随する構成をご紹介します。

ECS ServiceはAuto Scalingと連動しない

ECSのコンテナスケジューラであるServiceは、Dockerコンテナの状態とインスタンスの配置を管理しますが、EC2インスタンスを自動起動するAuto Scalingとは特に連携しない、別モノです。しかし、Auto Scalingで実行するインスタンス(Auto Scaling Group)をECSクラスタに参加させることはできるため、連携はしませんが組み合わせることが可能です。以下の図のイメージです。

ecs-autoscaling03

このAuto Scaling GroupとECS ServiceをLambdaでつなごう!というのが今回のテーマになります。ECSの運用には、コンテナを実行していてもいなくてもインスタンス単位で課金されるという課題があるので、Auto Scalingのインスタンス数に追随することで常に用意してあるインスタンスの分だけコンテナが実行される仕組みが実現できます。

よろしい、では実装しよう

例によって、有りモノで実装するべく組み合わせを考えました。

  • コンテナ数変更のトリガー : Auto Scalingイベント→SNS通知
  • コンテナ数変更の実装 : AWS Lambda

ポイントはトリガーの部分です。Auto Scalingには、インスタンス増減時にSNS通知を発行する機能があるので、それで通知を発行し、そのあとは前回と同じパターンでSNS通知からLambda関数をキック、LambdaでECSのAPIをコールしてServiceのDesiredCountを変更します。以下にイメージを示します。

ecs-autoscaling04

Auto Scaling通知の追加

EC2の管理画面からAuto Scaling Groupの設定を表示し、[Notification]タブにある[Create notification]ボタンをクリックします。

ecs-autoscaling21

[create topic]リンクをクリックし、任意のトピック名、メールアドレスを入力します。イベントはいくつか選択できますが、今回は既定のまま[Save]をクリックして通知を追加します。

ecs-autoscaling22

必要に応じてメール確認を済ませましょう。通知側はこれでOKです。

LambdaのIAM権限設定

続いて、Lambda関数を実行するためのIAMロールをIAMの管理画面から作成します。前回の記事で作成したロール権限と特に変わらないので、未作成であれば、以下のように作成します。

今回は、ロール名「lambda_exec_role」、[AWS Service Roles] - [AWS Lambda]を選択し、Attach Policyは空のまま作成します。その後、IAMロールのプロパティからInline Policiesの作成([click here]リンク)から[Custom Policy]で任意のポリシー名と以下のPolicy Documentを入力します。

Lambda標準のログ出力先であるCloudWatch LogsとECSのサービス管理の一部のアクションを許可しました。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:*"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "ecs:DescribeServices",
        "ecs:UpdateService"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

Lambda関数の作成とSNSの登録

では、Lambda関数を定義します。Lambdaの管理画面から[Create a Lambda function]ボタンをクリックし、以下を入力します。

  • Name : 任意の関数名(今回は「followServiceCountToAutoScalingGroupSize」)
  • Description : 今回は空のまま
  • Code entry type : 「Edit code inline」のまま
  • Code template : 「None」を選択し、以下をコピペ
  • Handler name : 初期値「handler」のまま
  • Role : 先ほど作成したIAMロール(lambda_exec_role)を選択
console.log('Loading event');
var aws = require('aws-sdk');

exports.handler = function(event, context) {
  var ecsService = 'sample-webapp';
  var ecsRegion = 'us-west-2';
  var snsSubject = event.Records[0].Sns.Subject;

  var ecs = new aws.ECS({region: ecsRegion});
  ecs.describeServices({services:[ecsService]}, function(err, data) {
    if (err) {
      console.log(err, err.stack);
    } else {
      var desiredCount = data.services[0].desiredCount;
      if (snsSubject.match(/launch$/)) {
        console.log('countUp! current :' + desiredCount);
        desiredCount++;
      } else if (snsSubject.match(/termination$/)) {
        console.log('countDown! current :' + desiredCount);
        desiredCount--;
      }
    
      var params = {
        service:      ecsService, 
        desiredCount: desiredCount
      };
      ecs.updateService(params, function(err, data) {
        if (err) {
          console.log(err, err.stack);
        } else {
          console.log(data);
          context.succeed();
        }
      });
    }
  });
};

簡単に解説します。

  • 7行目: SNSの件名にAuto Scalingの台数増減(launch/termination)が含まれるので、それを変数に代入しておく
  • 15〜21行目: 件名の内容に合わせてdesiredCountの増減を判断する

その他の記述は前回の記事と大きくは変わりません。

関数を作成したら、関数一覧から「followServiceCountToAutoScalingGroupSize」を選択し、[Actions] - [Add event source]をクリックします。

ecs-autoscaling07

[Event Source Type]から「SNS」、[SNS Topic]では作成したトピック「ECS-AS-notifies」を選択し[Submit]で確定します。

動作確認

では、Auto Scaling Groupの管理画面の[Details]タブから台数(DesiredおよびMin)を1台から2台に変更してみます。

ecs-autoscaling23

しばらく待つと、Auto Scalingのインスタンスが2台に増え、さらにECS Serviceのコンテナ数も2台に増えることが確認できます。

ecs-autoscaling24

追随しましたね!
ちなみに、ServiceのdesiredCountは相対的に+1/-1するので設定時点でAuto Scaling Groupのインスタンス数と同値にしておかないとズレたままになってしまうことに注意しましょう。

まとめ

Lambdaを利用して、ECS Serviceのコンテナ数とAuto Scaling Groupのインスタンス数が連動する機能を実装しました。
ちなみに、Auto Scalingのスケーリングに合わせるにあたりいくつか留意点があります。

  • Auto Scalingのスケーリングのトリガーは別途検討しなければならない。関係がややこしくなりますが、ECS Serviceと連携するELBにCloudWatchアラームを設定してもOKです。
  • Auto Scalingのインスタンス起動、ECSクラスタに登録のあとコンテナが実行されるので、トリガーからELBの配下に入るまで時間がかかる。
  • ECSインスタンスで同一のコンテナを実行することになるので、Beanstalk Docker Multi Containerと似たような構成になりECSを使わなくてもいいかも?と思ってしまうかも知れない。

皆さんもECSとLambdaの組み合わせをいろいろ考えてみて下さい!