Некоторым приложениям также нужно хранить данные, но они достаточно спокойно относятся к тому, что данные не будут сохранены после перезапуска.
Например сервисы для кэширования ограничены по оперативной памяти, но также могут перемещать данные, которые редко используются, в хранилище, работающее медленнее, чем оперативная память, с небольшим влиянием на общую производительность. Другим приложениям нужно знать, что в файлах могут быть некоторые входные данные только для чтения, например настройки или секретные ключи.
В 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, а также какие альтернативы были уже рассмотрены и отклонены: