Azure Blob Storage を外部ステージに設定しアウトバウンドプライベート接続によるバルクロードと Snowpipe を試してみた #SnowflakeDB
はじめに
Azure Blob Storage を外部ステージに設定し Azure Praivate Link によるアウトバウンドプライベート接続でのデータロードを試してみましたので、その際の手順を本記事でまとめてみます。
機能概要
現在の Snowflake では、インバウンド アクセス向けのプライベート接続機能の他、Snowflake アカウントからの送信接続(アウトバウンド接続)にも Private Link のサポートが拡張されています。これにより、 AWS や Microsoft Azure 上のリソースへの接続時のトラフィックをプライベート エンドポイントを経由させ、各種サービスへの通信をプライベート ネットワーク内で保護されるように構成できます。
本機能については、以下の公式ブログがとても参考になりますので、ぜひこちらもご参照ください。
Azure Private Link によるインバウンド アクセス向けのプライベート接続については、以下の記事ご参照ください。
コストと制約
主な特徴は以下です。
- コスト
- エンドポイント一つあたりの料金:$14.00/1,000時間
- エンドポイントで処理されたデータの料金:$10.24/TB(最初の1PB)
- ※それぞれ AzureJapan East (Tokyo)の場合
- 制約
- Snowflake アカウントごとに 5 つ以上のプライベート エンドポイントを持つことはできない
詳細は以下をご参照ください。
前提条件
本機能の使用には Business Ciritical 以上のエディションが必要です。
本記事では以下の環境で検証しています。
- Snowflake
- エディション:Business Ciritical
- クラウドサービス:Microsoft Azure
- リージョン:japaneast
- Microsoft Azure
- ストレージ アカウントのリージョン:japaneast
- 以下のリソースは作成済み
- リソースグループ
- ストレージ アカウント
- Snowpipe で使用するストレージキュー向けのストレージ アカウント
手順は以下に記載があるので、こちらに沿って進めます。
ストレージ統合を使用する外部ステージアクセスの構成
バルクロードや Snowpipe のいずれであっても、外部ステージへのプライベート接続アクセスを構成する必要があります。
後の手順で必要となるので、Azure 側で以下の情報を取得しておきます。
- Microsoft Entra テナントID
- ストレージ アカウントの名称
- ストレージアカウントのリソースID
プライベートリンク エンドポイントをプロビジョニング
アウトバウンド プライベート接続のためには、Snowflake 側でプライベートリンク エンドポイントをプロビジョニングする必要があります。
ACCOUNTADMIN で SYSTEM$PROVISION_PRIVATELINK_ENDPOINT を実行します。これにより、Snowflake アカウント側の VNet にプライベートエンドポイントがプロビジョニングされます。
USE ROLE ACCOUNTADMIN;
SELECT SYSTEM$PROVISION_PRIVATELINK_ENDPOINT(
'<ストレージアカウントのリソースID>',
'<ストレージ アカウントの名称>',
'blob'
);
ストレージ アカウントでプライベート エンドポイントを承認
先のコマンド実行後、Azure ポータルで対象のストレージ アカウントの「ネットワーク > プライベート エンドポイント」を開くと、下図のように新しいプライベート エンドポイントが「Pending」ステータスの状態で表示されます。
プライベート エンドポイントの接続を承認します。
承認後「Connection state」が「Approved」に変わります。
Azure 側での承認作業後、Snowflake 側で SYSTEM$GET_PRIVATELINK_ENDPOINTS_INFO を実行し、ステータスを確認します。
SELECT SYSTEM$GET_PRIVATELINK_ENDPOINTS_INFO();
出力は以下のようになっており、対象のストレージ アカウントに関するstatus
がApproved
になっていることを確認します。
[
"{
\"provider_resource_id\":\"<ストレージアカウントのリソースID>",
\"snowflake_resource_id\":\"<プライベートエンドポイントのID>",
\"host\":\"<ストレージアカウント名>.blob.core.windows.net\",
\"endpoint_state\":\"CREATED\",
\"subresource\":\"blob\",
\"status\":\"Approved\"
}"
]
ストレージ統合の作成
Snowflake 側でストレージ統合を作成します。
ポイントとして、プライベート接続を使用するためにUSE_PRIVATELINK_ENDPOINT
オプションをTRUE
としてオブジェクトを作成します。
CREATE OR REPLACE STORAGE INTEGRATION outbound_private_link_int
TYPE = EXTERNAL_STAGE
STORAGE_PROVIDER = AZURE
AZURE_TENANT_ID = '<Microsoft Entra テナントID>'
STORAGE_ALLOWED_LOCATIONS = ('azure://<ストレージアカウント名>.blob.core.windows.net/<コンテナー名>/')
USE_PRIVATELINK_ENDPOINT = TRUE
ENABLED = TRUE;
ストレージ統合に対するアクセスを許可
ストレージ統合を作成したら、Azure 側で Snowflake にアクセス権を付与する作業が必要です。こちらは、プライベート接続を使用しない通常のストレージ統合オブジェクト作成時と同様の手順です。
以下を実行し、Microsoft のアクセス許可リクエストページへの URL とアプリケーション名を取得します。
DESC STORAGE INTEGRATION outbound_private_link_int;
出力のAZURE_CONSENT_URLが Microsoft のアクセス許可リクエストページへの URL となるので、コピーしてブラウザで開き、承諾します。
また、AZURE_MULTI_TENANT_APP_NAMEプロパティの値も後ほど使用するので控えておきます。
承諾後は、Azure ポータルの「Microsoft Entra ID > エンタープライズ アプリケーション」より Snowflake サービスプリンシパルが作成されていることを確認できます。
続けて、Snowflake サービスプリンシパルにストレージへのアクセス権(ロール)を付与します。
付与対象のロールは以下のいずれかです。
- ストレージ BLOB データ閲覧者
- 読み取りアクセスのみを許可
- ストレージ BLOB データ共同作成者
- 読み取りおよび書き込みアクセスを許可
- データのロードに加え、アンロードや REMOVE コマンドによるファイルの削除も可能になります
ロールの割り当て先は、DESC STORAGE INTEGRATION
で取得したAZURE_MULTI_TENANT_APP_NAME
のアンダースコアより前の文字列を検索することで表示されるアプリケーションです。
上記の手順の詳細は以下をご参照ください。
ストレージ統合を使用する外部ステージを作成
Azure 側の設定が完了したので、Snowflake 側で外部ステージを作成します。STORAGE_INTEGRATION
には、先の手順で作成したUSE_PRIVATELINK_ENDPOINT
オプションがTRUE
の統合オブジェクトを指定します。
CREATE OR REPLACE STAGE my_storage_private_stage
URL = 'azure://<ストレージアカウント名>.blob.core.windows.net/<コンテナー名>/';
STORAGE_INTEGRATION = outbound_private_link_int;
問題なければ、この時点で Snowflake 側からステージ上のファイルの確認も可能です。
バルクロードによるデータロード
外部ステージを作成できたので、COPY コマンドによるバルクロードを試してみます。
--ファイルフォーマットを作成
CREATE OR REPLACE FILE FORMAT my_csv_format
TYPE = CSV
FIELD_DELIMITER = ','
PARSE_HEADER = TRUE
EMPTY_FIELD_AS_NULL = true
COMPRESSION = AUTO;
--スキーマ検出機能によりテーブルを定義
>CREATE OR REPLACE TABLE mytable
USING TEMPLATE (
SELECT ARRAY_AGG(OBJECT_CONSTRUCT(*))
FROM TABLE(
INFER_SCHEMA(
LOCATION=>'@my_storage_private_stage/',
FILE_FORMAT=>'my_csv_format'
)
)
);
--コピーコマンドでロード
>COPY INTO mytable
FROM @my_storage_private_stage
FILE_FORMAT=(FORMAT_NAME = 'my_csv_format')
MATCH_BY_COLUMN_NAME = CASE_INSENSITIVE;
問題なくロードできました。
プライベート接続を使用する Snowpipe の構成
続けてプライベート接続を使用する Snowpipe の構成を行います。前提として Snowpipe でも外部ステージからのデータロードを行うことになるので、対象の外部ステージにはプライベート接続を使用する統合オブジェクト経由で接続します。
この統合オブジェクトはこれまでの手順で作成したものを使用します。
Event Grid サブスクリプションの構成
前提条件でストレージキュー向けのストレージ アカウントの作成は実施済みなので、新しいストレージキューを作成します。
# 環境変数を設定
resource_group_name="rg-test" # 作成済みのリソースグループ名
data_storage_account_name="<Blobストレージの作成先ストレージアカウント名>"
storage_queue_account_name="<ストレージキューの作成先となるストレージアカウント名>"
queue_name="snowpipe-private-link-queue" # キューの名前
subscription_name="snowpipe-private-link-subscription" # サブスクリプション名
# ストレージキューを作成
az storage queue create \
--name $queue_name \
--account-name $storage_queue_account_name
作成されたストレージキューは「(対象の)ストレージアカウント > データストレージ > キュー」から確認できます。
続けて以下のコマンドで Event Grid サブスクリプションを作成しました。
# データ用ストレージアカウントIDを取得 (Event Gridのソース)
storageid=$(az storage account show \
--name $data_storage_account_name \
--resource-group $resource_group_name \
--query id \
--output tsv)
# キュー用ストレージアカウントIDを取得 (Event Gridの宛先)
queue_storage_id=$(az storage account show \
--name $storage_queue_account_name \
--resource-group $resource_group_name \
--query id \
--output tsv)
# キュー用ストレージアカウントIDを使用し、キューのIDを作成
queueid="$queue_storage_id/queueservices/default/queues/$queue_name"
# Event Gridサブスクリプションを作成
az eventgrid event-subscription create \
--source-resource-id $storageid \
--name $subscription_name \
--endpoint-type storagequeue \
--endpoint $queueid \
--advanced-filter data.api stringin CopyBlob PutBlob PutBlockList FlushWithClose SftpCommit
完了後「Event Grid > Azure サービス イベント > システム トピック」よりトピックをクリックするとサブスクリプションを確認できます。
ストレージ キュー向けのプライベートリンク エンドポイントをプロビジョニング
ACCOUNTADMIN で SYSTEM$PROVISION_PRIVATELINK_ENDPOINT を実行し、Snowflake アカウント側の VNet にストレージ キュー向けのプライベートエンドポイントを作成します。
事前に Azure 側で以下を確認します。
- ストレージキューの作成先ストレージアカウントの名称
- ストレージキューの作成先ストレージアカウントのリソースID
上記の情報を使用し、以下を実行します。
USE ROLE ACCOUNTADMIN;
SELECT SYSTEM$PROVISION_PRIVATELINK_ENDPOINT(
'ストレージキューの作成先ストレージアカウントのリソースID',
'<ストレージキューの作成先ストレージアカウント名>.queue.core.windows.net',
'queue'
);
ストレージ アカウントでプライベート エンドポイントを承認
コマンド実行後、Azure ポータルで対象のストレージ アカウントの「ネットワーク > プライベート エンドポイント」を開くと、下図のように新しいプライベート エンドポイントが「Pending」ステータスの状態で表示されるので、プライベート エンドポイントを承認します。
承認後、Snowflake 側で SYSTEM$GET_PRIVATELINK_ENDPOINTS_INFO を実行し、ステータスを確認します。
SELECT SYSTEM$GET_PRIVATELINK_ENDPOINTS_INFO();
出力は以下のようになっており、対象のストレージ アカウントに関するstatus
がApproved
になっていることを確認します。先の手順でストレージ向けのエンドポイントも作成している場合、配列で複数表示されます。
[
"{
\"provider_resource_id\":\"<ストレージアカウントのリソースID>",
\"snowflake_resource_id\":\"<プライベートエンドポイントのID>",
\"host\":\"<ストレージアカウント名>.blob.core.windows.net\",
\"endpoint_state\":\"CREATED\",
\"subresource\":\"queue\",
\"status\":\"Approved\"
}"
]
通知統合の作成
Snowflake 側でストレージ統合を作成します。
通知統合の作成には、以下の情報が必要なので、事前に Azure 側で確認しておきます。
- ストレージキュー URL
- 「ストレージアカウント > データストレージ > キュー」より確認できます
- テナント ID
こちらも先と同様に、プライベート接続を使用するためにUSE_PRIVATELINK_ENDPOINT
オプションをTRUE
としてオブジェクトを作成します。
CREATE OR REPLACE NOTIFICATION INTEGRATION private_notification_int
ENABLED = TRUE
TYPE = QUEUE
NOTIFICATION_PROVIDER = AZURE_STORAGE_QUEUE
AZURE_STORAGE_QUEUE_PRIMARY_URI = "<ストレージキュー URL>"
AZURE_TENANT_ID = '<テナント ID>'
USE_PRIVATELINK_ENDPOINT = TRUE;
オブジェクト作成後は、Azure 側で Snowflake にアクセス権を付与する作業が必要です。
以下を実行し、Microsoft のアクセス許可リクエストページへの URL とアプリケーション名を取得します。
DESC NOTIFICATION INTEGRATION private_notification_int;
その後の手順(ロールの割り当てなど)は通常の Snowpipe 構成時と同じです。詳細は以下をご参照ください。
Snowpipe の作成
さいごに、Snowflake 側で外部ステージやパイプ、パイプによりデータロード対象となるテーブルを作成します。外部ステージは前半の手順で作成したプライベート接続構成済みのものを使用します。
テーブルやファイルフォーマットも事前に作成済みのものを使用します。テーブルはCREATE OR REPLACE TABLE
で再作成しておきました。
--スキーマ検出機能によりテーブルを再度定義
CREATE OR REPLACE TABLE mytable
USING TEMPLATE (
SELECT ARRAY_AGG(OBJECT_CONSTRUCT(*))
FROM TABLE(
INFER_SCHEMA(
LOCATION=>'@my_storage_private_stage/',
FILE_FORMAT=>'my_csv_format'
)
)
);
パイプを定義します
CREATE OR REPLACE PIPE mypipe_private
AUTO_INGEST = true
INTEGRATION = 'PRIVATE_NOTIFICATION_INT'
AS
COPY INTO test_db.public.mytable
FROM @test_db.public.my_storage_private_stage/
FILE_FORMAT=(FORMAT_NAME = 'my_csv_format')
MATCH_BY_COLUMN_NAME = CASE_INSENSITIVE;
対象の Blob ストレージにファイルを配置します。
少しして、テーブルのデータを確認すると自動でデータがロードされていました。
ロード履歴
プライベート接続を拒否してみる
Blob ストレージの作成先ストレージ アカウントのプライベート エンドポイントを拒否するとどのようになるか確認してみます。
拒否する
その他は特に設定変更せずにコピーコマンドを実行してみます。
この場合、各種権限は付与しているためか明示的にエラーとはならず、クエリが数分実行される状態が続いたので、途中でキャンセルしました。
また、こちらは未検証ですが、一度接続を拒否した後、再度承認するには、もう一度 Snowflake 側でプライベート エンドポイントのプロビジョニングが必要そうでした。
さいごに
Azure Blob Storage を外部ステージに設定し Azure Praivate Link によるアウトバウンドプライベート接続でのデータロードを試してました。
こちらの内容が何かの参考になれば幸いです。