Pulumi Databricks リソース プロバイダー

この記事では、サードパーティによって開発されたPulumiについて説明します。 プロバイダーに問い合わせるには、 Pulumi サポートを参照してください。

この記事では、使い慣れたプログラミング言語、ツール、エンジニアリング手法を使用して Databricks リソースを作成、配置、管理できるサードパーティのInfrastructure-as-Code (IaC) プラットフォームである Python と Pulumi を使用する方法について説明します。 この記事では Python と Pulumi Databricks リソース プロバイダーの使用方法について説明しますが、 Pulumi は Python for Databricks に加えて、TypeScript、JavaScript、Go、C# などの他の言語をサポートしています。

Pulumi Databricks リソース プロバイダーは、 Databricks Terraform プロバイダーに基づいています。 詳細については、「 Terraform クラウド」を参照してください。

要件

次のステップ は、Python を使用してプルミ Databricks プロジェクトを作成する方法を示しています。 代わりに、純粋にクラウドプロバイダーファーストの観点からのチュートリアルについては、Pulumiドキュメントの 「AWSの使用開始 」を参照してください。 プログラミング言語ファーストの観点からのチュートリアルについては、Pulumi ドキュメントの 「PythonNode.js (JavaScript、TypeScript)、Go、および .NET (C#、VB、F#) 」を参照してください。

ステップ 1: Pulumi プロジェクトを作成する

このステップでは、ローカル開発マシンで、Pulumi プロジェクトに必要なディレクトリ構造を設定します。 次に、このディレクトリ構造内に Pulumi プロジェクトを作成します。

  1. ターミナルまたはPowerShellから、空のディレクトリを作成し、次のように切り替えます。

    mkdir pulumi-demo
    cd pulumi-demo
    
    md pulumi-demo
    cd pulumi-demo
    
  2. オペレーティングシステムに応じて、次のコマンドを実行してPulumiをインストールします。

    Unix または Linux に Pulumi をインストールするには、 curl を使用します。

    curl -fsSL https://get.pulumi.com | sh
    

    自作を使用してmacOSにプルミをインストールします。

    brew install pulumi/tap/pulumi
    

    Chocolatey パッケージ マネージャーを使用して昇格されたアクセス許可を持つ PowerShell を使用して、Windows に Pulumi をインストールします。

    choco install pulumi
    

    代替の Pulumi インストール オプションについては、Pulumi ドキュメントの「 ダウンロードとインストール 」を参照してください。

  3. 次のコマンドを実行して、基本的な Python Pulumi プロジェクトを作成します。

    pulumi new python
    

    ヒント

    PulumiアカウントからオンラインでPulumiプロジェクトを作成することもできます(プロジェクト>プロジェクトの作成)。ただし、Databricks のプロジェクト テンプレートはありません。

  4. プロンプトが表示されたら、 Enter キーを押してから、Web ブラウザーを使用して Pulumi アカウントにオンラインでサインインします (まだサインインしていない場合)。 サインインしたら、ターミナルまたは PowerShell に戻ります。

  5. プロジェクト名の入力を求めるプロンプトが出されたら、Enter キーを押してデフォルトのプロジェクト名 pulumi-demo を受け入れ ます

  6. プロジェクトの説明の入力を求めるプロンプトが出されたら、 A demo Python Pulumi Databricks project を入力して Enter キーを押します。

  7. スタック名の入力を求められたら、Enter キーを押してデフォルトのスタック名 dev を受け入れ ます。Pulumi は、 pulumi-demo ディレクトリに次のファイルとサブディレクトリを作成します。

    • Pulumi.yamlこれは、Pulumi プロジェクトの設定のリストです。

    • __main__.pyこれには、Pulumi プロジェクト用に記述した Python コードが含まれています。

    • requirements.txtこれは、Pulumi がプロジェクト用にインストールするサポートする Python コード パッケージのリストです。

    • .gitignoreこれは、このプロジェクトをリモート Git リポジトリにプッシュする場合に Git が無視するファイルとディレクトリのリストです。

    • venv サブディレクトリには、Pulumi がプロジェクトに使用するサポート Python 仮想環境コードが含まれています。

  8. 次のコマンドを実行して、プロジェクトの dev スタックの初期デプロイを実行します。

    pulumi up
    
  9. この更新を実行するように求められたら、上方向キーを押して [はい ] に移動し、 Enter キーを押します。

  10. 表示される [ライブを表示 ] リンクをコピーして Web ブラウザーのアドレス バーに貼り付けると、 オンラインで Pulumi アカウントに移動します。 pulumi-demo プロジェクトの dev スタックのアクティビティの詳細が表示されます。スタックにはまだリソースがないため、現在見るものはあまりありません。 これらのリソースは次のステップで作成します。

ステップ 2: Databricks リソースを作成する

このステップでは、Pulumi Databricks リソース プロバイダーを使用して、既存の Databricks ワークスペースにノートブックと、そのノートブックを実行するジョブを作成します。

  1. Pulumi が生成した __main.py__ ファイルで、任意のテキスト エディターまたは統合開発環境 (IDE) を使用して次のコードを入力します。 このコードでは、Pulumi Databricks ノートブックジョブ のリソースとその設定を宣言します。

    """A Python Pulumi program"""
    
    import pulumi
    from pulumi_databricks import *
    from base64 import b64encode
    
    # Get the authenticated user's workspace home directory path and email address.
    # See https://www.pulumi.com/registry/packages/databricks/api-docs/getcurrentuser
    user_home_path     = get_current_user().home
    user_email_address = get_current_user().user_name
    
    # Define the name prefix to prepend to the resource names that are created
    # for the Notebook and Job resources. To do this, you can use a Pulumi
    # configuration value instead of hard-coding the name prefix in this file.
    #
    # To set a Pulumi configuration value, run the following command, which sets
    # a "resource-prefix" configuration value to "pulumi-demo" in the
    # associated "Pulumi.<stack-name>.yaml" configuration file:
    #
    # pulumi config set resource-prefix "pulumi-demo"
    #
    # For more information about defining and retrieving hard-coded values, see
    # https://www.pulumi.com/docs/intro/concepts/config
    config = pulumi.config.Config()
    resource_prefix = config.require('resource-prefix')
    
    # Define cluster resource settings.
    node_type = config.require('node-type')
    
    # Create a Notebook resource.
    # See https://www.pulumi.com/registry/packages/databricks/api-docs/notebook
    # This example adds a single cell to the notebook, which is constructed from
    # a single base64-encoded string. In practice, you would replace this:
    #
    # language       = "PYTHON",
    # content_base64 = b64encode(b"display(spark.range(10))").decode("UTF-8")
    #
    # With this:
    #
    # source         = "path/to/local/my-notebook.py"
    #
    # To provide more notebook content easier and faster. Also, the notebook's language
    # is automatically detected. If you specify a notebook path, be sure that it does
    # not end in .ipynb, as Pulumi relies on the workspace import API, which doesn't
    # rely on any specific extensions such as .ipynb in the notebook path.
    notebook = Notebook(
      resource_name  = f"{resource_prefix}-notebook",
      path           = f"{user_home_path}/Pulumi/{resource_prefix}-notebook.py",
      language       = 'PYTHON',
      content_base64 = b64encode(b"display(spark.range(10))").decode("UTF-8")
    )
    
    # Export the URL of the Notebook, so that you can easily browse to it later.
    # See https://www.pulumi.com/docs/intro/concepts/stack/#outputs
    pulumi.export('Notebook URL', notebook.url)
    
    # Create a Job resource.
    # See https://www.pulumi.com/registry/packages/databricks/api-docs/job
    # This job uses the most recent Databricks Runtime long-term support (LTS)
    # runtime programmatic version ID at the time this article was first published,
    # which is 14.3.x-scala2.12. You can replace this with a later version.
    job = Job(
      resource_name = f"{resource_prefix}-job",
      name = f"{resource_prefix}-job",
      tasks = [
        JobTaskArgs(
          task_key = f"{resource_prefix}-task",
          new_cluster   = JobNewClusterArgs(
            num_workers   = 1,
            spark_version = "14.3.x-scala2.12",
            node_type_id  = node_type
          ),
          notebook_task = JobNotebookTaskArgs(
            notebook_path = f"{user_home_path}/Pulumi/{resource_prefix}-notebook.py"
          )
        )
      ],
      email_notifications = JobEmailNotificationsArgs(
        on_successes = [ user_email_address ],
        on_failures  = [ user_email_address ]
      )
    )
    
    # Export the URL of the Job, so that you can easily browse to it later.
    # See https://www.pulumi.com/docs/intro/concepts/stack/#outputs
    pulumi.export('Job URL', job.url)
    
  2. 次のコマンドを実行して、 resource-prefixという名前の構成値を定義し、ハードコーディングされた値 pulumi-demoに設定します。 Pulumi は、この構成値を使用して、ノートブックとジョブに名前を付けます。

    pulumi config set resource-prefix "pulumi-demo"
    

    Pulumi は、 __main__.py ファイルと同じディレクトリに Pulumi.dev.yaml という名前のファイルを作成し、この YAML ファイルに次のコードを追加します。

    config:
      pulumi-demo:resource_prefix: pulumi-demo
    

    構成値を使用すると、コードのモジュール化と再利用性が向上します。 これで、他のユーザーが __main__.py ファイルを再利用し、 __main__.py ファイルの内容を変更せずに resource_prefix 変数に別の値を定義できます。

  3. node-typeという名前の構成値を定義し、次のコマンドを実行して、次のハードコーディングされた値に設定します。Pulumi は、この構成値を使用して、ジョブが実行されるクラスターのタイプを判別します。

    pulumi config set node-type "i3.xlarge"
    

    Pulumi.dev.yaml ファイルの内容は次のようになります。

    config:
      pulumi-demo:node-type: i3.xlarge
      pulumi-demo:resource-prefix: pulumi-demo
    
  4. Pulumi が Databricks ワークスペースで認証できるようにするには、関連するコマンドを実行して Databricks 固有の構成値を定義します。 たとえば、Databricks 個人用アクセストークン認証の場合は、次のコマンドを実行します。 これらのコマンドでは、次のようになります。

    • <workspace-instance-url>ワークスペース インスタンスの URL(https://dbc-a1b2345c-d6e7.cloud.databricks.comなど) に置き換えます。

    • <access-token> をアクセストークンの値に置き換えます。必ず --secret オプションを指定してください。 これにより、セキュリティのベストプラクティスとしてアクセストークンを暗号化するようにPulumiに指示されます。

      デフォルトにより、Pulumi は Pulumi サービスによって管理されるスタックごとの暗号化キーと値ごとのソルトを使用して値を暗号化します。 別の暗号化プロバイダーを使用するには、Pulumi ドキュメントの 「シークレット暗号化の構成 」を参照してください。

    pulumi config set databricks:host "<workspace-instance-url>"
    pulumi config set databricks:token "<access-token>" --secret
    

    Pulumi.dev.yaml ファイルの内容は次のようになります。

    config:
      databricks:host: <your-workspace-instance-url>
      databricks:token:
        secure: <an-encrypted-version-of-your-access-token>
      pulumi-demo:node-type: i3.xlarge
      pulumi-demo:resource_prefix: pulumi-demo
    

    別の Databricks 認証の種類を使用するには、「 要件」を参照してください。 GitHub の Pulumi Databricks リポジトリの 「構成 」も参照してください。

ステップ 3: リソースをデプロイする

このステップでは、Pulumi Python プロジェクト テンプレートの実行の一環として、Pulumi がプロジェクトに提供する Python 仮想環境 をアクティブ化します。 この仮想環境は、正しいバージョンの Python、Pulumi、および Pulumi Databricks リソース プロバイダーを一緒に使用していることを確認するのに役立ちます。 venvvirtualenvpipenv など、いくつかの Python 仮想環境フレームワークが利用可能です。この記事と Pulumi Python プロジェクト テンプレートでは、 venvを使用します。 venv はすでに Python に含まれています。 詳細については、「 仮想環境の作成」を参照してください。

  1. オペレーティングシステムとシェルタイプに応じて、 pulumi-demo ディレクトリから次のコマンドを実行して、Python仮想環境をアクティブ化します。

    プラットホーム

    シェル

    仮想環境をアクティブにするコマンド

    Unix, Linux, macOS

    バッシュ/ZSH

    source venv/bin/activate

    source venv/bin/activate.fish

    csh/tcsh

    source venv/bin/activate.csh

    PowerShell Core

    venv/bin/Activate.ps1

    Windows

    うん.exe

    venv\Scripts\activate.bat

    PowerShell

    venv\Scripts\Activate.ps1

  2. 次のコマンドを実行して、 Pulumi Databricks リソース プロバイダーPython パッケージ インデックス (PyPI) から仮想環境にインストールします。

    pip install pulumi-databricks
    

    pip のインストールによっては、 pipの代わりに pip3 を使用する必要がある場合があります。その場合は、この記事全体で pip3 pip に置き換えてください。

  3. Pulumi が作成するリソースをプレビューするには、次のコマンドを実行します。

    pulumi preview
    

    エラーが報告された場合は、それらを修正してからコマンドを再実行します。

    PulumiアカウントのPulumiの動作に関する詳細なレポートをオンラインで表示するには、表示される[ ライブを表示 ]リンクをコピーして、Webブラウザーのアドレスバーに貼り付けます。

  4. 次のコマンドを実行して、リソースを作成して Databricks ワークスペースにデプロイします。

    pulumi up
    
  5. この更新を実行するように求められたら、上方向キーを押して [はい ] に移動し、 Enter キーを押します。 エラーが報告された場合は、それらを修正してからコマンドを再実行します。

  6. PulumiアカウントのPulumiが行ったことの詳細なレポートをオンラインで表示するには、表示される[ ライブを表示 ]リンクをコピーして、Webブラウザのアドレスバーに貼り付けます。

ステップ 4: リソースを操作する

このステップでは、指定したノートブックを実行する Databricks ワークスペースでジョブを実行します。

  1. ジョブがワークスペースで実行されるノートブックを表示するには、表示される [ノートブック URL ] リンクをコピーし、Web ブラウザーのアドレス バーに貼り付けます。

  2. ワークスペースでノートブックを実行するジョブを表示するには、表示される [ ジョブ URL ] リンクをコピーし、Web ブラウザーのアドレス バーに貼り付けます。

  3. ジョブを実行するには、ジョブ ページの [ 今すぐ実行 ] ボタンをクリックします。

  4. ジョブの実行が完了したら、ジョブの実行結果を表示するには、ジョブ ページの [完了した実行 (過去 60 日間)] ボックスの一覧で、[開始時刻] 列の最新の 時刻 エントリをクリックします。 [ 出力 ] ウィンドウには、ノートブックのコードを実行した結果が表示され、1 から 10 までの数字が出力されます。

(オプション)ステップ 5: リソースに変更を加える

このオプションの手順では、ノートブックのコードを変更し、変更されたノートブックを再デプロイしてから、ジョブを使用して変更されたノートブックを再実行します。

ノートブックに変更を加えない場合は、「 ステップ 6: クリーンアップ」に進みます。

  1. __main.py__ ファイルに戻り、次のコード行を変更します。

    content_base64 = b64encode(b"display(spark.range(10))").decode("UTF-8")
    

    これに、ファイルを保存します。

      content_base64 = b64encode(b'''
    data = [
             { "Category": 'A', "ID": 1, "Value": 121.44 },
             { "Category": 'B', "ID": 2, "Value": 300.01 },
             { "Category": 'C', "ID": 3, "Value": 10.99 },
             { "Category": 'E', "ID": 4, "Value": 33.87}
           ]
    
    df = spark.createDataFrame(data)
    
    display(df)
    ''').decode("UTF-8")
    

    この変更により、ノートブックは、1 から 10 までの数字ではなく、指定された DataFrame の内容を印刷するように指示されます。

    data で始まり ''').decode("UTF-8") で終わるコード行が、コード エディターの端に揃っていることを確認します。そうしないと、Pulumi はノートブックに追加の空白を挿入し、新しい Python コードの実行が失敗する可能性があります。

  2. 必要に応じて、次のコマンドを実行して、Pulumi が変更するリソースをプレビューします。

    pulumi preview
    

    エラーが報告された場合は、それらを修正してからコマンドを再実行します。

    PulumiアカウントのPulumiの動作に関する詳細なレポートをオンラインで表示するには、表示される[ ライブを表示 ]リンクをコピーして、Webブラウザーのアドレスバーに貼り付けます。

  3. 次のコマンドを実行して、リソースの変更を Databricks ワークスペースにデプロイします。

    pulumi up
    
  4. この更新を実行するように求められたら、上方向キーを押して [はい ] に移動し、 Enter キーを押します。 エラーが報告された場合は、それらを修正してからコマンドを再実行します。

  5. PulumiアカウントのPulumiが行ったことの詳細なレポートをオンラインで表示するには、表示される[ ライブを表示 ]リンクをコピーして、Webブラウザのアドレスバーに貼り付けます。

  6. 変更されたノートブックをワークスペースに表示するには、表示される [ノートブック URL] リンクをコピーし、Web ブラウザーのアドレス バーに貼り付けます。

  7. 変更したノートブックでジョブを再実行するには、表示される [ ジョブ URL ] リンクをコピーし、Web ブラウザーのアドレス バーに貼り付けます。 次に、ジョブ ページの [ 今すぐ実行 ] ボタンをクリックします。

  8. ジョブの実行が完了したら、ジョブの実行結果を表示するには、ジョブ ページの [完了した実行 (過去 60 日間)] ボックスの一覧で、[開始時刻] 列の最新の 時刻 エントリをクリックします。 [ 出力 ] ウィンドウには、指定した DataFrame の内容を出力するノートブックのコードを実行した結果が表示されます。

ステップ 6: クリーンアップ

このステップでは、Databricks ワークスペースからノートブックとジョブを削除し、 pulumi-demo プロジェクトとその dev スタックを Pulumi アカウントからオンラインから削除するように Pulumi に指示します。

  1. 次のコマンドを実行して、Databricks ワークスペースからリソースを削除します。

    pulumi destroy
    
  2. この削除を実行するように求められたら、上方向キーを押して [はい ] に移動し、 Enter キーを押します。

  3. 次のコマンドを実行して、Pulumi pulumi-demo プロジェクトとその dev スタックを Pulumi アカウントからオンラインで削除します。

    pulumi stack rm dev
    
  4. この削除を実行するように求めるメッセージが表示されたら、「 dev 」と入力し、 Enter キーを押します。

  5. venv Python 仮想環境を非アクティブ化するには、次のコマンドを実行します。

    deactivate
    

テスティング

Pulumi プロジェクトは、デプロイする前にテストできます。 Pulumi ドキュメントの「Pulumi プログラムのテスト」を参照してください。

Python ベースの Pulumi プロジェクトの単体テストでは、Python テスト フレームワークunittestと Pulumi パッケージのpulumi.runtime名前空間を使用して単体テストを記述および実行できます。 シミュレートされたリソースに対してテストを実行するには、Pulumi (および Databricks) への呼び出しをモックに置き換えます。 Pulumi ドキュメントの「Pulumi プログラムの単体テスト」を参照してください。

次のinfra.pyという名前のサンプル ファイルは、この記事のmain.pyファイルで宣言されたノートブックとジョブの実装をモック化します。 この例の単体テストでは、 小説、 ジョブの名前、および ジョブ実行が成功した場合の E メール受信者の Base64 エンコードされたコンテンツがすべて期待どおりの値を返すかどうかを確認します。 そのため、ここでは関連するプロパティのみが値の例でモックされています。 また、ユニット テストで使用する予定がない場合でも、必要なリソース プロパティ値を常に提供する必要があります。 この例では、これらの必須値はランダムな my-mock- 値に設定され、これらの値はテストされません。

# infra.py

from pulumi_databricks import (
  Notebook,
  Job,
  JobEmailNotificationsArgs
)

notebook = Notebook(
  resource_name  = 'my-mock-notebook-resource-name',
  path           = 'my-mock-notebook-path',
  content_base64 = 'ZGlzcGxheShzcGFyay5yYW5nZSgxMCkp'
)

job = Job(
  resource_name = 'my-mock-job-resource-name',
  name          = 'pulumi-demo-job',
  email_notifications = JobEmailNotificationsArgs(
    on_successes = [ 'someone@example.com' ]
  )
)

次のサンプル ファイルでは test_main.py 関連するプロパティが予期される値を返すかどうかをテストします。

# test_main.py

import pulumi
from pulumi_databricks import *
import unittest
import infra

# Set up mocking.
class MyMocks(pulumi.runtime.Mocks):
  def new_resource(self, type_, name, inputs, provider, id_):
    return [name + '_id', inputs]

  def call(self, token, args, provider):
    return {}

pulumi.runtime.set_mocks(MyMocks())

class TestNotebookAndJob(unittest.TestCase):
  @pulumi.runtime.test
  def test_notebook(self):
    def check_notebook_content_base64(args):
      content_base64 = args
      # Does the notebook's Base64-encoded content match the expected value?
      self.assertIn('ZGlzcGxheShzcGFyay5yYW5nZSgxMCkp', content_base64)

    # Pass the mocked notebook's content_base64 property value to the test.
    return pulumi.Output.all(infra.notebook.content_base64).apply(check_notebook_content_base64)

  @pulumi.runtime.test
  def test_job(self):
    def check_job_name_and_email_onsuccesses(args):
      name, email_notifications = args
      # Does the job's name match the expected value?
      self.assertIn('pulumi-demo-job', name)
      # Does the email address for successful job runs match the expected value?
      self.assertIn('someone@example.com', email_notifications['on_successes'])

    # Pass into the test the mocked job's property values for the job's name
    # and the job's email address for successful runs.
    return pulumi.Output.all(
      infra.job.name,
      infra.job.email_notifications
    ).apply(check_job_name_and_email_onsuccesses)

これらのテストを実行し、テスト結果を表示するには、Pulumi プロジェクトのルート ディレクトリから次のコマンドを実行します。

python -m unittest

実行できる他の種類のテストに関する情報については、Pulumi ドキュメントの次の記事を参照してください。