Kubernetes APIサーバーは、etcd互換のバッキングストア(多くの場合、バッキングストレージはetcd自体です)にオブジェクトを保存します。 各オブジェクトは、それぞれのAPIタイプの特定のバージョンを使用してシリアライズされ、ConfigMapのv1表現などがその例です。 Kubernetesでは、クラスター内でのオブジェクトの保存方法を ストレージバージョン という用語で表現します。
Kubernetes APIは、自動的なバージョン変換にも対応しています。 たとえば、HorizontalPodAutoscalerがある場合、HorizontalPodAutoscaler APIのv1とv2のバージョンを任意に組み合わせて、そのHorizontalPodAutoscalerとやり取りできます。 Kubernetesは、実際にシリアライズされているバージョンがクライアントからは見えないようにするため、各API呼び出しを変換する役割を担っています。
クラスター管理者にとって、オブジェクトのストレージバージョンは理解しておくべき重要な概念です。 なぜなら、これは、オブジェクトのAPI表現をストレージバックエンドの実際のエンコーディングに結び付けるものであるためです。 これは、保存時の暗号化やAPIの非推奨化など、オブジェクトの基礎となるバイナリエンコーディングが問題となる場合に重要です。
同じAPIは複数のストレージバージョンを持つことができ、APIサーバーはそれらをオブジェクトスキーマに変換できます。 そのリソースの一部である単一のオブジェクトは、任意の時点で1つのストレージバージョンのみを持つ必要があります。 これは、APIサーバーがオブジェクトのバイナリエンコーディングを認識しており、保存されているすべてのバージョンをオブジェクトのAPI表現に動的に変換できることを意味します。
オブジェクトのバージョンは、ストレージバージョンとは完全に別のものです。
たとえば、同じリソースに対するv1alpha1とv1beta1のAPIオブジェクトは、2つのオブジェクト間でストレージバージョンが更新されていない限り、ストレージ内では同じようにエンコードされます。
すべてのリソースは、任意の時点で1つのアクティブなストレージバージョンを持ちます。 つまり、オブジェクトへのあらゆる書き込みは、その特定のストレージバージョンでオブジェクトを保存します。 ただし、ストレージバージョンは更新できるため、オブジェクトは異なるバージョンで保存される可能性があります。 1つのオブジェクトは、任意の時点で1つのストレージバージョンでのみ保存されます。
APIサーバーからの読み取りは、保存されたデータをオブジェクトのAPI表現に変換します。 これにより、オブジェクトへの更新が発生しない限り、古いストレージバージョンが無期限に存在できます。 一方、書き込みは、更新時に保存されたオブジェクトを新しい表現に変換します。
カスタムリソースは動的に定義されるため、組み込みのKubernetesタイプとはストレージバージョンの扱いが異なります。 組み込みオブジェクトは通常、ストレージエンコーディングがAPIタイプとは別に定義されており、保存されたオブジェクトがハブとして機能します。 そのため、リソースの特定のバージョンは、オブジェクトスキーマ内のフィールドとして記録される以外、特に重要ではありません。
しかし、カスタムリソースの場合、リソースの特定のバージョンをストレージバージョンとして設定する必要があります。 そのカスタムリソースの特定のバージョンによって定義されたスキーマが、ストレージレイヤーでのリソースのエンコーディングとして使用されます。 APIのセットアップとバージョン管理の詳細については、CRDの高度な機能を参照してください。
たとえば、crontabs のこのCustomResourceDefinitionを参照してください:
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: crontabs.example.com
spec:
group: example.com
# このCustomResourceDefinitionでサポートされているバージョンのリスト
versions:
- name: v1beta1
# 各バージョンは、Servedフラグで有効/無効にできます。
served: true
# 1つのバージョンのみをストレージバージョンとしてマークする必要があります。
storage: true
schema:
openAPIV3Schema:
type: object
properties:
host:
type: string
port:
type: string
- name: v1
served: true
storage: false
schema:
openAPIV3Schema:
type: object
properties:
host:
type: string
port:
type: string
time:
type: string
conversion:
strategy: None
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
shortNames:
- ct
v1beta1 API定義がストレージバージョンとして使用されます。
つまり、crontabsを更新または作成すると、v1beta1 APIのオブジェクトスキーマで保存されることを意味します。
この場合、実際にはv1 APIオブジェクトはtimeフィールドを保存できないことを意味します。
これは、ストレージ定義の一部ではないためです。
このスキーマは、オブジェクト自体のバイナリエンコーディングとしてストレージレイヤーで使用されます。
2つのバージョンを同時に保存バージョンとして設定しようとすることは無効と見なされます。
これは、2つのデータスキーマが同時にオブジェクトを保存する有効な方法と見なされることを意味するためです。
ストレージに使用されるバージョンを変更すると、新規作成または更新されるCRは、そのバージョンのAPIを使って保存されます。 オブジェクトの監視や取得によって、そのオブジェクトは使用中の状態になりますが、古いストレージバージョンから変換されるだけで、オブジェクト自体は変更されません。 オブジェクトを更新または作成した場合にのみ、新しく定義されたストレージバージョンが使用されます。
クラスターの保存時のストレージを暗号化するツールがあります。 特にクラスターのSecretに対して使用されます。 これにより、クラスターに実際に保存されているデータが暗号化されるため、データ流出に対する追加の保護層が提供されます。 つまり、APIサーバーは、ストレージからデータを取得する際に、実際にデータを復号しています。 APIサーバーは、オブジェクトを適切にデコードするために、そのストレージバージョンの鍵を持っている必要があります。
この場合、ストレージバージョンは、オブジェクトのバイナリエンコーディング形式だけを指すわけではありません。 保存されたデータが何らかの方法でAPIオブジェクトに変換できる限り、それをストレージバージョンとして扱えます。
単一のリソースに対して複数のストレージバージョンが存在すると、クラスター管理者にとって問題が生じる可能性があります。 クラスター管理者は、すべてのオブジェクトが古いストレージバージョンを使用していないことを確認するまで、CRDのサポートされていない古いバージョンのAPIを削除できない場合があります。 多数のオブジェクトが存在し、どれが新しくどれがまだ古いストレージバージョンに基づいているかが不透明な場合、あるバージョンを安全に削除できるタイミングを判断するのは困難です。 バージョンを早まって削除すると、オブジェクトを完全に読み取れなくなる可能性があります。
もう1つの重要な問題は、上記のセクションで定義されている暗号化鍵の使用です。 リソースはストレージバージョンを更新するために実際に使用されている必要があるため、鍵のローテーションが行われると、すべてのオブジェクトが少なくとも1回書き込まれたことを管理者が確認するまで、古い暗号化鍵と新しい暗号化鍵の両方を使用し続ける必要があります。 これは、それまで鍵を完全に使用停止できないため、セキュリティリスクとユーザビリティの問題の両方をもたらします。
手動操作なしですべてのオブジェクトが新しいストレージバージョンを使用するように移行を実行する方法の例については、ストレージバージョンの移行を参照してください。