Kafkaブローカー自体はメッセージの構造を検証しません。実運用では、Schema Registryと命名戦略、互換性レベルを組み合わせて「どのトピックにどのスキーマが来るべきか」を強制する仕組み作りが重要です。
本稿では、トピックとスキーマの結び付け(バインディング)を中心に、互換性違反を未然に防ぐための設計と運用を解説します。Confluent CCDAKで頻出のSubject命名戦略、互換性レベル、ブローカー側検証の要点も押さえます。
Kafkaは高スループットのログであり、ブローカーはメッセージ構造を知りません。スキーマ検証を担うのはクライアントのシリアライザ/デシリアライザとSchema Registryです。従って、トピックとスキーマの関係を曖昧にすると、破壊的変更がすり抜け、本番でデシリアライズ失敗や集計ジョブの停止を招きます。
バインディング設計の核は、Subject命名戦略で“どの単位でスキーマ互換性を管理するか”を決め、Registry側で互換性を強制し、必要に応じてブローカー側検証を有効化することです。さらに、CI/CDで事前に互換性チェックを行い、レジストリ登録時点で破壊的変更を拒否できるようにします。
エンドツーエンドの検証ポイント
Schema Registryでは、登録単位であるSubjectの命名戦略によって互換性の束ね方が変わります。典型はTopicNameStrategy(デフォルト)で、<topic>-value に値スキーマをひも付けます。複数のイベント型を1トピックに混在させる場合は、RecordNameStrategyやTopicRecordNameStrategyが実務で有効です。
CCDAKでは、どの戦略がどのユースケースに適するか、混在時の落とし穴、同じトピックに異なるイベント型を入れるときの選択肢が問われがちです。
| 戦略 | Subjectの単位 | 適したケース | 注意点 |
|---|---|---|---|
| TopicNameStrategy | <topic>-key/value | 1トピック=1イベント型。プロデューサ/コンシューマが明確に同一型を扱う | 複数型を同一トピックに混在させると互換性チェックが衝突しやすい |
| RecordNameStrategy | <recordFullName> | 型中心で再利用。異なるトピック間で同一レコード名を共有したい場合 | 同一レコード名を別トピックで進化させると影響範囲が広がる |
| TopicRecordNameStrategy | <topic>-<recordFullName> | 同一トピックに複数イベント型を混在。型ごとに互換性を独立管理したい | レコード名の一貫性管理が必要。サブジェクト数が増える |
クライアントでのSubject命名戦略指定(Java例)
Properties p = new Properties();
p.put("key.serializer", "io.confluent.kafka.serializers.KafkaAvroSerializer");
p.put("value.serializer", "io.confluent.kafka.serializers.KafkaAvroSerializer");
p.put("schema.registry.url", "https://registry:8081");
// トピック内に複数イベント型を入れる場合
p.put("value.subject.name.strategy",
"io.confluent.kafka.serializers.subject.TopicRecordNameStrategy");Schema Registryはグローバルとサブジェクト単位の互換性レベルを持ちます。一般的な既定はBACKWARD(後方互換)で、最新スキーマで旧データを読むことを保証します。運用では多くの場合、BACKWARD_TRANSITIVE(過去すべてのバージョンとの互換)を推奨します。
Avro/JSON Schema/Protobufは進化ルールに差異がありますが、実務では「フィールドの追加はデフォルト値を付ける」「必須フィールドの削除・型変更は避ける」が共通の安全策です。トピック内に複数型がある場合でも、TopicRecordNameStrategyなら型ごとに独立して互換性チェックできます。
互換性の設定と事前チェック(REST API)
# グローバル互換性をBACKWARD_TRANSITIVEに
curl -s -X PUT -H 'Content-Type: application/json' \
--data '{"compatibility":"BACKWARD_TRANSITIVE"}' \
https://registry:8081/config
# サブジェクト単位で上書き
curl -s -X PUT -H 'Content-Type: application/json' \
--data '{"compatibility":"FULL_TRANSITIVE"}' \
https://registry:8081/config/orders-value
# 登録前に互換性をテスト(v=latestと新スキーマの組合せを検証)
curl -s -X POST -H 'Content-Type: application/json' \
--data '{"schemaType":"AVRO","schema":"{\\"type\\":\\"record\\",...}"}' \
https://registry:8081/compatibility/subjects/orders-value/versions/latest最も多い事故は“レビューをすり抜けた破壊的変更のレジストリ登録”です。回避策は、スキーマをアプリとは独立したアーティファクトとして管理し、PR時にRegistryへの互換性チェックを行い、失敗したらマージを止めることです。登録権限はCIのロボットユーザーに限定します。
環境分離はレジストリを環境ごとに分け、DEV→STG→PRDへスキーマを昇格させます。IDは環境間で一致しない前提で、内容(フィンガープリント)で同一性を判断します。
CIジョブ例(擬似): 互換性チェック→合格なら登録
# 1) 互換性テスト
curl -f -X POST -H 'Content-Type: application/json' \
--data @candidate-schema.json \
https://registry:8081/compatibility/subjects/orders-value/versions/latest
# 2) 合格後に登録
curl -f -X POST -H 'Content-Type: application/json' \
--data @candidate-schema.json \
https://registry:8081/subjects/orders-value/versionsConfluent Serverでは、トピック単位でブローカー側スキーマ検証を有効化できます。これにより、Schema Registryに存在しないスキーマIDや互換性違反のメッセージをブローカーで拒否できます。クリティカルなトピックでは有効化を検討してください。
Kafka Connectでは、Avro/JSON Schema/Protobufの各ConverterをSchema Registry連携で設定し、schemas.enableを有効にします。ksqlDBは対象トピックのスキーマに依存するため、進化時は下流のストリーム/テーブルに影響が及ぶかを事前に評価します。
トピックでブローカー側スキーマ検証を有効化(Confluent Server)
# 既存トピックに付与
kafka-configs --bootstrap-server broker:9092 \
--alter --topic orders \
--add-config confluent.value.schema.validation=true,confluent.key.schema.validation=true
# Connectの例(Avro)
"key.converter":"io.confluent.connect.avro.AvroConverter",
"value.converter":"io.confluent.connect.avro.AvroConverter",
"key.converter.schema.registry.url":"https://registry:8081",
"value.converter.schema.registry.url":"https://registry:8081",
"value.converter.schemas.enable": true互換性違反は登録時点で止めるのが最善ですが、実行時の失敗に備えた観測性も必要です。典型はコンシューマのデシリアライズ例外、ブローカー拒否、Registryの409エラーです。DLQとメトリクス、監査ログを組み合わせて早期検知と切り戻しを可能にします。
切り戻しは“スキーマを戻す”か“コンシューマを新スキーマ対応に更新する”かの二択です。前者は過去バージョンを最新として再登録、後者は段階的ロールアウト(カナリア)で影響を抑えます。
CCDAK
問題 1
単一トピック orders に OrderCreated と OrderCancelled など複数のイベント型を格納します。既存コンシューマは後方互換で読み続けられることが要件で、破壊的変更は登録時点で拒否したい。最も適切な組み合わせはどれか。
正解: B
複数イベント型を同一トピックに入れる場合は、型ごとに互換性を独立管理できる TopicRecordNameStrategy が適しています。互換性は BACKWARD_TRANSITIVE が本番向きで、Registry登録時に破壊的変更を拒否できます。加えて Confluent Server のトピック設定でブローカー側スキーマ検証を有効にすると、実行時にも誤ったメッセージを拒否できます。
グローバル互換性とサブジェクト互換性、どちらを設定すべき?
初期はグローバル既定をBACKWARDにしつつ、実運用では各サブジェクトで明示的に設定します。重要トピックは BACKWARD_TRANSITIVE か FULL_TRANSITIVE を推奨します。グローバルは“デフォルト値”、最終権限はサブジェクト側に置くのが安全です。
同一トピックでAvroとJSON Schemaを混在させられる?
避けるべきです。Confluentのワイヤ形式はスキーマIDで解決しますが、デシリアライザの種類は自動判別されません。同一トピックでフォーマット混在はコンシューマ実装と運用を不必要に複雑化させ、障害要因になります。トピック単位でフォーマットを統一してください。
キーのスキーマはどのように進化させるべき?
キーは分散やコンパクションに直結するため、原則として進化させないのが安全です。やむを得ず変更する場合は、<topic>-key サブジェクトで別管理し、後方互換を厳密に評価します。ハッシュ安定性が崩れる変更(型変更やシリアライズ形式の変更)は避け、必要なら新トピックへ移行する設計を検討してください。
NicheeLab編集部
データエンジニアリング・クラウド資格の専門家。Databricks・Snowflake等の認定資格を保有し、実務経験に基づいた問題作成・解説を行っています。NicheeLab運営。
Kafka Topic と Partition の基礎: 分散とスケーラビリティの要
CCDAK 対策と実務の両立を意識し、Topic/Partition/Replica/Consumer Group の役...
CCDAK 試験ガイド:出題範囲・配点・申込み・対策
Confluent Certified Developer for Apache Kafka (CCDAK) の出題範囲...
Confluent Certified Administrator (CCAAK) 対策: 出題範囲・配点の考え方・運用観点の要点
CCAAKに向けて、試験領域の押さえどころを運用目線で整理。プロダクションで通用する設定・監視・セキュリティの実践知を、...
Kafka の Replica と In-Sync Replicas を正しく設計する: 耐障害性と一貫性
レプリカとISRの仕組みを起点に、acks と min.insync.replicas、クリーン/アンクリーンリーダー選...
Kafka の Offset とコミット: ポジション管理と at-least-once の基礎
CCDAK 対策と実務の両立を意識して、Kafka コンシューマのオフセット管理とコミット戦略を整理。at-least-...