【Databricks】外部ロケーションを試してみた

# 背景・目的
以前、[こちらの記事](https://qiita.com/zumax/items/c84ea4f13459f887b393#%E5%A4%96%E9%83%A8%E3%83%AD%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E5%A4%96%E9%83%A8%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%8A%E3%82%88%E3%81%B3%E5%A4%96%E9%83%A8%E3%83%9C%E3%83%AA%E3%83%A5%E3%83%BC%E3%83%A0%E3%81%AE%E7%AE%A1%E7%90%86)に書いたように、Databricksでは、外部ロケーションを設定することで、UnityCatalogを介してクラウドテナント上のデータを読み書きできます。

本記事では、実際に外部ロケーション上のデータを読み書きしてみます。

# まとめ
- Unity Catalogを介してクラウド上のデータを読み書きを行うには、外部ロケーションとストレージ資格情報を設定する
- 外部ロケーションは、下記が含まれる。
    - クラウドストレージパス
    - ストレージ資格情報
        - IAMロールを使用してクラウドに保存されているデータにアクセスするための認証と承認メカニズム
        - アカウント管理者である必要がある
    - 下記の権限が必要である。
        - メタストア管理者
        - Create External Location権限を持つユーザ

# 概要
## 外部ロケーションとストレージ資格情報とは
> 外部ロケーションとストレージ資格情報を使用すると、Unity Catalogはユーザーに代わってクラウドテナント上のデータを読み書きできます。これらのオブジェクトは、次の目的で使用されます。

- Unity Catalogを介してクラウド上のデータを読み書きを行うには、外部ロケーションとストレージ資格情報を設定する

> ストレージ認証情報 は、IAMロールを使用してクラウド テナントに保存されているデータにアクセスするための認証および承認メカニズムを表します。各ストレージ資格情報は、資格情報にアクセスできるユーザーとグループを制御する Unity Catalog アクセス制御ポリシーの対象となります。 ユーザーが Unity Catalogのストレージ資格情報にアクセスできない場合、要求は失敗し Unity Catalog ユーザーに代わってクラウド テナントに対する認証を試行しません。 ストレージ資格情報をread-only]としてマークして 、 ユーザーがストレージ資格情報を使用する外部ロケーションに書き込むことができないようにすることができます。

- ストレージ資格情報とは、IAMロールを使用してクラウドに保存されているデータにアクセスするための認証と承認メカニズムを指す。
- 各ストレージ資格情報は、資格情報にアクセスできるユーザーとグループを制御する Unity Catalog アクセス制御ポリシーの対象となる。

> 外部ロケーション は、クラウドストレージパスと、クラウドストレージパスへのアクセスを承認するストレージ資格情報を組み合わせたオブジェクトです。各ストレージの場所には、資格情報にアクセスできるユーザーとグループを制御する Unity Catalog アクセス制御ポリシーが適用されます。 ユーザーが Unity Catalogのストレージの場所にアクセスできない場合、要求は失敗し Unity Catalog ユーザーに代わってクラウド テナントに対する認証を試行しません。 外部ロケーションを read-only] としてマークすると、ユーザーはその場所に書き込めず、ユーザーはその場所にテーブルまたはボリューム (外部または管理対象) を作成できなくなります。

- 外部ロケーションとは、クラウドストレージパスと、クラウドストレージパスへのアクセスを承認するストレージ資格情報の組み合わせを指す。
    - つまり、ストレージ資格情報が前提になる

## 要件
> - ストレージ資格情報を作成するには、Databricksアカウント管理者である必要があります。ストレージ資格情報を作成するアカウント管理者は、所有権を別のユーザーまたはグループに委任して、そのアクセス許可を管理できます。
> - 外部ロケーションを作成するには、メタストア管理者またはCREATE EXTERNAL LOCATION権限を持つユーザーである必要があります。

- ストレージ資格情報は、アカウント管理者である必要がある
- 外部ロケーションの作成には、下記が必要
    - メタストア管理者
    - Create External Location権限を持つユーザ

# 実践

## ストレージ資格情報の管理
[ストレージ資格情報の管理](https://docs.databricks.com/ja/data-governance/unity-catalog/manage-external-locations-and-credentials.html#manage-storage-credentials)を元に試してみます。

### ストレージ資格情報の作成
> ストレージ認証情報を作成するには、S3 バケットパスへのアクセス (読み取りまたは読み取りと書き込み) を許可する IAMロールが必要です。 その IAMロールは、ストレージ認証情報を作成するときに参照します。

#### ステップ1: IAMロールを作成する
S３バケットへのアクセス許可するIAMロールを作成します。

1. AWSにログインします。
1. IAMロール作成画面で「カスタム信頼ポリシー」を選択します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/d915053d-5e22-7bc6-a028-4e82ef651a97.png)

1. カスタム信頼ポリシーフィールドに下記のポリシーを貼り付けます。
    - \<DATABRICKS-ACCOUNT-ID>は、自分のDatabricksアカウントのIDをを入力します。
    ```
    {
      "Version": "2012-10-17",
      "Statement": [{
        "Effect": "Allow",
        "Principal": {
          "AWS": [
            "arn:aws:iam::414351767826:role/unity-catalog-prod-UCMasterRole-14S5ZJVKOTYTL"
          ]
        },
        "Action": "sts:AssumeRole",
        "Condition": {
          "StringEquals": {
            "sts:ExternalId": "<DATABRICKS-ACCOUNT-ID>"
          }
        }
      }]
    }
    ```
1. このまま、次へをクリックしIAMロール名を入力し保存します。
1. 作成したIAMロールで、①「信頼関係」タブをクリックし、②「信頼ポリシーを編集」をクリックします。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/7d7c109c-7643-d123-665f-e3a83e28f741.png)

1. 下記のARNをPrincipalに追加します。
    - IAMロールのARNに置き換えます。
    ```
    "arn:aws:iam::<YOUR-AWS-ACCOUNT-ID>:role/<THIS-ROLE-NAME>"
    ```
1. インラインポリシーを追加を選択し、下記のポリシーをコピーします。
    - KMSが不要な場合は、削除します。
    - 作成したIAMロールに置き換えます。
    - S3バケットを指定します。
    ```
    {
      "Version": "2012-10-17",
      "Statement": [
          {
              "Action": [
                  "s3:GetObject",
                  "s3:PutObject",
                  "s3:DeleteObject",
                  "s3:ListBucket",
                  "s3:GetBucketLocation",
                  "s3:GetLifecycleConfiguration",
                  "s3:PutLifecycleConfiguration"
              ],
              "Resource": [
                  "arn:aws:s3:::<BUCKET>/*",
                  "arn:aws:s3:::<BUCKET>"
              ],
              "Effect": "Allow"
          },
          {
              "Action": [
                  "kms:Decrypt",
                  "kms:Encrypt",
                  "kms:GenerateDataKey*"
              ],
              "Resource": [
                  "arn:aws:kms:<KMS-KEY>"
              ],
              "Effect": "Allow"
          },
          {
              "Action": [
                  "sts:AssumeRole"
              ],
              "Resource": [
                  "arn:aws:iam::<AWS-ACCOUNT-ID>:role/<AWS-IAM-ROLE-NAME>"
              ],
              "Effect": "Allow"
          }
        ]
    }
    ```
1. ポリシー名を入力し作成します。

#### ステップ 2：IAMロールの詳細をDatabricksに渡す
1. ワークスペースにログインします。
1. ナビゲーションペインで「Catalog」をクリックします。
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/1f664799-1d89-4ed8-a0ec-4ad2582e895c.png" width=250>

1. 画面右上の「＋Add」、「Add a storage credential」をクリックします。
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/ed3cb09a-2f96-94a4-d8d3-ce949a1dc0d0.png" width=250>

1. ポップアップが表示されるので、下記を入力し「Create」をクリックします。
    1. Storage credential name
    1. IAM roleのARN
    1. Limit to read-only useをチェック
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/0da359f2-f06a-ed92-f80e-3a382c6797d7.png)

1. Catalog ExplorerでStorage Credentialsをクリックします。
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/f9774e8e-cc37-2662-2f92-884a1b70cc69.png" width=500> 

1. 登録されたCredentialが確認できました。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/fe2bd2e2-d975-3ef1-613d-3dde4cc2e267.png)

## 外部ロケーションを管理する
[外部ロケーションを管理する](https://docs.databricks.com/ja/data-governance/unity-catalog/manage-external-locations-and-credentials.html#manage-external-locations)を元に試します。

### 外部ロケーションを作成する
1. ナビゲーションペインで「SQL Editor」をクリックします。
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/67738b44-db58-4cfc-eca6-21f585771bfa.png" width=250 >

1. 次のクエリを実行し、外部ロケーションを作成します。
    ```
    CREATE EXTERNAL LOCATION IF NOT EXISTS ext_location
    URL 's3://<bucket-path>'
    WITH (STORAGE CREDENTIAL <storage-credential-name>)
    [COMMENT <comment-string>];
    ```

1. ナビゲーションペインで「Catalog」をクリックします。
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/73a84457-a612-482f-b474-df53ba214f16.png" width=250>

1. External locationをクリックすすると、作成されていました。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/6ae5d103-dd25-ac44-e7eb-638a46fc6888.png)


1. ロケーション名をクリックし、「Test connection」をクリックします。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/00f2b437-8213-15d3-0d8c-4e3d38d4c489.png)

1. 下記のクエリを実行し、外部ロケーションの詳細を確認します。
    ```
    DESCRIBE EXTERNAL LOCATION ext_location
    ```
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/044fb4e4-1f46-5882-60c6-b9760c0eb842.png)

1. 下記のクエリを実行し、外部ロケーションの中身を確認します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/d5c449ab-9040-0205-4fb5-8fedc4aae29d.png)


### 外部テーブルを作成する
1. 下記のテストデータを準備し、S3バケットにアップロードする。
    ```
    {"id":1,"value":"xxx"}
    {"id":2,"value":"yyy"}
    {"id":3,"value":"zzz"}
    ```

1. ナビゲーションペインで「SQL Editor」をクリックします。
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/67738b44-db58-4cfc-eca6-21f585771bfa.png" width=250 >

1. SQL Editorで下記のクエリを実行します。
    ```
    SELECT * FROM json.`s3://XXXXX/test-json/`
    ```

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/7aab74c6-795d-741f-334a-23e14a10a90a.png)

1. ナビゲーションペインで「Catalog」をクリックします。
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/73a84457-a612-482f-b474-df53ba214f16.png" width=250>

1. 「Create catalog」をクリックします。
1. 下記を入力し、「Create」をクリックします。
    - Storage locationに、外部ロケーションを指定します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/57dce369-efd8-1f74-c571-667085a008da.png)

1. 出来ました。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/dc7481c2-c2fe-2ed7-b71b-f5421f67ab27.png)

1. SQL Editorで下記のクエリを実行します。
    ```
    CREATE TABLE ext_catalog.default.test
    ```
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/f13c2e64-edd4-a161-2bb6-c22e1fdcc010.png)

1. COPY INTOを使用することで、作成したテーブルに読み込ませることが出来ます。
    ```
    COPY INTO ext_catalog.default.test
    FROM (
      SELECT *
      FROM 's3://XXXX/test-json/'
    )
    FILEFORMAT = json COPY_OPTIONS ("mergeSchema" = "true");
    
    ```
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/d7f265ab-ab5d-f6c3-1bda-cbd57352fc7c.png)

1. SELECTします。読み込めました。
    ```
    SELECT * FROM ext_catalog.default.test
    ```
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/52e094fa-eae9-ae5e-d181-87d609d05be2.png)


# 考察
外部テーブルを使うことで、直接クラウドストレージからデータを読み込むことが出来ました。

# 参考
https://qiita.com/taka_yayoi/items/7a9aad3d4c7560af9f11#%E3%82%AB%E3%82%BF%E3%83%AD%E3%82%B0%E3%81%AE%E4%BD%9C%E6%88%90

https://docs.databricks.com/ja/data-governance/unity-catalog/manage-external-locations-and-credentials.html#create-external-location