Databricks からAzure Data Lake Storage への接続手順 (サービスプリンシパル認証)


#　概要

DatabricksからAzure Data Lake Storage (ADLS gen2) へのアクセス手順についてまとめたいと思います。

# 1. Azure上での認証方式の選択
ストレージアクセスの際の認証方式をまずは決めます。主に以下の３つがあります。

- [ストレージアカウントキー](https://qiita.com/maroon-db/items/8cbb8bb14adeead7bed0)
- [SAS Token](https://qiita.com/maroon-db/items/1df46d0c088a550ab053)
- サービスプリンシパル (今回はこちら）

どの認証方式を利用するべきか、簡単な比較を作ってみました。（個人的主観が含まれます）

|認証方式|設定対象範囲|設定の容易さ|お勧め用途|概要|
|:--|--|--|--|--:|
|ストレージアカウントキー|ストレージアカウント全体|◎|個人利用・検証環境|ストレージアカウントに発行されるキーを利用|
|SAS Token|コンテナ|○|検証環境・小規模環境|コンテナあるいはストレージアカウント毎に発行ができ、有効期限も設定できるため検証環境での利用が便利|
|サービスプリンシパル|コンテナ|△|本番環境|設定は少し複雑だが、アプリケーションに権限ロールを付与して運用するため、複数コンテナの管理などが可能|

今回はサービスプリンシパル認証を利用した設定手順をご紹介します。

# 2. Azure Data Lake Storage の作成

それでは、早速ストレージ(Azure Data Lake Storage) を作成します。
[作成手順については、こちらをご覧ください。](https://qiita.com/maroon-db/items/4477587ec36af481b3f0)



# 3. サービスプリンシパルの設定

##　サービスプリンシパルとは？
サービスプリンシパルとは、Application用のIDで、アプリケーションがアクセスできるための権限を付与することができます。またサービスプリンシパルをOAuthの認証認可で利用することで、データブリックスではストレージオブジェクトにアクセスできたり、Jobを特定の権限を付与した状態で実行できたりします。またストレージオブジェクトとサービスプリンシパルを紐づけるため、複数のストレージオブジェクトを一括で管理できるようになります。

参考：　[もう多分怖くないサービスプリンシパル](https://tech-lab.sios.jp/archives/23371#OAuth)

##　サービスプリンシパルの作成

サービスプリンシパルの作成については、[こちらの記事](
https://sajpstorage.blob.core.windows.net/demo-asset-workshop2021/blog/adls_container_level_ac_by_secrets.html)をご覧ください。

大まかな作業の流れは以下のとおりです。
1. Azure Active Directoryサービスで、アプリケーションIDの新規登録
2. クライアントシークレットの発行
3. 対象ストレージ(ADLSコンテナ)から、上記のアプリケーションIDに対して、アクセスロールを付与する

この後、データブリックス側で利用するサービスプリンシパルの情報は以下になります。
- **application_id**: Service PrincipalのApplication ID
- **directory_id**: Service PrincipalのDirectory ID
- **client Secret Key**: Service Principalで作成したシークレットキー


<img src='https://sajpstorage.blob.core.windows.net/maruyama/qiita_adls/ServicePricipal2.png' width='800' />

参考：
1. [Azure サービスプリンシパルで OAuth 2.0 を使用して Azure Data Lake Storage Gen2 にアクセスする](https://docs.microsoft.com/ja-jp/azure/databricks/data/data-sources/azure/adls-gen2/azure-datalake-gen2-sp-access#--register-an-azure-active-directory-application)
1. [Azure Data Lake StorageのContainerレベルでのアクセス制御](https://sajpstorage.blob.core.windows.net/demo-asset-workshop2021/blog/adls_container_level_ac_by_secrets.html)
1. [Azure Databricks: 3-2. DBFSにAzure Data Lake Storage Gen2をマウント](https://qiita.com/KcMichael/items/98493d2e9bb3eb3f12db)



# Databricksからのアクセス方法

Databricksからのアクセス方法としては、DBFS経由とDirect Accessの２つのアクセス方法があります。

<img src="https://sajpstorage.blob.core.windows.net/maruyama/qiita_adls/adls-access.png" width='600'/>

またDirectAccessの場合、ノートブック上のセッション資格とクラスターに設定するクラスター資格の２つの設定方法があります。それぞれの特徴は以下の通りです。

|アクセス方法|資格|設定箇所|有効期間|アクセスユーザー対象範囲|
|:--------------------+---+---+-------+---+----------:|
|DBFS経由||Notebook(一度誰かがすればOK)|umountするまで永続|ワークスペース内の全ユーザー|
|Direct|セッション資格|Notebook(毎回実行)|Notebookセッション中|ノートブック利用者|
|Direct|クラスター資格|Cluster設定|Cluster起動中|Cluster利用者|

また、設定時にはアカウントキーがそのまま見えないように、シークレット設定することが可能です。これによりキーを安全に管理することができます。シークレット設定をする場合は、追加でシークレットスコープ設定が必要になります。[詳細はこちらの手順をご覧ください](https://sajpstorage.blob.core.windows.net/demo-asset-workshop2021/blog/databricks_cli_and_secrets.html)

# サンプルノートブック

### DBFS経由でのアクセス設定

DBFS経由でアクセスさせる場合は、DBFSの /mnt 配下にストレージがマウントされます。これによりワークスペース利用者は誰でもこのストレージにアクセスすることが可能になります。また一度設定すれば umount しない限りノートブックを閉じても、クラスターが切り替わってもアクセスすることができるため、全員で利用するストレージを設定する際にお勧めな方法です。

[サンプルノートブック](https://sajpstorage.blob.core.windows.net/maruyama/qiita_adls/notebook/ADLS_ServicePrincipal/ADLS_DBFS_ServicePrincipal_import.html)


### Directアクセス(セッション資格)設定

DBFSを利用せずに、Directアクセスさせることも勿論可能です。この場合２つの方法があり、ノートブック上で認証設定を行いアクセスするセッション資格設定と、クラスター上で設定することでクラスター利用者全員が利用できるようにするクラスター資格設定です。

[サンプルノートブック](https://sajpstorage.blob.core.windows.net/maruyama/qiita_adls/notebook/ADLS_ServicePrincipal/ADLS_DirectFromNotebook_ServicePrincipal_import.html)


### Directアクセス(クラスター資格)設定

アクセスキーをクラスター上のSpark Configにて設定することで、クラスターを利用するユーザーは簡単にストレージにアクセスできるようになります。この場合クラスターのアクセス権限にてユーザーのパーミッション管理を行えます。

[サンプルノートブック](https://sajpstorage.blob.core.windows.net/maruyama/qiita_adls/notebook/ADLS_ServicePrincipal/ADLS_DirectFromCluster_ServicePrincipal_import.html)