15分の壁を越えろ!GlueのPython shellでVPC Lambdaもどきの長時間ジョブを動かしてみた

2019.10.08

こんにちは、平野です。

Lambdaが便利すぎて何でもLambdaで処理したい病になっているのですが、やはりタイムアウトが大敵です。 15分あるので、分割したりできる処理であればかなりのタスクがLambda関数に落とし込めるのですが、 分割しようもないタスクでは15分という壁は非常に高いものになってしまいます。

業務で、時々取得に15分以上も時間がかかるWebAPIへのアクセスというのがあったので、 今回はGlueのPython shellを使ってこの15分の壁を越えてみたいと思います。 (越えるというよりは、壁がない道を選んでるだけですけど...)

また、そのWebAPIへはアクセスできるIPに制限がかかっており、 特定のVPC内からでないとアクセスが届かないようになっていたので、 そのVPCの中でPython shellを起動してきちんとデータ取得ができるかも確かめてみました。

アクセス制限のない場合

まずは特にVPCに縛りがない場合です。

Python shellのジョブ作成を行っていきます。

TypeはPython shellを選択します。 Pythonのバージョンは最新を選び、「ユーザーが作成する新しいスクリプト」を選択します。

次の「接続」の設定は、特に何も選択せずにスクリプトの作成に移ります。

スクリプトは以下のような、URLを叩いて結果を表示しつつS3にgzip保存するものを用意します。 解説するようなポイントはないのですが、むしろそれが大事で、 外部パッケージなどが必要になるとPython shellで利用するのはなかなか大変そう(まだやったことない)なので、 Python shellで標準で使用できるパッケージだけで利用できることは非常に重要です。

import boto3
import urllib.request
import gzip

try:
    url = "https://www.yahoo.co.jp"
    req = urllib.request.Request(url)
    with urllib.request.urlopen(req) as res:
        text = res.read()
        print(text.decode('utf8'))
        text_gz = gzip.compress(text)
    s3_client = boto3.client('s3')
    s3_client.put_object(
        Bucket="cm-hirano-debug-bucket", Key="test_output.gz", Body=text_gz)
except Exception as e:
    print(str(e))
    raise e

スクリプトを保存してジョブを実行すれば(パラメータ等の設定は不要)、 期待通りURLの中身が取得できました。

上記は数十秒程度で完了します(Python shellの起動にちょっと時間かかる)が、 Lambda関数ではないので、当然15分という縛りなどはありません。 Python shellのデフォルトのタイムアウト時間は......、2440分 = 48時間です!

VPC内での実行

次に、IP制限がかかっているなどの理由で、 特定のVPCからのみアクセス可能なURLに対してリクエストする場合を想定して、 目的のVPC内で実行するように設定を行います。

Glueの場合、Lambdaのように明示的にVPC内部でジョブを動かすという設定はありません。 しかしVPC内にあるRDSなどに接続する際には、指定したVPC内で動作するように設定が可能です。 なので、ダミーの接続情報を作成することで、VPC内Python shellを実現します。

なお、VPC、サブネットなどはすでに作成済みとします。

接続の作成

ダミーの接続を作成していきます。

接続名を適当に設定し、接続タイプはJDBCを選びます。

JDBC URLはダミーの適当なもので大丈夫ですが、バリデーションがあるので、 jdbc:protocol://host:0/databasename のような文字列を入力しておきます。 ユーザ名、パスワードについても適当な文字列で大丈夫です。

VPCとサブネットは、ジョブを実行させたい目的のものを選択します。

また、セキュリティグループとしては、JDBC データストアに接続するための VPC の設定に記載されている通り、 自己参照のインバウンドルールを持つセキュリティグループを指定します。 以下のように、自分自身のセキュリティグループに対してインバウンドが全て開いたものを作成して設定します。

ジョブに接続を付与する

作成した「接続」をジョブに付与します。

S3のVPCエンドポイントの作成

最後にVPCからS3へ接続できるようにエンドポイントを作成します。

S3のサービスを選び、

エンドポイント作成先のVPCとルートテーブルを選択します。

実行

以上で設定は完了です。 これでジョブが目的のVPC内で実行されるようになりました。

スクリプト内のターゲットURLを変更して実行してみると、 実際に15分以上の処理が実行できていることが確認できました。 (Lambda関数じゃないので当たり前ですが)

※横長の画像を折り返して貼っています

料金

Lambda関数で実行した時との料金を比較してみます。

Lambdaのメモリは128MBとして、15分のジョブを実行した際の料金は以下のようになり、 Python shellを使った場合は4倍弱程度の料金となります。

Lambda AWS Lambda 料金

$0.0000002 + 0.0000166667 * 1/8(GB) * 900(秒) = $0.0018752

Glue Python shell AWS Glue の料金

$0.44 * 0.0625(DPU) * 1/4(時間) = $0.006875

※20191008現在での東京リージョンでの料金
※Lambdaは無料枠があります

まとめ

Lambda関数の15分制限に収まらないWebAPIへのアクセスを、GlueのPython shellを使って実現できました。 また、特定VPCからの接続に限定されているWebAPIへアクセスを行うため、ジョブが動くVPCの設定も行いましたが、 こちらも期待通りの挙動となり、無事15分の壁を超えてデータ取得をすることができました!

Python shellはLambda関数ほど柔軟性がなく色々制約もありますが、 ユースケースが合えば、実質タイムアウトなしのサーバレスPython実行環境として使うことができそうです! 他にも応用できる場面もあるかもしれませんので、機会があればまた挑戦してみたいです。

以上、誰かの参考になれば幸いです。

その他参照リンク

Amazon S3 における Amazon VPC エンドポイント