Label Studio 接入 Microsoft Azure Blob Storage:源存储、目标存储与 Service Principal 完整配置指南
【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio
本指南围绕 Label Studio 开源仓库中的 Azure Blob 存储集成(storage_azure.md)展开,讲解如何将 Azure Blob Storage 容器同时配置为 Label Studio 的源存储(Source Storage,用于导入标注任务)与目标存储(Target Storage,用于导出标注结果),并覆盖 CORS 配置、存储账户密钥认证、本地部署环境变量以及 Enterprise 版的 Service Principal 认证方式。读完本文,你将能够从零完成 Azure Blob 与 Label Studio 的对接、校验连接、配置同步策略,并通过源码理解其底层数据流转机制。
前置概念:Label Studio 云存储的工作方式
在动手配置之前,先明确两个基础概念。Label Studio 中的云存储分为两类(详见 io_storages/README.md):
- 导入存储(Source / Import Storage):从云存储容器中把数据文件或任务定义同步进项目,对应源码中的
ImportStorage抽象基类; - 导出存储(Target / Export Storage):把标注结果写回云存储容器,对应
ExportStorage抽象基类。
Azure Blob 在源码中由label_studio/io_storages/azure_blob/目录实现,其中 models.py 定义了AzureBlobImportStorage(导入)与AzureBlobExportStorage(导出)两个模型,URL scheme 为azure-blob。
预签名 URL 与平台代理:两种数据加载方式
原文档特别强调了一个关键选择:Use pre-signed URLs(预签名 URL)与Proxy through the platform(平台代理)两种模式,这决定了标注媒体文件如何从 Azure 流向标注者的浏览器:
- 预签名 URL 模式:Label Studio 后端为容器对象生成带 SAS token 的临时 HTTPS 链接,浏览器直接向
https://<account>.blob.core.windows.net/<container>/<blob>?<sas>发起请求(HTTP 303 重定向)。媒体流量不经过 Label Studio 服务器,速度更快、扩展性更好,但要求容器正确配置 CORS,且存储账户需要具备预签名(SAS 生成)权限。 - 平台代理模式:后端从 Azure 下载文件后再以流式方式转发给浏览器,所有媒体流量都经过 Label Studio 服务器。数据始终停留在 Label Studio / 网络边界内,每次请求都会执行任务级访问权限检查,也无需配置 CORS,但会消耗更多 worker 资源,速度略慢。
从源码看,两种模式分别对应 models.py 中的generate_http_url()(生成 SAS 预签名 URL)与get_bytes_stream()(AZURE.download_stream_response()流式下载)。UI 中的开关映射到模型的presign字段(默认True),预签名 URL 的有效期由presign_ttl字段控制(默认 1 分钟,UI 默认值 15 分钟,最小 1 分钟,见 form_layout.yml)。
第一步:为 Azure Blob 配置 CORS
如果你计划使用平台代理模式,可以跳过本节。只要使用预签名 URL,就必须配置 CORS。
- 登录 Azure 门户,进入存储账户(Storage account)页面;
- 从左侧菜单滚动到设置 > 资源共享(CORS)(Settings > Resource sharing (CORS));
- 在Blob service下添加如下规则:
| 配置项 | 取值 |
|---|---|
| Allowed origins | https://app.humansignal.com(或你实际使用的 Label Studio 域名) |
| Allowed methods | GET, HEAD, OPTIONS |
| Allowed headers | * |
| Exposed headers | * |
| Max age | 3600 |
- 点击保存。
配置完成后,浏览器才能通过预签名 URL 跨域读取容器中的媒体对象。若使用 Service Principal 认证且同样启用预签名 URL,也必须在存储账户的 Blob service 上完成上述 CORS 配置。
第二步:使用存储账户密钥连接 Azure Blob Storage
这是 Label Studio 开源版(OSS)的标准接入方式。开始前,请先在 Azure 门户的存储账户资源页面收集以下三项信息:
- 容器名称(Container Name):位于存储账户资源页数据存储 > 容器(Data storage > Containers)下;
- 存储账户名称(Account Name):存储账户资源页顶部;
- 访问密钥(Access Key):位于安全 + 网络 > 访问密钥(Security + networking > Access keys)下。
创建源存储连接(导入数据)
在 Label Studio 中打开项目,进入Settings > Cloud Storage > Add Source Storage,选择Azure Blob Storage并点击Next。
配置连接(Configure Connection)
填写以下字段后点击Test connection验证连通性:
| 字段 | 说明 |
|---|---|
| Storage Title | 为该存储连接起一个用于标识的名称。 |
| Container Name | 输入 Azure 存储容器名称,即上文在 Azure 控制台数据存储 > 容器下找到的名称。 |
| Account Name | 输入 Azure 存储账户名称。 |
| Account Key | 输入存储账户的访问密钥,位于安全 + 网络 > 访问密钥下。 |
| Use pre-signed URLs (On) / Proxy through the platform (Off) | 决定容器数据如何加载:预签名 URL 模式生成指向 Azure 对象的临时 HTTPS 链接(浏览器直连,需正确 CORS 与预签名权限);平台代理模式由后端下载并流式转发(数据不离开 Label Studio 边界,每次请求都做任务级访问检查,但更耗 worker 资源)。 |
| Expire pre-signed URLs (minutes) | 控制预签名 URL 的剩余有效分钟数。 |
导入设置与预览(Import Settings & Preview)
点击Load preview确认正在同步的数据符合预期:
| 字段 | 说明 |
|---|---|
| Bucket Prefix | 可选。填写容器内要使用的目录名,例如data-set-1或data-set-1/subfolder-2。 |
| Import Method | 选择“为容器中每个文件创建一个任务”,或使用 JSON/JSONL/Parquet 文件来定义每个任务的数据。 |
| File Name Filter | 填写正则表达式过滤容器对象,使用.*收集所有对象。 |
| Scan all sub-folders | 开启后对容器内子文件夹执行递归扫描。 |
复核与确认(Review & Confirm)
确认无误后点击Save & Sync立即同步,或点击Save保存设置、稍后再同步。
创建目标存储连接(导出标注)
在 Label Studio 中打开项目,进入Settings > Cloud Storage > Add Target Storage,选择Azure Blob Storage并点击Next,填写以下字段:
| 字段 | 说明 |
|---|---|
| Storage Title | 输入用于标识该存储连接的名称。 |
| Container Name | 输入 Azure 存储容器名称。 |
| Container Prefix | 可选。填写容器内要使用的目录名,例如data-set-1或data-set-1/subfolder-2。 |
| Account Name | 输入 Azure 存储账户名称。 |
| Account Key | 输入存储账户访问密钥。 |
| Can delete objects from storage | 开启后,当标注在 Label Studio 中被删除时,容器中对应的标注文件也会被删除。 |
添加完成后点击Sync推送导出数据。
本地部署:使用环境变量代替 UI 输入
如果你运行的是本地/自托管(on-prem)部署,可以不把密钥手动填进 UI,而是通过环境变量注入:
export AZURE_BLOB_ACCOUNT_NAME="<your-storage-account-name>" export AZURE_BLOB_ACCOUNT_KEY="<your-storage-account-key>"源码中AzureBlobStorageMixin.get_account_name()与get_account_key()(models.py)的逻辑是:优先使用模型字段account_name/account_key,字段为空时回退到环境变量。若两者都未提供,连接校验会抛出明确错误提示。底层连接使用 Azure Python SDK 的BlobServiceClient.from_connection_string(),构造的字符串格式为DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net(见 utils.py)。
同时要注意:AzureBlobImportStorageSerializer将account_name、account_key标记为secure_fields(serializers.py),序列化返回时会主动移除这两个字段,避免密钥通过 API 响应泄露。
第三步(Enterprise):使用 Service Principal 认证连接 Azure Blob
Label Studio Enterprise 支持通过 Azure Service Principal 认证连接 Azure Blob Storage,无需使用存储账户访问密钥。该方式基于 Entra ID(原 Azure Active Directory)身份与访问管理,可授予细粒度权限并支持审计,且权限可随时撤销或轮换——相比拥有存储账户完全访问权的账户密钥,安全性更高。
注:本节为 Enterprise 专属能力,开源版(OSS)不支持。
前置条件
- 一个 Azure 订阅和一个存储账户;
- 有权限创建应用注册(App Registration)并在存储账户上分配角色;
- 一个私有的数据容器(如无则先创建)。
在 Entra 中创建 Service Principal
1. 在 Entra 注册应用
- 打开 Microsoft Entra 管理中心;
- 右侧选择应用注册,点击新注册。填写名称、选择适当的账户类型,重定向 URI 可留空;
- 在“概述”页复制应用程序(客户端)ID和目录(租户)ID;
- 进入证书和机密,添加一个新的客户端机密,并复制值(Value)字段。
2. 在 Azure 中授予主体存储访问权限
返回 Azure 门户并进入你的存储账户:
- 从存储账户左侧选择访问控制 (IAM);
- 选择添加 > 添加角色分配;
- 使用搜索框定位Storage Blob Data Contributor角色并点击选中;
- 选择上方成员标签页;
- 选择用户、组或服务主体,然后点击选择成员;
- 在搜索框中找到之前创建的应用名称并点击选择;
- 点击审阅 + 分配。
3. 创建容器
- 仍在存储账户页面,点击左侧数据存储;
- 选择容器;
- 如没有容器,创建一个私有访问级别的新容器。
警告:如果计划使用预签名 URL,必须在存储账户 Blob service 上配置 CORS,详见上文“为 Azure Blob 配置 CORS”一节。
4. 所需权限清单
- 源存储(Source Storage)需要:
Microsoft.Storage/storageAccounts/blobServices/containers/read.../containers/blobs/read
- 目标存储(Target Storage)需要:
.../containers/blobs/read.../containers/blobs/write.../containers/read.../containers/blobs/delete(可选)
以上权限均包含在内置的Storage Blob Data Contributor角色中。
创建源存储连接(Service Principal)
进入Settings > Cloud Storage > Add Source Storage,选择Azure Blob Storage with Service Principal并点击Next。
配置连接,填写后点击Test connection:
| 字段 | 说明 |
|---|---|
| Storage Title | 输入该存储连接在 Label Studio 中显示的名称。 |
| Storage Name | 输入 Azure 存储账户名称。 |
| Container Name | 输入 Azure 存储账户内的容器名称。 |
| Tenant ID | 填写应用注册中的目录(租户)ID。 |
| Client ID | 填写应用注册中的应用程序(客户端)ID。 |
| Client Secret | 填写之前复制的客户端机密值。 |
| Use pre-signed URLs / Proxy through the platform | 启用或禁用预签名 URL。 |
| Expiration minutes | 调整预签名 URL 的有效分钟数。 |
导入设置与预览、复核与确认步骤与存储账户密钥方式完全一致:填写 Bucket Prefix、Import Method、File Name Filter、Scan all sub-folders 后点击Load preview校验,最后Save & Sync立即同步或Save稍后同步。
创建目标存储连接(Service Principal)
进入Settings > Cloud Storage > Add Target Storage,选择Azure Blob Storage with Service Principal并点击Next,填写:
| 字段 | 说明 |
|---|---|
| Storage Title | 输入该存储连接在 Label Studio 中显示的名称。 |
| Storage Name | 输入 Azure 存储账户名称。 |
| Container Name | 输入 Azure 存储账户内的容器名称。 |
| Container Prefix | 可选,填写容器内要使用的目录名,例如data-set-1或data-set-1/subfolder-2。 |
| Tenant ID | 填写应用注册中的目录(租户)ID。 |
| Client ID | 填写应用注册中的应用程序(客户端)ID。 |
| Client Secret | 填写客户端机密值。 |
| Can delete objects from storage | 开启后,标注在 Label Studio 中被删除时,容器中对应对象也会被删除;凭据需具备删除容器对象的权限。 |
添加后点击Sync推送导出。
验证与排错
添加存储后连接会被自动检查,若失败请依次核实:
- Tenant ID / Client ID / Client Secret:值是否正确(无多余空格、机密未过期);
- 存储账户名与容器名:区分大小写,需严格匹配;
- 角色分配:应用注册是否在存储账户上拥有Storage Blob Data Contributor角色;
- CORS:使用预签名 URL 时必须配置;排查阶段可先切换到代理模式测试。
第四步:通过 Label Studio API 创建与同步存储
除了 UI,你还可以完全通过 REST API 以编程方式管理 Azure 存储连接。仓库中 api.py 与 urls.py 定义了完整的端点:
| 端点 | 方法 | 用途 |
|---|---|---|
api/storages/azure/ | GET / POST | 列出 / 创建 Azure 导入存储 |
api/storages/azure/<id> | GET / PATCH / DELETE | 读取 / 更新 / 删除指定导入存储 |
api/storages/azure/<id>/sync | POST | 同步导入存储(拉取任务) |
api/storages/azure/validate | POST | 校验导入存储连接 |
api/storages/azure/files | GET | 列出容器内文件 |
api/storages/export/azure/ | GET / POST | 列出 / 创建 Azure 导出存储 |
api/storages/export/azure/<id> | GET / PATCH / DELETE | 读取 / 更新 / 删除指定导出存储 |
api/storages/export/azure/<id>/sync | POST | 同步导出存储(推送标注) |
api/storages/export/azure/validate | POST | 校验导出存储连接 |
创建时使用type: "azure"标识 Azure 类型(由序列化器StorageTypeField(default='azure')保证)。例如创建一个 Azure 导入存储的请求体大致为:
{ "type": "azure", "title": "my-azure-source", "container": "my-container", "prefix": "data-set-1", "account_name": "<account-name>", "account_key": "<account-key>", "presign": true, "presign_ttl": 15, "regex_filter": ".*", "use_blob_urls": false, "recursive_scan": true, "project": 1 }创建后调用对应端点的sync接口即可触发同步;校验接口则复用序列化器中的validate_connection()逻辑(serializers.py),异常会被提取为人类可读的错误信息返回。
源码视角:Azure 同步与数据流原理
理解底层实现有助于你在实际项目中排错与调优。
连接校验:validate_connection()(models.py)会尝试读取容器属性,容器不存在时抛出KeyError('Container not found: ...');对于导入存储还会校验prefix前缀下是否存在 blob(list_blob_names(name_starts_with=prefix)),找不到则报告azure-blob://<container>/<prefix> not found。
对象枚举与导入:AzureBlobImportStorageBase.iter_objects()支持两种扫描模式——recursive_scan=True时用list_blobs全量递归;recursive_scan=False时用walk_blobs(delimiter='/')按层级遍历并跳过目录占位符。两种模式下都会应用regex_filter正则过滤(跳过不匹配的对象,见日志is skipped by regex filter)。get_data()则根据use_blob_urls决定导入方式:开启时把每个文件包装成azure-blob://<container>/<key>形式的任务数据(即“每个文件一个任务”);关闭时把 JSON/JSONL/Parquet 文件内容解析成任务定义(load_tasks_json)。
预签名 URL 生成:generate_http_url()使用generate_blob_sas()生成带BlobSasPermissions(read=True)权限、有效期presign_ttl分钟的 SAS token,最终 URL 形如https://<account>.blob.core.windows.net/<container>/<blob>?<sas>。
标注导出:AzureBlobExportStorage.save_annotation()将标注序列化为 JSON,上传到prefix/key对应的 blob(upload_blob(..., overwrite=True)),并创建AzureBlobExportStorageLink建立映射。标注保存后通过 Djangopost_save信号触发异步导出(start_job_async_or_sync);删除标注时,若开启了can_delete_objects,会同步删除容器中对应对象(pre_delete信号 +delete_blob())。
同步状态机:每次同步都会驱动StorageInfo状态流转(queued → in_progress → completed / completed_with_errors / failed),同步失败时的 traceback、已同步任务数、耗时等元数据都会记录在存储连接的last_sync/last_sync_count/traceback/meta字段中(base_models.py),可在存储详情页或通过 API 查看。
流式媒体代理:代理模式下get_bytes_stream()解析azure-blob://URI,通过AZURE.download_stream_response()统一处理 HTTP Range 请求(支持bytes=start-end,包含Content-Range、ETag、Last-Modified等响应头),并以分块迭代器方式流式输出,避免大文件(音视频、高分图)整块加载占用内存。
常见问题速查
- Test connection 报 “Container not found”:容器名拼写错误或大小写不匹配,检查 Azure 控制台中的准确名称。
- 报 “prefix not found”:
prefix下没有对象,确认目录名正确且存在文件。 - 预签名 URL 打开后浏览器报 CORS 错误:确认 Blob service CORS 规则中 Allowed origins 包含你的 Label Studio 域名,且方法包含
GET, HEAD, OPTIONS。 - 导入时报 “JSONDecodeError / UnicodeDecodeError”:容器内混入了非 JSON 文件而导入方式仍为 “Tasks”,此时应在 Import Method 中开启 “Files” 模式(
use_blob_urls=True)让每个文件成为一个任务。 - 导出后容器中没有文件:确认目标存储已点击Sync,且
prefix没有写错;导出由后台 RQ 任务异步执行,可在存储状态中查看last_sync与traceback。 - Service Principal 连接校验失败:按“验证与排错”清单逐项核对 Tenant/Client/Secret、账户容器名大小写、角色分配与 CORS。
通过以上步骤,你已完成 Label Studio 与 Azure Blob Storage 的完整对接:既能从容器批量导入任务,也能把标注结果实时写回容器,实现数据标注流程与 Azure 云生态的无缝集成。
【免费下载链接】label-studioLabel Studio is a multi-type data labeling and annotation tool with standardized output format项目地址: https://gitcode.com/GitHub_Trending/la/label-studio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考