Pythonで書いたコードをzipにまとめてAWS Lambdaで実行する方法について紹介します。 pipでインストールしたモジュールも一緒にLambdaにアップロードします。
個人的な興味として、気象情報を取得してDynamoDBに保存するコードを定期的に実行する方法も書きます。こういったバッチ処理を切り出すとメインサーバの負荷が減ったり、コードの見通しが良くなり保守性も上がります。
この2つの続きであり、目標は気象データのAPIを作成することです。
環境
- Linux Mint (Mac, Windows 10でも動作確認済み)
- Python 3.6
Lambdaで動かすための準備
ローカルにて作成したコードをLambdaで動かすための準備をします。
Lambdaで外部モジュールを利用するには?
LambdaではPythonコードを直接書いて(インラインコード)実行することが出来ます。 ただし、この場合は外部モジュールをpipでインストールすることは出来ません。 そこで、コードとモジュールをzipでまとめてアップロードする必要があります。
手作業でzipにまとめる場合は、まずモジュールをコードと同じディレクトリにインストールします。
pipでカレントディレクトリにインストールする
pipでコードと同じディレクトリにインストールします。 前回作成したコードに必要なモジュールを用意していきます。 以下の2つをインストールする。
- boto3
- requests
実行ファイルのあるディレクトリに移動して、オプション-tで指定の場所にモジュールをインストールする。
$ cd [実行ファイルのディレクトリ]
$ pip install boto3 -t ./
$ pip install requests -t ./
そうすると以下のようなファイルが置かれます。
lambda_function.pyは前回製作したLambdaで実行するメインコード。
$ ls
bin chardet jmespath s3transfer
boto3 chardet-3.0.4.dist-info jmespath-0.9.3.dist-info s3transfer-0.1.13.dist-info
boto3-1.9.86.dist-info dateutil lambda_function.py six-1.12.0.dist-info
botocore docutils __pycache__ six.py
botocore-1.12.86.dist-info docutils-0.14.dist-info python_dateutil-2.7.5.dist-info urllib3
certifi idna requests urllib3-1.24.1.dist-info
certifi-2018.11.29.dist-info idna-2.8.dist-info requests-2.21.0.dist-info
一度、lambda_function.pyを実行して確認してみてください。
実行権限を変更する
Lambdaが実行できるようにファイルの権限を変更します。
755をつけます。 実行と読みこみの権限があれば大丈夫です。
$ chmod -R 755 ./*
-Rはディレクトリ内も再帰的に処理します。
zipに圧縮してまとめる
続いて、Lambdaにアップロードするためコードとモジュールをzipにまとめます。 コマンドでも圧縮ツールでまとめるてもどちらでも良いです。
この時、コードのディレクトリ内のファイルを選択してzipにして下さい。 ディレクトリを選択してzipにすると、1階層分深くなりLambdaのハンドラが読めません。 (Lambdaの設定で回避できそうだが、上手くいかなかった。)
ツールで圧縮する。
ツールで圧縮しても大丈夫です。 ただし、Windowsの場合はAWSのドキュメントにある通り7-ZIPでzip化することが推奨されています。

図のように、ディレクトリ内のファイルを選択してzipにして下さい。
コマンドでzipにする。(LinuxまたはMac)
以下のコマンドでzipにできます。
$ cd [実行ファイルのディレクトリ]
$ zip -r zip_file ./*
zip_file.zipというファイルができているはずです。
ファイル名は適宜変更して構いません。
zipコマンドの詳細はこちら。
AWS Lambdaを用意する
続いてAWS Lambdaを用意していきます。
Lambda関数を作成する
AWSコンソールのサービスからLambdaを選びます。 「ダッシュボード」か「関数」から「関数の作成」ボタンを押してLambda関数を作成します。 この後は「一から作成」の手順を解説します。
それぞれ項目を記入していきます。

Pythonのランタイムは2.7、3.6、3.7とあり利用したいバージョンを選びます。 バージョンは後からも変更可能です。 (図では3.7を選んでいますが3.6で前回作ったことを思い出して、後で変更しました。)
ロールは既存のロールを選択して、「microService」を選んでみました。 DynamoDBの操作と定期実行するためのCloudWatch Eventsを利用できます。
下図は関数を作成後の初期画面です。 左がLambdaを実行するトリガー、右がLambdaが操作できるサービスです。 選択できるサービスはロールの「microService」が権限を持つものです。

CloudWatch Eventsを設定して定期実行する
定期実行するために左のトリガーからCloudWatch Eventsを選択します。

CloudWatch Eventsがトリガーとして追加されました。 CloudWatch Eventsをクリックすると実行する時間を設定できます。

cron(2/30 * * * ? *)利用するOpenWeatherMapのAPIの更新頻度が30分間隔なので、スケジュールも30分ごとに設定しました。 ちょうどだと更新出来ていないことがあるので、2分待ってから取得する設定です。
AWS公式による設定の詳細はこちら。
設定が終わるとこうなります。

追記:Eventの詳細で実行した日時の記録が見れました。設定は間違っていないようです。

Lambdaの設定は以上です。
zipファイルをLambdaにアップロードする
間が空きましたが、作成したzipファイルをLambdaにアップロードします。

「アップロード」ボタンからzipファイルを選んでアップロードします。
ファイルサイズの制限とAmazon S3経由でのアップロード
この方法では、ファイルサイズに10MBまでの制限があります。 それ以上に大きい場合は、一度S3を経由してLambdaへアップロードすることになります。 「コード エントリ タイプ」を変更して、Amazon S3 のリンク URLを張り付けます。

ハンドラの設定
「ハンドラ」はLambdaが実行する関数を指定する箇所です。
デフォルトではlambda_function.lambda_handlerとなっています。
これはlambda_function.pyのlambda_handler()という関数を実行します。
前回作ったコードもデフォルトに合わせてあります。 また、zipファイルを作成するとき階層を意識してzip化したのもこのためです。
アップロードが終わったら、上部の「保存」を押してコードを保存して下さい。
Lambda関数をテストしてみる。
以上で実行できるはずです。 大丈夫かどうかテストしてみます。
「保存」ボタンの横に「テスト」があるので押します。

こんな感じになりますが、今回のコードはパラメータを受け取るタイプではないので適当に作っても実行できます。 イベント名をつけたら保存します。
先ほど作ったテストイベントを選んで「テスト」を押すと実行できます。

成功するとこうなります。
詳細を開くとprint()で出力したログが出るのでちゃんと動いているか確認できます。
処理に失敗した場合
失敗した場合はエラーログを確認してください。
よくあるのはpermission deniedでファイルの実行権限がない。また、「ファイルもしくは関数が見つかりません」です。
ディレクトリ構造と権限を見直して何度が試してみてください。
スケジュール実行は待ってみて結果が解ると思います。
おわりに
以上で定期的に気象データを取得してDynamoDBに貯めることが出来ました。
zipでLamdaにあげる以外にも、zappaを使ってコマンドで行う方法もあります。 zappaはモジュールをまとめるだけでなく、API Gatewayなども自動で用意させることも出来ます。 そちらを知りたい方は、Zappaを使ってPythonのコードをAWS Lambdaにデプロイするを見てください。