簡単スタート！Azure Databricksジョブ実行の基本とAPIによる自動化

みなさん、こんにちは！

Azure Databricks のジョブ実行方法についてまとめました。
API経由で実行する方法については情報が少なく分かりづらかったため、なかなか苦戦しました...
参考にしていただけますと幸いです。

## ノートブック準備
はじめに、ジョブ実行で使用するノートブックを準備しておきます。

今回はテスト用に、現在時刻を表示する以下の Python プログラムを記述したノートブック「job_test1」を作成しました。
```ruby:qiita.rb
from datetime import datetime
from zoneinfo import ZoneInfo

dt_now = datetime.now(ZoneInfo('Asia/Tokyo'))
print(dt_now.strftime('%Y/%m/%d %H:%M:%s'))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/8e0f4308-c3eb-4fe6-8353-48960a481b1b.png)

## ジョブ作成
ノートブックを準備したので、早速ジョブを作っていきます。

まず、メニューから「ワークフロー」を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/8744f0f2-c36c-4eea-b80c-3b77abd377e1.png)

「ジョブを作成」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/7ccd37d9-aa85-4241-aa70-78423303c300.png)

ジョブ作成画面が開くので、下記の項目について設定を行います。
 * タスク名：作成するタスク名。
 * 種類：ジョブの種類。ノートブックの他にも Python スクリプト、JAR、ジョブなどを選択可能。
 * ソース：ノートブックの参照場所。デフォルトは Databricks のワークスペースだが、Git プロバイダーを選択してリモートリポジトリを設定することも可能。
 * パス：ノートブックのパス。
 * クラスター：ジョブ実行用のクラスター。

他にも実行時のパラメータを設定したり、通知や再試行の設定を行うことが可能です。
設定が完了したら「タスクを作成」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/26140a30-874f-4875-a6b2-b1118b3632f2.png)
タスクを作成すると「今すぐ実行」ボタンが表示されます。
「タスクを追加」で後続タスクを追加することも可能です。

## ジョブ実行（直接実行）
「今すぐ実行」ボタンを押せばジョブを実行できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/b107279b-eede-4ed9-adff-462b3314c157.png)

「ジョブの実行」からジョブの実行結果を確認することも可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/50f3b121-e932-4abd-8631-50300fdba621.png)

「開始時刻」のリンクをクリックすると、ノートブックの実行結果詳細を確認できます。

## ジョブ実行（API利用）
ジョブをAPIでプログラムから実行してみます。

### サービスプリンシパル設定
プログラムからAPIを実行する場合、サービスプリンシパルの利用が推奨されています。
※サービスプリンシパルはAWSやGCPのサービスアカウントと同じような、自動化の目的で使用されるbotアカウント。
そのため、まずはサービスプリンシパルの設定を行っていきます。

Azure Portal から「アプリの登録」に移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/4cafbec8-8373-48fd-8d24-091c8c88616c.png)
「新規登録」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/d4cc5d24-7dec-42c8-a555-8dcf1c406ffa.png)
サービスプリンシパルの名前を入力します。
サポートされているアカウントの種類は、サービスアカウントを利用可能なアカウントの範囲についての設定です。組織内のみ利用可能とするため、今回はデフォルトののままとします。
「登録」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/bc8265ce-ed91-420e-8a17-e50d6dc688b5.png)
後で使うため、以下の値を控えてきます。
 * アプリケーション（クライアント）ID
 * ディレクトリ（テナント）ID

「証明書またはシークレットの追加」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/29f05cbb-70d9-4478-a67d-6e9a82ceaaab.png)

クライアントシークレットの説明を入力し、「追加」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/c199697c-1898-4078-9759-63f9806593bd.png)
シークレットが作成されました。
後で使うため、クライアントシークレットの値を控えておきます。

Databricks に戻ってワークスペース右上のアカウントアイコンをクリックし、「設定」→「IDとアクセス」を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/30b62b31-6f5d-42dc-9f0e-060334b7fa1a.png)

サービスプリンシパルの「管理」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/60885631-5ec6-4798-aef9-aee6d15b6cc3.png)

「Service Principalを追加」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/09841711-7434-4dc4-ad34-131a7facb674.png)

管理は「Microsoft Entra ID で管理」を選択し、先ほど取得した「アプリケーション（クライアント）ID」を指定します。サービスプリンシパル名は任意の名前を指定します。

「Add」をクリックしてサービスプリンシパルを追加します。
以上でサービスプリンシパルの設定は完了です！

### ジョブ起動プログラム実行
いよいよジョブの実行をしていきます。
事前準備として、環境変数を以下のように設定しておきます。

```ruby:qiita.rb
INSTANCE_ID=<インスタンスID>
TENANT_ID=<テナントID>
CLIENT_ID=<アプリケーションID>
CLIENT_SECRET=<クライアントシークレット>
```

<インスタンスID>はワークスペースURLの「https://」以降の部分です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/08e98ac2-6527-4398-b61f-97e31e63c35a.png)

環境変数の設定ができたら、以下の Python プログラムを実行します。
```ruby:qiita.rb
import os
import requests

def get_token():
    instance_id = os.environ.get("INSTANCE_ID")
    tenant_id = os.environ.get("TENANT_ID")
    client_id = os.environ.get("CLIENT_ID")
    client_secret = os.environ.get("CLIENT_SECRET")

    # Microsoft Entra ID アクセストークンを取得
    url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
    data = {
        "grant_type": "client_credentials",
        "scope": "2ff814a6-3304-4ab8-85cb-cd0e6f879c1d/.default" # /.default の前部分は固定IDなので変更しないこと
    }
    response = requests.post(
        url=url,
        headers=headers,
        auth=(client_id, client_secret),
        data=data
    )

    # Databricks API アクセストークンを取得
    url = f"https://{instance_id}/api/2.0/token/create"
    headers = {"Authorization": f"Bearer {response.json()['access_token']}"}
    json = {
        "comment": "no comment",
        "lifetime_seconds": 15768000
    }
    response = requests.post(
        url=url,
        headers=headers,
        json=json
    )

    token = response.json()["token_value"]
    return token

def call_api(job_id):
    instance_id = os.environ.get("INSTANCE_ID")
    api_version = 'api/2.0'
    api_command = 'jobs/run-now'
    url = f"https://{instance_id}/{api_version}/{api_command}"
    token = get_token()
    headers = {"Authorization": f"Bearer {token}"}
    params = {"job_id": job_id}

    response = requests.post(
        url=url,
        headers=headers,
        params=params
    )

    print(response.text)

def main():
    call_api('')

if __name__ == "__main__":
    main()
```

上記を実行すると、（起動していない場合は）クラスターが起動し、対象ジョブが実行されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3956386/8dc2c5cd-1c45-4dd1-b89c-ca7830b16fb7.png)
実行履歴からも、確かにジョブが実行されたことを確認できました！
※クラスターの起動から始まったため、最初よりも実行時間が長くなっています。

## 最後に
Azure Databricks は Azure 上ですぐに使い始められるため便利ですが、APIを利用する場合など通常の Databricks と仕様が異なる部分があるため注意が必要です。

Azure Databricks では記事中で行ったようにサービスプリンシパルを Microsoft Entra ID と連携させて使う必要があり、APIを利用する場合は「Microsoft Entra ID のアクセストークンを取得してから Databricks API 用のアクセストークンを取得する」という2段階の処理が必要になります。

#### 参考記事
 * [Databricks Jobsを使ってみる](https://qiita.com/taka_yayoi/items/10194fd3b44e9575de26)
 * [Azure DatabricksでサービスプリンシパルからDatabricks REST APIを使う](https://qiita.com/taka_yayoi/items/f081628f042375e0c73a)
 * [Get Microsoft Entra ID tokens for service principals](https://learn.microsoft.com/en-us/azure/databricks/dev-tools/service-prin-aad-token)