Migración desde una clase de almacenamiento en árbol a controladores CSI en Azure Kubernetes Service (AKS)
La implementación del controlador Container Storage Interface (CSI) se introdujo en Azure Kubernetes Service (AKS) a partir de la versión 1.21. Si se adopta CSI y se usa como el estándar, las cargas de trabajo con estado existentes que utilicen volúmenes persistentes (VV) en árbol deben migrarse o actualizarse para usar el controlador de CSI.
Para simplificar este proceso todo lo posible y asegurarse de que no se pierden datos, en este artículo se proporcionan diferentes opciones de migración. Estas opciones incluyen scripts que ayudar a garantizar una migración sin problemas desde un árbol a Azure Disks y controladores CSI de Azure Files.
Antes de empezar
- La versión 2.37.0 de la CLI de Azure, o cualquier versión posterior. Ejecute
az --version
para buscar la versión y ejecuteaz upgrade
para actualizar la versión. Si necesita instalarla o actualizarla, vea Instalación de la CLI de Azure. - Los administradores de kubectl y clústeres tienen acceso para crear, obtener, enumerar y eliminar el acceso a un pvc o PV, una instantánea de volumen o el contenido de instantáneas de volumen. Para un clúster habilitado para RBAC de Microsoft Entra es miembro del rol de Administración de clústeres de RBAC de Azure Kubernetes Service.
Migración de volúmenes de disco
Nota
Las etiquetas failure-domain.beta.kubernetes.io/zone
y failure-domain.beta.kubernetes.io/region
quedaron en desuso en AKS 1.24 y se quitaron en la versión 1.28. Si los volúmenes persistentes existentes siguen usando nodeAffinity que coincidan con estas dos etiquetas, debe cambiarlos a las etiquetas topology.kubernetes.io/zone
y topology.kubernetes.io/region
en la nueva configuración de volúmenes persistentes.
La migración desde en árbol a CSI se admite mediante dos opciones de migración:
- Creación de un volumen estático
- Creación de un volumen dinámico
Creación de un volumen estático
Esta opción permite crear un PV mediante la asignación estática de claimRef
a un nuevo PVC que se creará más adelante y especificar volumeName
para PersistentVolumeClaim.
Las ventajas de este enfoque son:
- Es sencillo y se puede automatizar.
- No es necesario limpiar la configuración original mediante una clase de almacenamiento en árbol.
- Bajo riesgo, ya que solo se está realizando una eliminación lógica de PV/PVC de Kubernetes, los datos físicos reales no se eliminan.
- No se incurre en ningún coste adicional al no tener que crear objetos de Azure adicionales, como discos, instantáneas, etc.
A continuación encontrará consideraciones importantes que se deben evaluar:
- La transición a volúmenes estáticos de volúmenes de estilo dinámico requiere construir y administrar objetos PV manualmente para todas las opciones.
- Posible tiempo de inactividad de la aplicación al volver a implementar la nueva aplicación con referencia al nuevo objeto PVC.
Migración
Actualice el PV
ReclaimPolicy
existente de Eliminar a Retener ejecutando el siguiente comando:kubectl patch pv pvName -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
Reemplace pvName por el nombre del objeto PersistentVolume seleccionado. Como alternativa, si desea actualizar el valor de reclaimPolicy para varios PV, cree un archivo denominado patchReclaimPVs.sh y cópielo en el código siguiente.
#!/bin/bash # Patch the Persistent Volume in case ReclaimPolicy is Delete NAMESPACE=$1 i=1 for PVC in $(kubectl get pvc -n $NAMESPACE | awk '{ print $1}'); do # Ignore first record as it contains header if [ $i -eq 1 ]; then i=$((i + 1)) else PV="$(kubectl get pvc $PVC -n $NAMESPACE -o jsonpath='{.spec.volumeName}')" RECLAIMPOLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" echo "Reclaim Policy for Persistent Volume $PV is $RECLAIMPOLICY" if [[ $RECLAIMPOLICY == "Delete" ]]; then echo "Updating ReclaimPolicy for $pv to Retain" kubectl patch pv $PV -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}' fi fi done
Ejecute el script con el parámetro
namespace
para especificar el espacio de nombres./PatchReclaimPolicy.sh <namespace>
del clúster.Para obtener una lista de todos los PVC que hay en el espacio de nombres ordenados por creationTimestamp, ejecute el siguiente comando. Establezca el espacio de nombres mediante el argumento
--namespace
junto con el espacio de nombres del clúster real.kubectl get pvc -n <namespace> --sort-by=.metadata.creationTimestamp -o custom-columns=NAME:.metadata.name,CreationTime:.metadata.creationTimestamp,StorageClass:.spec.storageClassName,Size:.spec.resources.requests.storage
Este paso es útil si hay muchos de PV que deba migrar y desea migrar unos pocos a la vez. La ejecución de este comando le permite identificar qué PVC se crearon en un período de tiempo determinado. Al ejecutar el script de CreatePV.sh, dos de los parámetros son la hora de inicio y la hora de finalización, que le permiten migrar solo los PVC durante ese período.
Cree un archivo denominado CreatePV.sh y cópielo en el código siguiente. El script hace lo siguiente:
- Crea una instancia de persistentVolume con el nombre
existing-pv-csi
de todas las instancias de persistentVolume de los espacios de nombres para la clase de almacenamientostorageClassName
. - Configure el nuevo nombre de PVC como
existing-pvc-csi
. - Crea un PVC con el nombre de PV que especifique.
#!/bin/bash #kubectl get pvc -n <namespace> --sort-by=.metadata.creationTimestamp -o custom-columns=NAME:.metadata.name,CreationTime:.metadata.creationTimestamp,StorageClass:.spec.storageClassName,Size:.spec.resources.requests.storage # TimeFormat 2022-04-20T13:19:56Z NAMESPACE=$1 FILENAME=$(date +%Y%m%d%H%M)-$NAMESPACE EXISTING_STORAGE_CLASS=$2 STORAGE_CLASS_NEW=$3 STARTTIMESTAMP=$4 ENDTIMESTAMP=$5 i=1 for PVC in $(kubectl get pvc -n $NAMESPACE | awk '{ print $1}'); do # Ignore first record as it contains header if [ $i -eq 1 ]; then i=$((i + 1)) else PVC_CREATION_TIME=$(kubectl get pvc $PVC -n $NAMESPACE -o jsonpath='{.metadata.creationTimestamp}') if [[ $PVC_CREATION_TIME >= $STARTTIMESTAMP ]]; then if [[ $ENDTIMESTAMP > $PVC_CREATION_TIME ]]; then PV="$(kubectl get pvc $PVC -n $NAMESPACE -o jsonpath='{.spec.volumeName}')" RECLAIM_POLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" STORAGECLASS="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.storageClassName}')" echo $PVC RECLAIM_POLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" if [[ $RECLAIM_POLICY == "Retain" ]]; then if [[ $STORAGECLASS == $EXISTING_STORAGE_CLASS ]]; then STORAGE_SIZE="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.capacity.storage}')" SKU_NAME="$(kubectl get storageClass $STORAGE_CLASS_NEW -o jsonpath='{.parameters.skuname}')" DISK_URI="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.azureDisk.diskURI}')" PERSISTENT_VOLUME_RECLAIM_POLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" cat >$PVC-csi.yaml <<EOF apiVersion: v1 kind: PersistentVolume metadata: annotations: pv.kubernetes.io/provisioned-by: disk.csi.azure.com name: $PV-csi spec: accessModes: - ReadWriteOnce capacity: storage: $STORAGE_SIZE claimRef: apiVersion: v1 kind: PersistentVolumeClaim name: $PVC-csi namespace: $NAMESPACE csi: driver: disk.csi.azure.com volumeAttributes: csi.storage.k8s.io/pv/name: $PV-csi csi.storage.k8s.io/pvc/name: $PVC-csi csi.storage.k8s.io/pvc/namespace: $NAMESPACE requestedsizegib: "$STORAGE_SIZE" skuname: $SKU_NAME volumeHandle: $DISK_URI persistentVolumeReclaimPolicy: $PERSISTENT_VOLUME_RECLAIM_POLICY storageClassName: $STORAGE_CLASS_NEW --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: $PVC-csi namespace: $NAMESPACE spec: accessModes: - ReadWriteOnce storageClassName: $STORAGE_CLASS_NEW resources: requests: storage: $STORAGE_SIZE volumeName: $PV-csi EOF kubectl apply -f $PVC-csi.yaml LINE="PVC:$PVC,PV:$PV,StorageClassTarget:$STORAGE_CLASS_NEW" printf '%s\n' "$LINE" >>$FILENAME fi fi fi fi fi done
- Crea una instancia de persistentVolume con el nombre
Para crear una instancia de persistentVolume para todas las instancias de persistentVolumes en el espacio de nombres, ejecute el script CreatePV.sh con los parámetros siguientes:
namespace
: el espacio de nombres del clústersourceStorageClass
: StorageClass basado en el controlador de almacenamiento en árboltargetCSIStorageClass
: StorageClass basado en el controlador de almacenamiento CSI, que puede ser cualquiera de las clases de almacenamiento predeterminadas que tienen el aprovisionamiento establecido en disk.csi.azure.com o file.csi.azure.com. O bien, puede crear una clase de almacenamiento personalizada, siempre que esté establecida cualquiera de esos dos aprovisionadores.startTimeStamp
: proporcione una hora de inicio antes de la hora de creación del PVC en el formato yyyy-mm-ddthh:mm:sszendTimeStamp
: especifique una hora de finalización con el formato aaaa-mm-ddthh:mm:ssz.
./CreatePV.sh <namespace> <sourceIntreeStorageClass> <targetCSIStorageClass> <startTimestamp> <endTimestamp>
Actualice la aplicación para que use el PVC nuevo.
Creación de un volumen dinámico
Esta opción permite crear dinámicamente un volumen persistente a partir de una notificación de volumen persistente.
Las ventajas de este enfoque son:
Es menos arriesgado porque los objetos nuevos se crean, mientras se conservan otras copias con instantáneas.
No es preciso construir PV por separado y agregar el nombre del volumen al manifiesto de PVC.
A continuación encontrará consideraciones importantes que se deben evaluar:
Aunque este enfoque es menos arriesgado, crea varios objetos, lo que aumentará los costos de almacenamiento.
Durante la creación de los nuevos volúmenes la aplicación no está disponible.
Los pasos de eliminación deben realizarse con precaución. Se pueden aplicar bloqueos de recursos temporales a un grupo de recursos hasta que se complete la migración y la aplicación se compruebe correctamente.
La comprobación o la validación de los datos se debe realizar cuando se crean discos a partir de instantáneas.
Migración
Antes de continuar, compruebe lo siguiente:
En el caso de cargas de trabajo concretas en las que los datos se escriben en memoria antes de escribirse en el disco, la aplicación debe detenerse y permitir que los datos en memoria se vacíen en el disco.
La clase
VolumeSnapshot
debe existir como se muestra en el ejemplo siguiente de YAML:apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshotClass metadata: name: custom-disk-snapshot-sc driver: disk.csi.azure.com deletionPolicy: Delete parameters: incremental: "false"
Para obtener una lista de todos los PVC que hay en un espacio de nombres especificado ordenados por creationTimestamp, ejecute el siguiente comando. Establezca el espacio de nombres mediante el argumento
--namespace
junto con el espacio de nombres del clúster real.kubectl get pvc --namespace <namespace> --sort-by=.metadata.creationTimestamp -o custom-columns=NAME:.metadata.name,CreationTime:.metadata.creationTimestamp,StorageClass:.spec.storageClassName,Size:.spec.resources.requests.storage
Este paso es útil si hay muchos de PV que deba migrar y desea migrar unos pocos a la vez. La ejecución de este comando le permite identificar qué PVC se crearon en un período de tiempo determinado. Al ejecutar el script MigrateCSI.sh, dos de los parámetros son la hora de inicio y la hora de finalización, que le permiten migrar solo los PVC durante ese período.
Cree un archivo denominado MigrateToCSI.sh y cópielo en el código siguiente. El script hace lo siguiente:
- Crea una instantánea del disco completo mediante la CLI de Azure
- Crea
VolumesnapshotContent
- Crea
VolumeSnapshot
- Crea un PVC desde
VolumeSnapshot
- Crea un archivo llamado
<namespace>-timestamp
, que contiene una lista de todos los recursos antiguos que deben limpiarse.
#!/bin/bash #kubectl get pvc -n <namespace> --sort-by=.metadata.creationTimestamp -o custom-columns=NAME:.metadata.name,CreationTime:.metadata.creationTimestamp,StorageClass:.spec.storageClassName,Size:.spec.resources.requests.storage # TimeFormat 2022-04-20T13:19:56Z NAMESPACE=$1 FILENAME=$NAMESPACE-$(date +%Y%m%d%H%M) EXISTING_STORAGE_CLASS=$2 STORAGE_CLASS_NEW=$3 VOLUME_STORAGE_CLASS=$4 START_TIME_STAMP=$5 END_TIME_STAMP=$6 i=1 for PVC in $(kubectl get pvc -n $NAMESPACE | awk '{ print $1}'); do # Ignore first record as it contains header if [ $i -eq 1 ]; then i=$((i + 1)) else PVC_CREATION_TIME=$(kubectl get pvc $PVC -n $NAMESPACE -o jsonpath='{.metadata.creationTimestamp}') if [[ $PVC_CREATION_TIME > $START_TIME_STAMP ]]; then if [[ $END_TIME_STAMP > $PVC_CREATION_TIME ]]; then PV="$(kubectl get pvc $PVC -n $NAMESPACE -o jsonpath='{.spec.volumeName}')" RECLAIM_POLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" STORAGE_CLASS="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.storageClassName}')" echo $PVC RECLAIM_POLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" if [[ $STORAGE_CLASS == $EXISTING_STORAGE_CLASS ]]; then STORAGE_SIZE="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.capacity.storage}')" SKU_NAME="$(kubectl get storageClass $STORAGE_CLASS_NEW -o jsonpath='{.parameters.skuname}')" DISK_URI="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.azureDisk.diskURI}')" TARGET_RESOURCE_GROUP="$(cut -d'/' -f5 <<<"$DISK_URI")" echo $DISK_URI SUBSCRIPTION_ID="$(echo $DISK_URI | grep -o 'subscriptions/[^/]*' | sed 's#subscriptions/##g')" echo $TARGET_RESOURCE_GROUP PERSISTENT_VOLUME_RECLAIM_POLICY="$(kubectl get pv $PV -n $NAMESPACE -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" az snapshot create --resource-group $TARGET_RESOURCE_GROUP --name $PVC-$FILENAME --source "$DISK_URI" --subscription ${SUBSCRIPTION_ID} SNAPSHOT_PATH=$(az snapshot list --resource-group $TARGET_RESOURCE_GROUP --query "[?name == '$PVC-$FILENAME'].id | [0]" --subscription ${SUBSCRIPTION_ID}) SNAPSHOT_HANDLE=$(echo "$SNAPSHOT_PATH" | tr -d '"') echo $SNAPSHOT_HANDLE sleep 10 # Create Restore File cat <<EOF >$PVC-csi.yml apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshotContent metadata: name: $PVC-$FILENAME spec: deletionPolicy: 'Delete' driver: 'disk.csi.azure.com' volumeSnapshotClassName: $VOLUME_STORAGE_CLASS source: snapshotHandle: $SNAPSHOT_HANDLE volumeSnapshotRef: apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshot name: $PVC-$FILENAME namespace: $1 --- apiVersion: snapshot.storage.k8s.io/v1 kind: VolumeSnapshot metadata: name: $PVC-$FILENAME namespace: $1 spec: volumeSnapshotClassName: $VOLUME_STORAGE_CLASS source: volumeSnapshotContentName: $PVC-$FILENAME --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: csi-$PVC namespace: $1 spec: accessModes: - ReadWriteOnce storageClassName: $STORAGE_CLASS_NEW resources: requests: storage: $STORAGE_SIZE dataSource: name: $PVC-$FILENAME kind: VolumeSnapshot apiGroup: snapshot.storage.k8s.io EOF kubectl create -f $PVC-csi.yml LINE="OLDPVC:$PVC,OLDPV:$PV,VolumeSnapshotContent:volumeSnapshotContent-$FILENAME,VolumeSnapshot:volumesnapshot$FILENAME,OLDdisk:$DISK_URI" printf '%s\n' "$LINE" >>$FILENAME fi fi fi fi done
Para migrar los volúmenes de disco, ejecute el script MigrateToCSI.sh con los siguientes parámetros:
namespace
: el espacio de nombres del clústersourceStorageClass
: StorageClass basado en el controlador de almacenamiento en árboltargetCSIStorageClass
: StorageClass basado en el controlador de almacenamiento CSIvolumeSnapshotClass
: nombre de la clase de la instantánea de volumen. Por ejemplo,custom-disk-snapshot-sc
.startTimeStamp
: especifique una hora de inicio con el formato aaaa-mm-ddthh:mm:ssz.endTimeStamp
: especifique una hora de finalización con el formato aaaa-mm-ddthh:mm:ssz.
./MigrateToCSI.sh <namespace> <sourceStorageClass> <TargetCSIstorageClass> <VolumeSnapshotClass> <startTimestamp> <endTimestamp>
Actualice la aplicación para que use el PVC nuevo.
Elimine manualmente los recursos más antiguos, incluidos PVC/PV, VolumeSnapshot y VolumeSnapshotContent en árbol. De lo contrario, el mantenimiento de los objetos PVC/PC e instantánea en el árbol generará más costo.
Migración de volúmenes de recursos compartidos de archivos
La migración desde el árbol a CSI se admite mediante la creación de un volumen estático:
- No es necesario limpiar la configuración original mediante una clase de almacenamiento en árbol.
- Bajo riesgo, ya que solo se está realizando una eliminación lógica de PV/PVC de Kubernetes, los datos físicos reales no se eliminan.
- No se incurre en ningún coste adicional al no tener que crear objetos de Azure adicionales, como recursos compartidos, etc.
Migración
Actualice el PV
ReclaimPolicy
existente de Eliminar a Retener ejecutando el siguiente comando:kubectl patch pv pvName -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
Reemplace pvName por el nombre del objeto PersistentVolume seleccionado. Como alternativa, si desea actualizar el valor de reclaimPolicy para varios PV, cree un archivo denominado patchReclaimPVs.sh y cópielo en el código siguiente.
#!/bin/bash # Patch the Persistent Volume in case ReclaimPolicy is Delete namespace=$1 i=1 for pvc in $(kubectl get pvc -n $namespace | awk '{ print $1}'); do # Ignore first record as it contains header if [ $i -eq 1 ]; then i=$((i + 1)) else pv="$(kubectl get pvc $pvc -n $namespace -o jsonpath='{.spec.volumeName}')" reclaimPolicy="$(kubectl get pv $pv -n $namespace -o jsonpath='{.spec.persistentVolumeReclaimPolicy}')" echo "Reclaim Policy for Persistent Volume $pv is $reclaimPolicy" if [[ $reclaimPolicy == "Delete" ]]; then echo "Updating ReclaimPolicy for $pv to Retain" kubectl patch pv $pv -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}' fi fi done
Ejecute el script con el parámetro
namespace
para especificar el espacio de nombres./PatchReclaimPolicy.sh <namespace>
del clúster.Cree una instancia de Storage Class con el aprovisionamiento establecido en
file.csi.azure.com
, o bien puede usar una de las clases de StorageClasses predeterminadas con el aprovisionador de archivos CSI.Obtenga
secretName
yshareName
de la instancia de persistentVolumes existente mediante la ejecución del siguiente comando:kubectl describe pv pvName
Cree un PV y utilice para ello la nueva clase StorageClass y
shareName
ysecretName
desde el PV en el árbol. Cree un archivo denominado azurefile-mount-pv.yaml y cópielo en el código siguiente. Encsi
, actualiceresourceGroup
,volumeHandle
yshareName
. En el caso de las opciones de montaje, el valor predeterminado de fileMode y dirMode es 0777.El valor predeterminado de
fileMode
ydirMode
es 0777.apiVersion: v1 kind: PersistentVolume metadata: annotations: pv.kubernetes.io/provisioned-by: file.csi.azure.com name: azurefile spec: capacity: storage: 5Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: azurefile-csi csi: driver: file.csi.azure.com readOnly: false volumeHandle: unique-volumeid # make sure volumeid is unique for every identical share in the cluster volumeAttributes: resourceGroup: EXISTING_RESOURCE_GROUP_NAME # optional, only set this when storage account is not in the same resource group as the cluster nodes shareName: aksshare nodeStageSecretRef: name: azure-secret namespace: default mountOptions: - dir_mode=0777 - file_mode=0777 - uid=0 - gid=0 - mfsymlinks - cache=strict - nosharesock - nobrl # disable sending byte range lock requests to the server and for applications which have challenges with posix locks
Cree un archivo denominado azurefile-mount-pvc.yaml con una instancia de persistentVolumeClaim que use PersistentVolume mediante el código siguiente.
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: azurefile spec: accessModes: - ReadWriteMany storageClassName: azurefile-csi volumeName: azurefile resources: requests: storage: 5Gi
Use el comando
kubectl
para crear persistentVolume.kubectl apply -f azurefile-mount-pv.yaml
Use el comando
kubectl
para crear PersistentVolumeClaim.kubectl apply -f azurefile-mount-pvc.yaml
Compruebe que PersistentVolumeClaim se ha creado y que está enlazado al objeto PersistentVolume, para lo que debe ejecutar el siguiente comando.
kubectl get pvc azurefile
La salida debe ser similar a la siguiente:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE azurefile Bound azurefile 5Gi RWX azurefile 5s
Actualice la especificación de contenedor para hacer referencia al objeto PersistentVolumeClaim y actualice su pod. Por ejemplo, copie el siguiente código y cree un archivo denominado azure-files-pod.yaml.
... volumes: - name: azure persistentVolumeClaim: claimName: azurefile
La especificación del pod no se puede actualizar. Use los siguientes comandos
kubectl
para eliminar y volver a crear el pod.kubectl delete pod mypod
kubectl apply -f azure-files-pod.yaml
Pasos siguientes
- Para obtener más información sobre los procedimientos recomendados de almacenamiento, vea Procedimientos recomendados para el almacenamiento y las copias de seguridad en Azure Kubernetes Service (AKS).
- Proteja los PVs recién migrados basados en CSI Driver haciéndoles una copia de seguridad mediante Azure Backup para AKS.
Azure Kubernetes Service