Azure DatabricksからアプリログをLog Analytics ワークスペースに送信する

Azure

Azure DatabricksからアプリログをLog Analyticsワークスペースに送信するにはひと手間必要です。基本的にはライブラリが用意されているので、それを使うことになると思います。しかし、せっかくですので、今回は1からカスタムログを送信する仕組みを実装してみたいと思います。

やることとしては、大きく以下の3になります。

  1. Log Analyticsにログを送信するために必要なAzureリソースの作成
  2. ログ出力用のプログラム
  3. Azure Databricksの設定

1番目では、Log Analytics上でのカスタムテーブルの作成や、そのカスタムテーブルにログを連携するためのAzureリソースを作成します。2番目に、1番目で作成したAzureリソース宛にログを送信するプログラムを作成します。そして3番目に、2番目で作成したプログラムを実行させるための設定をAzure Databricksに行います。

ログ出力に必要なAzureリソースの作成

カスタムログをLog Analyticsワークスペースに送信するためには、、データコレクションルール(以降、DCR)、データ収集エンドポイント(以降、DCE)を利用します。DCRではログの送信先の指定やフォーマット変換を定義できます。またDCEと関連付けることで、DCEにリクエスト(ログ)を送ることで、最終的にLog Analyticsワークスペースに連携できるようになります。それでは以下の流れで作成していきます。

  • DCEの作成
  • 送信先テーブル(Log Analytics)とDCRの作成
  • アプリとシークレットの作成(Entra ID)

基本的に下記ドキュメントを参考にしています。

DCEの作成

DCEはその名の通り、エンドポイントであり、ログをこのエンドポイントに向かって送信します。

送信先テーブル(Log Analytics)とDCRの作成

続いて、カスタムログの出力先となるテーブルとDCEで受けたカスタムログをテーブルに連携するためのDCRを作成します。テーブルとDCRは別々に作成しても大丈夫ですが、テーブル作成時に「DCR-based」を選択することで同時に作成できます。またリソース同士を自動的に関連付けてくれるため、簡単に設定できます。

「Create a new data collection rule」を選択して、DCRの名前を入力します。またDCEには先ほど作成したものを選択します。

次にスキーマを指定します。今回は以下のような簡単な形式でログを送信します。

{
  "TimeGenerated": "2023-12-12 10:00:00",
  "LogLevel": "Information",
  "Message": "test"
}

上記をjsonファイルとしてアップロードします。ちなみに、「TimeGenerated」は必須です。

作成後、概要を確認すると、自動的にDCRとDCEが紐づいていることが分かります。

アプリの作成

カスタムログをDCEに送信する際に認証が必要です。今回はサービスプリンシパル(custom-logという名前で作成)を使います。そのため、Entra IDでアプリを作成し、シークレットを追加します。

アプリ作成とシークレットの追加に関してはこちらをご覧ください。

作成後、DCRに対する「Monitoring Metrics Publisher」ロールをサービスプリンシパルに割り当てます。

カスタムログ出力プログラムの作成

まずは今回簡易的に作成したカスタムログプログラムのコードを記載します。

import datetime
import os

from azure.core.exceptions import HttpResponseError
from azure.identity import DefaultAzureCredential
from azure.monitor.ingestion import LogsIngestionClient


class CustomLog:

    def __init__(self) -> None:
        # AZURE_から始まる3つの環境変数を読み取り、サービスプリンシパルで認証する
        self.cred = DefaultAzureCredential()
        self.endpoint = os.environ["LOG_ENDPOINT"]
        # クレデンシャル情報とDCEのエンドポイントをもとに、ログ送信用のクライアントを作成する
        self.log_client = LogsIngestionClient(self.endpoint, self.cred)
        # DCRのimmutable id。AzureポータルのDCRの概要から「JSON view」から確認できる
        self.rule_id = "dcr-836a631e7b5d4bb18ac050e580acd438"
        # DCRのデータソース名。DCRの「Data sources」から確認できる(Custom-<テーブル名>)
        self.stream_name = "Custom-CustomDatabricks_CL"

    def LogInformation(self, message: str) -> None:

        log_body = []
        # DCR作成時に定義したスキーマに沿った形式でログデータを作成する
        log_body.append({
            "TimeGenerated": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "LogLevel": "Information",
            "Message": message
        })

        try:
            self.log_client.upload(rule_id=self.rule_id, stream_name=self.stream_name, logs=log_body)
            print("ok")
        except HttpResponseError as e:
            print(e)

DCEへのログの送信には「azure.monitor.ingestion」を利用します。またそのための認証には「azure.identity」を利用します。

Python SDKでのAzureへの認証には様々な方法があります。「DefaultAzureCredential()」は認証方法の順番が決まっています。一番最初に、特定の環境変数を読み取り、その情報をもとにサービスプリンシパルでの認証を行います。設定すべき環境変数は次章で説明します。

Databricksの設定

ここからは、Databricks上で先ほど作成したログ出力プログラムを実行するための設定を行っていきます。ここでは以下の設定を行います。

  • シークレットの作成(Databricks)
  • 環境変数の設定
  • Pythonライブラリのインストール
  • ファイルのアップロード

シークレットの作成

テナントIDやシークレット値などをDatabricksのシークレットとして登録します。よりセキュリティを高めるのであれば、KeyVaultを用いてシークレットを管理することもできます。今回は簡単にDatabricks上でシークレットを作成します。※アプリとシークレットの作成で作成したシークレットとは別物です。

シークレットの作成はVS Codeの拡張機能である「Databricks Power Tools」を使って行います。認証方法などについてはリンク先をご確認ください。認証が完了したら、GUIで簡単に「プラスマーク」からスコープとシークレットを登録できます。

「AZURE_」から始まる3つのシークレットは、Python SDKからサービスプリンシパルで認証するために必要です。「LOG_ENDPOINT」は認証には無関係です。

  • AZURE_CLIENT_ID:「アプリの作成」で作成したアプリのApplication(Client)ID
  • AZURE_CLIENT_SECRET:アプリに追加したシークレットのValue
  • AZURE_TENANT_ID:Databricksを作成したサブスクリプションが存在するテナントID
  • LOG_ENDPOINT:DCEのLog Ingest URL

Databricksクラスターでの環境変数の設定

ログ出力プログラムでは、認証に必要な値やログ送信先のURLを環境変数から読み取る設計としています。そのためDatabricksクラスター(VM)の環境変数として、先ほど作成したシークレットを設定する必要があります。

まずは、DatabricksクラスターのAdvanced optionのSpark欄に環境変数を設定する個所があります。そこに以下の形式で指定します。

シークレットの読み取りは、「dbutils.secrets」のモジュールを利用して読み取ることもできますが、今回は簡単にクラスターの環境変数に設定します。画像では認証に必要な物のみ表示されていますが、環境変数として扱うものはすべてここに「{{secrets/<scope名>/<secret名>}}」の形式で記載します。

DatabricksクラスターにPythonライブラリをインストールする

DatabricksクラスターにはデフォルトでAzureのPython SDKはインストールされていません。そのため手動でインストールします。インストール方法は複数ありますが、今回はクラスターレベルでインストールを行います。(azure.monitor.ingestionなど)

ファイルのアップロード

最後に作成したPythonプログラムなどをDatabricks上にアップロードします。

動作確認

それでは、カスタムログ出力プログラムを使ってLog Analyticsワークスペースのカスタムテーブルにログを送信できるか確認してみます。ログ出力プログラムは以下のノートブックと同じ階層においてください。

「ok」が表示されたので、正常に実行できたようです。実際にカスタムテーブルを確認してみると、以下のようにログが出力されていることが確認できました。

最後に

Azure Databricksに限らず、Log Analyticsワークスペースへのログ出力を自作しないといけない場面があるかもしれません。そんな時は今回の方法を応用して実装できると思います。ただし、本番で利用するためには各種制限や性能、セキュリティ設定などをしっかりテストする必要があります。

ログ出力モジュールも事前のセットアップが必要です。もう少し簡単にLog Analyticsワークスペースにログを送信できるようになるとありがたいです。

コメント