Migrar da classe de armazenamento em árvore para drivers CSI no Serviço Kubernetes do Azure (AKS)
A implementação do driver CSI (Container Storage Interface) foi introduzida no Serviço Kubernetes do Azure (AKS) a partir da versão 1.21. Ao adotar e usar o CSI como padrão, suas cargas de trabalho com monitoração de estado existentes usando volumes persistentes (PVs) na árvore devem ser migradas ou atualizadas para usar o driver CSI.
Para tornar esse processo o mais simples possível e garantir que não haja perda de dados, este artigo fornece diferentes opções de migração. Essas opções incluem scripts para ajudar a garantir uma migração suave da árvore para os drivers CSI de Discos do Azure e Arquivos do Azure.
Antes de começar
- A CLI do Azure versão 2.37.0 ou posterior. Execute
az --version
para localizar a versão e executeaz upgrade
para atualizar a versão. Se precisar de instalar ou atualizar, veja Install Azure CLI (Instalar o Azure CLI). - Kubectl e administradores de cluster têm acesso para criar, obter, listar, excluir acesso a um PVC ou PV, instantâneo de volume ou conteúdo de instantâneo de volume. Para um cluster habilitado para RBAC do Microsoft Entra, você é membro da função de Administrador de Cluster RBAC do Serviço Kubernetes do Azure.
Migrar volumes de disco
Nota
Os rótulos failure-domain.beta.kubernetes.io/zone
e failure-domain.beta.kubernetes.io/region
foram preteridos no AKS 1.24 e removidos no 1.28. Se seus volumes persistentes existentes ainda estiverem usando nodeAffinity que corresponda a esses dois rótulos, você precisará alterá-los para topology.kubernetes.io/zone
e topology.kubernetes.io/region
rótulos na nova configuração de volume persistente.
A migração de in-tree para CSI é suportada usando duas opções de migração:
- Criar um volume estático
- Criar um volume dinâmico
Criar um volume estático
Usando essa opção, você cria um PV atribuindo claimRef
estaticamente a um novo PVC que será criado posteriormente e especifica o volumeName
para PersistentVolumeClaim.
Os benefícios desta abordagem são:
- A migração é simples e pode ser automatizada.
- Não é preciso limpar a configuração original com a classe de armazenamento na árvore.
- De baixo risco, uma vez que só está a executar uma eliminação lógica do PV/PVC do Kubernetes; os dados físicos reais não são eliminados.
- Nenhum custo extra incorrido como resultado de não ter que criar objetos adicionais do Azure, como disco, instantâneos, etc.
São considerações importantes a avaliar:
- A transição para volumes estáticos a partir de volumes de estilo dinâmico originais requer a construção e a gestão manuais de objetos PV para todas as opções.
- Potencial período de indisponibilidade da aplicação ao reimplementar a nova aplicação com referência ao novo objeto PVC.
Migração
Atualize o PV
ReclaimPolicy
existente de Delete para Retain executando o seguinte comando:kubectl patch pv pvName -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
Substitua pvName pelo nome do PersistentVolume selecionado. Como alternativa, se você quiser atualizar a reclaimPolicy para vários PVs, crie um arquivo chamado patchReclaimPVs.sh e copie no código a seguir.
#!/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
Execute o script com o
namespace
parâmetro para especificar o namespace./PatchReclaimPolicy.sh <namespace>
do cluster .Obtenha uma lista de todos os PVCs no namespace classificados por creationTimestamp executando o seguinte comando. Defina o namespace usando o
--namespace
argumento junto com o namespace de cluster 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
Esta etapa é útil se você tiver um grande número de PVs que precisam ser migrados e quiser migrar alguns de cada vez. A execução deste comando permite identificar quais PVCs foram criados em um determinado período de tempo. Quando você executa o script CreatePV.sh, dois dos parâmetros são a hora de início e a hora de término que permitem migrar apenas os PVCs durante esse período de tempo.
Crie um arquivo chamado CreatePV.sh e copie no código a seguir. O script faz o seguinte:
- Cria um novo PersistentVolume com nome
existing-pv-csi
para todos os PersistentVolumes em namespaces para classestorageClassName
de armazenamento. - Configure o novo nome do PVC como
existing-pvc-csi
. - Cria um novo PVC com o nome PV que você especificar.
#!/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
- Cria um novo PersistentVolume com nome
Para criar um novo PersistentVolume para todos os PersistentVolumes no namespace, execute o script CreatePV.sh com os seguintes parâmetros:
namespace
- O namespace do clustersourceStorageClass
- O StorageClass baseado em driver de armazenamento em árvoretargetCSIStorageClass
- O StorageClass baseado em driver de armazenamento CSI, que pode ser uma das classes de armazenamento padrão que têm o provisionador definido como disk.csi.azure.com ou file.csi.azure.com. Ou você pode criar uma classe de armazenamento personalizada, desde que ela esteja definida para qualquer um desses dois provisionadores.startTimeStamp
- Forneça um tempo de início antes do tempo de criação do PVC no formato aaaa-mm-ddthh:mm:sszendTimeStamp
- Forneça uma hora de término no formato aaaa-mm-ddthh:mm:ssz.
./CreatePV.sh <namespace> <sourceIntreeStorageClass> <targetCSIStorageClass> <startTimestamp> <endTimestamp>
Atualize seu aplicativo para usar o novo PVC.
Criar um volume dinâmico
Usando essa opção, você cria dinamicamente um Volume Persistente a partir de uma Reivindicação de Volume Persistente.
Os benefícios desta abordagem são:
É menos arriscado porque todos os novos objetos são criados ao reter outras cópias com instantâneos.
Não é necessário criar PVs em separado e adicionar o nome do volume no manifesto do PVC.
São considerações importantes a avaliar:
Embora esta abordagem seja menos arriscada, cria vários objetos que aumentarão os custos de armazenamento.
Durante a criação dos novos volumes, a aplicação não estará disponível.
Os passos de eliminação devem ser executados com cuidado. Os bloqueios temporários de recursos podem ser aplicados ao seu grupo de recursos até que a migração seja concluída e seu aplicativo seja verificado com êxito.
Realize a validação/verificação dos dados à medida que novos discos são criados a partir de instantâneos.
Migração
Antes de prosseguir, verifique o seguinte:
Para cargas de trabalho específicas em que os dados são gravados na memória antes de serem gravados no disco, o aplicativo deve ser interrompido e permitir que os dados na memória sejam liberados no disco.
VolumeSnapshot
classe deve existir como mostrado no exemplo a seguir 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"
Obtenha uma lista de todos os PVCs em um namespace especificado classificado por creationTimestamp executando o seguinte comando. Defina o namespace usando o
--namespace
argumento junto com o namespace de cluster 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
Esta etapa é útil se você tiver um grande número de PVs que precisam ser migrados e quiser migrar alguns de cada vez. A execução deste comando permite identificar quais PVCs foram criados em um determinado período de tempo. Quando você executa o script MigrateCSI.sh, dois dos parâmetros são a hora de início e a hora de término que permitem migrar apenas os PVCs durante esse período de tempo.
Crie um arquivo chamado MigrateToCSI.sh e copie no código a seguir. O script faz o seguinte:
- Cria um instantâneo de disco completo usando a CLI do Azure
- Cria
VolumesnapshotContent
- Cria
VolumeSnapshot
- Cria um novo PVC a partir de
VolumeSnapshot
- Cria um novo arquivo com o nome do arquivo
<namespace>-timestamp
, que contém uma lista de todos os recursos antigos que precisam ser limpos.
#!/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 os volumes de disco, execute o script MigrateToCSI.sh com os seguintes parâmetros:
namespace
- O namespace do clustersourceStorageClass
- O StorageClass baseado em driver de armazenamento em árvoretargetCSIStorageClass
- O StorageClass baseado em driver de armazenamento CSIvolumeSnapshotClass
- Nome da classe de instantâneo de volume. Por exemplo,custom-disk-snapshot-sc
.startTimeStamp
- Forneça uma hora de início no formato aaaa-mm-ddthh:mm:ssz.endTimeStamp
- Forneça uma hora de término no formato aaaa-mm-ddthh:mm:ssz.
./MigrateToCSI.sh <namespace> <sourceStorageClass> <TargetCSIstorageClass> <VolumeSnapshotClass> <startTimestamp> <endTimestamp>
Atualize seu aplicativo para usar o novo PVC.
Exclua manualmente os recursos mais antigos, incluindo PVC/PV na árvore, VolumeSnapshot e VolumeSnapshotContent. Caso contrário, manter o PVC/PC na árvore e os objetos do instantâneo gerará mais custos.
Migrar volumes de compartilhamento de arquivos
A migração de in-tree para CSI é suportada pela criação de um volume estático:
- Não é preciso limpar a configuração original com a classe de armazenamento na árvore.
- De baixo risco, uma vez que só está a executar uma eliminação lógica do PV/PVC do Kubernetes; os dados físicos reais não são eliminados.
- Nenhum custo extra incorrido como resultado de não ter que criar objetos adicionais do Azure, como compartilhamentos de arquivos, etc.
Migração
Atualize o PV
ReclaimPolicy
existente de Delete para Retain executando o seguinte comando:kubectl patch pv pvName -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
Substitua pvName pelo nome do PersistentVolume selecionado. Como alternativa, se você quiser atualizar a reclaimPolicy para vários PVs, crie um arquivo chamado patchReclaimPVs.sh e copie no código a seguir.
#!/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
Execute o script com o
namespace
parâmetro para especificar o namespace./PatchReclaimPolicy.sh <namespace>
do cluster .Crie uma nova classe de armazenamento com o provisionador definido como
file.csi.azure.com
, ou você pode usar uma das StorageClasses padrão com o provisionador de arquivos CSI.Obtenha o
secretName
eshareName
dos PersistentVolumes existentes executando o seguinte comando:kubectl describe pv pvName
Crie um novo PV usando o novo StorageClass e o
shareName
esecretName
a partir do PV na árvore. Crie um arquivo chamado azurefile-mount-pv.yaml e copie no código a seguir. Emcsi
, atualizarresourceGroup
,volumeHandle
eshareName
. Para opções de montagem, o valor padrão para fileMode e dirMode é 0777.O valor padrão para
fileMode
edirMode
é 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
Crie um arquivo chamado azurefile-mount-pvc.yaml com um PersistentVolumeClaim que usa o PersistentVolume usando o código a seguir.
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: azurefile spec: accessModes: - ReadWriteMany storageClassName: azurefile-csi volumeName: azurefile resources: requests: storage: 5Gi
Use o
kubectl
comando para criar o PersistentVolume.kubectl apply -f azurefile-mount-pv.yaml
Use o
kubectl
comando para criar o PersistentVolumeClaim.kubectl apply -f azurefile-mount-pvc.yaml
Verifique se seu PersistentVolumeClaim foi criado e vinculado ao PersistentVolume executando o seguinte comando.
kubectl get pvc azurefile
A saída é semelhante à seguinte:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE azurefile Bound azurefile 5Gi RWX azurefile 5s
Atualize as especificações do contêiner para fazer referência ao seu PersistentVolumeClaim e atualize seu pod. Por exemplo, copie o código a seguir e crie um arquivo chamado azure-files-pod.yaml.
... volumes: - name: azure persistentVolumeClaim: claimName: azurefile
A especificação do pod não pode ser atualizada no local. Use os seguintes
kubectl
comandos para excluir e recriar o pod.kubectl delete pod mypod
kubectl apply -f azure-files-pod.yaml
Próximos passos
- Para obter mais informações sobre práticas recomendadas de armazenamento, consulte Práticas recomendadas para armazenamento e backups no Serviço Kubernetes do Azure.
- Proteja seus PVs baseados em Driver CSI recém-migrados fazendo backup deles usando o Backup do Azure para AKS.
Azure Kubernetes Service