Lambda から Snowflake にシュッとつなぎたい！

この記事は[Snowflakeアドベントカレンダー](https://adventar.org/calendars/5085)と[ちゅらデータアドベントカレンダー](https://qiita.com/advent-calendar/2020/churadata)の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 ランタイムを作っておきましょう。
えいえい！（作ってる音）

![Lambda-8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75613/6680a8f3-dd7f-79b5-cb8a-1f5d61995e90.png)
※関数名は適当です

# ローカルでサクッと開発

まずは、開発するフォルダを作ります。

```bash
$ mkdir snow_lambda; cd $_
```

次に、この後ネイティブバイナリをつかうから、ライブラリは Docker のコンテナで作ってしまいましょう。
下記のファイルを `Dockerfile` という名前で上で作った開発用のフォルダに置きます。

```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` という名前で同じく開発用のフォルダに置きます。

```docker
version: "3.5"
services:
  app:
    restart: always
    build: .
    working_dir: "/root/"
    tty: true
    volumes:
      - ./:/root/
```

上記のコンテナ関係の作り方は気に入らなかったら随意に直してください。
私がいつも使ってる設定とかを適当に入れてます。

次にコンテナを立ち上げて、その後はコンテナの中でライブラリを zip に固める作業をします。

```bash
$ 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のものを使ってね。

```python

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 ファイルに追加しよう。

```bash
$ zip -g snow_lambda.zip lambda_function.py
```

そして、最後はこの zip ファイルを使って Lambda 関数を更新します。
aws コマンドで、更新しようと思ったのだけど、じつは zip ファイルが４０MBくらいになってしまったので、下記のコマンドでは、 Lambda 関数を更新できません。参考程度に載せておきます。

```bash
# 多分動かないコマンド
$ 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-connect_-_Lambda-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75613/1655f0a7-f494-6d65-d3f5-2654fb2f2499.png)


# 終わりに

さて、いかがでしたか？

`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 がはかどりますよ！
ぜひ、皆さんも試してみてください！