Некоторым приложениям также нужно хранить данные, но они достаточно спокойно относятся к тому, что данные не будут сохранены после перезапуска.

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

В Kubernetes уже есть несколько типов эфемерных томов, но их функциональность ограничена тем, что реализовано в K8s.

Эфемерные тома CSI позволили расширять Kubernetes с помощью драйверов CSI для обеспечения поддержки легковесных локальных томов. Этим способом возможно применять произвольные структуры: настройки, секреты, данные для идентификации, переменные и так далее. CSI драйверы должны быть доработаны для поддержки этой функции Kubernetes, поскольку предполагается, что обычные стандартизированные драйверы не будут работать - но предполагается, что такие тома можно использовать на любом узле, выбранном для пода.

Это может стать проблемой для томов со значительным потреблением ресурсов узла или для хранилища, доступного только на некоторых узлах. Поэтому в Kubernetes 1.19 представлены две новые фунции томов для альфа-тестирования, концептуально похожих на тома EmptyDir:

  • эфемерные тома общего назначения;

  • отслеживание емкости хранилища CSI.

Преимущества нового подхода:

  • хранилище может быть локальным, либо подключаемым по сети;

  • тома могут иметь заданный размер, который не может быть превышен приложением;

  • работает с любыми драйверами CSI, поддерживающими предоставление постоянных томов и (для поддержки отслеживания емкости) реализующими вызов GetCapacity;

  • тома могут иметь некоторые начальные данные, зависящие от драйвера и параметров;

  • все типовые операции с томом (создание снимка состояния, изменение размера и т.п.) поддерживаются;

  • тома можно использовать с любым контроллером приложений, принимающим спецификацию модуля или тома;

  • планировщик Kubernetes сам выбирает подходящие узлы, поэтому больше не нужно обеспечивать и настраивать расширения планировщика и изменять webhooks.

Варианты применения

Таким образом эфемерные тома общего назначения подходят для следующих вариантов применения:

Постоянная память в качестве замены оперативной памяти для memcached

Последние выпуски memcached добавили поддержку использования постоянной памяти (Intel Optane и т.п., прим. переводчика) вместо обычной оперативной памяти. При развертывании memcached через контроллер приложений можно с помощью эфемерных томов общего назначения сделать запрос на выделение тома заданного размера из PMEM с помощью CSI драйвера, например PMEM-CSI.

Локальное хранилище LVM в качестве рабочего пространства

Приложения, работающие с данными, размер которых превышает размер оперативной памяти, могут запрашивать локальное хранилище с размером или метриками производительности, которые не могут обеспечить обычные тома EmptyDir от Kubernetes. К примеру, для этой цели был написан TopoLVM.

Доступ только для чтения для томов с данными

Выделение тома может привести к созданию заполненного тома при:

Эти тома могут быть смонтированы в режиме только для чтения.

Как это работает

Эфемерные тома общего назначения

Ключевой особенностью эфемерных томов общего назначения является новый источник тома, EphemeralVolumeSource, содержащий все поля для создания запроса к тому (исторически это называется запрос на постоянный том, PVC). Новый контроллер в kube-controller-manager просматривает поды, создающие такой источник тома, а затем создает PVC для этих подов. Для CSI драйвера этот запрос выглядит так же, как и остальные, поэтому здесь не нужно особенной поддержки.

Пока такие PVC существуют - могут использоваться, как и любые другие запросы на том. В частности они могут быть ссылкой в качестве источника данных при копировании тома или создании снимка с тома. Объект PVC также содержит текущее состояние тома.

Имена автоматически создаваемых PVC предопределены: это комбинация имени пода и имени тома, разделенных между собой дефисом. Предопределенность имен упрощает взаимодействие с PVC, поскольку его не надо искать, если известно имя пода и имя тома. Недостатком является то, что имя уже может быть использовано, что обнаруживается Kubernetes и в результате запуск пода блокируется.

Для того, чтобы быть уверенным в том, что том удаляется вместе с подом, контроллер делает под владельцем запроса на том. Когда под удаляется - отрабатывает штатный механизм уборки мусора, который удаляет как запрос, так и том.

Запросам ставится в соответствие драйвер хранилища через обычный механизм класса хранилища. Хотя классы с немедленным и поздним связыванием (они же WaitForFirstConsumer) поддерживаются, для эфемерных томов имеет смысл использовать WaitForFirstConsumer, тогда планировщик может учесть как использование узла, так и доступность хранилища при выборе узла. Здесь же появляется новая функция.

Отслеживание емкости хранилища

Обычно планировщик не имеет данных о том, где CSI драйвер создаст том. Также у планировщика нет возможности связаться с драйвером напрямую для запроса этой информации. Поэтому планировщик опрашивает узлы до тех пор, пока не найдет тот, на котором тома могут быть доступными (позднее связывание), либо полностью оставит выбор места за драйвером (немедленное связывание).

Новый API CSIStorageCapacity, находящийся в стадии alpha, позволяет хранение нужных данных в etcd, так что они доступны планировщику. В отличие от поддержки эфемерных томов общего назначения при развертывании драйвера нужно включить отслеживание емкости хранилища: external-provisioner должен опубликовать информацию о емкости, получаемую от драйвера через обычный GetCapacity.

Если планировщику надо выбрать узел для пода с непривязанным томом, использующим позднее связывание, а драйвер при развертывании активировал эту функцию устанавливая флаг CSIDriver.storageCapacity, то будут автоматически отброшены узлы, у которых нету достаточно емкости хранилища. Это работает как для эфемерных общего назначения, так и постоянных томов, но не для эфемерных томов CSI, поскольку их параметры не могут быть считаны Kubernetes.

Как обычно, тома с немедленными связыванием создаются перед планированием подов, а их размещение выбирается драйвером хранилища, поэтому при настройке external-provisioner по-умолчанию пропускаются классы хранения с немедленным связыванием, поскольку эти данные все равно не будут использоваться.

Так как планировщик kubernetes вынужден работать с потенциально устаревшей информацией, нет гарантий, что емкость будет доступна в любом случае когда том будет создаваться, но, тем не менее, шансы, что он будет создан без повторных попыток, повышаются.

N.B. Более подробную информацию вы сможете получить, а также безопасно «потренироваться на кошках стенде», а в случае совсем уж непонятной ситуации получить квалифицированную помощь техподдержки на интенсивах - Kubernetes База пройдёт 28-30 сентября, а для более продвинутых специалистов Kubernetes Мега 14–16 октября.

Безопасность

CSIStorageCapacity

Объекты CSIStorageCapacity находятся в пространствах имен, при раскатке каждого драйвера CSI в своем пространстве имен рекомендуется ограничить права RBAC для CSIStorageCapacity в этом пространстве, поскольку очевидно, откуда приходят данные. В любом случае Kubernetes не проверяет это, а обычно драйверы ставятся в одном пространстве имен, так что в конечном итоге ожидается, что драйвера будут работать и не будут публиковать неверные данные (и тут мне карта поперла, прим. переводчика по мотивам бородатого анекдота)

Эфемерные тома общего назначения

Если пользователи имеют права для создания пода (напрямую или опосредованно) - они также смогут создать эфемерные тома общего назначения даже если у них нет прав на создание запроса на том. А все потому, что проверки прав RBAC применяются к контроллеру, который создает PVC, а не к пользователю. Это основное изменение, которое нужно добавить к учетной записи, перед включением этой функции в кластерах, если ненадежные пользователи не должны иметь права на создание томов.

Пример

Отдельная ветка в PMEM-CSI содержит все нужные изменения для запуска кластера Kubernetes 1.19 внутри виртуальных машин QEMU со всеми фунциями, находящимися на alpha стадии. Код драйвера не изменялся, поменялось только развертывание.

На подходящей машине (Linux, обычный пользователь может использовать Docker, смотрите тут детали) эти команды поднимут кластер и установят драйвер PMEM-CSI:

git clone --branch=kubernetes-1-19-blog-post https://github.com/intel/pmem-csi.git
cd pmem-csi
export TEST_KUBERNETES_VERSION=1.19 TEST_FEATURE_GATES=CSIStorageCapacity=true,GenericEphemeralVolume=true TEST_PMEM_REGISTRY=intel
make start && echo && test/setup-deployment.sh

После того, как все отработает, вывод будет содержать инструкции для использования:

The test cluster is ready. Log in with [...]/pmem-csi/_work/pmem-govm/ssh.0, run
kubectl once logged in.  Alternatively, use kubectl directly with the
following env variable:
   KUBECONFIG=[...]/pmem-csi/_work/pmem-govm/kube.config

secret/pmem-csi-registry-secrets created
secret/pmem-csi-node-secrets created
serviceaccount/pmem-csi-controller created
...
To try out the pmem-csi driver ephemeral volumes:
   cat deploy/kubernetes-1.19/pmem-app-ephemeral.yaml |
   [...]/pmem-csi/_work/pmem-govm/ssh.0 kubectl create -f -

Объекты CSIStorageCapacity не подразумевают чтение людьми, так что надо некая обработка. С помощью фильтров шаблона на Golang будут показаны классы хранилищ, в этом примере будут отображены имя, топология и емкость:

$ kubectl get         -o go-template='{{range .items}}{{if eq .storageClassName "pmem-csi-sc-late-binding"}}{{.metadata.name}} {{.nodeTopology.matchLabels}} {{.capacity}}
{{end}}{{end}}'         csistoragecapacities
csisc-2js6n map[pmem-csi.intel.com/node:pmem-csi-pmem-govm-worker2] 30716Mi
csisc-sqdnt map[pmem-csi.intel.com/node:pmem-csi-pmem-govm-worker1] 30716Mi
csisc-ws4bv map[pmem-csi.intel.com/node:pmem-csi-pmem-govm-worker3] 30716Mi

Отдельный объект имеет такое содержимое:

$ kubectl describe csistoragecapacities/csisc-6cw8j
Name:         csisc-sqdnt
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  storage.k8s.io/v1alpha1
Capacity:     30716Mi
Kind:         CSIStorageCapacity
Metadata:
  Creation Timestamp:  2020-08-11T15:41:03Z
  Generate Name:       csisc-
  Managed Fields:
    ...
  Owner References:
    API Version:     apps/v1
    Controller:      true
    Kind:            StatefulSet
    Name:            pmem-csi-controller
    UID:             590237f9-1eb4-4208-b37b-5f7eab4597d1
  Resource Version:  2994
  Self Link:         /apis/storage.k8s.io/v1alpha1/namespaces/default/csistoragecapacities/csisc-sqdnt
  UID:               da36215b-3b9d-404a-a4c7-3f1c3502ab13
Node Topology:
  Match Labels:
    pmem-csi.intel.com/node:  pmem-csi-pmem-govm-worker1
Storage Class Name:           pmem-csi-sc-late-binding
Events:                       <none>

Давайте попробуем создать демонстрационное приложение с одним эфемерным томом общего назначения. Содержимое файла pmem-app-ephemeral.yaml:

# This example Pod definition demonstrates
# how to use generic ephemeral inline volumes
# with a PMEM-CSI storage class.
kind: Pod
apiVersion: v1
metadata:
  name: my-csi-app-inline-volume
spec:
  containers:
    - name: my-frontend
      image: intel/pmem-csi-driver-test:v0.7.14
      command: [ "sleep", "100000" ]
      volumeMounts:
      - mountPath: "/data"
        name: my-csi-volume
  volumes:
  - name: my-csi-volume
    ephemeral:
      volumeClaimTemplate:
        spec:
          accessModes:
          - ReadWriteOnce
          resources:
            requests:
              storage: 4Gi
          storageClassName: pmem-csi-sc-late-binding

После создания, как показано в инструкции выше, у нас появился дополнительный под и PVC:

$ kubectl get pods/my-csi-app-inline-volume -o wide
NAME                       READY   STATUS    RESTARTS   AGE     IP          NODE                         NOMINATED NODE   READINESS GATES
my-csi-app-inline-volume   1/1     Running   0          6m58s   10.36.0.2   pmem-csi-pmem-govm-worker1   <none>           <none>
$ kubectl get pvc/my-csi-app-inline-volume-my-csi-volume
NAME                                     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS               AGE
my-csi-app-inline-volume-my-csi-volume   Bound    pvc-c11eb7ab-a4fa-46fe-b515-b366be908823   4Gi        RWO            pmem-csi-sc-late-binding   9m21s

Владелец PVC - под:

$ kubectl get -o yaml pvc/my-csi-app-inline-volume-my-csi-volume
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  annotations:
    pv.kubernetes.io/bind-completed: "yes"
    pv.kubernetes.io/bound-by-controller: "yes"
    volume.beta.kubernetes.io/storage-provisioner: pmem-csi.intel.com
    volume.kubernetes.io/selected-node: pmem-csi-pmem-govm-worker1
  creationTimestamp: "2020-08-11T15:44:57Z"
  finalizers:
  - kubernetes.io/pvc-protection
  managedFields:
    ...
  name: my-csi-app-inline-volume-my-csi-volume
  namespace: default
  ownerReferences:
  - apiVersion: v1
    blockOwnerDeletion: true
    controller: true
    kind: Pod
    name: my-csi-app-inline-volume
    uid: 75c925bf-ca8e-441a-ac67-f190b7a2265f
...

Ожидаемо обновилась информация для pmem-csi-pmem-govm-worker1:

csisc-2js6n map[pmem-csi.intel.com/node:pmem-csi-pmem-govm-worker2] 30716Mi
csisc-sqdnt map[pmem-csi.intel.com/node:pmem-csi-pmem-govm-worker1] 26620Mi
csisc-ws4bv map[pmem-csi.intel.com/node:pmem-csi-pmem-govm-worker3] 30716Mi

Если другому приложению надо будет больше, чем 26620Mi, планировщик не будет брать в расчет pmem-csi-pmem-govm-worker1 при любом раскладе.

Что дальше?

Обе функции все еще в разработке. Было открыто несколько заявок при alpha-тестировании. По ссылкам с предложениями по улучшениям ведется документирование работы, которую надо сделать, чтобы перейти в стадию beta, а также какие альтернативы были уже рассмотрены и отклонены: