Skip to content

第4部: Schema Registry(スキーマレジストリ)

サポート対象バージョン: Karapace 4.x, Apicurio Registry 3.x, Confluent Schema Registry (compatible API)
最終更新: July 9, 2026

Schema Registry が必要な理由

Kafka 自体は、すべてのメッセージを不透明なバイト配列として扱います。producer がその配列にどのような形式で書き込むかは気にしません。問題は、producer と consumer が通常は別々のアプリケーションであり、異なるチームが所有し、異なるスケジュールでデプロイされることです。producer がフィールドを追加したり型を変更したりした瞬間、その変更を知らない consumer はメッセージのデシリアライズに失敗するか、壊れた値を読み取ってしまいます。

Schema-less JSON の問題

json
{"orderId": "ORD-1001", "amount": 42.5, "currency": "USD"}

このような生の JSON payload は人間が読みやすい一方で、実際のコストを伴います。

  • 強制される契約がない: producer が amount を密かに文字列に変更しても、それを止めるものがありません。
  • 検証は runtime のみ: 欠落フィールドや型の不一致は、consumer が payload を解析しようとしたときに初めて表面化します。
  • Payload サイズ: フィールド名がすべてのメッセージで繰り返されるため、バイナリ形式より大きくなり、高スループットでは実際のネットワーク/ストレージコストになります。
  • バージョン履歴がない: 「この topic の schema のバージョン 3 はどのようなものだったか?」に答える方法がありません。

Schema Registry が解決すること

Schema Registry は、Avro、Protobuf、JSON Schema のような構造化形式の schema を一元的に保存し、バージョン管理し、バージョン間の互換性ルールを強制する独立した service です。流れはおおよそ次のようになります。

  1. メッセージを送信する前に、producer は自分の schema を registry に登録します(または検索します)。
  2. registry は schema ID を返し、producer は完全な schema の代わりに、その ID だけを先頭に付けて(通常は 5-byte の magic-byte + ID header)payload をシリアライズします。
  3. consumer はメッセージに埋め込まれた schema ID を読み取り、対応する schema を registry から取得して、それに従ってデシリアライズします。
  4. 新しい schema version が登録されると、registry は互換性ルールに照らして確認し、違反している場合は登録を即座に拒否します。

これにより、producer と consumer は 互いの deployment schedule を知らなくても 独立して進化できます。また、wire payload には schema ID だけが含まれるため、Avro/Protobuf のバイナリエンコーディングは JSON よりも大幅に小さくなります。

主要な実装の比較

KarapaceApicurio RegistryConfluent Schema Registry
ベンダーAivenRed HatConfluent
ライセンスApache License 2.0Apache License 2.0Confluent Community License (2018 年以降、完全な open source ではない)
サポート形式Avro, JSON SchemaAvro, Protobuf, JSON Schema, OpenAPI, AsyncAPI, GraphQL, Kafka Connect schemas, etc.Avro, Protobuf, JSON Schema
API 互換性Confluent REST API と互換Confluent-compatible mode (ccompat)元の API (de facto standard)
Storage backendKafka topicKafka topic or SQL (e.g. PostgreSQL)Kafka topic
同梱 REST ProxyYes (Karapace REST Proxy)No (registry only)Separate commercial REST Proxy
Commercial support termsAiven の managed service、または community 経由Red Hat subscription 経由scale 時に Confluent Platform licensing が必要
EKS/Strimzi との適合性強い — 純粋な open source、軽量強い — multi-format、multi-backendライセンス確認が必要

自己管理の EKS + Strimzi stack では、Karapace または Apicurio Registry を推奨します。 どちらも Apache-2.0 license で提供され、再配布や変更に制限はありません。対照的に、Confluent Schema Registry の Confluent Community License は、競合する managed service として提供することを明示的に禁止しており、2018 年以降は完全な open source ではありません。kafka-avro-serializer のような client-side library は現在も Confluent から公開されていますが、REST API には互換性があるため、schema.registry.url を Karapace または Apicurio に向けるだけで、通常はコード変更なしで動作します。

Serialization Formats

Avro

Avro は schema を JSON として定義し、data をコンパクトなバイナリ形式にシリアライズします。Kafka ecosystem で最も広く使われている形式であり、特に優れている機能は schema resolution です。writer schema(data が書き込まれたときに使用された schema)と reader schema(読み戻すときに使用される schema)は完全に一致している必要がなく、Avro が明確に定義されたルールに従って差分を解決します。

json
{
  "type": "record",
  "name": "Order",
  "namespace": "com.example.orders",
  "fields": [
    { "name": "orderId", "type": "string" },
    { "name": "customerId", "type": "string" },
    { "name": "amount", "type": "double" },
    { "name": "currency", "type": "string", "default": "USD" },
    { "name": "createdAt", "type": "long", "logicalType": "timestamp-millis" }
  ]
}

Protobuf

Protobuf schema は .proto file で定義され、protoc でコンパイルして各 target language のコードを生成します。Avro と同様にコンパクトなバイナリエンコーディングを生成しますが、明示的なフィールド番号を割り当て、より厳密な型システムを持つため、言語をまたいでより高品質な生成コードを作りやすくなります。Kafka ecosystem での Protobuf 採用は着実に増えています。

protobuf
syntax = "proto3";

package com.example.orders;

message Order {
  string order_id = 1;
  string customer_id = 2;
  double amount = 3;
  string currency = 4;
  int64 created_at = 5;
}

JSON Schema

JSON Schema は JSON payload 自体の検証ルールを定義します。人間が読みやすく debug しやすい一方で、フィールド名がすべてのメッセージで繰り返されるため、payload は Avro や Protobuf よりかなり大きくなります。schema validation が必要だが、throughput や storage cost への感度が比較的低い workload に適しています。

3 つの形式の比較

AvroProtobufJSON Schema
Schema definitionJSON.proto IDLJSON Schema
Payload size小さい小さい大きい
Human-readableschema のみschema のみpayload も可
Cross-language codegen良い非常に良い良い
Kafka ecosystem adoption非常に高い高い (成長中)中程度
Schema evolution rulesWriter/reader resolutionField-number basedJSON Schema validation rules

互換性戦略

新しい schema version が登録されると、registry は設定された compatibility mode に従って、前の version と照らし合わせて確認します。この 4 つの mode を正しく理解することは重要です。これは schema 管理で最もよく混同される概念です。

ModeMeaningDeployment order
BACKWARDnew schema を使う reader が、old schema で書かれた data を読める必要があるconsumers を先に upgrade
FORWARDold schema を使う reader が、new schema で書かれた data を読める必要があるproducers を先に upgrade
FULLBACKWARD と FORWARD の両方が成立どちらの順序でも安全
NONE互換性チェックなし手動調整が必要

多くの人が最もよく逆に理解してしまう点は次のとおりです。

  • BACKWARD は「new schema(reader として)が old data を読める」ことを意味します。実際には、new-schema consumer を先にデプロイしても安全 ということです。producer がまだ old schema で書き込んでいる間でも、upgrade 済みの consumer はそれを問題なく読み取れます。
  • FORWARD は「old schema(reader として)が new data を読める」ことを意味します。つまり、producer を new schema に先に upgrade しても安全 ということです。old schema のまま動作している consumer も引き続き動作します。

Backward-Compatible Change の例

Order schema に default value 付きの optional field を追加することは、BACKWARD compatible です。

json
{ "name": "discountCode", "type": ["null", "string"], "default": null }

new schema を使う consumer が old data(このフィールドを持たない data)を読む場合、単に default value(null)を取得するだけで、失敗しません。

破壊的変更の例

これらは典型的な BACKWARD-compatibility 違反です。

  • default のない required field の追加: default のない新しい discount_code field を追加すると、new-schema reader は old data に存在しなかった field を期待するため、失敗します。(逆に、field を 削除 することは BACKWARD compatible ですが、代わりに FORWARD を壊します。old-schema reader は、削除済みの field が new data 上でも required であることを依然として期待するためです。)
  • field type の変更: amountdouble から string に切り替えると、既存のバイナリエンコード済み data を new type として decode できなくなります。
  • field の rename(alias なし): reader は新しい名前で field を探しますが、old data には古い名前でしか存在しません。

Strimzi/EKS へのデプロイ

Apicurio Registry のデプロイ (Kafka-Topic Storage)

Strimzi-managed Kafka cluster がすでに動作している前提で、Kafka-topic storage engine を backend として、同じ namespace に Deployment として Apicurio Registry をデプロイできます。

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: apicurio-registry
  namespace: kafka
spec:
  replicas: 1
  selector:
    matchLabels:
      app: apicurio-registry
  template:
    metadata:
      labels:
        app: apicurio-registry
    spec:
      containers:
        - name: apicurio-registry
          image: quay.io/apicurio/apicurio-registry:3.0.6
          ports:
            - containerPort: 8080
          env:
            - name: APICURIO_STORAGE_KIND
              value: "kafkasql"
            - name: APICURIO_KAFKASQL_BOOTSTRAP_SERVERS
              value: "my-kafka-cluster-kafka-bootstrap.kafka.svc:9092"
---
apiVersion: v1
kind: Service
metadata:
  name: apicurio-registry
  namespace: kafka
spec:
  selector:
    app: apicurio-registry
  ports:
    - port: 8080
      targetPort: 8080

Apicurio は kafkasql の代わりに SQL backend(APICURIO_STORAGE_KIND=sql)もサポートしているため、すでに PostgreSQL/RDS instance を運用している場合は、registry をそちらに向けることもできます。対照的に Karapace は常に schema を Kafka topic(_schemas)に保存し、別個の backend configuration は必要ありません。

Schema の登録

registry が起動したら、schema は REST API(Confluent-compatible endpoint を使用)を通じて登録します。

bash
curl -X POST http://apicurio-registry.kafka.svc:8080/apis/ccompat/v6/subjects/orders-value/versions \
  -H "Content-Type: application/vnd.schemaregistry.v1+json" \
  -d '{"schema": "{\"type\":\"record\",\"name\":\"Order\",\"fields\":[{\"name\":\"orderId\",\"type\":\"string\"}]}"}'

Client Configuration

Kafka producer/consumer application は、serializer を registry URL に向けます。

properties
value.serializer=io.confluent.kafka.serializers.KafkaAvroSerializer
schema.registry.url=http://apicurio-registry.kafka.svc:8080/apis/ccompat/v6

同じ KafkaAvroSerializer class は Karapace に対しても動作します。schema.registry.url を Karapace の REST endpoint(default では port 8081)に向けるだけです。registry implementation を入れ替えても application code を変更する必要がないことこそ、Confluent-compatible API が提供する価値です。

次のステップ

この部では、Schema Registry が、producer と consumer の両方が独立して進化する中で、data contract を安全に保つ方法を扱いました。第5部では Kafka Connect と MirrorMaker に進み、external system との統合と cluster 間の data replication を扱います。

メインページに戻る

クイズ

この章で学んだ内容を確認するために、Topic Quiz を試してみてください。