CCCMKホールディングス TECH LABの Tech Blog

TECH LABのエンジニアが技術情報を発信しています

ブログタイトル

VSCodeで開発したアプリをDatabricks Appsにデプロイする~ローカル開発からデプロイまでの手順をまとめてみました~

こんにちは、CCCMKホールディングスAIエンジニアの三浦です。

最近社内向けアプリをGithub Copilotを使って開発する機会が増えてきました。Github Copilotを使うとローカルで動作するデモアプリの開発まではあっという間に進んでしまいます。その後本番運用に乗せようとするとインフラの構築等々が必要になるのですが、デモアプリ~本番運用までの手順をもう少し簡潔に出来ないかな、と考えるようになりました。

私が普段作るアプリはそれほど複雑な構成のものではなく、Pythonだけで動くものがほとんどです。ふとこの構成だったらDatabricks Appsをアプリの提供基盤として使うのがいいのかもしれない、と考え、ローカルでの開発からDatabricks Appsへのデプロイまでの一連の流れを一度調べてみようと思いました。

Databricks Appsって?

Databricks Apps は、Databricks Workspace 上でWebアプリをホスティングし、ユーザーに提供できるサーバレスのアプリ提供基盤です。 Pythonなどで実装したアプリを Workspaceで公開でき、Databricksの認証と統合されているため、ユーザー管理やアクセス制御を別で作り込まずに運用できます。

また、アプリから Unity Catalog(Volumes)や Model Serving(Foundation Model API)など Workspace内の機能にアクセスできるため、データ活用・生成AIを組み込んだ社内向けアプリを素早く形にしやすいのが特徴です。

learn.microsoft.com

Databricks AppsをVSCodeで開発したい

Databricks Appsで動かすアプリはDatabricksのWeb UIでも開発出来るのですが、最近VSCode + Github Copilotの開発体験にどっぷりとつかってしまっているため、この環境で開発したいです。

また、Pythonの環境は先日このブログでも触れたuvがとても便利なので、uvを使って構築したいな、と考えました。

まずそのためのプロジェクトの構成を作ってみました。

Databricks Apps開発プロジェクトの構成

Databricks Appsで稼働するアプリ開発プロジェクトの構成のテンプレートを以下のように作成しました。

databricks-apps-project
├── .github/            # GitHub Copilot設定(copilot-instructions.md など)
├── .vscode/
│   ├── extensions.json
│   └── settings.json
├── data/               # ローカル実行時のアプリデータ
├── docs/               # 設計書(仕様書)置き場
├── src/
│   ├── templates/      # HTMLテンプレート(Jinja2)
│   ├── main.py         # FastAPIアプリのエントリーポイント
│   └── ...
├── tests/              # テストコード
├── .env.sample         # ローカル開発用の環境変数サンプル(.env用)
├── .gitignore
├── .python-version
├── app.yaml            # Databricks Apps実行設定(command/env など)
├── pyproject.toml      # uvプロジェクト定義(依存・Python制約)
├── README.md
└── requirements.txt    # Databricks Apps実行環境用の依存定義

開発環境で必要なもの、本番環境(Databricks Apps)で必要なものがあります。双方の環境について説明しつつ、それらにも触れていきます。

開発環境での作業の流れ

Pythonのバージョンの固定

Azure DatabricksのDatabricks Appsの実行環境は以下のドキュメントによるとOSは"Ubuntu 22.04 LTS"でPythonのバージョンは"3.11"、依存パッケージはrequirements.txtに定義する必要があります。

learn.microsoft.com

そのため、まず最初にuvpyproject.tomlに使用するPythonのバージョンを以下のように"3.11"を使うように指定しておきます。

[project]
...
requires-python = ">=3.11,<3.12"
...

.env

アプリがDatabricksのサービス、たとえばFoundation Model APIで提供されるLLMを使う場合、開発環境からはPAT(Personal Access Token)を使うようにしました。さらにWorkspaceのURLなど、Databricksに接続するために必要な情報は環境変数ファイル.envにまとめました。

以下が.envの例です。

# Databricks (Foundation Model APIに使用)
DATABRICKS_HOST=adb-xxxxxx.x.azuredatabricks.net/
DATABRICKS_TOKEN=...
DATABRICKS_MODEL=databricks-meta-llama-3-1-70b-instruct

# 保存先(開発は./dataなど、本番は /Volumes/...)
CHAT_STORAGE_ROOT=./data

# ローカル開発時はDatabricksヘッダーが無いので代替ユーザーをセット
DEV_USER=local_user

ユーザー情報の取得

Databricks Appsで起動したアプリにWorkspaceのユーザーがアクセスすると、リクエストヘッダにそのユーザーの情報が格納されます。アプリではその情報を使ってユーザーを認識し、各ユーザー固有の動きを実現することが可能です。

開発環境ではそのような情報は送られてこないため、開発環境で使う代替ユーザーを環境変数DEV_USERにセットしました。

ユーザー情報を取得する処理は以下のように実装しました。

def get_username(req: Request) -> str:
    # Databricks Apps で渡される想定のヘッダー
    username = req.headers.get("x-forwarded-preferred-username")
    email = req.headers.get("x-forwarded-email")
    user = username or email or os.getenv("DEV_USER", "local_user")
    return user


@app.get("/", response_class=HTMLResponse)
def index(request: Request):
    username = get_username(request)
    ...
...

アプリケーションの起動

uv runコマンドのオプション--env-fileで読み込む.envファイルを指定して起動します。

uv run --env-file .env uvicorn src.main:app --host 0.0.0.0 --port 8000 --reload

requirements.txtの出力

Databricks Appsはデプロイ時に依存パッケージをrequirements.txtの内容に従ってインストールします。uvではuv syncで環境を作ることが出来ますが、それが出来ないため、以下のコマンドでrequirementst.xtを出力しておきます。

uv export --no-annotate --no-hashes --format requirements-txt > requirements.txt

Databricks Appsデプロイ準備

app.yamlの作成

Databricks Appsはapp.yamlの内容に従ってアプリを実行します。app.yamlには実行コマンドや環境変数を記入します。

環境変数について、一部の環境変数はDatabricks Appsのシステム環境変数として自動的にセットされます。

learn.microsoft.com

たとえばWorkspaceのURLDATABRICKS_HOSTはDatabricks Appsで自動的にセットしてくれるため、アプリ側で用意する必要はありません。

また認証周りについても開発環境ではPATを使用しましたが、Databricks Appsではアプリのサービスプリンシパルで認証をし、サービスプリンシパルに対し使用が許可されたDatabricksのリソースにアクセスさせるようにします。サービスプリンシパルの認証に必要な情報DATABRICKS_CLIENT_IDDATABRICKS_CLIENT_SECRETもシステム環境変数としてセットされるため、本番環境では個人のPATによる認証を避けることが出来ます。

app.yamlは以下のように作成しました。

command:
  - uvicorn
  - src.main:app
  - --host
  - 0.0.0.0
  - --port
  - "8000"

env:
  # Databricks Foundation Model API(Serving Endpoints)で使うモデル
  - name: DATABRICKS_MODEL
    value: databricks-gemma-3-12b

  # 会話履歴の保存先
  - name: CHAT_STORAGE_ROOT
    value: ./data

tokenを生成する処理の実装

サービスプリンシパルに対して許可されたFoundation Model APIを呼び出すためのtokenを生成するコードは、以下のドキュメントの"Manually generate OAuth M2M access tokens"のセクションを参考にすると、たとえば以下のように書くことが出来ます。

learn.microsoft.com

host = "https://" + os.getenv("DATABRICKS_HOST", "").rstrip(
    "/"
)
client_id = os.getenv(
    "DATABRICKS_CLIENT_ID"
)                
client_secret = os.getenv(
    "DATABRICKS_CLIENT_SECRET"
)

token_url = f"{host}/oidc/v1/token"  # workspace-level token endpoint 

data = {
    "grant_type": "client_credentials",
    "scope": all-apis,
}

with httpx.Client(timeout=15.0) as client:
    resp = client.post(
        token_url,
        data=data,
        auth=(
            client_id,
            client_secret,
        ),  
    )
    resp.raise_for_status()
    payload = resp.json()

access_token = payload["access_token"]
...

ローカルでの開発環境では環境変数にセットされたPATを、Databricks Appsにデプロイされた後はこの処理で生成したtokenを使ってFoundation Model APIなどにアクセスさせることが出来ました。

Create new App

デプロイ前に事前にDatabricksのUIでデプロイ先のAppを作っておく必要があります。

"Compute" → "Apps" → "Create app" → "Create a custom app"

と進み、空のAppを作成しておきます。

空のAppの作成

Databricks Appsデプロイ

プロジェクトのアップロード

開発環境からDatabricks Workspaceにプロジェクトをアップロードします。プロジェクト内で以下のコマンドを実行すると、Workspace内の指定したパスに関連ファイルがアップロードされます。(.gitignoreに指定されたファイルは対象外)

--watchオプションを付けると開発環境に変更があるたびにWorkspaceと同期をとってくれます。

databricks sync --watch . /Workspace/Users/email@xxx.xxx/apps/databricks-demo-app

アプリのデプロイ

アップロードが完了したら、次のコマンドでアプリがデプロイされます。

databricks apps deploy databricks-demo-app --source-code-path /Workspace/Users/email@xxx.xxx/apps/databricks-demo-app

デプロイが完了したら、DatabricksのUIで確認できるアプリのURLからアプリにアクセスが出来るようになります。

まとめ

今回はVSCodeでDatabricks Appsを開発し、デプロイするまでの必要な手順を調べてまとめてみました。アプリへのアクセス権などを自力で設定しようとすると結構大変だったりするのですが、Databricksではデータと同じようにアプリへの権限が設定出来るので、アプリの運用がだいぶスッキリした形に出来そうです。今回の流れをベースにしてGithub Copilotで開発~Databricks Appsでアプリの提供までをもっと作りこんでいきたいと思います。