Migrar da classe de armazenamento na árvore para drivers CSI no Serviço de Kubernetes do Azure (AKS)
A implementação do driver da Interface de Armazenamento de Contêiner (CSI) foi introduzida no Serviço de 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 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 o Azure Disks e drivers do CSI dos Aquivos 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 eaz upgrade
para atualizar a versão. Se você precisa instalar ou atualizar, consulte Instalar a CLI do Azure. - Os administradores do Kubectl e do cluster têm acesso para criar, obter, listar, excluir o acesso a um PVC ou PV, instantâneo de volume ou conteúdo de instantâneo de volume. Para um cluster habilitado para o RBAC do Microsoft Entra, você é membro da função de Administração do Cluster RBAC do Serviço de Kubernetes do Azure.
Migrar volumes de disco
Observação
Os rótulos failure-domain.beta.kubernetes.io/zone
e failure-domain.beta.kubernetes.io/region
foram preteridos no AKS 1.24 e removidos na versão 1.28. Se os volumes persistentes existentes ainda estiverem usando nodeAffinity correspondente a esses dois rótulos, você precisará alterá-los para rótulos topology.kubernetes.io/zone
e topology.kubernetes.io/region
na nova configuração de volume persistente.
Há suporte para a migração da árvore para o CSI 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 você criará posteriormente e especificará o volumeName
para o PersistentVolumeClaim.
Os benefícios dessa abordagem são:
- É simples e pode ser automatizado.
- Não é necessário limpar a configuração original usando a classe de armazenamento na árvore.
- Baixo risco, pois você está executando apenas uma exclusão lógica de PV/PVC do Kubernetes, os dados físicos reais não são excluídos.
- Nenhum custo extra incorrido como resultado de não ter que criar objetos adicionais no Azure, como discos, instantâneos, etc.
Veja a seguir considerações importantes para avaliar:
- A transição para volumes estáticos de volumes de estilo dinâmico originais requer a construção e o gerenciamento manual de objetos PV para todas as opções.
- Tempo de inatividade potencial do aplicativo ao reimplantar o novo aplicativo com referência ao novo objeto PVC.
Migração
Atualize o PV
ReclaimPolicy
existente de Excluir para Reter 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árias 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 do cluster./PatchReclaimPolicy.sh <namespace>
.Obtenha uma lista de todos os PVCs no namespace classificado por creationTimestamp executando o comando a seguir. Defina o namespace usando o argumento
--namespace
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
Essa etapa será útil se você tiver um grande número de PVs que precisam ser migradas e quiser migrar alguns de cada vez. Executar esse 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 hora de início e 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 tem a seguinte função:
- Cria um novo PersistentVolume com nome
existing-pv-csi
para todos os PersistentVolumes em namespaces para a classe de armazenamentostorageClassName
. - Configure o novo nome do PVC como
existing-pvc-csi
. - Cria um novo PVC com o nome de PV especificado.
#!/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 na á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 seja definida como um desses dois provisionadores.startTimeStamp
- Forneça um horário de início antes do horário 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 declaração de volume persistente.
Os benefícios dessa abordagem são:
É menos arriscado porque todos os novos objetos são criados enquanto retêm outras cópias com instantâneos.
Não é necessário construir PVs separadamente e adicionar o nome do volume no manifesto do PVC.
Veja a seguir considerações importantes para avaliar:
Embora essa abordagem seja menos arriscada, ela cria vários objetos que aumentarão os custos de armazenamento.
Durante a criação dos novos volumes, seu aplicativo não está disponível.
As etapas de exclusão devem ser executadas com cuidado. Bloqueios de recursos temporários podem ser aplicados ao grupo de recursos até que a migração seja concluída e seu aplicativo seja verificado com êxito.
Execute a validação/verificação de 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 em disco, o aplicativo deve ser interrompido e permitir que os dados na memória sejam liberados para o disco.
A classe
VolumeSnapshot
deve existir conforme mostrado no seguinte exemplo 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 no namespace classificado por creationTimestamp executando o comando a seguir. Defina o namespace usando o argumento
--namespace
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
Essa etapa será útil se você tiver um grande número de PVs que precisam ser migradas e quiser migrar alguns de cada vez. Executar esse 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 hora de início e 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 tem a seguinte função:
- 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 na árvoretargetCSIStorageClass
: o StorageClass baseado em driver de armazenamento na árvorevolumeSnapshotClass
: 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 os objetos PVC/PC e instantâneo na árvore gerará mais custo.
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 é necessário limpar a configuração original usando a classe de armazenamento na árvore.
- Baixo risco, pois você está executando apenas uma exclusão lógica de PV/PVC do Kubernetes, os dados físicos reais não são excluídos.
- Nenhum custo extra incorrido como resultado de não ter que criar objetos adicionais no Azure, como compartilhamentos de arquivos, etc.
Migração
Atualize o PV
ReclaimPolicy
existente de Excluir para Reter 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árias 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 do cluster./PatchReclaimPolicy.sh <namespace>
.Crie uma nova Classe de Armazenamento com o provisionador definido como
file.csi.azure.com
ou você pode usar um dos StorageClasses padrão com o provisionador de arquivos CSI.Obtenha o
secretName
eshareName
do PersistentVolumes existente executando o seguinte comando:kubectl describe pv pvName
Crie um novo PV usando o novo StorageClass e o
shareName
esecretName
do PV na árvore. Crie um arquivo chamado service.yaml e copie no YAML a seguir. Emcsi
, atualizarresourceGroup
,volumeHandle
eshareName
. O valor padrão de 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 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 comando
kubectl
para criar o PersistentVolume.kubectl apply -f azurefile-mount-pv.yaml
Use o comando
kubectl
para criar o PersistentVolumeClaim.kubectl apply -f azurefile-mount-pvc.yaml
Para verificar se o PersistentVolumeClaim foi criado e associado ao PersistentVolume, execute o seguinte comando:
kubectl get pvc azurefile
A saída é semelhante ao seguinte:
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE azurefile Bound azurefile 5Gi RWX azurefile 5s
Atualize a especificação do contêiner para referenciar o 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 comandos
kubectl
a seguir para excluir e, em seguida, recriar o pod.kubectl delete pod mypod
kubectl apply -f azure-files-pod.yaml
Próximas etapas
- Para obter mais informações sobre as melhores práticas de armazenamento, confira Melhores práticas para armazenamento e backups no Serviço de Kubernetes do Azure.
- Proteja seus PVs baseados em driver CSI recém-migrados fazendo o backup deles por meio do Backup do Azure para AKS.
Azure Kubernetes Service