AWSリソースのIPアドレスレンジは
https://ip-ranges.amazonaws.com/ip-ranges.json
でJSON形式で提供されている。このレンジは変更されることがあるので、定期的に確認が必要になる。

今回は、AWS Lambda Fuction for Javaを使って、日次でip-ranges.jsonをチェックし、変更があれば差分をSNSで通知する仕組みを作ってみた。

Lambda Function for Javaの作り方

作成したLambda Functionはこちら

JavaでLambda Functionを実装する場合、Handlerの実装は以下の2つのアプローチがる。

  • 特定のInterfaceを実装せずにハンドラメソッドをロードする方法
  • aws-lambda-java-coreライブラリで提供されるInterfaceを実装する方法

今回は前者で実装。ハンドラメソッドの一般的なシンタックスは↓

outputType handler-name(inputType input, Context context) {
   ...
}
  • inputType
    最初のパラメータはハンドラの入力パラメータで、文字列やカスタムデータオブジェクト等で提供される。シリアライズ可能なデータである必要がある。
  • outputType
    Lambda Functionを同期実行したい場合、サポートされてるデータ型を使って出力を返すことができる。
    Lambda Functionを非同期で実行したい場合は、outputTypeはvoidになる。(S3やKinesis、SNSなどのイベントソースを利用して実行する場合)
  • inputTypeとoutputTypeで利用できる型
    • (Stringやintのような)Javaのプリミティブ型
    • aws-lambda-java-eventsライブラリに定義されてる(S3Eventのような)AWS event型
    • 独自定義したPOJOクラス。Lambdaが自動的にシリアライズ/デシリアライズを行う。
  • Context引数は省略可能

どのクラスのどのメソッドがハンドラのメソッドとして実行されるかは、Lambda Functionの登録時に”jp.co.haw.aws.iprangechecker.RangeCheckHandler::handleRequest”と対象のクラス名とメソッド名を指定する。
もしクラス内に同名のメソッドが存在する場合は以下のルールで対象メソッドが決められる。

  1. 引数が多いメソッドを選択する
  2. 引数の数が同じ場合は、Contextが最後の引数になっているメソッドを選択する。
    Context引数が存在しない場合の動作は未定義。

Logging

Lambda Functionでは以下のコードでCloudWatchへのログエントリを作成することができる。

  • System.out()
  • System.err()
  • LambdaLogger.log()

System.outとSystem.errの各行は別のイベントとして扱われるため、ログエントリの作成にはLambdaLogger.log()の使用が推奨されている。
(例えば、System.out.println(“Hello n world”) のように途中で改行された場合、Helloとwordが別のイベントとして扱われてしまう)

例外

作成したLambda Function実行時に例外が発生した場合、Lambdaが障害を認識し、例外情報(Exceptionのスタックトレース等)をJSONにシリアライズする。

この例外情報は、Invocation TypeがRequestResponseの場合はシリアライズされた例外情報がレスポンスとして戻され、Invocation TypeがEventの場合はCloudWatch LogsやCloudWatch metricsで確認できる。

パッケージング

Lambda FunctionはZIPやJarにパッケージングする必要がある。この時、依存ライブラリが存在する場合はその依存ライブラリもJarの中に含める必要がある。
今回はGradleを使ってjarにパッケージングした。その際build.gradleに記載されている依存ライブラリのクラスを同じjarに入れるため以下の記述を追加する。

jar{
  from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}

日次処理のトリガー

Lambda FunctionのトリガーはAPI Gatewayを作成し、cronで日次でAPIコールすることで対応する。

この辺、AWS Lambdaで定期実行できるトリガーができると便利なんだけどなー。

2015.10.09 追記

AWS re:Invent 2015で、Lambdaのアップデートでスケジュール機能がリリースされたので、スケジュール機能を使って定期実行するようにしてみる。
(このLambda Functionのやってる内容であれば、スケジュール機能を使わなくてもIPのレンジに更新があった場合のSNS通知をトリガーとするのがベストだけど、とりあえずスケジュール機能で設定してみる。)

登録してあるLambda FunctionのEvent sourcesタブのAdd event sourceでEvent sourceを追加する。

eventsource

Event source type にScheduled Eventを選択するとスケジュール実行する設定が追加できる。今回は日次で実行できればいいので、Schedule expressionにrate(1 day)を選択。
この他に5分、15分、1時間の設定とCronライクな形式の設定が可能。

スケジュール機能のおかげでサーバレスな環境がより一層実現しやすくなる!