この記事はSnowflakeアドベントカレンダーとちゅらデータアドベントカレンダーの8日目の記事でっす。
Lambda 好きですか?
Lambda 好きっすよね?
サーバレスですし、最近じゃコンテナサポートされたり、メモリが10Gまで使えるようになったり、もはやサーバレスにこれまでみんなが抱いていた、イメージってだいぶ払拭されたんじゃないかしら?
- 参考1:コンテナの話 https://aws.amazon.com/jp/blogs/news/new-for-aws-lambda-container-image-support/
- 参考2:10Gの話 https://aws.amazon.com/jp/blogs/aws/new-for-aws-lambda-functions-with-up-to-10-gb-of-memory-and-6-vcpus/
そんな Lambda から Snowflake に繋げば、もう簡単なETLは全部これにしちゃっても良いんじゃないですかね?
今回は Lambda の Python ランタイムを想定してます。
前準備
とりあえず、いつものように Lambda で Python ランタイムを作っておきましょう。
えいえい!(作ってる音)
ローカルでサクッと開発
まずは、開発するフォルダを作ります。
$ mkdir snow_lambda; cd $_
次に、この後ネイティブバイナリをつかうから、ライブラリは Docker のコンテナで作ってしまいましょう。
下記のファイルを Dockerfile
という名前で上で作った開発用のフォルダに置きます。
FROM python:3.8
USER root
RUN apt-get update
RUN apt-get -y install locales && \
localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm
RUN apt-get install -y vim less zip
RUN pip install --upgrade pip
RUN pip install --upgrade setuptools
私はめんどくさがりだから、 docker-compose.yml
作ってしまいます。
下記のようなファイルを docker-compose.yml
という名前で同じく開発用のフォルダに置きます。
version: "3.5"
services:
app:
restart: always
build: .
working_dir: "/root/"
tty: true
volumes:
- ./:/root/
上記のコンテナ関係の作り方は気に入らなかったら随意に直してください。
私がいつも使ってる設定とかを適当に入れてます。
次にコンテナを立ち上げて、その後はコンテナの中でライブラリを zip に固める作業をします。
$ docker-compose up -d
# …ビルドログがブワーッと流れる…
Starting snowflake_lambda_app_1 ... done
$ docker-compose exec app bash
# ここからはコンテナ内での作業
$ python3 -m venv --without-pip venv
$ source venv/bin/activate
$ cd venv/lib/python3.8/site-packages
$ pip install snowflake-connector-python --target .
$ chmod -R 755 .; deactivate
$ zip -r9 ../../../../snow_lambda.zip .
シャキン!!
これであとはコードを書いてzipに加えるだけ
下記の Python コードを lambda_function.py
という名前で保存してね。
※user、password、accountは自分のSnowflakeのものを使ってね。
import snowflake.connector
def lambda_handler(event, context):
print('## START FUNCTION')
user='HOGE_USER' # 自分の
password='HOGE_PASSWD'
account='hogehoge.ap-northeast-1.aws'
ctx = snowflake.connector.connect(
user=user,
password=password,
account=account
)
cs = ctx.cursor()
try:
cs.execute("SELECT current_version()")
one_row = cs.fetchone()
print(one_row[0])
finally:
cs.close()
ctx.close()
print('## END FUNCTION')
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
できた!
中身は Snowflake のバージョンを出力するクエリを叩いています。
うまく行けば、 Lambda から Snowflake につないでクエリができます。
上記のコードの動作が心配な人は、ローカルで main を作って動かしてみても良いかもです。
さぁ次のコマンドで、今書いた Python コードを、さっき固めた zip ファイルに追加しよう。
$ zip -g snow_lambda.zip lambda_function.py
そして、最後はこの zip ファイルを使って Lambda 関数を更新します。
aws コマンドで、更新しようと思ったのだけど、じつは zip ファイルが40MBくらいになってしまったので、下記のコマンドでは、 Lambda 関数を更新できません。参考程度に載せておきます。
# 多分動かないコマンド
$ aws lambda update-function-code --function-name snowflake-connect --zip-file fileb://snow_lambda.zip --profile hogehoge
かわりに、さっきの snow_lambda.zip を S3 に手でアップロードしよう。(コマンドでアップロードしてもいいです。
そして今度こそ最後にアップロードした S3 パスをコピーして、 Lambda の UI に入力して、 Lambda 関数を更新しましょう。
そしてテストする
ここまでできたらあとはテストを叩いて動かして見るだけ。
テストの結果、下記のようなログ出力が出れば成功です!
終わりに
さて、いかがでしたか?
snowflake.connector
ライブラリが、一部ネイティブライブラリに依存していたので、 Linux 環境で pip を使わないと、 Lambda 用のライブラリを作れないというのが唯一のハマリポイントですが、最初から Linux で作業してる人はコンテナを作ったりする工程は不要だったりします。
もし Mac でつくったりすると、下記のような invalid ELF header
が Lambda からエラーとして出力されます。
[ERROR] Runtime.ImportModuleError: Unable to import module 'lambda_function': /var/task/cryptography/hazmat/bindings/_openssl.abi3.so: invalid ELF header
このあたりを気をつけていただくだけで、 サーバレス Snowflake がはかどりますよ!
ぜひ、皆さんも試してみてください!