Azure Databricks: 3-2. DBFSにAzure Data Lake Storage Gen2をマウント

# サービスプリンシパルの作成
Azure DatabricksのDBFSにAzure Data Lake Storage Gen2 (ADLS Gen2)をマウントするには、サービスプリンシパルの設定が必要になるため,あらかじめ作成しておきます。
(サービスプリンシパルはアプリケーションに対して割り当てるユーザーIDのようなものです。)

「すべてのサービス」の「ID」カテゴリから「Azure Active Directory」を開きます。
![img03-0101.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/c77db2e9-a721-4b70-fcd8-313929e4c21e.png)
メニューの「App registrations」を開き、「＋新規作成」をクリックします。
![img03-0102.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/7b0f6b9b-d01a-c194-ccf1-b406bfa86e38.png)
「名前」に任意の名称を入力し、「リダイレクトURI」にAzure DatabricksのURL( https://japaneast.azuredatabricks.net/ )を入力して「登録」をクリックします。
![img03-0103.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/60fe56f8-53b9-2d36-72d4-8c0c32ccfcfd.png)
登録後に表示される「アプリケーション(クライアント)ID」と「ディレクトリ(テナント)ID」はマウント時のパラメータとして必要になるため、コピーして控えておきます。
![img03-0104.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/47f17212-1854-9728-9b52-aaabb27df9b1.png)
続いて、サービスプリンシパルのクライアントシークレットを作成します。
メニューの「証明書とシークレット」を開き、「+新しいクライアントシークレット」をクリックします。
「説明」と「有効期限」を設定して「追加」をクリックします。
![img03-0105.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/147e2aa0-6a8a-b4c2-feb6-91c21291edf5.png)
作成されるとクライアントシークレットの文字列が表示されます。この文字列は作成後最初の1回しか表示されないため、コピーして控えておきます。
クライアントシークレットもマウント時の必要パラメータとなります。
(秘密情報の管理にAzure Key Vaultを使用する場合はクライアントシークレットを[キーコンテナーに登録](./content2-1#キーの登録)しておきます。)
![img03-0106.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/3eb572c3-8b5e-2157-75dd-812614606d68.png)

# Azure Data Lake Storage Gen2 (ADLS Gen2)とファイルシステムの作成
ADLS Gen2はAzure Storageのオプション扱いであるため、作成は[ストレージアカウントの作成]()の手順に準じます。
作成の変更点は、作成の「詳細」設定で「階層構造の名前空間」を有効化することです。
(ADLS Gen2は後から有効化できないため、使用する場合は新規でストレージアカウント作成が必要です。)
![img03-0107.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/ba034fb8-7a6e-916c-09e1-486cace7d99e.png)
作成が完了したら、作成したリソースを開きます。
「階層構造の名前空間」を有効化していると、概要の「サービス」が「Blob」から「Data Lake Gen2ファイルシステム」に変化しています。
![img03-0108.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/950b1bdb-6e48-4f3d-329b-d7052e21a0d9.png)

# ファイルシステムの作成
ADLS Gen2にファイルシステムを作成します。
「Data Lake Gen2ファイルシステム」をクリックしてADLS Gen2メニューを開きます。
![img03-0109.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/9d2cbdd2-ae8d-7425-dfe5-b587d2a90397.png)
「＋ファイルシステム」をクリックし、「名前」に任意の名称を入力して作成を行います。
![img03-0110.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/3cab3ebe-6244-c96f-0ee2-0e8549ee1b17.png)

# ADLS Gen2へのアクセス権限の付与
サービスプリンシパルに対するADLS Gen2のアクセス権限の付与します。
作成したファイルシステムを開きます。
![img03-0111.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/66271e74-0488-e02f-1cd9-5a9174690e64.png)
メニューから「アクセス制御(IAM)」を開いて「ロールの割り当てを追加する」をクリックします。
![img03-0112.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/33e40e66-bbac-c079-cc3a-1da0cb01c324.png)
「役割」を「ストレージBLOBデータ共同作成者」に変更し、「選択」で作成したサービスプリンシパルを選択して「保存」をクリックします。
![img03-0113.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/e2effa68-52e3-0661-337f-b87ffc01c9aa.png)
「ロールの割り当て」タブをクリックするとサービスプリンシパルに割り当てられた権限が確認できます。
![img03-0114.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/6db2b6d5-b670-db16-d4e8-4fd8d1829d35.png)

# ADLS Gen2をDBFSにマウント
DBFSにADLS Gen2をマウントするには、下記のスクリプトを使用します。
(ストレージ、サービスプリンシパル情報、マウント先DBFSディレクトリは作成したリソースに合わせて変更してください。)

```python:Cmd&nbsp;1
# ストレージ情報
storage_alds = {
    "account": "{ストレージアカウント名}",
    "file_system": "{ファイルシステム名}"
}

# サービスプリンシパル情報
service_principal = {
  "client_id": "{アプリケーション(クライアント)ID}",
  "directory_id": "{ディレクトリ(テナント)ID}",
  "client_secret": "{クライアントシークレット}"
  # "client_secret": dbutils.secrets.get(scope="{スコープ名}", key="{キー名}") # Key Vault使用時
}

# マウントするDBFSディレクトリ
mount_point_adls = "/mnt/{マウント先ディレクトリ}"

try:
  # マウント状態のチェック
  mount_dir = mount_point_adls
  if mount_dir[-1] == "/":
    mount_dir = mount_dir[:-1]
  if len(list(filter(lambda x: x.mountPoint == mount_dir, dbutils.fs.mounts()))) > 0:
    print("Already mounted.")
    mounted = True
  else:
    mounted = False
  
  if not mounted:
    # Azure Data Lake Storage Gen2のマウント
    configs = {
      "fs.azure.account.auth.type": "OAuth",
      "fs.azure.account.oauth.provider.type": "org.apache.hadoop.fs.azurebfs.oauth2.ClientCredsTokenProvider",
      "fs.azure.account.oauth2.client.id": service_principal["client_id"],
      "fs.azure.account.oauth2.client.secret": service_principal["client_secret"],
      "fs.azure.account.oauth2.client.endpoint": "https://login.microsoftonline.com/{directory_id}/oauth2/token".format(**service_principal)
    }

    mounted = dbutils.fs.mount(
      source="abfss://{file_system}@{account}.dfs.core.windows.net/".format(**storage_alds),
      mount_point=mount_point_adls,
      extra_configs=configs
    )
    
except Exception as e:
    raise e

"mounted: {}".format(mounted)
```
![img03-0115.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/b215eb91-0861-ec12-a75d-f19611d113a0.png)
![img03-0116.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322093/d9b1655e-deeb-a4fa-7f5a-002efea5c614.png)

# 参考
- [3分でわかるAzureでのService Principal](https://www.slideshare.net/ToruMakabe/3azure-service-principal)
- [ナレコムAzureレシピ - Azure Databricksを使ってみた](https://azure-recipe.kc-cloud.jp/2018/08/azure-databricks)
- [Databricks Documentation - Data Sources - Azure Data Lake Storage Gen2](https://docs.databricks.com/spark/latest/data-sources/azure/azure-datalake-gen2.html)
- [チュートリアル:Spark を使用して Azure Databricks で Data Lake Storage Gen2 のデータにアクセスする](https://docs.microsoft.com/ja-jp/azure/storage/blobs/data-lake-storage-use-databricks-spark)