Snowflakeの外部関数(AWS Lambdaと連携)を試してみた

君はエクスターナル?それともインターナル?
2020.10.15

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

大阪オフィスの玉井です。

Snowflakeの機能に「外部関数」というものがあります。AWSのサービスとがっつり連携する機能ということで、実際にやってみました。

外部関数(external function)とは

ざっくりいうと

「Snowflakeから外部システムのコードを実行できる機能」です。

もう少しだけ詳しく

Snowflake上で外部関数を呼び出すクエリを実行すると、Snowflakeは事前に設定した情報に基づいて、HTTPリクエスト(POST)を作成し、連携済みのプロキシサービスに投げます。プロキシサービスはそのPOSTリクエストを受信し、実際の関数側にリクエストを投げて、結果を返す…という仕組みです。

ドキュメントに下記の概念図が載っていました。

ちなみに、2020年10月現在、Snowflakeが連携できる「プロキシサービス」はAmazon API Gatewayのみとなっております(将来的にAzureやGCPにも対応する模様)。

公式情報

今回やってみる内容の全体像

Snowflakeの公式ドキュメントに載っているチュートリアル的なものを実際にやってみたいと思います。Snowflake以外は全てAWSのリソースを使用します。

概要図

SnowflakeとAWSを色々設定するのですが、最終的に作るものの全体像が下記の通りです。

  1. Snowflake: API Gatewayに対してリクエスト送信
  2. API Gateway: Snowflakeからのリクエストの権限を確認
  3. API Gateway: Lambda関数に(Snowflakeからの)リクエストを送信
  4. Lambda: 受信したデータを元に処理を行い、結果を出力
  5. Snowflake: Lambda関数からの出力結果を取得

やってみた

参考ドキュメント

AWS側

AWS Lambdaで関数を作成する

まずは、Snowflakeから実行したい処理の内容を用意します。すなわち関数そのものの作成ですね。今回は公式ドキュメントに載っているサンプル関数をそのまま使います。

使用言語はPython3.7です。デフォルトの実行ロールは下記の通りです。

公式のサンプル関数のコードを貼り付けます。関数の内容ですが、ざっくりいうと、投げた値(数値と文字列を1つずつ)をそのまま返すというものです。

関数が出来たら、念の為テストします。テスト用のリクエストも公式にあるので、それを使います。

テストを実行して、問題ないことを確認します(テスト用に投げた値がそのまま返ってきてますね)。

IAMロールを作成する

SnowflakeをAWSに認証させるため、Snowflake側のIAMユーザー(Snowflakeが動いているAWS側のIAM)を、こちら側のAWSで引き受けるためのロールを用意します。

ロールの新規作成で、「別のAWSアカウント」を指定します。アカウントIDなのですが、ここはLambdaを設定したものと同じアカウントIDを設定します。

途中の設定は全部すっ飛ばして、ロール名をつけて保存します。ロールの中身を一切設定していませんが、このままで問題ありません。一旦、先に進みましょう。

API GatewayでAPIを作成する

SnowflakeからコールするAPIを作成します。

「REST API」を新規作成します。

基本的にデフォルト設定のままでOKです。名前をつけます。

POSTメソッドを作成します。POSTリクエストを受け取ったら、実際に動いてほしいのは、先程作成したLambda関数なので、Lambdaと連携するように、下記のように設定します。

一旦、APIをデプロイします。

ステージを用意して、いざデプロイ。

デプロイできました。URLは後ほど使用するので、メモっておきましょう。

一度デプロイしましたが、まだ設定が残っています。

まず、リソースポリシーを設定します。とりあえずドキュメントに載っているポリシーをそのまま貼り付けます。

{
    "Version": "2012-10-17",
    "Statement":
    [
        {
        "Effect": "Allow",
        "Principal":
            {
            "AWS": "arn:aws:sts::<12-digit-number>:assumed-role/<external_function_role>/snowflake"
            },
        "Action": "execute-api:Invoke",
        "Resource": "arn:aws:execute-api:us-west-2:123456789012:ljkfds890a/*/POST/MyResourceName"
        }
    ]
}

ただし、編集しないといけない部分が3つあります。このAPIを動かせるヤツを設定する感じですね。

  • <12-digit-number>:AWSのアカウントID
  • <external_function_role>:さっき作ったIAMロール名
  • Resourceの値:メソッドリクエストARN

メソッドリクエストのARNは下記に載っています。

あと、メソッドリクエストの設定で、認可を「AWS_IAM」に設定します(私はここを設定していなくてハマってました)。

Snowflake側

API Gatewayの設定が終わったら、AWS側は一旦置いといて、Snowflake側の設定に移ります。

API統合を作成する

Snowflakeが外部関数を使用するとき、プロキシサービス(Amazon API Gateway)にHTTPリクエストを送るわけですが、そのリクエストを生成するための情報を、「API統合」というオブジェクトとして予め作っておく必要があります。

今回は下記のクエリで作成します。ちなみに、作成するためにはCREATE INTEGRATIONができる権限をもつロール(もしくはACCOUNTADMIN)が必要です。

create or replace api integration my_api_integration_01
  api_provider = aws_api_gateway
  api_aws_role_arn = '<cloud_platform_role_ARN>'
  enabled = true
  api_allowed_prefixes = ('https://')
  • <cloud_platform_role_ARN>:先程作成したIAMロールのARN
  • api_allowed_prefixes:先程作成したAPIの呼び出しURL

作成したAPI統合の内容を確認

作成したAPI統合は、describeで確認することができます。

ここで、下記2つの値についてメモっといてください。

  • API_AWS_IAM_USER_ARN
  • API_AWS_EXTERNAL_ID

AWS側

SnowflakeにAPI統合を作成したら、再びAWSに戻ります。

ここでやることは、冒頭で作成したIAMロールに信頼関係のポリシーを追加することです。

信頼関係のポリシー編集画面を開くと、デフォルトで色々あると思いますが、下記のように編集します。

  • "AWS":先程作成したAPI統合のAPI_AWS_IAM_USER_ARNに置き換える
  • "Condition"
    • 中括弧の中を"StringEquals": { "sts:ExternalId": "xxx" }に置き換える
    • xxxの中は、先程作成したAPI統合のAPI_AWS_EXTERNAL_ID

最終的には下記のような感じになります。

Snowflake側

IAMロールの信頼関係の編集を終えたら、再びSnowflake側の設定です。

外部関数を作成する

いよいよ「外部関数」を作成します。

下記のクエリを使用します。

create external function my_external_function(n integer, v varchar)
    returns variant
    api_integration = <api_integration_name>
    as '<invocation_url>'
  • api_integration_name:API統合の名前に置き換える
  • invocation_url:APIを呼び出すURLに置き換える

引数に関しては、作成したLambda関数によります。今回は数値と文字列を1つずつ投げて、それがそのまま返えってくる関数なので、作成する外部関数もこのような形になります。

外部関数を実行する

SELECT句に実行したい外部関数をいれます。もちろん引数に値を入れてください。

成功しました! ちなみに、外部関数の実行についても、ロールに(その外部関数の)USAGE権限が無いと実行できないので注意してください。

外部関数のメリット

「決まった処理を行う関数が必要なら、UDFとかストアドプロシージャとかあるやん」って思いますよね。

しかし、UDFは使える言語がかなり限られています(ストアドはそのDB用の言語)。例えば今回使用したLambdaであれば、Python等のメジャーなものや、自分が使い慣れているものなど、幅広い選択が可能です。また、Lambdaは他のAWSサービスとの連携も非常に豊富なので、Snowflakeだけの機能を使うより、より高度な処理を実行させることができます(例:機械学習)。

未検証な事項

  • AWS以外で動作しているSnowflakeでの設定
    • ドキュメントを読む限り、今回と同様の設定は実現できそう
  • AWS Lambda以外のサービスの使用
    • Amazon EC2(内にデプロイしているコード)でも出来る、ということはドキュメントに書いてある

おわりに

最初の設定がちょっと複雑ですが、一度設定してしまえば、どのような処理をさせるかはコード内の話なので、使いようによってはSnowflakeがめちゃパワーアップする気がします。