在 Azure Kubernetes 服务 (AKS) 中,使用 Azure Blob 存储来创建和管理持久卷(Persistent Volumes,PV)

如果多个 Pod 需要并发访问同一存储卷,则可以使用 Azure Blob 存储通过 blobfuse网络文件系统(NFS)进行连接。

本文介绍如何动态和静态创建 Azure Blob 存储容器,供 Azure Kubernetes 服务 (AKS) 群集中的多个 Pod 使用。

先决条件

  • 在你的 AKS 集群上启用了 Azure Blob 存储 CSI 驱动程序

  • 若要在使用 blobfuse 装载时支持 Azure DataLake Gen2 存储帐户,需要执行以下操作:

    • 若要在动态预配中使用驱动程序创建 ADLS 帐户,请在存储类参数中指定 isHnsEnabled: "true"
    • 若要在静态预配中启用对 ADLS 帐户的 blobfuse 访问,请在持久卷中指定装载选项 --use-adls=true
    • 如果要启用具有分层命名空间的存储帐户,应使用 --use-adls=true 装载选项重新装载现有永久性卷。
  • 默认情况下,blobfuse 缓存位于 /mnt 目录中。 如果 VM SKU 提供临时磁盘,则会将 /mnt 目录装载到临时磁盘上。 但是,如果 VM SKU 不提供临时磁盘,则会将 /mnt 目录装载到 OS 磁盘上,可以设置 --tmp-path= 装载选项来指定不同的缓存目录。

使用内置存储类,通过 Azure Blob 存储创建动态 PV

storage类用于定义如何创建Azure Blob storage容器。 存储帐户会自动在节点资源组中创建,以便与存储类一起使用来保存 Azure Blob 存储容器。 在 AKS 上使用存储 CSI 驱动程序时,有两个额外的内置 StorageClasses,它们使用 Azure Blob 存储的 CSI 驱动程序。

这两种存储类的回收策略可确保当删除相应的持久卷时删除底层的 Azure Blob 存储。 默认情况下,storage类还会将容器配置为可扩展,因为 set allowVolumeExpansion 参数设置为 true

注释

不支持收缩永久性卷。

可以为存储类定义中的参数选择以下 Azure 存储冗余 SKUskuname 之一:

  • Standard_LRS:标准本地冗余存储
  • Premium_LRS:高级本地冗余存储
  • Standard_ZRS:标准区域冗余存储
  • Premium_ZRS:高级区域冗余存储
  • Standard_GRS:标准异地冗余存储
  • Standard_RAGRS:标准只读访问地理冗余存储

使用 Azure Blob 存储为动态 PV 创建自定义存储类

默认存储类适用于大多数方案。 在某些情况下,你可能想要使用自己的参数自定义自己的存储类。 在本部分中,我们提供了两个示例:一个使用 NFS 协议 ,一个使用 blobfuse

使用 NFS 协议的自定义存储类示例

此示例中的清单使用 NFS 协议装载 Blob 存储容器。 可以使用它添加 tags 参数。

  1. 在以下示例清单中创建一个名为 blob-nfs-sc.yaml 并粘贴的文件:

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: azureblob-nfs-premium
    provisioner: blob.csi.azure.com
    parameters:
      protocol: nfs
      tags: environment=Development
    volumeBindingMode: Immediate
    allowVolumeExpansion: true
    mountOptions:
      - nconnect=4
    
  2. 使用 kubectl apply 以下命令创建存储类:

    kubectl apply -f blob-nfs-sc.yaml
    

    输出应类似于以下示例输出:

    storageclass.storage.k8s.io/blob-nfs-premium created
    

使用 blobfuse 的自定义存储类示例

此示例中的清单使用 blobfuse 并装载 Blob 存储容器。 可以使用它更新 skuName 参数。

  1. 在以下示例清单中创建一个名为 blobfuse-sc.yaml 并粘贴的文件:

    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: azureblob-fuse-premium
    provisioner: blob.csi.azure.com
    parameters:
      skuName: Standard_GRS  # available values: Standard_LRS, Premium_LRS, Standard_GRS, Standard_RAGRS
    reclaimPolicy: Delete
    volumeBindingMode: Immediate
    allowVolumeExpansion: true
    mountOptions:
      - -o allow_other
      - --file-cache-timeout-in-seconds=120
      - --use-attr-cache=true
      - --cancel-list-on-mount-seconds=10  # prevent billing charges on mounting
      - -o attr_timeout=120
      - -o entry_timeout=120
      - -o negative_timeout=120
      - --log-level=LOG_WARNING  # LOG_WARNING, LOG_INFO, LOG_DEBUG
      - --cache-size-mb=1000  # Default will be 80% of available memory, eviction will happen beyond that.
    
  2. 使用 kubectl apply 以下命令创建存储类:

    kubectl apply -f blobfuse-sc.yaml
    

    输出应类似于以下示例输出:

    storageclass.storage.k8s.io/blob-fuse-premium created
    

使用 Azure Blob 存储的动态 PV 的存储类参数

下表包括可用于为 Azure Blob 存储上的持久性卷声明(PVC)定义自定义存储类的参数:

名称 含义 可用值 必需 默认值
skuName 指定 Azure 存储帐户类型(别名: storageAccountType) 。 Standard_LRSPremium_LRSStandard_GRSStandard_RAGRS Standard_LRS
location 指定 Azure 位置。 chinanorth3 如果为空,驱动程序使用与当前群集相同的位置名称。
resourceGroup 指定Azure资源组名称。 myResourceGroup 如果为空,驱动程序将使用与当前群集相同的资源组名称。
storageAccount 指定Azure storage帐户名称。 storageAccountName 如果未提供特定的存储帐户名称,驱动程序将查找与同一资源组中的帐户设置匹配的合适存储帐户。 如果找不到匹配的存储帐户,则会创建一个新帐户。 但是,如果指定了存储帐户名,则存储帐户必须已存在。
networkEndpointType 为由驱动创建的存储帐户指定网络终结点类型。 如果指定 privateEndpoint,则会为存储帐户创建私有终结点。 对于其他情况,将为 NFS 协议创建服务终结点。 privateEndpoint 对于 AKS 群集,请将 AKS 群集名称添加到托管 VNet 的资源组的“参与者”角色中。
protocol 指定 blobfuse 装载或 NFSv3 装载。 fusenfs fuse
containerName 指定现有容器(目录)名称。 容器 如果为空,驱动程序将创建一个新的容器名称,从 pvc-fuse blobfuse 或 pvc-nfs NFS v3 开始。
containerNamePrefix 指定驱动程序创建的 Azure 存储目录前缀。 只能包含小写字母、数字、连字符和长度应少于 21 个字符。
server 指定 Azure 存储帐户域名。 现有存储帐户的 DNS 域名,例如 <storage-account>.blob.core.chinacloudapi.cn 如果为空,驱动程序使用默认 <storage-account>.blob.core.chinacloudapi.cn 或其他主权云存储帐户 DNS 域名。
allowBlobPublicAccess 允许或禁止对驱动程序创建的存储帐户中的所有 blob 或容器进行公共访问。 truefalse false
storageEndpointSuffix 指定 Azure 存储终结点后缀。 core.chinacloudapi.cn 如果为空,驱动程序会根据云环境使用默认的存储终结点后缀。
tags 标记 将在新的存储帐户中创建。 标记格式:“foo=aaa,bar=bbb” ""
matchTags 驱动程序在尝试查找合适的存储帐户时匹配标记。 truefalse false
--- 以下参数仅适用于 blobfuse --- --- ---
subscriptionID 指定要在其中创建 Blob 存储目录的 Azure 订阅 ID。 Azure 订阅 ID 如果不为空,则必须提供 resourceGroup
storeAccountKey 将存储帐户密钥指定为 Kubernetes 机密。

注意:
false 表示驱动程序使用 kubelet 标识获取帐户密钥。
truefalse true
secretName 指定用于存储帐户密钥的机密名称。
secretNamespace 指定用于存储帐户密钥的机密的命名空间。 defaultkube-system 等。 PVC 命名空间
isHnsEnabled 为 Azure Data Lake 存储帐户启用 Hierarchical namespace truefalse false
--- 以下参数仅适用于 NFS 协议 --- --- ---
mountPermissions 指定装载的文件夹权限。 默认值为 0777。 如果设置为 0,则驱动程序在装载后不会执行 chmod 0777

注释

如果存储帐户由驱动程序创建,则只需在存储类中指定 networkEndpointType: privateEndpoint 参数。 CSI 驱动程序将创建专用终结点和专用 DNS 区域(命名 privatelink.blob.core.chinacloudapi.cn)以及帐户。 如果自带存储帐户,则需要为存储帐户 创建专用终结点 。 如果在网络隔离群集中使用 Azure Blob 存储,则必须使用“networkEndpointType: privateEndpoint”创建自定义存储类。 可以使用以下示例清单作为参考:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: blob-fuse
provisioner: blob.csi.azure.com
parameters:
  skuName: Premium_LRS  # available values: Standard_LRS, Premium_LRS, Standard_GRS, Standard_RAGRS, Standard_ZRS, Premium_ZRS
  protocol: fuse2
  networkEndpointType: privateEndpoint
reclaimPolicy: Delete
volumeBindingMode: Immediate
allowVolumeExpansion: true
mountOptions:
  - -o allow_other
  - --file-cache-timeout-in-seconds=120
  - --use-attr-cache=true
  - --cancel-list-on-mount-seconds=10  # prevent billing charges on mounting
  - -o attr_timeout=120
  - -o entry_timeout=120
  - -o negative_timeout=120
  - --log-level=LOG_WARNING  # LOG_WARNING, LOG_INFO, LOG_DEBUG
  - --cache-size-mb=1000  # Default will be 80% of available memory, eviction will happen beyond that.

使用 Azure Blob 存储创建 PVC

PVC 使用存储类对象动态预配 Azure Blob 存储。 可以使用本部分中的示例 YAML 清单创建大小 为 5 GB 且具有 ReadWriteMany 访问权限的 PVC。 有关访问模式的详细信息,请参阅 Kubernetes PV 访问模式

  1. 创建一个名为blob-nfs-pvc.yaml的文件,并在其中粘贴以下 YAML 清单:

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: azure-blob-storage
    spec:
      accessModes:
      - ReadWriteMany
      storageClassName: azureblob-nfs-premium
      resources:
        requests:
          storage: 5Gi
    
  2. 使用 kubectl create 以下命令创建 PVC:

    kubectl create -f blob-nfs-pvc.yaml
    
  3. 使用 [kubectl get pvc][kubectl-get-pvc] 命令查看 PVC 的状态:

    kubectl get pvc azure-blob-storage
    

    输出应类似于以下示例输出,其中显示 PVC 处于 Bound 状态:

    NAME                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS                AGE
    azure-blob-storage   Bound    pvc-aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb   5Gi        RWX            azureblob-nfs-premium       92m
    

在 Pod 中使用 PVC 装载 Azure Blob 存储

以下 YAML 创建一个 Pod,该 Pod 使用永久性卷声明 azure-blob-storage 将 Azure Blob 存储装载到“/mnt/blob”路径。

  1. 创建一个名为blob-nfs-pv的文件,并将以下 YAML 清单粘贴进去。 确保 claimName 与上一步中创建的 PVC 匹配。

    kind: Pod
    apiVersion: v1
    metadata:
      name: mypod
    spec:
      containers:
      - name: mypod
        image: mcr.azk8s.cn/oss/nginx/nginx:1.17.3-alpine
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 250m
            memory: 256Mi
        volumeMounts:
        - mountPath: "/mnt/blob"
          name: volume
          readOnly: false
      volumes:
        - name: volume
          persistentVolumeClaim:
            claimName: azure-blob-storage
    
  2. 使用以下命令 kubectl apply 创建 Pod:

    kubectl apply -f blob-nfs-pv.yaml
    
  3. 成功运行 Pod 后,使用以下命令创建一个名为 test.txt 的新文件:

    kubectl exec mypod -- touch /mnt/blob/test.txt
    
  4. 使用以下命令验证磁盘是否已正确装载,以列出装载的目录中的文件:

    kubectl exec mypod -- ls /mnt/blob
    

    输出应类似于以下示例输出,它展示了您在已挂载的 Azure Blob 存储中创建的 test.txt 文件:

    test.txt
    

使用 StatefulSet 管理利用 Azure Blob 存储的卷的生命周期

若要使存储卷在工作负载中持久存在,可以使用 StatefulSet。 这样,将现有卷与替换任何失败的 Pod 相匹配就更容易了。 以下示例演示如何使用 NFS 协议或 Blobfuse 为 Blob 存储设置 StatefulSet。

注释

如果使用 NFS 协议,则需要将 AKS 群集 控制平面 标识(即 AKS 群集名称)添加到 VNet 和网络安全组上的 参与者 角色。

  1. 创建一个名为azure-blob-nfs-ss.yaml的文件,并在其中粘贴以下 YAML 清单:

    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: statefulset-blob-nfs
      labels:
        app: nginx
    spec:
      serviceName: statefulset-blob-nfs
      replicas: 1
      template:
        metadata:
          labels:
            app: nginx
        spec:
          nodeSelector:
            "kubernetes.io/os": linux
          containers:
            - name: statefulset-blob-nfs
              image: mcr.azk8s.cn/azurelinux/base/nginx:1.25
              volumeMounts:
                - name: persistent-storage
                  mountPath: /mnt/blob
      updateStrategy:
        type: RollingUpdate
      selector:
        matchLabels:
          app: nginx
      volumeClaimTemplates:
        - metadata:
            name: persistent-storage
          spec:
            storageClassName: azureblob-nfs-premium
            accessModes: ["ReadWriteMany"]
            resources:
              requests:
                storage: 100Gi
    
  2. 使用 kubectl create 以下命令创建 StatefulSet:

    kubectl create -f azure-blob-nfs-ss.yaml
    

使用 Azure Blob 存储创建静态 PV

以下部分提供有关使用 Azure Blob 存储创建静态 PV 的说明。 静态 PV 是管理员手动创建的持久卷。 此 PV 可供集群中的 Pods 使用。 若要使用静态 PV,请创建引用 PV 的 PVC,然后创建引用 PVC 的 Pod。

使用 Azure Blob 存储的静态 PV 的存储类参数

下表包含可用于使用 Azure Blob 存储针对静态 PVC 定义自定义存储类的参数:

名称 含义 可用值 必需 默认值
volumeHandle 指定一个值,以供驱动程序在群集中唯一识别存储 Blob 容器。 生成唯一值的建议方法是组合全局唯一存储帐户名称和容器名称: {account-name}_{container-name}
注意:字符#/仅保留供内部使用,不能用于卷句柄。
是的
volumeAttributes.resourceGroup 指定 Azure 资源组名称。 myResourceGroup 如果为空,驱动程序将使用与当前群集相同的资源组名称。
volumeAttributes.storageAccount 指定现有的Azure storage帐户名称。 storageAccountName 是的
volumeAttributes.containerName 指定现有的容器名称。 容器 是的
volumeAttributes.protocol 指定 blobfuse 装载或 NFS v3 装载。 fusenfs fuse
--- 以下参数仅适用于 blobfuse --- --- ---
volumeAttributes.secretName 用于存储帐户名称和密钥的机密名称(仅适用于 SMB)。
volumeAttributes.secretNamespace 指定用于存储帐户密钥的机密命名空间。 default PVC 命名空间
nodeStageSecretRef.name 指定存储以下内容之一的机密名称:
azurestorageaccountkey
azurestorageaccountsastoken
msisecret
azurestoragespnclientsecret
现有 Kubernetes 机密名称
nodeStageSecretRef.namespace 指定机密的命名空间。 Kubernetes 命名空间 是的
--- 以下参数仅适用于 NFS 协议 --- --- ---
volumeAttributes.mountPermissions 指定装载的文件夹权限。 0777
--- 以下参数仅适用于 NFS VNet 设置 --- --- ---
vnetResourceGroup 指定托管虚拟网络的 VNet 资源组。 myResourceGroup 如果为空,驱动程序将使用 Azure 云配置文件中指定的 vnetResourceGroup 值。
vnetName 指定该虚拟网络的名称。 aksVNet 如果为空,驱动程序将使用 Azure 云配置文件中指定的 vnetName 值。
subnetName 指定代理节点的现有子网名称。 aksSubnet 如果为空,驱动程序将更新群集虚拟网络下的所有子网。
--- 以下参数仅适用于 blobfuse 功能
托管标识和服务主体名称身份验证
--- --- ---
volumeAttributes.AzureStorageAuthType 指定身份验证类型。 KeySASMSISPN Key
volumeAttributes.AzureStorageIdentityClientID 指定标识客户端 ID。
volumeAttributes.AzureStorageIdentityResourceID 指定标识资源 ID。
volumeAttributes.MSIEndpoint 指定 MSI 终结点。
volumeAttributes.AzureStorageSPNClientID 指定 Azure 服务主体名称(SPN)客户端 ID。
volumeAttributes.AzureStorageSPNTenantID 指定 Azure SPN 租户 ID。
volumeAttributes.AzureStorageAADEndpoint 指定 Microsoft Entra 终结点。
--- 以下参数仅适用于功能:Blobfuse 从密钥保管库读取帐户密钥或 SAS 令牌 --- --- ---
volumeAttributes.keyVaultURL 指定Azure Key Vault DNS 名称。 {vault-name}.vault.azure.cn
volumeAttributes.keyVaultSecretName 指定 Azure Key Vault 机密名称。 现有 Azure Key Vault 密钥名称。
volumeAttributes.keyVaultSecretVersion Azure Key Vault 机密版本。 现有版本 如果为空,驱动程序使用当前版本。

创建 Blob 存储容器

在为 AKS 创建 Azure Blob Storage 资源时,可以在节点资源组中创建该资源。 此方法允许 AKS 群集访问和管理 Blob 存储资源。

  1. 使用命令 az aks show 通过参数 --query nodeResourceGroup 获取 AKS 群集的节点资源组的名称。

    az aks show --resource-group myResourceGroup --name myAKSCluster --query nodeResourceGroup -o tsv
    

    该命令的输出类似于以下示例:

    MC_myResourceGroup_myAKSCluster_chinanorth3
    
  2. 按照 “管理 Blob 存储 ”中的步骤创建用于存储 Blob 的容器,以授权访问,然后创建容器。

装载卷

在本部分中,你将使用 NFS 协议或 Blobfuse 来挂载持久卷。

使用 NFS v3 协议装载 Blob 存储不会使用帐户密钥进行身份验证。 AKS 群集需要与代理节点位于同一虚拟网络或对等虚拟网络中。 保护存储帐户中的数据的唯一方法是使用虚拟网络和其他网络安全设置。 有关如何设置对存储帐户的 NFS 访问权限的详细信息,请参阅 使用网络文件系统 (NFS) 3.0 协议装载 Blob 存储

以下示例演示如何使用 NFS 协议将 Blob 存储容器装载为永久性卷。

  1. 创建一个名为pv-blob-nfs.yaml的文件,并粘贴以下 YAML 内容。 在 storageClass 下,更新 resourceGroupstorageAccountcontainerName

    注释

    volumeHandle 值应是集群中每个相同的存储 Blob 容器的唯一的 volumeID。 字符 #/ 保留供内部使用,不能使用。

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      annotations:
        pv.kubernetes.io/provisioned-by: blob.csi.azure.com
      name: pv-blob
    spec:
      capacity:
        storage: 1Pi
      accessModes:
        - ReadWriteMany
      persistentVolumeReclaimPolicy: Retain  # If set as "Delete" container would be removed after pvc deletion
      storageClassName: azureblob-nfs-premium
      mountOptions:
        - nconnect=4
      csi:
        driver: blob.csi.azure.com
        # make sure volumeid is unique for every identical storage blob container in the cluster
        # character `#` and `/` are reserved for internal use and cannot be used in volumehandle
        volumeHandle: account-name_container-name
        volumeAttributes:
          resourceGroup: resourceGroupName
          storageAccount: storageAccountName
          containerName: containerName
          protocol: nfs
    

    注释

    虽然 Kubernetes API容量 属性是必需的,但 Azure Blob 存储 CSI 驱动程序不使用此值,因为可以灵活写入数据,直到达到存储帐户的容量限制。 特性的值 capacity 仅用于 PV 和 PVC 之间的大小匹配。 建议使用虚构的较高数值。 Pod 看到一个装载的卷,其虚构大小为 5 PB。

  2. 使用以下命令创建 PV:kubectl create

    kubectl create -f pv-blob-nfs.yaml
    
  3. 创建一个名为pvc-blob-nfs.yaml的文件,并粘贴以下 YAML 内容。 在 volumeName 下,将该值更新为匹配在上一步中创建的 PV 的名称。

    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: pvc-blob
    spec:
      accessModes:
        - ReadWriteMany
      resources:
        requests:
          storage: 10Gi
      volumeName: pv-blob
      storageClassName: azureblob-nfs-premium
    
  4. 使用 kubectl create 以下命令创建 PVC:

    kubectl create -f pvc-blob-nfs.yaml
    

使用永久性卷

以下 YAML 创建一个 Pod,它使用之前创建的名为 pvc-blob 的 PV 或 PVC 在路径 /mnt/blob 装载 Azure Blob 存储。

  1. 创建一个名为nginx-pod-blob.yaml的文件,并将以下 YAML 清单粘贴进去。 请确保在为 NFS 或 Blobfuse 创建 PV 时,claimName 匹配上一步中创建的 PVC。

    kind: Pod
    apiVersion: v1
    metadata:
      name: nginx-blob
    spec:
      nodeSelector:
        "kubernetes.io/os": linux
      containers:
        - image: mcr.azk8s.cn/oss/nginx/nginx:1.17.3-alpine
          name: nginx-blob
          volumeMounts:
            - name: blob01
              mountPath: "/mnt/blob"
              readOnly: false
      volumes:
        - name: blob01
          persistentVolumeClaim:
            claimName: pvc-blob
    
  2. 使用 kubectl create 命令创建一个 pod 并装载 PVC:

    kubectl create -f nginx-pod-blob.yaml
    
  3. 使用 kubectl exec 以下命令创建与 Pod 的 shell 会话,验证是否已正确装载 Blob 存储:

    kubectl exec -it nginx-blob -- df -h
    

    输出应类似于以下示例输出,其中显示了 Blob 存储装载在 /mnt/blob 路径上:

    Filesystem      Size  Used Avail Use% Mounted on
    ...
    blobfuse         14G   41M   13G   1% /mnt/blob
    ...