3分で作る、スケジュール実行するLambda

AWS製の Chalice というサーバーレスアプリケーションフレームワークを使用して、スケジュール実行するLambdaを爆速で作成します。
2020.04.24

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

スケジュール実行するLambdaを、AWS製の Chalice というサーバーレスアプリケーションフレームワークを使ってサクッと構築したいと思います。Chalice はPython製のツールで開発言語もPythonに限られますが、軽量なフレームワークとなっており、学習コストが低くとっつきやすくなっています。API Gatewayと一緒に使われることが多いかと思いますが、各種イベントソースとの連携にも使えます。

環境

  • macOS Catalina 10.15.4
  • Python 3.7.7
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.15.4
BuildVersion:	19E287

$ python3 -VV
Python 3.7.7 (default, Mar 10 2020, 15:43:33)
[Clang 11.0.0 (clang-1100.0.33.17)]

やってみる

仮想環境の作成

今回は Python で標準機能の venvを利用します。

$ mkdir chalice-example && cd chalice-example
$ python3 -m venv venv

仮想環境の有効化

$ source ./venv/bin/activate
(venv) $

Chaliceのインストール

(venv) $ pip install chalice

Boto3のインストール

今回は処理の中で使用しますので、boto3をインストールします。

(venv) $ pip install boto3

プロジェクトの作成

new-projectコマンドを利用してプロジェクトの雛形を作成します。schedule-lambda フォルダが作成され、各種ファイルが作成されています。

(venv) $ chalice new-project schedule-lambda

ファイルの確認します。app.pyに処理内容をコーディングしていきます。

(venv) $ cd schedule-lambda
(venv) $ tree -a
.
├── .chalice
│   └── config.json
├── .gitignore
├── app.py
└── requirements.txt

コード

app.pyの内容を下記のようにします。処理内容は見ての通りで、リージョンの一覧を出力しています。処理内容に実務的な意味はありません。@app.scheduleにてスケジュール実行することを指定しています。簡単ですね。今回は最短で確認したいので、1分ごとに実行します。

app.py

import logging

import boto3
from chalice import Chalice, Rate

app = Chalice(app_name='schedule-lambda')
app.log.setLevel(logging.INFO)

ec2 = boto3.client('ec2')


@app.schedule(Rate(1, unit=Rate.MINUTES))
def regions(event):
    response = ec2.describe_regions()
    app.log.info(response['Regions'])

必要なライブラリの設定

実行に必要なライブラリを requirements.txtに記載します。これによりデプロイ時に一緒にアップロードされます。

(venv) $ pip freeze | grep boto3 >> requirements.txt
(venv) $ cat requirements.txt
boto3==1.12.39

デプロイ

下記のコマンドでデプロイします。

(venv) $ chalice deploy --stage dev
Creating deployment package.
Creating IAM role: schedule-lambda-dev
Creating lambda function: schedule-lambda-dev-regions
Resources deployed:
  - Lambda ARN: arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXXX:function:schedule-lambda-dev-regions

以上でスケジュール実行するLambdaが作成できました。

作成されたものを確認

Lambda

デコレータをつけた関数がそれぞれLambdaとして作成されています。

プロジェクト名、環境名、関数名というルールで作成されています。トリガーがCloudWatch Eventsになっていることや、ハンドラがそれぞれ設定されていることが分かります。

IAMロール

マネジメントコンソールで、schedule-lambda-dev ロールのポリシーを確認します。 特にポリシーの設定はしていませんが、ec2:DescribeRegions が追加されています。Chaliceではソースコードを解析し、必要な権限を自動で付与してくれます。これが非常に便利でいちいちソースコードを調べる必要がありませんし、面倒だからといって必要以上に大きな権限を付与する必要がありません。 今回はリージョンの一覧を取得する処理だったので、必要な権限が付与されていることが分かります。

ただ、自動で付与されるのは boto3.clientを使用した場合で、boto3.resourceを使った場合は手動で付与する必要があります。その場合は、別途ポリシーJsonを用意しておきます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeRegions"
            ],
            "Resource": [
                "*"
            ],
            "Sid": "7bd95f2e3b7744e9938edf34bb28ec66"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        }
    ]
}

CloudWatch Events ルール

1分間隔で実行するルールが作成されています。

実行ログの確認

正しく実行されているか確認します。マネジメントコンソールにログインし、/aws/lambda/schedule-lambda-dev-regionsというロググループが作成されていますので確認します。

正しく実行されていることが確認できました。

ログはコマンドでも確認することができます。--nameには関数名を指定します。

(venv) $ chalice logs --name regions

後片付け

次のコマンドで作成したリソースが削除できます。ロググループは削除されないので手動で消す必要があります。

(venv) $ chalice delete --stage dev

スケジュール実行の指定方法

文字列で指定する方法と、クラスを使用する方法があり、どちらの方法でも下記のルールに従っています。

Schedule Expressions for Rules - Amazon CloudWatch Events

固定レートの場合

文字列で指定する方法。単数形と複数形を使い分ける必要があります。

# 1時間ごと (単数形になっていることに注意)
@app.schedule('rate(1 hour)')
def every_hour(event):
    pass

# 2時間ごと (複数形になっていることに注意)
@app.schedule('rate(5 hours)')
def every_two_hours(event):
    pass

クラスを使用する方法。こちらは複数形しか使用しないのでより安全です。

# 1時間ごと
@app.schedule(Rate(1, unit=Rate.HOURS))
def every_hour(event):
    print(event.to_dict())

# 2時間ごと
@app.schedule(Rate(2, unit=Rate.HOURS))
def every_two_hours(event):
    print(event.to_dict())

cronの場合

# 文字列指定
@app.schedule('cron(15 10 ? * 6L 2002-2005)')
def cron_handler(event):
    pass

# クラス指定
@app.schedule(Cron(15, 10, '?', '*', '6L', '2002-2005'))
def cron_obj_handler(event):
    pass

その他できること

サポートしているイベントソース

IaCツールとの連携

CloudFormation、Terraformと連携してデプロイを管理することもできます。

その他

最後に

要件がシンプルな場合や、検証のためにサクッとLambdaを作りたいといったときには有用なツールだと感じました。

Serverlessのほうが機能が豊富で同じくらい簡単にスケジュール実行するLambdaを作成することができますが、ポリシーの自動生成とデプロイ時にCloudFormationを使用しないので、デプロイが即時に行われるところがメリットかと思います。要件によって使い分けていただければと思います。