Версии хранения

Версии хранения

API-сервер Kubernetes хранит объекты, опираясь на совместимое с etcd хранилище (часто это и есть сам etcd). Каждый объект сериализуется с использованием определённой версии своего API-типа — например, представление ConfigMap версии v1. То, в каком виде объект хранится в кластере, Kubernetes описывает термином версия хранения.

Кроме того, API Kubernetes опирается на автоматическое преобразование. Например, если у вас есть HorizontalPodAutoscaler, то работать с ним можно, произвольно смешивая версии v1 и v2 соответствующего API. Kubernetes сам преобразует каждый вызов API, поэтому клиенты не видят, какая версия сериализована на самом деле.

Администраторам кластера важно понимать, что такое версия хранения объекта: именно она связывает представление объекта в API с тем, как он в действительности закодирован в хранилище. Это существенно в тех случаях, когда двоичное кодирование объекта имеет значение, — например, при шифровании данных при хранении или при выводе версий API из обращения.

У одного и того же API может быть несколько версий хранения, которые API-сервер затем приводит к схеме объекта. При этом у отдельного объекта в составе ресурса в каждый момент времени может быть только одна версия хранения. Это значит, что API-серверу известны двоичные представления объектов и он способен на лету преобразовать любую из сохранённых версий в представление объекта в API.

Версия самого объекта и версия хранения — совершенно разные вещи. Например, объекты API версий v1alpha1 и v1beta1 для одного и того же ресурса будут закодированы в хранилище одинаково, если между их сохранением версия хранения не менялась.

Соответствие версии хранения и ресурса

У каждого ресурса в каждый момент времени активна ровно одна версия хранения: любая запись объекта сохранит его именно в этой версии. Но версию хранения можно изменить, и тогда объекты окажутся сохранены в разных версиях. При этом отдельный объект в любой момент времени хранится только в одной версии.

При чтении API-сервер преобразует сохранённые данные в представление объекта в API. Благодаря этому старые версии хранения могут лежать сколь угодно долго, пока объект не обновляют. А запись, наоборот, при обновлении преобразует сохранённый объект в новое представление.

Версии хранения для пользовательских ресурсов

Пользовательские ресурсы определяются динамически и потому отличаются по работе с версией хранения от встроенных типов Kubernetes. У встроенных объектов кодирование для хранения обычно задаётся отдельно от их API-типов: сохранённый объект выступает промежуточным представлением, и конкретная версия ресурса не имеет значения — это просто поле в схеме объекта.

Для пользовательских же ресурсов одна из версий ресурса обязана быть назначена версией хранения. Именно схема, заданная этой версией пользовательского ресурса, и будет использована для кодирования ресурса на уровне хранилища. Подробнее о настройке API и версионировании смотрите в разделе про расширенные возможности CRD.

Для примера рассмотрим такой CustomResourceDefinition для crontabs:

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.example.com
spec:
  group: example.com
  # список версий, которые поддерживает этот CustomResourceDefinition
  versions:
  - name: v1beta1
    # каждую версию можно включить или выключить флагом served
    served: true
    # ровно одна версия должна быть отмечена как версия хранения
    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

Здесь версией хранения назначено определение API v1beta1: значит, любое создание или обновление crontabs будет сохранено по схеме объекта из API v1beta1. На практике это означает, что объект API версии v1 вообще никогда не сможет сохранить поле time, поскольку его нет в определении версии хранения. Эта схема используется на уровне хранилища как двоичное представление самого объекта. Попытка назначить версией хранения сразу две версии считается недопустимой: это означало бы, что объекты одновременно допустимо хранить по двум разным схемам данных.

После смены версии, используемой для хранения, все новые и обновляемые пользовательские ресурсы будут сохраняться уже в этой версии API. Отслеживание объекта или его получение по-прежнему будут работать: объект просто преобразуется из старой версии хранения, и это на него никак не влияет. Эффект дают только обновление и создание — они и используют новую версию хранения.

Как версии хранения связаны с шифрованием при хранении

Существуют инструменты для шифрования данных при хранении в кластере, в первую очередь для секретов кластера. Это даёт дополнительный уровень защиты от утечки данных, поскольку в кластере хранятся уже зашифрованные данные. Соответственно, API-сервер расшифровывает данные при извлечении их из хранилища. Чтобы корректно декодировать объект, у API-сервера должен быть ключ для соответствующей версии хранения.

Версия хранения в этом случае — нечто большее, чем просто двоичное представление объекта. Версией хранения может выступать что угодно, из чего сохранённые данные так или иначе можно преобразовать в объект API.

Переход на другую версию хранения

Несколько версий хранения у одного ресурса могут создать администраторам кластера проблемы. Администратор не может удалить старые версии API для CRD, которые, возможно, уже не поддерживаются, пока не убедится, что ни один объект больше не использует связанную с ними версию хранения. При большом количестве объектов и без наглядной картины того, какие из них новые, а какие всё ещё лежат в старых версиях хранения, трудно понять, когда версию можно безопасно удалить. А если удалить версию преждевременно, объект может оказаться вообще нечитаемым.

Ещё один важный момент — использование ключей шифрования, о которых сказано выше. Поскольку версия хранения обновляется только при активном обращении к ресурсу, при ротации ключей и старый, и новый ключи шифрования должны оставаться в работе до тех пор, пока администратор не убедится, что каждый объект был записан хотя бы один раз. Это создаёт и риски для безопасности, и неудобства в работе, так как до этого момента ключ нельзя полностью вывести из обращения.

О том, как выполнить миграцию и без ручного вмешательства перевести все объекты на более новую версию хранения, читайте в разделе Storage Version Migration.