在 Azure Kubernetes 服务 (AKS) 中通过 Azure Blob 存储创建和使用卷

基于容器的应用程序通常需要访问数据并将数据保存在外部数据卷中。 如果多个 Pod 需要同时访问同一存储卷,则可以通过 blobfuse网络文件系统 (NFS) 使用 Azure Blob 存储进行连接。

本文介绍如何:

  • 通过安装容器存储接口 (CSI) 驱动程序并动态创建要附加到 Pod 的 Azure Blob 存储容器来使用动态永久性卷 (PV)。
  • 通过创建 Azure Blob 存储容器来使用静态 PV,或者使用现有容器并将其附加到 Pod。

有关 Kubernetes 卷的详细信息,请参阅 AKS 中应用程序的存储选项

开始之前

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

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

    • 若要在动态预配中使用驱动程序创建 ADLS 帐户,需要在存储类参数中指定 isHnsEnabled: "true"
    • 若要在静态预配中启用对 ADLS 帐户的 blobfuse 访问,请在永久性卷中指定装载选项 --use-adls=true
    • 如果要启用具有分层命名空间的存储帐户,应使用 --use-adls=true 装载选项重新装载现有永久性卷。
  • 关于 blobfuse 缓存

    • 默认情况下,blobfuse 缓存位于 /mnt 目录中。 如果 VM SKU 提供了临时磁盘,则 /mnt 目录将装载在临时磁盘上。 但是,如果 VM SKU 不提供临时磁盘,则 /mnt 目录装载在 OS 磁盘上,可以设置 --tmp-path= 装载选项以指定不同的缓存目录

动态预配卷

本部分为想要预配一个或多个永久性卷的群集管理员提供指导,这些卷中包含供工作负载使用的 Blob 存储的详细信息。 永久性卷声明 (PVC) 使用存储类对象来动态预配 Azure Blob 存储容器。

动态永久性卷的存储类参数

下表包含可用于为永久性卷声明定义自定义存储类的参数。

名称 步骤 示例 必需 默认值
skuName 指定 Azure 存储帐户类型(别名:storageAccountType)。 Standard_LRSPremium_LRSStandard_GRSStandard_RAGRS Standard_LRS
location 指定 Azure 位置。 chinaeast2 如果为空,驱动程序将使用与当前群集相同的位置名称。
resourceGroup 指定 Azure 资源组名称。 myResourceGroup 如果为空,驱动程序将使用与当前群集相同的资源组名称。
storageAccount 指定 Azure 存储帐户名称。 storageAccountName 否- 如果未提供特定的存储帐户名称,驱动程序将查找与同一资源组中的帐户设置匹配的合适存储帐户。 如果驱动程序找不到匹配的存储帐户,则将创建新的存储帐户。 但是,如果指定了存储帐户名称,则存储帐户必须已经存在。
networkEndpointType 为驱动程序创建的存储帐户指定网络终结点类型。 如果指定 privateEndpoint,则会为存储帐户创建专用终结点。 对于其他情况,将为 NFS 协议创建服务终结点。1 privateEndpoint 对于 AKS 群集,请将 AKS 群集名称添加到托管 VNET 的资源组中的“参与者”角色。
protocol 指定 blobfuse 装载或 NFSv3 装载。 fusenfs fuse
containerName 指定现有容器(目录)名称。 container 如果为空,驱动程序会创建一个新的容器名称,对于 blobfuse 以 pvc-fuse 开头,对于 NFS v3 以 pvc-nfs 开头。
containerNamePrefix 指定驱动程序创建的 Azure 存储目录前缀。 my 只能包含小写字母、数字、连字符,且字符长度不能超过 21 个字符。
server 指定 Azure 存储帐户域名。 现有存储帐户 DNS 域名,例如 <storage-account>.privatelink.blob.core.chinacloudapi.cn 如果为空,驱动程序将使用默认 <storage-account>.blob.core.chinacloudapi.cn 或其他主权云存储帐户 DNS 域名。
allowBlobPublicAccess 允许或禁止公共访问驱动程序创建的存储帐户的所有 Blob 或容器。 truefalse false
storageEndpointSuffix 指定 Azure 存储终结点后缀。 core.chinacloudapi.cn 如果为空,驱动程序将根据云环境使用默认存储终结点后缀。
标记 将在新的存储帐户中创建标记 标记格式:“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

1 如果存储帐户由驱动程序创建,则只需在存储类中指定 networkEndpointType: privateEndpoint 参数。 CSI 驱动程序与帐户一起创建专用终结点。 如果自带存储帐户,则需要为该存储帐户创建专用终结点

使用内置存储类创建永久性卷声明

永久性卷声明 (PVC) 使用存储类对象来动态预配 Azure Blob 存储容器。 可使用以下 YAML,通过内置存储类创建具有 ReadWriteMany 访问权限的永久性卷,其大小为 5 GB。 有关访问模式的详细信息,请参阅 Kubernetes 永久性卷文档。

  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 命令创建永久性卷声明:

    kubectl create -f blob-nfs-pvc.yaml
    

完成后,将创建 Blob 存储容器。 可以使用 kubectl get 命令查看 PVC 的状态:

kubectl get pvc azure-blob-storage

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

NAME                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS                AGE
azure-blob-storage   Bound    pvc-b88e36c5-c518-4d38-a5ee-337a7dda0a68   5Gi        RWX            azureblob-nfs-premium       92m

使用永久性卷声明

以下 YAML 创建的 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. 若要验证磁盘是否已正确装载,请运行以下命令并验证输出是否出现 test.txt 文件:

    kubectl exec mypod -- ls /mnt/blob
    

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

    test.txt
    

创建自定义存储类

默认存储类适合最常见的方案,但并非适合所有方案。 在某些情况下,你可能想要使用自己的参数来自定义自己的存储类。 在本部分中,我们提供了两个示例。 第一个示例使用 NFS 协议,第二个使用 blobfuse。

使用 NFS 协议的存储类

在此示例中,以下清单使用 NFS 协议配置装载 Blob 存储容器。 使用它添加 tag 参数。

  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
    

静态预配卷

本部分为想要创建一个或多个永久性卷的群集管理员提供指导,这些卷中包含供工作负载使用的 Blob 存储的详细信息。

永久性卷的静态预配参数

下表包含可用于定义永久性卷的参数。

名称 步骤 示例 必需 默认值
volumeHandle 指定一个值,该值可供驱动程序用来唯一标识群集中的存储 blob 容器。 生成唯一值的推荐方法是组合全局唯一存储帐户名称和容器名称:{account-name}_{container-name}
注意:#/ 字符保留供内部使用,不能在卷句柄中使用。
volumeAttributes.resourceGroup 指定 Azure 资源组名称。 myResourceGroup 如果为空,驱动程序将使用与当前群集相同的资源组名称。
volumeAttributes.storageAccount 指定现有的 Azure 存储帐户名称。 storageAccountName
volumeAttributes.containerName 指定现有的容器名称。 container
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 如果为空,驱动程序将使用 Azure 云配置文件中的 subnetName 值。
--- 以下参数仅适用于功能: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 存储资源时,可以在节点资源组中创建该资源。 此方法允许 AKS 群集访问和管理 Blob 存储资源。

对于本文,请在节点资源组中创建容器。 首先,使用 az aks show 命令获取资源组名称并添加 --query nodeResourceGroup 查询参数。 以下示例在名为 myResourceGroup 的资源组中获取名为 myAKSCluster 的 AKS 群集的节点资源组:

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

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

MC_myResourceGroup_myAKSCluster_chinaeast2

接下来,按照管理 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 容器的唯一卷 ID。 字符 #/ 保留供内部使用,不能使用它。

    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 特性的值仅用于在 PersistentVolumes 与 PersistentVolumeClaims 之间进行大小匹配。 建议使用虚构的较大值。 Pod 将看到虚构大小为 5 PB 的已装载卷。

  2. 运行以下命令以使用 kubectl create 命令创建永久性卷,该命令引用之前创建的 YAML 文件:

    kubectl create -f pv-blob-nfs.yaml
    
  3. 创建包含 PersistentVolumeClaim 的 pvc-blob-nfs.yaml 文件。 例如:

    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 命令创建永久性卷声明,该命令引用之前创建的 YAML 文件:

    kubectl create -f pvc-blob-nfs.yaml
    

使用永久性卷

以下 YAML 创建一个 Pod,该 Pod 使用前面创建的名为 pvc-blob 的永久性卷或永久性卷声明,在 /mnt/blob 路径中装载 Azure Blob 存储。

  1. 创建名为 nginx-pod-blob.yaml 的文件,并将其复制到以下 YAML 中。 确保在为 NFS 或 Blobfuse 创建永久性卷时,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,该命令引用之前创建的 YAML 文件:

    kubectl create -f nginx-pod-blob.yaml
    
  3. 运行以下命令来创建与 Pod 的交互式 shell 会话,从而确认 Blob 存储已装载:

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

    命令的输出如下例所示:

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

后续步骤