Label Studio 接入 Google Cloud Storage(GCS)完整指南:源/目标存储配置、WIF 与 GKE 服务账号模拟
【免费下载链接】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 开源仓库的官方指南 storage_gcp.md 与 GCS 存储模块源码(io_storages/gcs),系统讲解如何将 Google Cloud Storage(GCS)桶接入 Label Studio:既作为源存储动态导入标注任务,也作为目标存储回传标注结果,同时覆盖预签名 URL 与平台代理两种数据加载模式、Workload Identity Federation(WIF)临时凭据方案、GKE 服务账号模拟方案以及 IP 过滤加固。读完本文,你可以独立完成从 GCS 桶权限配置、CORS 设置到 Label Studio 内源/目标存储连接创建与同步的完整落地,并理解每一步背后的源码实现。
GCS 在 Label Studio 中的角色与整体工作流
Label Studio 支持将外部存储动态接入项目:源存储(Source Storage)用于扫描桶中的对象并生成标注任务,目标存储(Target Storage)用于在标注完成后把注解写回桶中。官方文档 Cloud storage for projects 指出,每个源/目标存储连接都是项目级的,同一个项目可以挂接多个桶作为源或目标。
从源码看,GCS 支持在 GCSImportStorage(导入)与 GCSExportStorage(导出)两个模型上实现,它们共享 GCSStorageMixin 提供的公共字段:
| 模型字段 | 对应 UI 表单 | 说明 |
|---|---|---|
bucket | Bucket Name | GCS 桶名称 |
prefix | Bucket Prefix | 桶内目录前缀,如data-set-1或data-set-1/subfolder-2 |
regex_filter | File Filter Regex | 用于过滤桶内对象的正则表达式 |
use_blob_urls | Import Method | true表示"每个对象创建一个任务"(Files 模式);false表示把 JSON/JSONL 当作任务定义(Tasks 模式) |
google_application_credentials | Google Application Credentials | 服务账号凭据 JSON 的文本内容(安全字段,序列化时会被剔除) |
google_project_id | Google Project ID | 桶所在的 Google 项目 ID |
导入存储还有三个特有字段(GCSImportStorageBase):presign(默认True,控制是否生成预签名 URL)、presign_ttl(预签名 URL 有效期,分钟)、recursive_scan(是否递归扫描子目录)。
第一步:配置 GCS 桶的访问权限
在 Label Studio 中创建任何 GCS 连接之前,需要先完成三项前置工作(详见 storage_gcp.md)。
1. 启用对桶的程序化访问
按照 Google Cloud Storage 的 Client Libraries 文档为你的 GCS 桶配置程序化访问。Label Studio 后端实际通过google-cloud-storagePython 客户端与 GCS API 交互(见 io_storages/gcs/utils.py),因此对桶的 API 访问能力是硬性前提。
2. 设置认证与 IAM 角色
你的账号必须具备以下权限:
- 项目级Service Account Token Creator(
roles/iam.serviceAccountTokenCreator)角色 - 项目级Storage Object Viewer(
roles/storage.viewer)角色 - 桶级
storage.buckets.get访问权限
如果使用 WIF(Workload Identity Federation)方案,则按后文 WIF 服务账号权限 配置。
3. 配置 CORS
仅当使用预签名 URL时才需要配置 CORS——因为标注者的浏览器会直接向 GCS 发起跨域 GET 请求。如果使用平台代理模式,则无需 CORS(详见 storage.md 中 Pre-signed URLs vs Storage proxies)。
官方推荐的 CORS 配置示例如下。先用echo生成cors-config.json:
echo '[ { "origin": ["*"], "method": ["GET"], "responseHeader": ["Content-Type","Access-Control-Allow-Origin"], "maxAgeSeconds": 3600 } ]' > cors-config.json再将YOUR_BUCKET_NAME替换为真实桶名并应用:
gsutil cors set cors-config.json gs://YOUR_BUCKET_NAME在生产环境中,建议把origin收敛为 Label Studio 部署的实际域名,而不是*。
第二步:使用 Google Application Credentials 建立标准 GCS 连接
这是社区版即可用的基础方案,认证基于服务账号的长期 JSON 密钥。
获取凭据 JSON 文件
在 Google Cloud Console 中依次操作:
- 进入IAM & Admin > Service Accounts;
- 选择需要凭据的服务账号(没有则新建);
- 在账号详情页进入Keys标签,点击Add Key > Create new key;
- 选择JSON密钥类型并点击Create,浏览器会自动下载生成好的 JSON 文件。
如果使用服务账号授权访问 GCP,请确保已通过gcloud auth activate-service-account激活该账号。
创建源存储连接
在 Label Studio 中打开项目,进入Settings > Cloud Storage > Add Source Storage,选择Google Cloud Storage并点击Next。
Configure Connection(连接配置)
填写以下字段后点击Test connection验证:
| 字段 | 说明 |
|---|---|
| Storage Title | 用于标识该存储连接的名称 |
| Bucket Name | GCS 桶名称 |
| Google Application Credentials | 你创建的服务账号凭据 JSON 内容。On-prem 用户可留空,改用GOOGLE_APPLICATION_CREDENTIALS环境变量或 Application Default Credentials(ADC),让用户无需手动配置凭据(见下文 ADC 章节) |
| Google Project ID | 桶所在 Google 项目的 ID(如my-label-studio-project),可在 Console 的IAM & Admin > Settings中查看 |
| Use pre-signed URLs (On) / Proxy through the platform (Off) | 决定数据加载方式,详见下文"预签名 URL 与平台代理"章节 |
| Expire pre-signed URLs (minutes) | 控制预签名 URL 的有效时长(表单默认 15 分钟,对应form_layout.yml中presign_ttl的value: 15) |
这些字段与源码模型一一对应:Test connection会调用 GCS.validate_connection,该函数用当前凭据创建客户端、get_bucket校验桶存在,若填写了 prefix 还会用list_blobs(prefix=..., max_results=1)检查该前缀下是否有对象,没有则抛出No blobs found错误。
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 | 开启后递归扫描容器内所有子目录 |
其中 Import Method 对应源码中的use_blob_urls字段:Files模式下get_data()直接构造{settings.DATA_UNDEFINED_NAME: 'gs://bucket/key'}形式的任务(models.py);Tasks模式下则读取 JSON/JSONL 内容并调用load_tasks_json解析(models.py)。File Name Filter 对应regex_filter,在 GCS.iter_blobs 中通过re.compile(str(regex_filter))编译后逐对象匹配,不匹配的对象直接跳过;Scan all sub-folders对应recursive_scan,为True时list_blobs不带delimiter递归列举全部对象,否则以delimiter='/'仅列出当前层级。
Review & Confirm(确认与同步)
确认无误后点击Save & Sync立即同步,或点击Save保存设置稍后再同步。也可以调用 import storage sync API 触发同步。
创建目标存储连接
进入Settings > Cloud Storage > Add Target Storage,选择Google Cloud Storage并点击Next,填写:
| 字段 | 说明 |
|---|---|
| Storage Title | 存储连接名称 |
| Bucket Name | GCS 桶名称 |
| Bucket Prefix | 可选。导出注解时写入的桶内目录 |
| Google Application Credentials | 同源存储说明 |
| Google Project ID | 桶所在项目 ID |
| Can delete objects from storage | 开启后,在 Label Studio 中删除标注时同步删除桶中的注解对象,要求凭据具备删除对象的权限 |
保存后点击Sync完成导出连接配置。
目标存储的写入逻辑在源码中非常清晰:标注被保存(post_save信号)后,export_annotation_to_gcs_storages 会通过 Redis 异步任务把注解写入所有关联的 GCS 导出存储;GCSExportStorage.save_annotation 把注解序列化为 JSON,按prefix + '/' + key作为对象名blob.upload_from_string(json.dumps(ser_annotation))上传。删除时,delete_annotation_from_gcs_storages 会检查storage.can_delete_objects,只有开启该选项才真正调用blob.delete()——这正是 UI 上"Can delete objects"选项的源码级验证(对应测试见 test_cloud_export_race_and_delete.py)。
预签名 URL 与平台代理:两种数据加载模式
源存储创建时有一个关键开关:Use pre-signed URLs (On) / Proxy through the platform (Off),它决定标注媒体数据如何从桶加载到标注者的浏览器(详见 storage.md)。
| 对比项 | 预签名 URL(默认开启) | 平台代理(关闭预签名) |
|---|---|---|
| 数据流 | Label Studio 生成带时效的 HTTPS 链接,浏览器收到 HTTP 303 重定向后直接从云存储下载媒体 | 后端从云存储下载文件后流式转发给浏览器,流量全部经过 Label Studio 服务器 |
| 性能 | 通常更快、可扩展性更好 | 占用更多 Label Studio worker 资源,可能稍慢 |
| 配置要求 | 需要桶上配置正确的 CORS 和签名权限 | 无需 CORS、无需签名权限 |
| 安全边界 | 媒体文件尽量与 Label Studio 网络隔离 | 数据始终处于 Label Studio/网络边界内,每次请求都执行任务级访问检查,缓存文件也会受访问控制约束 |
| 大文件 | — | 以 8 MB 分块流式传输(见下文环境变量) |
从源码看,预签名由 GCS.generate_http_url 实现:presign=True时调用blob.generate_signed_url(version='v4', expiration=timedelta(minutes=presign_ttl), method='GET')生成 v4 签名 URL;presign=False时则把对象下载下来编码为data:...;base64,...的 data URL。presign开关保存在GCSImportStorage.presign字段(默认True,见 models.py)。
如果开启平台代理模式,需要保证凭据具备以下 GCS 权限:storage.objects.get(读取对象数据与元数据)以及storage.objects.list(使用前缀时列举对象)。
对于 on-prem 部署,大文件流式传输按顺序以 8 MB 分块拆分为多个 GET 请求,可通过以下环境变量调优(定义于 core/settings/base.py):
RESOLVER_PROXY_BUFFER_SIZE:缓冲大小,默认 512 KBRESOLVER_PROXY_TIMEOUT:单请求最大耗时,默认 20 秒RESOLVER_PROXY_MAX_RANGE_SIZE:单次请求返回的最大分块大小,默认 8 MBRESOLVER_PROXY_GCS_DOWNLOAD_URL:GCS 下载端点模板,默认https://storage.googleapis.com/download/storage/v1/b/{bucket_name}/o/{blob_name}?alt=mediaRESOLVER_PROXY_GCS_HTTP_TIMEOUT:GCS HTTP 请求超时,默认 5 秒RESOLVER_PROXY_ENABLE_ETAG_CACHE/RESOLVER_PROXY_CACHE_TIMEOUT:ETag 缓存开关(默认开启)与缓存时长(默认 3600 秒)
使用 Application Default Credentials 增强安全
对于 on-prem 部署,可以不为每个存储连接手动粘贴凭据 JSON,而是通过Application Default Credentials(ADC)在平台层面全局提供云存储认证。推荐方式是通过GOOGLE_APPLICATION_CREDENTIALS环境变量指向凭据 JSON 文件:
export GOOGLE_APPLICATION_CREDENTIALS=json-file-with-GCP-creds-23441-8f8sd99vsd115a.json源码层面的支持见 GCS.get_client:当google_application_credentials为空时,直接以gcs.Client(project=google_project_id)构造客户端,底层自动回落到 ADC;只有显式提供了凭据 JSON 时,才通过service_account.Credentials.from_service_account_info()解析为服务账号凭据。此外,设置项GCS_CLOUD_STORAGE_FORCE_DEFAULT_CREDENTIALS(默认False,见 base.py)用于控制签名 URL 时是否强制使用默认凭据:开启后,仅当未提供google_application_credentials时才尝试从环境加载 ADC 凭据。
Google Cloud Storage 与 Workload Identity Federation(WIF)
WIF 方案(对应 UI 中的Google Cloud Storage (WIF Auth))与长期应用凭据不同:它使用临时凭据——每次向 GCS 发起请求时,Label Studio 都会连接你的身份池(identity pool)换取临时凭据,避免长期密钥的保管与轮换风险。该功能属于企业版能力。
WIF 服务账号权限
你需要一个具备以下权限的服务账号:
- 桶级Storage Admin(
roles/storage.admin) - 项目级Service Account Token Creator(
roles/iam.serviceAccountTokenCreator) - 项目级Storage Object Viewer(
roles/storage.viewer)
创建 Workload Identity Pool
官方文档提供了三种创建方式:Terraform、gcloud 命令行、Google Cloud Console。
方式一:Terraform
官方示例脚本(原文完整版见 storage_gcp.md)基于 AWS 托管的 HumanSignal Label Studio 身份进行信任配置,需要预先设置以下变量:
- GCP 项目变量:
var.gcp_project_name、var.gcp_region - HumanSignal 托管的 SaaS 标识:
var.aws_account_id = "490065312183"、var.aws_role_name = "label-studio-app-production"
然后依次执行:
terraform init terraform plan terraform apply脚本核心逻辑是:创建google_iam_workload_identity_pool(如label-studio-pool-${random_id})与google_iam_workload_identity_pool_provider(AWS 类型),通过attribute_condition校验attribute.aws_role(可选叠加external_id条件),并通过google_service_account_iam_binding将roles/iam.workloadIdentityUser授予principalSet://...attribute.aws_role/arn:aws:sts::490065312183:assumed-role/label-studio-app-production。应用成功后即获得一个信任 Label Studio AWS IAM Role 的可用 WIF 池,并输出GCP_WORKLOAD_ID与GCP_WORKLOAD_PROVIDER。
方式二:gcloud 命令行
将方括号变量([PROJECT_ID]、[POOL_ID]、[PROVIDER_ID]等)替换为实际值,注意引号转义。
创建身份池:
gcloud iam workload-identity-pools create [POOL_ID] \
--project=[PROJECT_ID]
--location="global"
--display-name="[POOL_DISPLAY_NAME]"
- `[POOL_ID]`:池 ID(如 `label-studio-pool-abc123`),之后需要复用; - `[PROJECT_ID]`:Google Cloud 项目 ID; - `[POOL_DISPLAY_NAME]`:池的人类可读名称(可选但推荐)。 2. 创建 AWS 提供方(provider)。由于发起请求的 Label Studio 资源托管在 AWS,必须允许具有正确 external ID 与 AWS role 的 AWS 主体模拟 Google 服务账号: ```shell gcloud iam workload-identity-pools providers create-aws [PROVIDER_ID] \ --workload-identity-pool="[POOL_ID]" \ --account-id="490065312183" \ --attribute-condition="attribute.aws_role==\"arn:aws:sts::490065312183:assumed-role/label-studio-app-production\"" \ --attribute-mapping="google.subject=assertion.arn,attribute.aws_account=assertion.account,attribute.aws_role=assertion.arn,attribute.external_id=assertion.external_id"[PROVIDER_ID]:提供方 ID(如label-studio-app-production)。
为前面创建的服务账号授予
iam.workloadIdentityUser角色:gcloud iam service-accounts add-iam-policy-binding [SERVICE_ACCOUNT_EMAIL] \ --role="roles/iam.workloadIdentityUser" \ --member="principalSet://iam.googleapis.com/projects/[PROJECT_NUMBER]/locations/global/workloadIdentityPools/[POOL_ID]/attribute.aws_role/arn:aws:sts::490065312183:assumed-role/label-studio-app-production"[SERVICE_ACCOUNT_EMAIL]:GCS 服务账号邮箱(如my-service-account@[PROJECT_ID].iam.gserviceaccount.com);[PROJECT_NUMBER]:Google 项目编号(与项目 ID 不同),可通过gcloud projects describe $PROJECT_ID --format="value(projectNumber)"查询。
配置完成后,在 Label Studio 建连前请记下这些值:[POOL_ID]、[PROVIDER_ID]、[SERVICE_ACCOUNT_EMAIL]、[PROJECT_NUMBER]、[PROJECT_ID]。
方式三:Google Cloud Console
进入IAM & Admin > Workload Identity Pools,点击Get Started启用相关 API;
在Create an identity pool中填写Name(即池 ID,如
label-studio-pool-abc123,需记住)与Description;在Add a provider pool中选择AWS作为提供方(Label Studio 发起请求的组件所在位置),Provider name 可自定义显示名,但Provider ID 必须为
label-studio-app-production,AWS Account ID 填入490065312183;在Configure provider attributes中:
- 添加条件:
attribute.aws_role=="arn:aws:sts::490065312183:assumed-role/label-studio-app-production" - 添加映射:
google.subject = assertion.arn、attribute.aws_role = ...(含assumed-role的提取表达式,可能已默认填好)、attribute.aws_account = assertion.account、attribute.external_id = assertion.external_id
- 添加条件:
点击Save;
到IAM & Admin > Service Accounts找到允许 AWS(Label Studio)模拟的服务账号,在其Principals with access标签页点击Grant Access;
在New principals中填入:
principalSet://iam.googleapis.com/projects/[PROJECT_NUMBER]/locations/global/workloadIdentityPools/[POOL_ID]/attribute.aws_role/arn:aws:sts::490065312183:assumed-role/label-studio-app-production(
[PROJECT_NUMBER]在IAM & Admin > Settings查看,[POOL_ID]为第 2 步填写的 Name)在Assign Roles中搜索并选择Workload Identity User角色,点击Save。
同样需要记下:池 ID、提供方 ID(应为label-studio-app-production)、服务账号邮箱、Google 项目编号、Google 项目 ID。
创建 WIF 源存储连接
在项目中进入Settings > Cloud Storage > Add Source Storage,选择Google Cloud Storage (WIF Auth)并点击Next,填写:
| 字段 | 说明 |
|---|---|
| Storage Title | 连接名称 |
| Bucket Name | GCS 桶名称 |
| Workload Identity Pool ID | 创建身份池时指定的 ID,可在 Console 的IAM & Admin > Workload Identity Pools查看 |
| Workload Identity Provider ID | 设置提供方时的 ID,同样在IAM & Admin > Workload Identity Pools查看 |
| Service Account Email | 前置步骤中服务账号的邮箱(如labelstudio@random-string-382222.iam.gserviceaccount.com),在IAM & Admin > Service Accounts的 Details 页查看 |
| Google Project ID | 项目 ID,IAM & Admin > Settings查看 |
| Google Project Number | 项目编号,IAM & Admin > Settings查看 |
| Use pre-signed URLs (On) / Proxy through the platform (Off) | 数据加载方式,同标准 GCS 说明 |
| Expire pre-signed URLs (minutes) | 预签名 URL 有效期 |
随后完成Import Settings & Preview(Bucket Prefix / Import Method / File Name Filter / Scan all sub-folders,含义与标准 GCS 一致),确认后点击Save & Sync或Save。WIF 导入存储也有对应的 sync API。
创建 WIF 目标存储连接
选择Google Cloud Storage (WIF Auth),除上述字段外还包含Can delete objects from storage(开启后删除标注时同步删除桶内注解,要求凭据具备删除权限)。保存后点击Sync。
GCS 与 GKE 场景下的服务账号模拟(SA Impersonation)
当 Label Studio 部署在 Google Kubernetes Engine(GKE)上时,工作负载通常通过 GKE Workload Identity 将 Kubernetes 服务账号映射到 Google Cloud 服务账号,但该直接绑定的服务账号未必对你的 GCS 桶有足够权限。服务账号模拟允许工作负载的 GCP 服务账号临时"扮演"另一个权限更充足的账号,全程无需创建、分发和轮换长期密钥,降低了凭据泄露风险。该功能需企业版并在 GKE 中部署。
该方案涉及两个 Google Cloud 项目:基础 GKE 项目(Label Studio 部署所在,由平台管理员一次性配置)与目标项目(GCS 桶所在,由数据团队配置)。
配置 GKE 项目(平台管理员一次性操作)
启用 feature flag
必须在 Label Studio 部署中启用fflag_feat_bros_763_gcs_sa_impersonationfeature flag。可通过 Helm chart 的 extraEnvironmentVars 注入:
global: extraEnvironmentVars: fflag_feat_bros_763_gcs_sa_impersonation: "true"注意:环境变量名必须小写,Label Studio 的 feature flag 解析器以区分大小写的方式匹配fflag_前缀。
创建基础服务账号
在 GKE 项目中创建将作为 Label Studio Pod 身份的服务账号(如lse-base),创建时无需授予额外角色,记下其邮箱(如lse-base@your-gke-project.iam.gserviceaccount.com)——这就是后续与数据团队共享的基础服务账号邮箱。
将基础服务账号绑定到 Kubernetes 服务账号
为 Label Studio 使用的 Kubernetes 服务账号添加 Workload Identity 注解:
kubectl annotate serviceaccount label-studio-sa \ --namespace=label-studio \ iam.gke.io/gcp-service-account=BASE_SA_EMAIL授予 Workload Identity User 角色,使 K8s 服务账号能以 GCP 服务账号身份工作:
gcloud iam service-accounts add-iam-policy-binding BASE_SA_EMAIL \ --role="roles/iam.workloadIdentityUser" \ --member="serviceAccount:GKE_PROJECT_ID.svc.id.goog[NAMESPACE/KSA_NAME]"BASE_SA_EMAIL:基础服务账号邮箱;GKE_PROJECT_ID:GKE 项目 ID;NAMESPACE:Label Studio 所在命名空间(如label-studio);KSA_NAME:Label Studio 使用的 Kubernetes 服务账号名(如label-studio-sa)。
验证绑定:运行测试 Pod,输出中应显示基础服务账号邮箱为当前活跃账号:
kubectl run workload-identity-test \ --image=google/cloud-sdk:slim \ --serviceaccount=label-studio-sa \ --namespace=label-studio \ -it --rm -- gcloud auth list
Terraform 等价配置(原文完整版见 storage_gcp.md):
resource "google_service_account" "lse_base" { project = "your-gke-project" account_id = "lse-base" display_name = "Label Studio Base SA" } resource "google_service_account_iam_member" "workload_identity_binding" { service_account_id = google_service_account.lse_base.name role = "roles/iam.workloadIdentityUser" member = "serviceAccount:your-gke-project.svc.id.goog[label-studio/label-studio-sa]" }配置目标 Google Cloud 项目
Step 1:在目标项目创建服务账号
在桶所在项目中创建 Label Studio 将模拟访问数据的服务账号(如sa-label-studio-data),记下邮箱(如sa-label-studio-data@your-data-project.iam.gserviceaccount.com)。
Step 2:授予目标服务账号桶访问权限
按用途选择角色:
- 源存储(导入数据):
roles/storage.objectViewer - 目标存储(导出注解):
roles/storage.objectAdmin
roles/storage.objectAdmin是目标存储必需的,因为 Label Studio 需要在桶中创建、覆盖并按需删除对象。若只需写入无需删除,roles/storage.objectCreator即可,但此时Can delete objects from storage选项将无法工作。
在 Console 中:进入Cloud Storage > Buckets选择桶 →Permissions > Grant Access→ 填入目标服务账号邮箱 → 按用途选择Storage Object Viewer或Storage Object Admin(可 Add another role 同时添加)→Save。
gcloud 等价命令:
# 源存储(读) gcloud storage buckets add-iam-policy-binding gs://YOUR_BUCKET_NAME \ --member="serviceAccount:TARGET_SA_EMAIL" \ --role="roles/storage.objectViewer" # 目标存储(写与删) gcloud storage buckets add-iam-policy-binding gs://YOUR_BUCKET_NAME \ --member="serviceAccount:TARGET_SA_EMAIL" \ --role="roles/storage.objectAdmin"Step 3:允许基础服务账号模拟目标服务账号
在目标服务账号上把roles/iam.serviceAccountTokenCreator授予基础服务账号。该角色包含signBlob权限——Label Studio 生成预签名 URL 供浏览器直连读取对象时正好需要它。
Console 操作:IAM & Admin > Service Accounts选择目标服务账号 →Permissions > Grant Access→ New principals 填基础服务账号邮箱 → 角色选Service Account Token Creator→Save。
gcloud 等价命令:
gcloud iam service-accounts add-iam-policy-binding TARGET_SA_EMAIL \ --member="serviceAccount:BASE_SA_EMAIL" \ --role="roles/iam.serviceAccountTokenCreator"Step 4:在桶上配置 CORS(预签名 URL 必需)
创建cors.json(将https://your-label-studio-domain.com替换为 Label Studio 实际域名):
[ { "origin": ["https://your-label-studio-domain.com"], "method": ["GET", "HEAD"], "responseHeader": ["Content-Type", "Content-Range", "Content-Disposition"], "maxAgeSeconds": 3600 } ]应用并验证:
gcloud storage buckets update gs://YOUR_BUCKET_NAME --cors-file=cors.json gcloud storage buckets describe gs://YOUR_BUCKET_NAME --format="default(cors_config)"如果使用Proxy through the platform模式而非预签名 URL,则无需配置 CORS(流量全部经过 Label Studio 服务器)。
创建 SA Impersonation 源存储连接
选择Google Cloud Storage (SA Impersonation),字段包括:Storage Title、Bucket Name、Target Service Account Email(Step 1 创建的目标服务账号邮箱)、Google Project ID、Use pre-signed URLs / Proxy、Expire pre-signed URLs。预签名所需的signBlob权限已包含在 Step 3 授予的roles/iam.serviceAccountTokenCreator中。Import Settings 与确认流程同前。
创建 SA Impersonation 目标存储连接
字段包含:Storage Title、Bucket Name、Bucket Prefix、Target Service Account Email、Google Project ID、Can delete objects from storage(需要目标服务账号具备roles/storage.objectAdmin,见 Step 2)。保存后点击Sync。
SA Impersonation 排障
| 错误 | 原因 | 解决方案 |
|---|---|---|
| Label Studio 中没有Google Cloud Storage (SA Impersonation)选项 | 未启用所需 feature flag | 在 GKE 部署中设置小写环境变量fflag_feat_bros_763_gcs_sa_impersonation |
Permission 'iam.serviceAccounts.getAccessToken' denied on resource | 基础服务账号无权模拟目标服务账号 | 确认基础服务账号在目标服务账号上拥有roles/iam.serviceAccountTokenCreator(见 Step 3) |
404 Service account not found | 目标服务账号邮箱错误或账号不存在 | 确认邮箱以.iam.gserviceaccount.com结尾且账号存在于 GCP 项目中 |
Permission 'storage.buckets.get' denied或403 Access denied to bucket | 目标服务账号缺少桶权限 | 在桶上为目标服务账号授予roles/storage.objectViewer(源)或roles/storage.objectAdmin(目标),见 Step 2 |
| 图片/媒体加载失败,浏览器控制台报 CORS 错误 | 桶未针对 Label Studio 域名配置 CORS(仅预签名 URL 场景) | 为桶添加允许从 Label Studio URL 发起GET/HEAD的 CORS 策略(见 Step 4) |
用 Label Studio API 编程式添加存储
除 UI 外,所有 GCS 连接均可通过 REST API 创建与管理。源码中为 GCS 定义了完整的视图集(io_storages/gcs/api.py):
- 导入存储:
GCSImportStorageListAPI(GET 列表 / POST 创建)、GCSImportStorageDetailAPI(GET / PATCH / DELETE)、GCSImportStorageSyncAPI(POST 触发同步)、GCSImportStorageValidateAPI(POST 校验连接)、GCSImportStorageFormLayoutAPI(表单布局) - 导出存储:
GCSExportStorageListAPI、GCSExportStorageDetailAPI、GCSExportStorageSyncAPI、GCSExportStorageValidateAPI、GCSExportStorageFormLayoutAPI
创建/更新请求体由 GCSImportStorageSerializer 与 GCSExportStorageSerializer 约束:导入序列化器强制presign默认True,并将google_application_credentials列为安全字段——序列化输出时会被剔除(to_representation中result.pop(attr)),避免凭据泄露;校验阶段则会调用validate_connection()实际连接 GCS 验证桶与前缀。API 的具体端点语义可通过仓库内 OpenAPI/SDK 生成配置 查看。
用 IP 过滤进一步加固 GCS 桶
GCS 的bucket IP filtering允许基于源 IP 地址限制桶访问,防止未授权访问,并实现细粒度的访问控制(详见 storage_gcp.md 与 security.md 中 Source storage behind your VPC)。
常见用例:
- 仅允许组织 IP 段访问桶
- 仅允许基础设施中特定 VPC 网络访问
- 将敏感数据限制在已知 IP 地址范围内
- 通过白名单 IP 控制第三方集成的访问
配置步骤:
- 通过 Console 或 CLI 创建 GCS 桶;
- 创建 JSON 配置文件定义过滤规则。
公网 IP 范围规则:
{ "mode": "Enabled", "publicNetworkSource": { "allowedIpCidrRanges": [ "xxx.xxx.xxx.xxx", // Your first IP address "xxx.xxx.xxx.xxx", // Your second IP address "xxx.xxx.xxx.xxx/xx" // Your IP range in CIDR notation ] } }VPC 网络来源规则:
{ "mode": "Enabled", "vpcNetworkSources": [ { "network": "projects/PROJECT_ID/global/networks/NETWORK_NAME", "allowedIpCidrRanges": [ RANGE_CIDR ] } ] }- 应用规则:
gcloud alpha storage buckets update gs://BUCKET_NAME --ip-filter-file=IP_FILTER_CONFIG_FILE- 移除规则:
gcloud alpha storage buckets update gs://BUCKET_NAME --clear-ip-filter已知限制:
- 所有规则合计最多 200 个 IP CIDR 块
- IP 过滤规则中最多 25 个 VPC 网络
- 不支持双区域(dual-regional)桶
- 可能影响部分 Google Cloud 服务的访问
运维建议与常见注意事项
综合官方指南与源码实现,落地 GCS 集成时需注意以下几点:
- 同步是单向的:源存储是从桶对象创建任务,目标存储是把注解推回桶。在桶侧直接改动内容并不保证与 Label Studio 结果一致;只有向桶上传新文件(或删除关联任务后重新同步)才能让 Tasks 模式下已导入的文件变更生效(storage.md 排障章节)。
- 源存储不存数据:从云存储同步的任务数据不落库,而是通过预签名 URL 访问(storage.md)。Files 模式下 Label Studio 只创建对象的"引用"而非导入内容,因此数据访问控制完全掌握在你手中。
- 权限最小化:Files 导入模式只需 LIST 权限;Tasks 模式还需 GET 权限读取 JSON 转任务;浏览器加载媒体时还需对桶具备 HEAD 与 GET 权限。
- 每个项目独立目录:官方建议为每个 Label Studio 项目使用独立的桶目录(Bucket Prefix),便于隔离与管理。
- 区域就近:把桶放在离标注团队更近的区域,而不是贴近 Label Studio 服务器,可降低延迟、提升效率。
- 凭据保护:
google_application_credentials属于安全字段,不会出现在任何 API 响应中(serializers.py);建议 on-prem 环境优先采用 ADC 或 WIF 避免在 UI 中粘贴长期密钥。
通过本文的配置路径与源码对照,你已经可以从零开始将 GCS 桶接入 Label Studio,并在预签名直连、平台代理、WIF 临时凭据与 GKE 服务账号模拟之间做出符合自身安全与性能要求的选型。
【免费下载链接】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),仅供参考