Cloud AutomatorとAWS Lambdaを連携させてみた

記事タイトルとURLをコピーする

みなさんこんにちは。

AWS Lambdaがプレビューからプロダクションとなって1ヶ月経ちますが、みなさまどうでしょう。

Lambda、使ってますか?
恥ずかしながら、私は今月になって初めて使ってみました…。

さて、今日はAWS LambdaとCloud Automatorの連携について、話をします。

登場人物紹介

AWS Lambda

AWS Lambda(以下Lambda) とは、イベント発生時に任意のコードを実行させることができるAWSのサービスです。
S3 Bucketの変更やSNSの通知、DynamoDBのレコード追加等をトリガーにLambdaを実行することができます。
また、コードが実行される環境は自動でスケールするため、1日に数件のリクエストから1秒に数千件のリクエストまで簡単にこなすことができます。
利用料金としては、主に任意のコードを実行している時間に料金が発生しますが、その額は微々たるものです。すてきですね。

詳細は以下のページをご覧下さい。
http://aws.amazon.com/jp/lambda/

Cloud Automator

Cloud Automator (以下CA)とは、弊社サーバーワークスが開発しているAWS運用自動化サービスです。
トリガーとアクションを組み合わせて、AWS上の操作を自動化することができます。
アクションは、EC2の起動/停止や、Redshiftの擬似的な起動/停止、Route53のレコード更新など、
基本的なものから少しニッチなものまで、多岐に渡ります。
また、Job完了時にSQSにメッセージを投げることができますので、
CAのJob同士をつなぎ合わせたり、他のサービスとの連携も可能であることも特徴のうちの一つです。

興味のある方は是非こちらよりご登録をよろしくお願いいたします!
http://cloudautomator.com/

本題

さて、登場人物紹介が終わったところで、本題です。

今日のお話は、CAとLambdaをつなぐお話です。
CAでJobを完了したことをトリガーに、CAにはない別の処理を実行したい時ってありませんか?
そんな時は、コードをLambdaにアップロードして、CAとLambdaを連携させましょう。

今日のやりたいことは、「CAでJobが完了したことをトリガーに、LambdaFunctionを起動する」です。

ca-lambda

イメージはこんな感じですね。

勘の良い人は、「CAはJob完了時にSQSにメッセージ投げられるし、SQSでつなげばいいじゃん」と気づくかもしれません。
ところがどっこい、なんとLambdaのイベントソース(トリガー)にはSQSが含まれていないのです。
つまり、CAでJob完了時にSQSにメッセージを投げても、Lambda側でそれをキャッチする術がありません。なんということでしょう。

どうしよう

まだ諦めてはいけません。
LambdaはSQSをトリガーにすることはできませんが、SNSの通知をトリガーにすることはできます。
つまり、SQSにメッセージが入ったことを検知してSNS通知が出せれば良いのです。
ということで、CloudWatchでSQSのメッセージの数を監視して、メッセージが来たらSNSでLambdaに教えてあげる、というような構成にします。

全体図

CAとLambdaの連携は、この図のような流れになります。

ca-lambda

  1. SQSのメッセージ数をCloudWatchで監視(前提)
  2. CAがJob完了時にSQSにメッセージをプッシュ
  3. CloudWatchがSQSのメッセージ数が閾値を超えたことを検知
  4. CloudWatchがSNSを通知
  5. LambdaがSNSの通知を拾ってLambdaFunctionを実行する

作ってみる

全体の構成が決まったので早速作っていきましょう。

2015年5月現在では、Lambdaはまだ東京リージョンには来ていないので、この記事ではOregonのLambdaを使っています。
メインの環境が東京の場合、LambdaのみOregonで、SQSやSNSはTokyoリージョン、ということも可能です。
今回の記事でも、LambdaのみがOregon、その他リソースはTokyoで作成しています。

1. Lambda用のコードを書く

ごりごりっと書きます。
今回は、以下のような流れのコードを書きました。

  1. SQSからメッセージを一つ取得
  2. Amazon LinuxをVPC環境にLaunch
  3. 1で取得したメッセージを削除
  4. 終了

コードの詳細については、この記事では割愛いたします。
 

2. LambdaFunctionを作成する

0-create-lambda-function

AWSマネジメントコンソールからLambdaFunctionを作成しましょう。
必要なファイルをzipで固めて、ブラウザからアップロードします。
ファイルをアップロードして、LambdaFunctionが作成されたら、とりあえずLambdaは置いておきましょう。

3. SQSのキューを作る

CAのJob完了時にメッセージを投げるSQS(キュー)を作ります。
入れ物を作成するだけなのでぽちぽちっと数クリックで完了します。AWSの良いところですね。

ここでは、ca_lambda_testというキューの名前にしました。

4. SNSのTopic/Subscriptionを作る

1-create-sns-topic-subscription

Topicを作成し、Topicに対してSubscription(通知先)を追加します。
ここでは、sqs-notificationというTopicを作成し、Subscriptionとして、先ほど指定したLambdaFunctionをしておきます。
(ちなみに、このタイミングでLambdaFunctionのイベントソースにSNSトリガーが自動で追加されます)

また、ここでSubscriptionとして自分のメールアドレスを追加しておけば、LambdaFunctionが実行されるタイミングで、メールを受信することもできます。SNS便利ですね!

5. CloudWatchのアラームを作成する

 0-create-alarm

手順3で作ったSQSのキューに対して、ApproximateNumberOfMessagesVisibleが1以上だった場合に、
4の手順で作ったSNSに通知を送信する、というアラームを作成します。
ちなみに、ApproximateNumberOfMessagesVisibleとは、受け取り可能な状態のメッセージの数を表します。

これでAWS側の操作はおしまいです!
最後に、一連の動作の起点となるCloud AutomatorのJobを作成していきましょう。

6. Cloud AutomatorでJobを作成する

3-create-ca-job

Cloud AutomatorでJobを作成します。
トリガー、アクション、と順に選んで行きます。
トリガーやアクションの種類は何でもよいですが、ジョブ実行時にSQSにメッセージを送る設定は必ず行いましょう。
ちなみに、お試しの場合はトリガーはHTTPトリガーがおすすめです。ジョブ作成時に専用のcurlコマンドが発行されますので、
それを使って簡単にテストを行うことができます。

4-ca-job-curl-command

動かしてみる

ここまで来たらあとは動かすのみです。
おもむろにterminalを開き、先ほどのcurlコマンドを実行します。

5-execute-curl

{"result": "ok"} が返ってきましたね。
次は、キューにメッセージが届くのを正座して待ちます。

6-checking-que

メッセージが届きましたね!

それでは、次はCloudWatchがSQSのキューの数を検知してアラートを上げてくれるのを正座して待ちます。

7-check-alarm

検知されました!
ALARTと赤文字で出ていて少し驚いてしまいますが、正常にキューが溜まっている証拠です。
CloudWatchでメッセージの存在を検知できたので、続いてLambdaFunctionによってインスタンスがLaunchされるのを待ちます。

そろそろ足がしびれてきました。

EC2のマネジメントコンソールを見る前に、一度Lambdaのマネジメントコンソールをみてみましょう。

9-lambda-execute-log


実行ログが出ていますね、これによりLambdaのログですが無事Launchされていることを確認できました!
それでは、あらためてEC2のマネジメントコンソールを見てみましょう。

91-launched-instance


ちゃんとLaunchできていますね!
というわけでこれにて動作テストは終了です。

まとめ

今回の記事では、CAでJobが完了したことをトリガーに、LambdaFunctionを起動することができました。
全体の流れをもう一度おさらいすると、CA -> SQS -> CloudWatch -> SNS -> Lambda という流れです。

将来的にはLambdaにSQSトリガーが実装され、よりスムーズな連携ができるようになることを期待しています!

それではみなさん、ごきげんよう。xxxxxxxxxx turnOnIframeRefreshTimer(18, 'n20036')