为Azure Functions选择文件访问策略

本文比较了从Azure Functions访问文件的两种方式:存储绑定和Azure 文件存储存储挂载。 你将了解每种方法之间的取舍,了解挂载何时是正确的选择,并识别真实场景中的模式。

存储绑定适用于所有托管计划。 存储挂载仅限Linux,且不被 Consumption Plan支持。

小窍门

如果你需要结构化数据存储而不是文件访问,Azure Functions 提供了 Azure SQLAzure Cosmos DB 的绑定扩展,或者你也可以使用任何带有 SDK 客户端连接的数据库。

注释

本文的代码示例在 Azure Functions Flex Consumption with Azure 文件存储 OS Mount Samples GitHub 存储库中提供。

文件访问选项一目了然

需要从函数访问文件时,有三个主要选项:

方法 Pros 缺点 最适用于 了解详细信息
存储绑定 简单、云原生、安全 网络开销,最终一致性 将数据移入/移出云服务(队列、Blob) Blob队列 绑定
Storage mount (Azure 文件存储) 直接文件访问、POSIX 语义、大型二进制文件 比本地磁盘慢,仅限于 Linux 大型文件、共享可执行文件、频繁访问 什么是存储挂载?

并非每个托管计划都提供每个选项:

托管计划 存储绑定 存储装载(Azure 文件存储)
弹性高级版
专用(应用服务)
consumption (仅限Windows)

本文的其余部分重点介绍装载:当它们是正确的选择时,以及如何安全地使用它们。

什么是存储挂载?

存储装载是将就像是本地目录一样装载的网络文件共享。 在函数应用中装载Azure 文件存储共享时,路径将显示在函数容器的文件系统中:

┌─────────────────────────────────────┐
│  Your function code                 │
│  (reads/writes to /mnt/mydata/)     │
├─────────────────────────────────────┤
│  POSIX file-system layer            │
│  (appears as a local directory)     │
├─────────────────────────────────────┤
│  SMB protocol (over network)        │
├─────────────────────────────────────┤
│  Azure Files share                  │
│  (in your storage account)          │
└─────────────────────────────────────┘

代码使用标准文件系统 API(例如,open()os.listdir() Python 或其他语言的等效调用),而无需知道它通过网络进行通信。 此设置提供 POSIX 语义,这意味着代码类似于本地文件 I/O。

何时不使用挂载

对于每种情况,安装都不是合适的选择。 请考虑以下替代方法:

情景 建议的替代方法
少量暂时性数据 Azure 队列存储
Azure Blob 存储
频繁的少量读取/写入 Azure Cosmos DBAzure Cache for Redis
实时流式处理 Azure 事件中心Azure IoT 中心
跨区域数据共享 Blob 存储 数据复制

重要

存储装载仅适用于 Linux,在消耗计划中不受支持。

比较存储选项

在处理存储在参考文件夹中的1000张图片(每张1MB)时,请考虑以下选项:

方法 机制 网络调用 相对成本 最适用于
Blob 存储绑定 下载每个文件 1,000 个 GET 请求 高带宽 + 延迟 一次性或不经常访问
存储装载 从共享读取 单次装载设置 最小带宽 重复访问或大容量访问
外部数据库 Azure Cosmos DB 一个查询 RU 费用 + 网络延迟 包含复杂查询的结构化数据

注释

以下代码示例使用Python,但相同的模式适用于支持文件系统 API 的任何语言,包括 C#、Java、JavaScript 和 PowerShell。

files = container_client.list_blobs(name_starts_with="reference/")
for blob in files:
    stream = container_client.download_blob(blob.name)

对于具有重复访问权限的大型共享文件,请使用共享装载。 让我们调查使用共享装载的更详细方案。

共享装载方案

这些示例方案还受益于使用装载的存储共享:

情景 已解决的问题 示例
并行文件分析 避免打包大型引用数据,也不要每次调用时下载它。 ML 模型、查找表、跨 1,000 多个实例共享的料库数据
共享可执行文件 将大型二进制文件排除在部署包之外 ffmpeg、ImageMagick 或其他 500+ MB 工具
跨应用数据共享 在不传递消息的情况下在生成者和使用者应用之间共享文件 应用 A 写入结果,应用 B 从同一挂载读取结果

选择每个选项卡以查看有关特定方案的详细信息:

用例: 你有 1,000 个分析任务,所有这些任务都需要从同一组引用数据文件(例如 ML 模型、查阅表或文库数据)读取。

问题: 如果没有装载,则有两个不理想的选项:

  • 使用函数打包引用文件:此方法会导致巨大的部署项目、缓慢的冷启动和存储冗余。
  • 每次从 Blob 存储下载:这种方法在每次函数调用时都会引入网络延迟,并浪费带宽。

基于装载的解决方案:所有实例直接从装载的共享目录读取。 装载初始化完成后,不会产生每次请求的网络开销,也不存在冗余存储。

┌─────────────────────────┐
│  Function Instance 1    │
│  Function Instance 2    ├──→  /mnt/models/  ──→  Azure Files share
│  Function Instance 3    │     (shared mount)
└─────────────────────────┘

实现模式:(Python)

import os
from pathlib import Path

MOUNT_PATH = "/mnt/models"

def analyze_data(item: str) -> dict:
    """Activity function: reads from shared mount."""
    model_path = Path(MOUNT_PATH) / "model.pkl"
    
    # Direct file I/O — no SDK call, no network overhead
    with open(model_path, "rb") as f:
        model = pickle.load(f)
    
    result = model.predict(item)
    return {"item": item, "score": result}

要点

  • 函数应用的所有实例都会看到相同的装载。
  • 文件读取符合 POSIX。 使用标准文件系统 API。
  • 无需对每次读取进行身份验证(装载在启动时进行身份验证一次)。
  • 由一个实例编写的更改立即对其他人可见。

安全注意事项:

  • 只读选项:如果工作负荷不需要写入,请将装载限制为只读。
  • Quotas:设置 Azure 文件存储 共享配额,以防止实例写入大型文件导致的意外高额成本。

装载限制

这些Azure 文件存储存储限制适用于支持装载的所有托管计划:

Limit 价值
共享大小 最多 100 TiB
文件大小 最多 4 TiB
吞吐量 约 60 MB/秒(标准),约 100+ MB/秒(高级)
并发 许多情况下(由 SMB 处理),但写入过程需按顺序进行

有关详细信息,请参阅 Azure 文件存储 缩放目标

这些限制因支持的托管计划而异:

Limit 弹性高级版 专用(应用服务)
每个应用的装载点 5 5
协议 SMB、NFS、Azure Blob(只读) SMB、NFS、Azure Blob(只读)

若要防止存储成本失控,请在Azure 文件存储共享上设置配额:

az storage share-rm update \
  --resource-group $RESOURCE_GROUP \
  --storage-account $STORAGE_ACCOUNT \
  --name myshare \
  --quota 100  # 100 GB limit

装载身份验证

Azure 文件存储存储挂载和Azure SDK使用了不同的身份验证机制。

  • 存储装载 (SMB):在装载时使用存储帐户访问密钥进行身份验证。 密钥存储在函数应用的站点配置中(azureStorageAccounts)。 Azure Functions 上的 SMB 装载目前不支持托管标识。
  • Azure SDK (REST API):若要使用 Azure 存储 SDK 进行编程访问,请尽可能使用托管标识。

本Bicep示例使用存储帐户共享密钥配置存储装载:

resource mountConfig 'Microsoft.Web/sites/config@2023-12-01' = {
  parent: functionApp
  name: 'azurestorageaccounts'
  properties: {
    dataMount: {
      type: 'AzureFiles'
      shareName: shareName
      mountPath: '/mounts/data'
      accountName: storageAccountName
      accessKey: storageAccount.listKeys().keys[0].value
    }
  }
}

重要

定期轮换存储帐户密钥。 轮换密钥时,更新引用帐户的每个函数应用上的装载配置。

最佳做法

  • 尽可能使用只读挂载。 如果您的函数仅从挂载点读取,请将其配置为只读,以防止意外写入。

  • 监视文件访问。 在存储帐户上启用诊断以跟踪装载访问模式:

    az monitor metrics list \
      --resource /subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/$STORAGE_ACCOUNT/fileServices/default \
      --metric Transactions
    
  • 清理临时文件。 如果函数写入装载,请执行清理以避免无限制增长:

    from pathlib import Path
    import time
    
    MOUNT_PATH = "/mnt/temp"
    MAX_AGE = 24 * 60 * 60  # 24 hours
    
    def cleanup_old_files():
        cutoff = time.time() - MAX_AGE
        for f in Path(MOUNT_PATH).iterdir():
            if f.stat().st_mtime < cutoff:
                f.unlink()
    

排查存储装载问题

下表列出了在函数应用中Azure 文件存储存储装载的常见问题:

問题 解决方案
在装载路径中未找到二进制文件或文件 验证文件是否位于正确的Azure 文件存储共享中。 检查在函数应用上配置的装载路径是否与代码引用的路径匹配。 在 Azure 门户中,检查 Settings>Configuration>Path Mappings
访问装载的文件时权限被拒绝 存储挂载通过使用存储帐户访问密钥进行身份验证。 验证装载配置中的密钥是否正确且未轮换。 轮换密钥时,更新引用帐户的每个函数应用上的装载配置。
二进制缺少执行权限 Azure 文件存储保留上传时设置的 POSIX 权限。 在本地运行 chmod +x 后重新上传二进制文件,或在上传后设置权限。
挂载会导致冷启动延迟增加 首次执行时,SMB 装载初始化添加大约 200-500 毫秒。 后续调用将重复利用挂载。