Автор | Сунь Чжихэн (Хуэй Чжи), инженер-разработчик Alibaba
**Введение:** Как мы все знаем, постоянное хранилище K8s (Persistent Storage) гарантирует, что данные приложения существуют независимо от жизненного цикла приложения, но его внутренняя реализация редко упоминается. Каков процесс хранения внутри K8s? Какая связь между PV, PVC, StorageClass, Kubelet, плагином CSI и т. д. Эти тайны будут раскрыты одна за другой в этой статье.
Основа постоянного хранилища K8s
Прежде чем объяснять процесс хранения в K8s, давайте рассмотрим основные концепции постоянного хранения в K8s.
1. Глоссарий
-
in-tree: Логика кода находится в официальном репозитории K8s;
-
out-of-tree: логика кода находится за пределами официального репозитория K8s, чтобы добиться отделения от кода K8s;
-
PV: PersistentVolume, ресурс на уровне кластера, созданный администратором кластера или внешним поставщиком. Жизненный цикл PV не зависит от модуля, использующего PV, а сведения об устройстве хранения сохраняются в .Spec PV;
-
PVC: PersistentVolumeClaim, ресурс уровня пространства имен, созданный пользователем или контроллером StatefulSet (согласно VolumeClaimTemplate). PVC похож на Pod, Pod потребляет ресурсы Node, а PVC потребляет ресурсы PV. Поды могут запрашивать определенные уровни ресурсов (ЦП и память), в то время как PVC могут запрашивать определенные размеры тома хранилища и режимы доступа;
-
StorageClass: StorageClass — это ресурс уровня кластера, созданный администратором кластера. SC предоставляет администраторам шаблон «класса» для динамического предоставления томов хранения..Spec в SC подробно определяет различные уровни качества обслуживания, политики резервного копирования и т. д. для томов хранения PV;
-
CSI: Container Storage Interface, целью которого является определение отраслевого стандарта «интерфейса контейнерного хранилища», чтобы подключаемые модули, разработанные поставщиками хранилищ (SP) на основе стандарта CSI, могли работать в различных системах оркестрации контейнеров (CO), включая Kubernetes. , Месос, Рой Подождите.
2. Введение компонента
-
PV Controller: Отвечает за привязку PV/PVC и управление циклом, выполняет объемы данных объемов данных в соответствии с требованиями;
-
AD Controller: Отвечает за операцию ** присоединения/отсоединения ** тома данных, привязывает устройство к целевому узлу;
-
Kubelet: Kubelet — это основной «агент узла», работающий на каждом узле узла, и его функции — управление жизненным циклом пода, проверка работоспособности контейнера, мониторинг контейнера и т. д.;
-
Volume Manager: компонент в Kubelet, отвечающий за управление операциями **монтирования/размонтирования** томов данных (также отвечает за операции **присоединения/отсоединения ** томов данных, для включения этой функции необходимо настроить параметры, связанные с kubelet), форматирование объемных устройств и т.д. Подождите;
-
Volume Plugins: Подключаемый модуль хранилища, разработанный поставщиками хранилищ, предназначенный для расширения возможностей управления томами различных типов хранилищ и реализации различных операционных возможностей сторонних хранилищ, а именносиняя операция вышереализация. Плагины тома бывают двух типов: внутри дерева и вне дерева;
-
External Provioner: External Provioner — это дополнительный контейнер, который вызывает функции CreateVolume и DeleteVolume в плагинах Volume для выполнения операций **Provision/Delete**. Поскольку контроллер PV K8s не может напрямую вызывать связанные функции Volume Plugins, он вызывается внешним поставщиком через gRPC;
-
External Attacher: External Attacher — это дополнительный контейнер, который вызывает функции ControllerPublishVolume и ControllerUnpublishVolume в плагинах Volume для выполнения операций **Attach/Detach**. Поскольку контроллер AD K8s не может напрямую вызывать связанные функции Volume Plugins, он вызывается внешним аттачменом через gRPC.
3. Постоянное использование тома
Kubernetes Чтобы позволить приложениям и их разработчикам нормально запрашивать ресурсы хранения,Избегайте работы с деталями хранилища, представил PV и PVC. Существует два способа создания PV:
-
Во-первых, администратор кластера вручнуюстатическое созданиеPV, требуемый приложением;
-
Другой заключается в том, что пользователь вручную создает PVC и настраивается компонентом Provisioner.динамически созданныйсоответствующее ПВ.
Давайте возьмем общее хранилище NFS в качестве примера, чтобы увидеть разницу между ними.
Статически создавать тома хранения
Процесс статического создания тома хранилища показан на следующем рисунке:
первый шаг: администратор кластера создает NFS PV, тип хранилища в дереве, изначально поддерживаемый K8s. Файл yaml выглядит следующим образом:
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
nfs:
server: 192.168.4.1
path: /nfs_storage
второй шаг: Пользователь создает PVC, файл yaml выглядит следующим образом:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
Вы можете видеть, что PV и PVC связаны командой kubectl get pv:
[root@huizhi ~]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nfs-pvc Bound nfs-pv-no-affinity 10Gi RWO 4s
третий шаг: пользователь создает приложение и использует PVC, созданный на втором этапе.
apiVersion: v1
kind: Pod
metadata:
name: test-nfs
spec:
containers:
- image: nginx:alpine
imagePullPolicy: IfNotPresent
name: nginx
volumeMounts:
- mountPath: /data
name: nfs-volume
volumes:
- name: nfs-volume
persistentVolumeClaim:
claimName: nfs-pvc
На этом этапе удаленное хранилище NFS монтируется в каталог /data контейнера nginx в поде.
Динамически создавать тома хранения
Для динамического создания томов хранилища требуется **nfs-client-provisioner** и соответствующийstorageclass.
По сравнению со статическим созданием томов хранения динамическое создание томов хранения требует меньшего вмешательства администратора кластера Процесс показан на следующем рисунке:
Администраторам кластера нужно только убедиться, что в среде есть классы хранения, связанные с NFS:
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: nfs-sc
provisioner: example.com/nfs
mountOptions:
- vers=4.1
первый шаг: Пользователь создает PVC, где storageClassName PVC указывается как имя класса хранилища NFS выше:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: nfs
annotations:
volume.beta.kubernetes.io/storage-class: "example-nfs"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Mi
storageClassName: nfs-sc
второй шаг: nfs-client-provisioner в кластере динамически создаст соответствующий PV. На этом этапе вы можете видеть, что PV в среде создан и привязан к PVC.
[root@huizhi ~]# kubectl get pv
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM REASON AGE
pvc-dce84888-7a9d-11e6-b1ee-5254001e0c1b 10Mi RWX Delete Bound default/nfs 4s
третий шаг: пользователь создает приложение и использует PVC, созданный на втором этапе, так же, как и на третьем этапе статического создания тома хранилища.
Процесс постоянного хранения K8s
1. Обзор процесса
Узнайте отсюда@ сокровище округасуществуетКурс по облачному хранилищублок-схема в
Процесс выглядит следующим образом:
-
Пользователь создает Pod, содержащий PVC, который требует использования динамических томов хранения;
-
**Планировщик ** В соответствии с конфигурацией модуля, статусом узла, конфигурацией PV и другой информацией, модуль назначается на подходящий рабочий узел;
-
**Контроллер PV **следите за тем, чтобы PVC, используемый этим модулем, находился в состоянии ожидания, поэтому вызовитеVolume Plugin(в дереве) создание томов хранилища и создание объектов PV (вне дерева обрабатывается External Provisioner);
-
Контроллер рекламыОбнаружено, что Pod и PVC находятся в состоянии ожидания, поэтому вызовите **Volume Plugin**, чтобы подключить устройство хранения к целевому рабочему узлу.
-
На рабочем узлеМенеджер томов в KubeletПодождите, пока устройство хранения будет подключено, и передайтеVolume Plugin установить устройство наглобальный каталог:/var/lib/kubelet/pods/[pod uid]/volumes/kubernetes.io~iscsi/[PV name](Возьмите iscsi в качестве примера);
-
**Kubelet** запускается через DockerКонтейнеры Pod,использовать bind mount способ сопоставления томов, смонтированных в локальный глобальный каталог, сконтейнерсередина.
Более подробный процесс выглядит следующим образом:
2. Подробный процесс
Различные версии K8s имеют немного разные процессы постоянного хранения. Эта статья основана на Kubernetes версии 1.14.8.
Как видно из приведенной выше блок-схемы, объем хранилища делится на три этапа от создания до использования приложения:Предоставление/удаление, присоединение/отсоединение, подключение/отключение.
provisioning volumes
В контроллере PV есть два работника:
- ClaimWorker: обрабатывать события, связанные с добавлением/обновлением/удалением PVC, и миграцию состояния PVC;
- VolumeWorker: Отвечает за переход состояния PV.
Миграция статуса PV (UpdatePVStatus):
- Начальное состояние PV — Доступен, а когда PV привязан к PVC, состояние меняется на Привязано;
- После удаления PVC, привязанного к PV, статус становится Released;
- Когда политика повторного использования PV имеет значение Recycled или .Spec.ClaimRef PV удаляется вручную, статус PV становится доступным;
- Если политика перезапуска PV неизвестна, или при сбое перезапуска, или при сбое удаления тома хранилища статус PV изменяется на Failed;
- Вручную удалите .Spec.ClaimRef PV, и статус PV станет доступным.
Миграция статуса PVC (UpdatePVCStatus):
- Если в кластере нет PV, соответствующих условиям PVC, состояние PVC равно Pending. После привязки PV и PVC статус PVC изменяется с Pending на Bound;
- PV, привязанный к PVC, удаляется из среды, а статус PVC меняется на Lost;
- После повторной привязки к PV с тем же именем состояние PVC становится Bound.
Процесс инициализации выглядит следующим образом (здесь моделируется пользователь для создания нового PVC):
Обработка тома статического хранилища (FindBestMatch):фотоэлектрический контроллерСреда сначала фильтруется для PV со статусом Доступен, чтобы соответствовать новому PVC.
-
DelayBinding: Контроллер PV определяет, нуждается ли PVC в **отложенной привязке: ** 1. Проверьте, содержит ли аннотация PVC Volume.kubernetes.io/selected-node, если она существует, значит, PVC был назначен узлом планировщик (принадлежитProvisionVolume), поэтому нет необходимости в отложенной привязке; 2. Если в аннотации PVC нет Volume.kubernetes.io/selected-node и отсутствует StorageClass, то значение по умолчанию означает, что отложенная привязка не требуется; есть StorageClass, проверьте его поле VolumeBindingMode, если для WaitForFirstConsumer требуется привязка Delay, а для Immediate не требуется привязка задержки;
-
FindBestMatchPVForClaim: Контроллер PV пытается найти существующий PV в среде, который соответствует требованиям PVC. Контроллер PV отфильтрует **все PV** один раз и выберет наиболее подходящий PV из тех PV, которые соответствуют условиям. Правила фильтрации: 1. Соответствует ли VolumeMode 2. Привязан ли PV к PVC 3. Доступна ли .Status.Phase PV 4. Проверяется LabelSelector, метка PV и PVC должен быть непротиворечивым, соответствует ли StorageClass PVC, 6. Каждая итерация обновляет PV, который соответствует минимальному запрошенному размеру PVC, и возвращает его как окончательный результат;
-
Bind: Контроллер PV связывает выбранный PV и PVC: 1. Обновите информацию .Spec.ClaimRef PV до текущего PVC 2. Обновите .Status.Phase PV на Bound 3. Добавьте аннотацию PV : pv.kubernetes.io/bound-by-controller: "yes" 4. Обновите .Spec.VolumeName PVC на имя PV 5. Обновите .Status.Phase PVC на Bound 6. Добавьте аннотация PVC: pv.kubernetes.io/bound-by-controller: «yes» и pv.kubernetes.io/bind-completed: «yes»;
**Процесс динамического тома хранилища (ProvisionVolume): **Если в среде нет подходящего PV, введите сценарий динамического выделения ресурсов:
-
Before Provisioning1. Контроллер PV сначала определяет, является ли StorageClass, используемый PVC, внутри дерева или вне дерева: проверяя, содержит ли поле Provisioner StorageClass префикс **"kubernetes.io/" **; 2 Контроллер PV Обновите аннотации PVC: Claim.Annotations["volume.beta.kubernetes.io/storage-provisioner"] = storageClass.Provisioner;
-
**In-tree Provisioning (внутренняя инициализация): **1.In-tree Provisioner реализует метод NewProvisioner интерфейса ProvisionableVolumePlugin для возврата нового Provisioner; 2. Контроллер PV вызывает функцию Provisioner Provisioner, которая возвращает 3. Контроллер PV создает объект PV, возвращенный на предыдущем шаге, и привязывает его к PVC. то же, что и PVC StorageClassName, также добавлена аннотация: "pv.kubernetes.io/bound-by-controller"="yes" и "pv.kubernetes.io/provisioned-by"=plugin.GetPluginName();
-
**Инициализация вне дерева (внешняя инициализация): **1. Внешний поставщик проверяет, является ли поле Claim.Spec.VolumeName в PVC пустым, если он не пуст, то PVC пропускается 2. Внешний поставщик проверяет PVC в Равно ли требование. Annotations["volume.beta.kubernetes.io/storage-provisioner"] своему собственному имени поставщика (внешний поставщик передаст параметр --provisioner при начале определения собственного имени поставщика); 3. Если VolumeMode=Block PVC, проверьте, поддерживает ли External Provisioner блочные устройства 4. External Provisioner вызывает функцию Provision: вызовите интерфейс CreateVolume подключаемого модуля хранилища CSI через gRPC 5. External Provisioner создает PV для представления тома , и в то же время связывает PV с предыдущим PVC, делает привязку.
deleting volumes
Процесс удаления является обратным процессу инициализации:
Пользователь удаляет PVC, удаляет контроллер PV, изменяет PV.Status.Phase на Released.
Когда PV.Status.Phase == Released, контроллер PV сначала проверяет значение Spec.PersistentVolumeReclaimPolicy и пропускает его напрямую, когда оно находится в состоянии Retain, а когда — в Delete:
-
**Удаление в дереве: **1.Провайонер внутри-дерева реализует метод NewDeleter интерфейса DeletableVolumePlugin для возврата нового Deleter 2. Контроллер вызывает функцию Delete Deleter для удаления соответствующего тома; 3. В томе После удаления контроллер PV удалит объект PV;
-
** Удаление вне дерева:** 1. Внешний поставщик вызывает функцию удаления и вызывает интерфейс DeleteVolume подключаемого модуля CSI через gRPC; 2. После удаления тома внешний поставщик удаляет объект PV.
Attaching Volumes
Операции присоединения/отсоединения могут выполнять как компоненты Kubelet, так и контроллеры AD.Если в параметре запуска Kubelet указано --enable-controller-attach-detach, то Kubelet будет это делать, иначе по умолчанию будет управляться AD. Далее в качестве примера для объяснения операции присоединения/отсоединения используется контроллер AD.
В контроллере AD есть две основные переменные:
- Желаемое состояние мира (DSW): ожидаемый статус подключения тома данных в кластере, включая информацию о узлах->томах->подах;
- Фактическое состояние мира (ASW): Текущий статус подключения тома данных в кластере, включая информацию о томах->узлах.
Процесс прикрепления выглядит следующим образом:
Контроллер AD инициализирует DSW и ASW в соответствии с информацией о ресурсах в кластере.
Внутри контроллера AD есть три компонента, которые периодически обновляют DSW и ASW:
- ** Примиритель. ** Периодически запускайте GoRoutine,Убедитесь, что объем прикреплен / отдельно. В этот период ASW постоянно обновлялся:
** присоединение в дереве: ** 1. Атташер внутри дерева будет реализовывать метод NewAttacher интерфейса AttachableVolumePlugin для возврата нового аттачмена 2. Контроллер AD вызывает функцию Attacher аттачмена для присоединения устройства; 3. Обновите ASW.
**присоединение вне дерева: **1. Вызвать встроенный в дерево CSIAttacher для создания объекта **VolumeAttachement (VA)**, который содержит информацию о присоединителе, имя узла, информацию о PV для присоединения; Аттач будет наблюдать за ресурсом VolumeAttachement в кластере, и когда он найдет том данных, который необходимо подключить, он вызовет функцию Attach и вызов интерфейса ControllerPublishVolume подключаемого модуля CSI через gRPC.
- DesiredStateOfWorldPopulator.Периодически запуская GoRoutine, основная функцияОбновить ДСВ:
findAndRemoveDeletedPods — пройтись по всем подам в DSW и удалить их из DSW, если они были удалены из кластера; findAndAddActivePods — просмотреть все модули в PodLister и добавить модуль в DSW, если он не существует в DSW.
- **Рабочий ПВХ. ** Наблюдайте за событиями добавления/обновления PVC, обрабатывайте модули, связанные с PVC, и обновляйте DSW в режиме реального времени.
Detaching Volumes
Процесс отсоединения выглядит следующим образом:
-
Когда Pod будет удален, контроллер AD будет следить за этим событием. Сначала контроллер AD проверяет, содержит ли ресурс Node, где находится Pod, метку «volumes.kubernetes.io/keep-terminated-pod-volumes», если да, то ничего не делать, если нет — удалить том из ДСВ;
-
Контроллер AD приближает состояние ActualStateOfWorld к состоянию DesiredStateOfWorld с помощью **Reconciler**.Когда он обнаруживает, что в ASW есть том, которого нет в DSW, он выполняет операцию Detach:
**отключение внутри дерева: **1. Контроллер AD реализует метод NewDetacher интерфейса AttachableVolumePlugin для возврата нового Detacher; 2. Контроллер вызывает функцию Detach Detacher для отсоединения соответствующего тома; 3. AD контроллер обновляет ASW.
**отключение вне дерева: **1.Контроллер AD вызывает встроенный в дерево CSIAttacher для удаления связанных объектов VolumeAttachement 2.Внешний присоединитель просматривает ресурсы VolumeAttachement (VA) в кластере и обнаруживает, что тома данных, которые необходимо удалить, вызовите функцию Detach и вызовите интерфейс ControllerUnpublishVolume подключаемого модуля CSI через gRPC; 3. Контроллер AD обновляет ASW.
В **Volume Manager** также есть две основные переменные:
- Желаемое состояние мира (DSW): Ожидаемый статус монтирования тома данных в кластере, включая Volumes->Pods Information;
- Фактическое состояние мира (ASW): Фактический статус подключения тома данных в кластере, включая информацию о томах->подах.
Процесс монтирования/демонтажа выглядит следующим образом:
Цель глобального пути монтирования: блочные устройства можно монтировать только один раз в Linux, в то время как в сценарии K8s PV можно монтировать в несколько экземпляров Pod на одном и том же узле. Если блочное устройство форматируется и монтируется во временный глобальный каталог на узле, а затем глобальный каталог монтируется в соответствующий каталог в поде с использованием технологии монтирования с привязкой в Linux, требования могут быть выполнены. В приведенной выше блок-схеме глобальным каталогом является /var/lib/kubelet/pods/[pod uid]/volumes/kubernetes.io~iscsi/[PV название]
VolumeManager инициализирует DSW и ASW в соответствии с информацией о ресурсах в кластере.
Внутри VolumeManager есть два компонента, которые периодически обновляют DSW и ASW:
- DesiredStateOfWorldPopulator: Периодически запускайте GoRoutine, основная функцияобновить ДСВ;
- Reconciler: периодически запускать через GoRoutine,Убедитесь, что том смонтирован/размонтирован. В этот период ASW постоянно обновлялся:
unmountVolumes: убедитесь, что тома отключены после удаления модуля. Пройдитесь по всем модулям в ASW, если они не находятся в DSW (что указывает на то, что модуль удален), в качестве примера возьмем VolumeMode=FileSystem, выполните следующие действия:
- Удалите все привязки-монтирования: вызовите интерфейс TearDown Unmounter (если он вне дерева, вызовите интерфейс NodeUnpublishVolume плагина CSI);
- Размонтировать том: вызвать функцию UnmountDevice класса DeviceUnmounter (если он находится вне дерева, вызвать интерфейс NodeUnstageVolume подключаемого модуля CSI);
- Обновите АСВ.
mountAttachVolumes: убедитесь, что тома, которые будут использоваться подом, успешно смонтированы. Пройдите все модули в DSW, если они не находятся в ASW (указывая, что каталог должен быть смонтирован и сопоставлен с модулем), возьмите в качестве примера VolumeMode=FileSystem и выполните следующие операции:
- Подождите, пока том будет присоединен к узлу (присоединен внешним аттачменом или самим Kubelet);
- Смонтируйте том в глобальный каталог: вызовите функцию MountDevice DeviceMounter (если он находится вне дерева, вызовите интерфейс NodeStageVolume подключаемого модуля CSI);
- Обновление ASW: том монтируется в глобальный каталог;
- Привязать том к поду: вызвать интерфейс SetUp монтировщика (если он находится вне дерева, вызвать интерфейс NodePublishVolume подключаемого модуля CSI);
- Обновите АСВ.
unmountDetachDevices: убедитесь, что тома, которые необходимо размонтировать, размонтированы. Пройдите все UnmountedVolumes в ASW, если его нет в DSW (это означает, что том больше не нужен), сделайте следующее:
- Размонтировать том: вызвать функцию UnmountDevice класса DeviceUnmounter (если он находится вне дерева, вызвать интерфейс NodeUnstageVolume подключаемого модуля CSI);
- Обновите АСВ.
Суммировать
В этой статье впервые представлены основные концепции и методы использования постоянного хранилища K8, а также проведен глубокий анализ внутреннего процесса хранения K8. На K8s использование любого вида хранилища неотделимо от вышеописанного процесса (в некоторых сценариях присоединение/отсоединение не используется), а проблема с хранилищем в окружении также должна быть вызвана сбоем в одном из звеньев.
В контейнерном хранилище много ям, особенно в среде частного облака. Но чем больше вызовов, тем больше возможностей! В настоящее время внутренний рынок проприетарных облачных сервисов также конкурирует в области хранения данных.Наша гибкая команда контейнеров PaaS приветствует героев, которые присоединятся к нам и вместе создадут лучшее будущее!
Ссылка на ссылку
- Исходный код сообщества Kubernetes
- [Открытый курс Cloud Native] Архитектура хранилища Kubernetes и использование подключаемых модулей (Junbao)
- [Открытый курс Cloud Native] Хранилище приложений и постоянные тома данных — основные знания (житянь)
- 【kubernetes-design-предложения】предоставление томов
- 【kubernetes-design-proposals】 Плагины CSI Volume в Kubernetes Design Doc
Команда разработчиков облачных приложений набирает сотрудников!
Команда собственной платформы приложений Alibaba Cloud в настоящее время ищет таланты, если вы соответствуете следующим требованиям:
-
Увлечен облачными технологиями в областях, связанных с контейнерами и инфраструктурой, и имеет богатый опыт и выдающиеся достижения в одном направлении облачной инфраструктуры, такой как Kubernetes, бессерверная платформа, контейнерная сеть и хранилище, а также платформа для эксплуатации и обслуживания (например, продукт посадка, реализация инновационных технологий, открытый исходный код, ведущие академические достижения);
-
Отличные презентационные навыки, коммуникативные навыки и навыки работы в команде, дальновидное мышление в области технологий и бизнеса, сильная ответственность, ориентированность на результат и умение принимать решения;
-
Знание хотя бы одного языка программирования Java и Golang;
-
Высшее образование, опыт работы от 3-х лет.
Резюме можно присылать на почтовый ящик: huizhi.szh@alibaba-inc.com, если есть вопросы, добавляйте WeChat консультация: TheBeatles1994.
Веб-семинар Cloud Native приглашает вас принять участие
Нажмите, чтобы заказать прямую трансляцию прямо сейчас
"Облачная нативная платформа AlibabaСосредоточьтесь на микросервисах, бессерверных технологиях, контейнерах, Service Mesh и других технических областях, сосредоточьтесь на популярных тенденциях облачных технологий и практиках крупномасштабного внедрения облачных технологий, а также станьте официальной учетной записью самых облачных разработчиков. "