Zerobus IngestではKafka互換APIを使用してください

Important

この機能は ベータ版です。 ワークスペース管理者は、[ プレビュー] ページからこの機能へのアクセスを制御できます。 Manage Azure Databricks プレビューを参照してください。

Zerobus Ingestは、Azure Databricks SDKを使わず、任意のApache Kafkaプロデューサークライアントを使って取り込むことができるKafka互換のプロデューサーAPIを提供しています。 既存のKafkaプロデューサーをZerobusエンドポイントに向け、ターゲットテーブルの名前が付けられたトピックに出力すると、そのレコードはUnity CatalogのDeltaテーブルに格納されます。 Kafka互換APIは、すでにKafkaプロデューサーやKafkaプロトコルを話すコレクタ、あるいはKafkaに出力するツールがあり、最小限のコード変更でそのデータをDeltaにルーティングしたい場合に適しています。

Kafka互換APIはTCP上で SASL_SSLOAUTHBEARER メカニズムを用いて動作し、 ProduceMetadataApiVersions、SASLのハンドシェイクAPIを含むKafkaプロトコルのプロデューサー側サブセットを実装しています。 消費者向け、管理用、トランザクション用APIは利用できません。 APIは書き込み専用です。

Kafka互換APIの使用時期

Kafka互換APIは以下のシナリオに最適です:

  • Zerobus SDKを採用しずにDeltaにデータを送信したい場合、すでにKafkaプロデューサーやアプリケーション、エージェント、コレクタを運用してKafkaに送信しています。
  • 既存のKafkaプロデューサーの設定、バッチ処理、運用ツールを再利用したいのです。
  • JSONレコードを送信すれば、プロトコルバッファ(protobuf)やApache Arrowは必要ありません。

新しいクライアントを一から構築し、最高スループット、レコードごとの確認応答、自動復旧を求める場合は、Kafka互換APIではなくZerobus SDKをgRPCで使用してください。 「 インターフェイスの選択」を参照してください。 列型またはバッチ型のワークロードについては、Arrow Flight と Zerobus Ingest の併用を参照してください。

インジェストモデルの仕組み

Kafka互換APIは、Kafkaの概念をZerobus Ingestに次のようにマッピングします:

  • トピック。 Kafkaのトピック名は、Unity Catalogの3レベルテーブル名(catalog.schema.table)の完全な名称です。 ターゲットテーブルはすでに存在しているはずです。なぜならZerobusはトピックを決して作成しないからです。
  • レコード。 Zerobusはレコード値のみを取り込みますが、それはターゲットのDeltaテーブルスキーマに一致するUTF-8エンコードされたJSONオブジェクトでなければなりません。 レコードキー、ヘッダー、クライアント提供のパーティションやタイムスタンプを無視し、永続化しません。
  • 認証。 各接続は、ターゲットテーブルにスコープされたAzure Databricks OAuthトークンで認証され、SASL/OAUTHBEARERを通じて提示されます。 認証に関するページを参照してください。
  • 謝辞。 ゼロバスは、レコードを持続的に永続化した後にのみ Produce 応答を返します。 プロデューサーを acks=allで設定してください。

ゼロバスは設計上パーティションレスです。 Zerobusエンドポイントは単一の論理ブローカー兼パーティションであり、トピックのすべてのレコードはパーティション 0上で認識されます。 プロデューサーはこれを考慮する必要はありません。 Zerobus Ingestは、入荷負荷に対応するために水平方向にスケールします。

また、Zerobus Ingestは少なくとも一度の配送を提供します。 Kafka互換APIはベータ版で独自のクォータがあり、Zerobus Ingestの他のAPIと同様の遅延、レコードサイズ、パーティションテーブル特性を共有しています。 ベータ割当やその他の割当については、 ゼロバスの取り込み割当についてご参照ください。

認証

Kafka互換APIはSASL/OAUTHBEARERを使用しています。 ベアラートークンは、ターゲットテーブルにアクセスできるサービスプリンシパルのクライアント認証情報で取得できるAzure Databricks OAuthトークンです。 トークンはOAuth authorization_detailsを通じてそのテーブルにスコープが割り当てられ、Zerobus REST APIと同じフローであるzerobusDirectWriteApiリソースを使用します。

OAuthトークンは1時間で期限切れになるため、静的文字列ではなくKafkaクライアントのトークンプロバイダーコールバックを通じてトークンを供給してください。 クライアントは再接続するたびに新しいトークンを再取得します。 接続にはサーバー側の寿命が制限されています。 接続がその限界に達すると、Zerobusはそれをクローズし、プロデューサーは自ら再接続して再認証を行います。 ライブ接続での再認証はサポートされていません。

接続前にターゲットテーブルで必要なUnity Catalog権限をサービスプリンシパルに付与してください。 「 サービスプリンシパルを作成し権限を付与する」を参照してください。

クライアントを書き込む

以下の例は、Use Zerobus Ingestの例で使われたのと同じair_qualityテーブルを生成します。 kafka-pythonを使用していますが、OAUTHBEARERメカニズムでSASL_SSLをサポートするKafkaプロデューサークライアントならどれでも動作します。 トークンプロバイダーのパターンをクライアントライブラリに合わせて適応させてください。

プロデューサーはポート 9092でZerobusブートストラップサーバーに接続します。 「 Get your workspace URL」と「Zerobus Ingest」エンドポイントで説明されているように、ワークスペースIDと地域を見つけてください。

  • Bootstrapサーバー:<workspace-id>.zerobus.<region>.azuredatabricks.net:9092
pip install kafka-python requests

ステップ1:トークンプロバイダーを構築する

Zerobus は、有効期間の短いテーブル単位の Azure Databricks OAuth トークンを使用して、各接続を認証します。 トークンが期限切れになるため、静的トークンではなく、オンデマンドで新しいトークンを発行するコールバックを渡してください。

fetch_zerobus_token() 関数は、サービス プリンシパルの資格情報を対象テーブルをスコープとするトークンに交換し、ZerobusTokenProvider そのトークンを kafka-python が想定するコールバック インターフェイスでラップします。

import json

import requests
from kafka.sasl.oauth import AbstractTokenProvider

# See "Get your workspace URL and Zerobus Ingest endpoint" in zerobus-ingest.md.
WORKSPACE_ID = "1234567890123456"
WORKSPACE_URL = "https://dbc-a1b2c3d4-e5f6.cloud.databricks.com"
TABLE_NAME = "main.default.air_quality"
CLIENT_ID = "your-client-id"
CLIENT_SECRET = "your-client-secret"


def fetch_zerobus_token():
    catalog, schema, table = TABLE_NAME.split(".")
    authorization_details = [
        {
            "type": "unity_catalog_privileges",
            "privileges": ["USE CATALOG"],
            "object_type": "CATALOG",
            "object_full_path": catalog,
        },
        {
            "type": "unity_catalog_privileges",
            "privileges": ["USE SCHEMA"],
            "object_type": "SCHEMA",
            "object_full_path": f"{catalog}.{schema}",
        },
        {
            "type": "unity_catalog_privileges",
            "privileges": ["SELECT", "MODIFY"],
            "object_type": "TABLE",
            "object_full_path": TABLE_NAME,
        },
    ]

    response = requests.post(
        f"{WORKSPACE_URL}/oidc/v1/token",
        auth=(CLIENT_ID, CLIENT_SECRET),
        data={
            "grant_type": "client_credentials",
            "scope": "all-apis",
            "resource": f"api://databricks/workspaces/{WORKSPACE_ID}/zerobusDirectWriteApi",
            "authorization_details": json.dumps(authorization_details),
        },
        timeout=30,
    )
    response.raise_for_status()
    return response.json()["access_token"]


# kafka-python calls token() whenever it needs a fresh OAuth token.
class ZerobusTokenProvider(AbstractTokenProvider):
    def token(self):
        return fetch_zerobus_token()

ステップ2:プロデューサーを設定し、レコードを送信する

プロデューサーをブートストラップサーバーに向け、OAUTHBEARERメカニズムでSASL_SSLを設定し、ステップ1のトークンプロバイダーを渡します。 各バッチが永続化された後にのみ認識される acks="all" を使い、レコードを圧縮せずに送信してください。

from kafka import KafkaProducer

BOOTSTRAP_SERVERS = "1234567890123456.zerobus.us-west-2.cloud.databricks.com:9092"

producer = KafkaProducer(
    bootstrap_servers=BOOTSTRAP_SERVERS,
    security_protocol="SASL_SSL",
    sasl_mechanism="OAUTHBEARER",
    sasl_oauth_token_provider=ZerobusTokenProvider(),
    # Wait for durable acknowledgment before treating a record as ingested.
    acks="all",
    # Compression is not supported by the endpoint; send records uncompressed.
    compression_type=None,
)

# Each send() returns a future immediately. The topic is the full table name.
futures = [
    producer.send(
        topic=TABLE_NAME,
        value=json.dumps(
            {"device_name": f"sensor-{i}", "temp": 20 + i % 15, "humidity": 50 + i % 40}
        ).encode("utf-8"),
    )
    for i in range(1000)
]

producer.flush()

# Block on each future to confirm every record was durably acknowledged.
for future in futures:
    future.get(timeout=30)

producer.close()
print("All records ingested successfully")

構成オプション

Kafka互換APIはKafkaプロトコルのプロデューサーサブセットを実装しています。 以下のオプションでプロデューサーを設定してください。

Option 詳細情報
レコード形式 JSON のみ。 各レコード値は、ターゲットテーブルスキーマに一致するUTF-8エンコードされたJSONオブジェクトでなければなりません。 protobufやAvroを送るにはZerobus SDKを使います。
Compression サポートされていません。 例えば、 compression.type=noneのように非圧縮バッチを送信します。 Zerobusは gzipsnappylz4zstd のバッチを UNSUPPORTED_COMPRESSION_TYPE エラーコードで拒否します。
レコード フィールド 価値だけ。 Zerobusはレコード値を取り込み、キー、ヘッダー、パーティション割り当て、タイムスタンプを永続化しません。
API のサポート 書き込み専用です。 Zerobusは Produce リクエストに加え、セッション確立に必要なメタデータおよびSASLリクエストも受け付けます。 消費者向け、管理用、トランザクション用APIはサポートされていません。
フロントエンド プライベート リンク サポートされていません。 代わりにパブリックエンドポイント経由で接続してください。
Schema 適用。 Zerobusはターゲットテーブルスキーマと一致しないフィールドのレコードを拒否し、追加のnullable Delta列を非破壊的な変更として扱います。 マッチしないフィールドを拒否する代わりに取得するには 、レスキュー列を設定しましょう。

遅延、クォータ、レコードサイズ、パーティションテーブルの制限については、 Zerobus Ingest quotaを参照してください。

ベスト プラクティス

Kafka互換APIから最高のパフォーマンスと信頼性を得るために、以下のガイドラインに従ってください。

  • バッチごとにプロデューサーを作成するのではなく、長期間使用できるプロデューサーを多くのレコードにまたがって再利用してください。プロデューサーの作成や SASL ハンドシェイクには初期化コストがかかります。
  • プロデューサーがレコードをバッチにまとめて蓄積できるようにしましょう。例えば、レコードごとにフラッシュするのではなく、 linger.ms や、 batch.sizeをチューニングするなどです。 バッチ処理はスループットを最も大きく左右する手段です。
  • acks=all を使用すると、Zerobus の「少なくとも 1 回」というセマンティクスに準拠し、各バッチに対して永続的な確認応答を取得できます。
  • クライアントのトークンプロバイダーコールバックを通じてOAuthトークンを取得し、再接続時に自動的にリフレッシュできるようにしてください。期限切れの静的トークンを渡すのではなく。
  • 最大のスループットを得るために、Zerobusエンドポイントと同じクラウドリージョン内でプロデューサーを稼働させましょう。

エラー処理

Zerobusは、影響を受けたトピックとパーティションで標準的なKafkaエラーコードを使って障害を報告します。 一般的なコードには、次のようなものがあります。

カフカの誤り Meaning
SASL_AUTHENTICATION_FAILED OAuthトークンが欠落しているか、無効であるか、テーブル上で必要なUnity Catalogの権限が欠けている場合があります。
UNKNOWN_TOPIC_OR_PARTITION ターゲットテーブルが存在しないか、削除されているか、トークンが書き込みの権限を持っていないかです。
INVALID_RECORD レコードがスキーマ検証に失敗するか、UTF-8 JSONとしてデコードできなかった場合。
MESSAGE_TOO_LARGE 1つのレコードは10MBのレコードサイズ制限を超えます。 レコードサイズを参照してください。
UNSUPPORTED_COMPRESSION_TYPE バッチは圧縮されていました。 レコードを非圧縮で送信してください。

Produceリクエストが失敗すると、ゼロバスはパーティションごとのエラーコードを返し、接続を終了します。 Kafkaプロデューサーは自動的に再接続しますが、クライアントは送信失敗を表面化するよう設計します。例えば、各送信の結果を検査することで、レコードが静かにドロップしないようにします。

その他のリソース

  • Zerobus Ingestを活用してください:まだZerobus Ingestを設定していない場合は、ワークスペースURLの見つけ方、ターゲットのDeltaテーブルの作成、サービスプリンシパルの設定手順をこちらからご覧ください。 これらのステップはすべてのインターフェースで共有されます。
  • Zerobus Ingest クォータ:本番環境にデプロイする前にデフォルトの Zerobus クォータを確認しましょう。
  • Arrow Flight と Zerobus Ingest を使用: gRPC による列単位またはバッチ単位のインジェストに使用されます。