Lea el proceso de almacenamiento persistente de K8 en un artículo (transferencia)

Introducción: Como todos sabemos, el almacenamiento persistente de K8 (almacenamiento persistente) garantiza que los datos de la aplicación existan independientemente del ciclo de vida de la aplicación, pero su implementación interna rara vez se menciona. ¿Cuál es el proceso de almacenamiento interno de los K8? ¿Cuál es la relación de llamada entre PV, PVC, StorageClass, Kubelet, CSI plug-in, etc. Estos misterios se revelarán uno por uno en este artículo.

Base de almacenamiento persistente K8

Antes de explicar el proceso de almacenamiento de K8, primero revise los conceptos básicos del almacenamiento persistente en K8.

1. Explicación de términos

  • en árbol: la lógica del código está en el almacén oficial de K8;
  • fuera del árbol: la lógica del código está fuera del almacén oficial del K8, para lograr el desacoplamiento del código del K8;
  • PV: PersistentVolume, un recurso de nivel de clúster, creado por el administrador del clúster o el aprovisionador externo. El ciclo de vida de PV es independiente del Pod que usa PV, y los detalles del dispositivo de almacenamiento se almacenan en las especificaciones de PV.
  • PVC: PersistentVolumeClaim, un recurso de nivel de espacio de nombres, creado por el usuario o el controlador StatefulSet (de acuerdo con VolumeClaimTemplate). El PVC es similar al Pod. El Pod consume recursos de Nodo y el PVC consume recursos PV. Pod puede solicitar un nivel específico de recursos (CPU y memoria), mientras que PVC puede solicitar un tamaño de volumen de almacenamiento específico y un modo de acceso (Modo de acceso);
  • StorageClass: StorageClass es un recurso de nivel de clúster, creado por el administrador del clúster. SC proporciona a los administradores una plantilla de "clase" para proporcionar dinámicamente volúmenes de almacenamiento. El .Spec en SC define en detalle los diferentes niveles de calidad de servicio, estrategias de respaldo, etc. de PV de volúmenes de almacenamiento;
  • CSI: Interfaz de almacenamiento de contenedores, el propósito es definir la "interfaz de almacenamiento de contenedores" estándar de la industria, para que los complementos de proveedores de almacenamiento (SP) desarrollados basados ​​en el estándar CSI puedan trabajar en diferentes sistemas de orquestación de contenedores (CO), los sistemas de CO incluyen Kubernetes, Mesos, Enjambre, etc.

2. Introducción de componentes

  • Controlador PV: responsable de la unión de PV / PVC y la gestión del ciclo, realizar operaciones de provisión / eliminación de volúmenes de datos de acuerdo con los requisitos;
  • Controlador AD: responsable de la operación de conexión / desconexión del volumen de datos, conectando el dispositivo al nodo de destino;
  • Kubelet: Kubelet es el "agente de nodo" principal que se ejecuta en cada nodo Nodo, y sus funciones son la gestión del ciclo de vida del Pod, la comprobación del estado del contenedor, la supervisión del contenedor, etc.
  • Administrador de volúmenes: el componente en Kubelet, que es responsable de administrar la operación de montaje / desmontaje del volumen de datos (también responsable de la operación de conexión / desconexión del volumen de datos, debe configurar los parámetros relevantes del kubelet para habilitar esta función), el formateo del dispositivo de volumen, etc.
  • Complementos de volumen: complementos de almacenamiento, desarrollados por proveedores de almacenamiento, el propósito es expandir las capacidades de administración de volumen de varios tipos de almacenamiento, para lograr diversas capacidades operativas de almacenamiento de terceros, que es la realización de la operación azul anterior. Los complementos de volumen están dentro del árbol y fuera del árbol;
  • External Provioner: External Provioner es un contenedor de sidecar, cuya función es llamar a las funciones CreateVolume y DeleteVolume en Volume Plugins para realizar operaciones de Provision / Delete. Debido a que el controlador PV K8s no puede llamar directamente a las funciones relacionadas de los complementos de volumen, el Provionador externo lo llama a través de gRPC;
  • External Attacher: External Attacher es un contenedor de sidecar. Su función es llamar a las funciones ControllerPublishVolume y ControllerUnpublishVolume en Volume Plugins para realizar operaciones de Adjuntar / Separar. Debido a que el controlador AD de K8 no puede llamar directamente a las funciones relacionadas de los complementos de volumen, External Attacher lo llama a través de gRPC.

3. Uso de volumen persistente

Kubernetes introdujo PV y PVC para permitir que las aplicaciones y sus desarrolladores normalmente soliciten recursos de almacenamiento y eviten procesar los detalles de las instalaciones de almacenamiento. Hay dos formas de crear un PV:

  • Una es que el administrador del clúster crea manualmente el PV requerido por la aplicación estáticamente;
  • La otra es que el usuario crea manualmente un PVC y el componente Provisioner crea dinámicamente el PV correspondiente.

Tomemos el almacenamiento compartido NFS como ejemplo para ver la diferencia entre los dos.

Crea estáticamente un volumen de almacenamiento

El proceso de creación estática de un volumen de almacenamiento se muestra en la siguiente figura:

1.png

Paso 1: el administrador del clúster crea NFS PV, que pertenece al tipo de almacenamiento en árbol compatible de forma nativa con K8. El archivo yaml es el siguiente:

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

Paso 2: el usuario crea PVC y el archivo yaml es el siguiente:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

El comando kubectl get pv muestra que PV y PVC están vinculados:

[root@huizhi ~]# kubectl get pvc
NAME      STATUS   VOLUME               CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nfs-pvc   Bound    nfs-pv-no-affinity   10Gi       RWO                           4s

Paso 3: el usuario crea una aplicación y utiliza el PVC creado en el segundo paso.

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

En este momento, el almacenamiento remoto de NFS está montado en el directorio / data del contenedor nginx en el Pod.

Crear volumen de almacenamiento de forma dinámica

Para crear dinámicamente volúmenes de almacenamiento, se requiere que nfs-client-provisioner y la clase de almacenamiento correspondiente se implementen en el clúster.

En comparación con la creación de volumen de almacenamiento estático, la creación dinámica de volumen de almacenamiento reduce la intervención de los administradores de clúster. El proceso se muestra en la siguiente figura:

2.png

El administrador del clúster solo necesita asegurarse de que haya clases de almacenamiento relacionadas con NFS en el entorno:

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: nfs-sc
provisioner: example.com/nfs
mountOptions:
  - vers=4.1

Paso 1: el usuario crea un PVC, donde el nombre de clase de almacenamiento del PVC se especifica como el nombre de clase de almacenamiento del NFS anterior:

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

Paso 2: nfs-client-provisioner en el clúster creará dinámicamente el PV correspondiente. En este punto, puede ver que el PV se ha creado en el entorno y está unido al 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

Paso 3: El usuario crea la aplicación y usa el PVC creado en el segundo paso, lo mismo que el tercer paso de crear estáticamente un volumen de almacenamiento.

Proceso de almacenamiento persistente de K8

1. Descripción del proceso

Aquí hay una referencia al diagrama de flujo de @ 郡 宝 en el curso de almacenamiento nativo en la nube

3.png

El proceso es el siguiente:

  1. El usuario creó un Pod que contiene un PVC, que requiere un volumen de almacenamiento dinámico;
  2. El programador programa el Pod en un nodo Worker adecuado según la configuración del Pod, el estado del nodo, la configuración PV y otra información;
  3. El controlador PV observa que el PVC utilizado por el Pod está en el estado Pendiente, por lo que llama al complemento de volumen (en el árbol) para crear un volumen de almacenamiento y crea un objeto PV (el aprovisionador externo maneja el fuera del árbol);
  4. El controlador AD descubre que el Pod y el PVC están en estado pendiente, por lo que llama al complemento de volumen para montar el dispositivo de almacenamiento en el nodo de destino del trabajador
  5. En el nodo Trabajador, el Administrador de volúmenes en Kubelet espera a que se monte el dispositivo de almacenamiento y lo monta en el directorio global a través del Complemento de volumen: ** / var / lib / kubelet / pods / [pod uid] /volumes/kubernetes.io ~ iscsi / [
    nombre de PV ] ** (tome iscsi como ejemplo);
  6. Kubelet usa Docker para iniciar los Contenedores de Pod, y usa el montaje de enlace para asignar el volumen que se ha montado en el directorio global local al contenedor.

El proceso más detallado es el siguiente:

4.png

2. Proceso detallado

Las diferentes versiones de K8 tienen procesos de almacenamiento persistente ligeramente diferentes. Este artículo está basado en la versión 1.14.8 de Kubernetes.

Como se puede ver en el diagrama de flujo anterior, el volumen de almacenamiento se divide en tres etapas desde la creación hasta el uso de la aplicación: suministro / eliminación, conexión / desconexión, montaje / desmontaje.

volúmenes de aprovisionamiento

5.png

Hay dos trabajadores en el controlador PV:

  • ClaimWorker: maneja los eventos relacionados de agregar / actualizar / eliminar PVC y la migración de estado de PVC;
  • VolumeWorker: responsable de la migración del estado PV.

Migración de estado de PV (UpdatePVStatus):

  • El estado inicial de PV está disponible, y cuando PV está unido a PVC, el estado se convierte en enlazado;
  • Después de eliminar el PVC vinculado al PV, el estado se libera;
  • Cuando la estrategia de reciclaje de PV se Recicla o elimina manualmente .Spec.ClaimRef de PV, el estado de PV se vuelve Disponible;
  • Cuando se desconoce la política de reciclaje de PV o falla el reciclaje o falla la eliminación del volumen de almacenamiento, el estado de PV se vuelve fallido;
  • Elimine manualmente el PV Spec.ClaimRef, el estado PV se convierte en Disponible.

Migración de estado de PVC (UpdatePVCStatus):

  • Cuando no hay PV que cumpla con las condiciones de PVC en el clúster, el estado de PVC es Pendiente. Después de que el PV y el PVC están unidos, el estado del PVC cambia de Pendiente a Atado;
  • El PV vinculado al PVC se elimina en el entorno y el estado del PVC se pierde;
  • Después de vincularse a un PV con el mismo nombre nuevamente, el estado del PVC se convierte en Vinculado.

El proceso de aprovisionamiento es el siguiente (los usuarios simulados crean un nuevo PVC aquí):

Proceso de volumen de almacenamiento estático (FindBestMatch): el controlador PV primero filtra un PV con el estado de Disponible en el entorno para que coincida con el nuevo PVC.

  • DelayBinding: el controlador PV determina si el PVC necesita un enlace de retraso: 1. Compruebe si la anotación de PVC contiene volume.kubernetes.io/selected-node. Si existe, significa que el PVC ha sido designado por el planificador (perteneciente a ProvisionVolume) ), Por lo que no es necesario un enlace retrasado; 2. Si no hay volume.kubernetes.io/selected-node en la anotación de PVC y no hay StorageClass, el valor predeterminado significa que no se requiere un enlace retrasado; si hay un StorageClass, verifique su campo VolumeBindingMode, si Para WaitForFirstConsumer, debe retrasar el enlace, si es Inmediato, no necesita retrasar el enlace;
  • FindBestMatchPVForClaim: el controlador PV intenta encontrar un PV existente en un entorno que cumpla con los requisitos de PVC. El controlador de PV filtrará todos los PV una vez y seleccionará el PV que mejor se adapte a los PV que cumplan las condiciones. Reglas de detección: 1. Si el modo de volumen coincide; 2. Si el PV está unido al PVC; 3. Si el PV. Estado.Fase está disponible; 4. La verificación del Selector de etiquetas, las etiquetas de PV y PVC deben ser consistentes; 5. PV y Si la clase de almacenamiento de PVC es consistente: 6. Cada iteración actualiza el PV que cumple con el tamaño mínimo solicitado de PVC y devuelve como resultado final;
  • Enlace: El controlador de PV enlaza el PV y el PVC seleccionados: 1. Actualice la información de PV.ReclamacionesRef del PV al PVC actual; 2. Actualice. .kubernetes.io / bound-by-controller: "yes"; 4. Actualice .Spec.VolumeName de PVC al nombre PV; 5. Actualice .Status.Phase de PVC a enlazado; 6. Agregue la anotación de PVC: pv. kubernetes.io/bound-by-controller: "yes" y pv.kubernetes.io/bind-completed: "yes";

Proceso de volumen de almacenamiento dinámico (ProvisionVolume): si no hay un PV adecuado en el entorno, ingrese el escenario de aprovisionamiento dinámico:

  • Antes del aprovisionamiento: 1. El controlador PV primero juzga si el StorageClass utilizado por el PVC está dentro o fuera del árbol: juzgue verificando si el campo del aprovisionador del StorageClass contiene el prefijo "kubernetes.io/"; 2. El controlador PV actualiza el PVC Anotación: Claim.Annotations ["volume.beta.kubernetes.io/storage-provisioner"] = storageClass.Provisioner;
  • Aprovisionamiento en árbol (aprovisionamiento interno): 1. El Provioner en árbol implementará el método NewProvisioner de la interfaz ProvisionableVolumePlugin para devolver un nuevo Provisioner; 2. El controlador PV llama a la función Provisioner Provisioner, que devuelve un objeto PV ; 3. El controlador PV crea el objeto PV devuelto en el paso anterior, lo vincula a PVC, Spec.ClaimRef se establece en PVC, .Status.Phase se establece en Bound y .Spec.StorageClassName se establece en el mismo StorageClassName que PVC; mientras tanto, nuevo Aumente la anotación: "pv.kubernetes.io/bound-by-controller"="yes" y "pv.kubernetes.io/provisioned-by" = plugin.GetPluginName ();
  • Aprovisionamiento fuera del árbol (aprovisionamiento externo): 1. El aprovisionador externo verifica si el reclamo. Spec.VolumeName en el PVC está vacío, y omite el PVC si no está vacío; 2. El aprovisionador externo verifica el reclamo. Anotaciones en el PVC [ "volume.beta.kubernetes.io/storage-provisioner"] es igual a su propio nombre de aprovisionador (el aprovisionador externo pasará el parámetro --provisioner al inicio para determinar su propio nombre de aprovisionador); 3. Si el modo de volumen de PVC = bloque, Verifique si el aprovisionador externo admite dispositivos de bloque; 4. El aprovisionador externo llama a la función de aprovisionamiento: llama a la interfaz CreateVolume del complemento de almacenamiento CSI a través de gRPC; 5. El aprovisionador externo crea un PV para representar el volumen y une el PV al PVC anterior.

borrando volúmenes

El proceso de eliminación es la operación inversa de aprovisionamiento:

El usuario elimina el PVC, elimina el controlador PV y cambia PV.Status.Phase a Liberado.


Cuando PV.Status.Phase == Lanzado, el controlador PV primero verifica el valor de Spec.PersistentVolumeReclaimPolicy, omite directamente cuando Retener, y Eliminar cuando:

  • Eliminación en árbol: 1. El Provioner de árbol implementará el método NewDeleter de la interfaz DeleteableVolumePlugin para devolver un nuevo Deleter; 2. El controlador llama a la función Eliminar de Deleter para eliminar el volumen correspondiente; 3. Después de eliminar el volumen, el PV El controlador eliminará el objeto PV;
  • Eliminación fuera del árbol: 1. El aprovisionador externo llama a la función Eliminar y llama a la interfaz DeleteVolume del complemento CSI a través de gRPC; 2. Después de eliminar el volumen, el aprovisionador externo elimina el objeto PV

Adjuntar volúmenes

Tanto los componentes de Kubelet como los controladores de AD pueden realizar operaciones de conexión / desconexión. Si --enable-controller-attach-detach se especifica en los parámetros de inicio de Kubelet, Kubelet lo hará; de lo contrario, AD lo hará de manera predeterminada. El controlador AD se toma como un ejemplo para explicar la operación de conexión / desconexión.

6.png

Hay dos variables principales en el controlador AD:

  • DesiredStateOfWorld (DSW): el estado de montaje del volumen de datos esperado en el clúster, que contiene información de nodos-> volúmenes-> pods;
  • ActualStateOfWorld (ASW): el estado de montaje del volumen de datos real en el clúster, que contiene la información de volúmenes-> nodos.

El proceso de conexión es el siguiente:

El controlador AD inicializa DSW y ASW de acuerdo con la información de recursos en el clúster.

Hay tres componentes dentro del controlador AD que actualizan periódicamente DSW y ASW:

  • Reconciliador Se ejecuta periódicamente una GoRoutine para garantizar que el volumen se monte / retire. ASW se actualiza continuamente durante este período:

conexión en árbol: 1. El atacante en árbol implementará el método NewAttacher de la interfaz AttachableVolumePlugin para devolver un nuevo atacante; 2. El controlador AD llama a la función Attach's Attach para conectar el dispositivo; 3. Actualice el ASW.

conexión fuera del árbol: 1. Llame al CSIAttacher dentro del árbol para crear un objeto VolumeAttachement (VA), que contiene la información del Attacher, el nombre del nodo y la información PV que se adjuntará; 2. El Attacher externo observará los recursos de VolumeAttachement en el clúster Cuando descubre que hay un volumen de datos que necesita ser montado, llama a la función Adjuntar y llama a la interfaz ControllerPublishVolume del complemento CSI a través de gRPC.

  • DesiredStateOfWorldPopulator. Ejecutando periódicamente a través de una GoRoutine, la función principal es actualizar el DSW:

findAndRemoveDeletedPods-atraviesa todos los Pods en DSW, si se ha eliminado del clúster, luego quítelo de DSW;
findAndAddActivePods-atraviesa todos los Pods en PodLister, si no hay tal Pod en DSW, agréguelo a DSW

  • Trabajador de PVC. Observe el evento de agregar / actualizar de PVC, procesar los pods relacionados con PVC y actualizar el DSW en tiempo real.

Separar volúmenes

El proceso de separación es el siguiente:

  • Cuando se elimina el Pod, el controlador AD observará el evento. Primero, el controlador AD verifica si el recurso Node donde se encuentra el Pod contiene la etiqueta "volumes.kubernetes.io/keep-terminated-pod-volumes". Si lo hace, no realiza la operación; si no lo hace, elimina el volumen del DSW;
  • El controlador AD usa el Reconciliador para acercar el estado ActualStateOfWorld al estado DesiredStateOfWorld. Cuando encuentra que hay un volumen que no existe en el DSW en ASW, realizará una operación de desconexión:

desconexión en árbol: 1. El controlador AD implementará el método NewDetacher de la interfaz AttachableVolumePlugin para devolver un nuevo Detacher; 2. El controlador llama a la función Detacher's Detach, y detach corresponde al volumen; 3. El controlador AD actualiza ASW.

desconexión fuera del árbol: 1. El controlador AD llama al CSIAttacher en el árbol para eliminar el objeto VolumeAttachement relacionado; 2. El atacante externo observará el recurso VolumeAttachement (VA) en el clúster y buscará la función Separar cuando haya un volumen de datos que deba eliminarse , Llame a la interfaz ControllerUnpublishVolume del complemento CSI a través de gRPC: 3. El controlador AD actualiza ASW.

7.png

También hay dos variables principales en Volume Manager:

  • DesiredStateOfWorld (DSW): el estado de montaje del volumen de datos esperado en el clúster, incluida la información de volúmenes-> pods;
  • ActualStateOfWorld (ASW): el estado real de montaje del volumen de datos en el clúster, incluida la información de volúmenes-> pods.

El proceso de montaje / desmontaje es el siguiente:

El propósito del directorio global (ruta de montaje global): los dispositivos de bloque solo se pueden montar una vez en Linux, y en el escenario K8s, se puede montar un PV en varias instancias de Pod en el mismo nodo. Si el dispositivo de bloque está formateado y montado en un directorio global temporal en Node, utilice la tecnología de montaje de enlace en Linux para montar este directorio global en el directorio correspondiente en el Pod, puede cumplir los requisitos. En el diagrama de flujo anterior, el directorio global es / var / lib / kubelet / pods / [pod uid] /volumes/kubernetes.io~iscsi/ [PV

nombre]

VolumeManager inicializa DSW y ASW de acuerdo con la información de recursos en el clúster.

Hay dos componentes dentro de VolumeManager que actualizan periódicamente DSW y ASW:

  • DesiredStateOfWorldPopulator: se ejecuta periódicamente a través de una GoRoutine, la función principal es actualizar el DSW;
  • Reconciliador: ejecute periódicamente una GoRoutine para asegurarse de que el volumen esté montado / desmontado. ASW se actualiza continuamente durante este período:

unmountVolumes: asegúrese de que los volúmenes se desmonten después de que se elimine el Pod. Itere a través de todos los Pods en ASW, si no está en DSW (lo que indica que Pod se ha eliminado), tome VolumeMode = FileSystem como ejemplo, luego realice las siguientes operaciones:

  1. Elimine todos los montajes de enlace: llame a la interfaz TearDown de Unmounter (si está fuera del árbol, llame a la interfaz NodeUnpublishVolume del complemento CSI);
  2. Desmontar volumen: llame a la función UnmountDevice de DeviceUnmounter (si está fuera del árbol, llame a la interfaz NodeUnstageVolume del complemento CSI);
  3. Actualiza ASW.

mountAttachVolumes: asegúrese de que los volúmenes que va a utilizar el Pod se monten correctamente. Itere a través de todos los Pods en el DSW, si no está en el ASW (lo que indica que el directorio a montar está asignado al Pod), aquí toma VolumeMode = FileSystem como ejemplo, realice las siguientes operaciones:

  1. Espere a que el volumen se adjunte al nodo (adjunto por External Attacher o Kubelet);
  2. Monte el volumen en el directorio global: llame a la función MountDevice de DeviceMounter (si está fuera del árbol, llame a la interfaz NodeStageVolume del complemento CSI);
  3. Actualizar ASW: el volumen se ha montado en el directorio global;
  4. Bind-mount volume to Pod: llame a la interfaz Seter de Mounter (si está fuera del árbol, llame a la interfaz NodePublishVolume del complemento CSI);
  5. Actualiza ASW.

unmountDetachDevices: asegúrese de que los volúmenes que deben desmontarse estén desmontados. Itere a través de UnmountedVolumes en todos los ASW, si no está en el DSW (lo que indica que el volumen ya no es necesario), realice las siguientes operaciones:

  1. Desmontar volumen: llame a la función UnmountDevice de DeviceUnmounter (si está fuera del árbol, llame a la interfaz NodeUnstageVolume del complemento CSI);
  2. Actualiza ASW.

Resumen

Este artículo presenta primero los conceptos básicos y los métodos de uso del almacenamiento persistente de K8, y analiza en profundidad el proceso de almacenamiento interno de K8. En los K8, el uso de cualquier tipo de almacenamiento es inseparable del proceso anterior (la conexión / desconexión no se usa en algunos escenarios), y el problema de almacenamiento en el entorno también debe ser un fallo en uno de los enlaces.

Hay muchos pozos para el almacenamiento de contenedores, especialmente en un entorno de nube patentado. ¡Pero cuantos más desafíos, más oportunidades! En la actualidad, el mercado doméstico de nube patentada también es líder en el campo del almacenamiento ¡Nuestro ágil equipo de contenedores PaaS da la bienvenida a los héroes para que se unan a nosotros para crear un futuro mejor juntos!

Supongo que te gusta

Origin www.cnblogs.com/IT-Evan/p/12673218.html
Recomendado
Clasificación