Label Studio 持久化存储配置实战:Kubernetes PVC、Amazon S3、Google Cloud Storage 与 Azure 全面指南
Label Studio 持久化存储配置实战Kubernetes PVC、Amazon S3、Google Cloud 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-studioLabel Studio 支持将上传的任务数据、用户头像和各类媒体文件持久化保存确保应用重启或 Pod 重建后数据不丢失并让 app、rqworker 等所有组件共享同一份存储。本文以官方指南docs/source/guide/persistent_storage.md为核心骨架结合仓库源码与部署配置系统讲解 Kubernetes PVC、Amazon S3、Google Cloud StorageGCS与 Microsoft Azure 四套持久化存储方案的完整配置步骤、权限模型、CORS 规则与底层文件分发机制读者可在 AWS、GCP、Azure 或自建 Docker Compose 环境中按图索骥完成接入并理解 Label Studio 是如何在「nginx 代理模式」与「Django 直连模式」之间切换的。为什么需要持久化存储默认情况下Label Studio 依赖 nginx 对外提供上传媒体的访问。持久化存储要正常工作需要让 nginx 与 Label Studio 一起运行这也是官方推荐的做法nginx 直接从对象存储或共享卷取回文件能显著降低 Label Studio 应用服务器uwsgi worker的负载。如果使用不含 nginx 的最小化部署可以设置USE_NGINX_FOR_UPLOADSfalse USE_NGINX_FOR_EXPORT_DOWNLOADSfalse此时所有上传和导出文件都由 Label Studio 自己负责分发。官方明确提示这种方式不推荐它会显著增加 uwsgi worker 的 I/O 压力当用户操作大文件时容易引发服务不可用。从源码可以看到这两个开关的定义位置在 label_studio/core/settings/base.pyUSE_NGINX_FOR_EXPORT_DOWNLOADS get_bool_env(USE_NGINX_FOR_EXPORT_DOWNLOADS, False) USE_NGINX_FOR_UPLOADS get_bool_env(USE_NGINX_FOR_UPLOADS, True)注意两者的默认值不同上传默认走 nginxTrue而导出下载默认由 Django 直接处理False。部署方案速览根据你的部署环境选择对应的持久化存储方案部署环境推荐方案配置入口KubernetesPersistent Volume ClaimPVC需 ReadWriteManyls-values.yaml的persistence.config.volumeAWS / Docker ComposeAmazon S3ls-values.yaml或env.listGoogle Cloud / Docker ComposeGoogle Cloud StorageGCSls-values.yaml或env.listAzure / Docker ComposeAzure Blob Storagels-values.yaml或env.list方案一Kubernetes PVC在 Kubernetes 上部署 Label Studio 时可以直接用 Persistent Volume Claim 代替云对象存储。核心约束是PVC 必须支持ReadWriteMany访问模式这样 app 与 rqworker 等多个 Pod 才能同时读写同一个卷。在ls-values.yaml中通过persistence.config.volume段配置并将accessModes设为ReadWriteManyglobal: persistence: enabled: true type: volume config: volume: size: 50Gi accessModes: - ReadWriteMany重要前提集群必须提供支持ReadWriteMany的 StorageClass 或默认存储例如 NFS、EFS、Azure Files 等网络存储。标准块存储如多数云厂商默认云盘通常只支持ReadWriteOnce当多个 Pod 需要挂载同一 PVC 时并不适用会导致挂载失败或数据不一致。方案二Amazon S3Label Studio 托管在 AWS 或使用 Docker Compose 时可将 Amazon S3 作为持久化存储。完整流程分为三步创建 bucket → 配置 CORS → 配置访问权限。创建 S3 bucket先在 AWS 控制台按 S3 用户指南的步骤创建一个 bucket。若需要静态加密at-rest encryption可以为该 bucket 配置默认的服务端加密SSE。配置 S3 bucket 的 CORS如果计划使用**直接文件上传direct upload**功能并存储音频、视频、CSV 等媒体文件必须配置 CORS。参考以下规则可直接使用或按需修改[ { AllowedHeaders: [ * ], AllowedMethods: [ GET, PUT, POST, DELETE, HEAD ], AllowedOrigins: [ * ], ExposeHeaders: [ x-amz-server-side-encryption, x-amz-request-id, x-amz-id-2 ], MaxAgeSeconds: 3600 } ]要点说明AllowedMethods覆盖 GET/PUT/POST/DELETE/HEAD对应浏览器端的上传与媒体播放请求ExposeHeaders暴露x-amz-*系列响应头便于前端读取服务端加密与请求追踪信息MaxAgeSeconds: 3600缓存预检结果减少 OPTIONS 请求次数。配置 S3 访问权限S3 bucket 创建完成后需要配置 IAM 权限让 Label Studio 访问该 bucket。官方提供四种方式按推荐程度排序IAM role OIDC provider推荐适用于 EKS 的 IRSA 模式Access keys适用于非 OIDC 环境或简单场景IAM role不使用 OIDC直接挂到 EKS 节点组Access keys Docker Compose适用于 Docker Compose 部署。四种方式共用同一份 IAM Policy将YOUR_S3_BUCKET替换为实际 bucket 名{ Version: 2012-10-17, Statement: [ { Effect: Allow, Action: [ s3:ListBucket ], Resource: [ arn:aws:s3:::YOUR_S3_BUCKET ] }, { Effect: Allow, Action: [ s3:PutObject, s3:GetObject, s3:DeleteObject ], Resource: [ arn:aws:s3:::YOUR_S3_BUCKET/* ] } ] }该策略遵循最小权限原则s3:ListBucket只授权到 bucket 本身对象级操作只授权到该 bucket 下的对象。方式 AIAM roleOIDC / IRSA推荐前提是集群已配置并预置 OIDC providerEKS 控制台或eksctl均可完成。按 EKS 用户指南为 service account 创建 IAM role 与 policypolicy 使用上面的 JSON以Web identity类型创建 IAM roleIdentity Provider 选择集群的 OIDC provider URLAudience 填写sts.amazonaws.com将上一步的权限附加到该 role 并命名记下 Role ARN将 Role ARN 以注解annotation形式写入ls-values.yamlapp 与 rqworker 都需要。可选参数folder指定存储子目录默认可省略global: persistence: enabled: true type: s3 config: s3: bucket: YOUR_BUCKET_NAME region: YOUR_BUCKET_REGION folder: app: serviceAccount: annotations: eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME_FROM_STEP_3 rqworker: serviceAccount: annotations: eks.amazonaws.com/role-arn: arn:aws:iam::ACCOUNT_ID:role/ROLE_NAME_FROM_STEP_3方式 BAccess keys创建具有Programmatic access的 IAM 用户权限选择「直接附加已有策略」并附加上述 JSON 策略保存生成的 Access Key ID 与 Secret Access Key写入ls-values.yamlglobal: persistence: enabled: true type: s3 config: s3: accessKey: YOUR_ACCESS_KEY_ID secretKey: YOUR_SECRET_ACCESS_KEY bucket: YOUR_BUCKET_NAME region: YOUR_BUCKET_REGION folder: 可选项使用已有的 Kubernetes Secret也可以把凭据提前存入 Kubernetes Secret避免明文写在 values 文件中kubectl create secret generic YOUR_SECRET_NAME --from-literalaccesskeyYOUR_ACCESS_KEY_ID --from-literalsecretkeyYOUR_SECRET_ACCESS_KEYglobal: persistence: enabled: true type: s3 config: s3: accessKeyExistingSecret: YOUR_SECRET_NAME accessKeyExistingSecretKey: accesskey secretKeyExistingSecret: YOUR_SECRET_NAME secretKeyExistingSecretKey: secretkey bucket: YOUR_BUCKET_NAME region: YOUR_BUCKET_REGION方式 CIAM roleEKS 节点组不使用 OIDC在 AWS 控制台进入EKS Clusters 集群名 Node Group选中部署 Label Studio 的节点组在 Details 页找到Node IAM Role ARN选择「直接附加已有策略」附加上述 JSON 策略在ls-values.yaml中配置 bucket 与 region节点组已通过节点角色获得 S3 权限无需再配置密钥global: persistence: enabled: true type: s3 config: s3: bucket: YOUR_BUCKET_NAME region: YOUR_BUCKET_REGION folder: 方式 DAccess keys Docker Compose按方式 B 创建带 Programmatic access 的 IAM 用户并保存密钥在env.list文件中写入以下环境变量STORAGE_AWS_FOLDER可选默认表示 bucket 根目录STORAGE_TYPEs3 STORAGE_AWS_ACCESS_KEY_IDYOUR_ACCESS_KEY_ID STORAGE_AWS_SECRET_ACCESS_KEYYOUR_SECRET_ACCESS_KEY STORAGE_AWS_BUCKET_NAMEYOUR_BUCKET_NAME STORAGE_AWS_REGION_NAMEYOUR_BUCKET_REGION STORAGE_AWS_FOLDERS3 环境变量的源码映射这些STORAGE_AWS_*环境变量在 label_studio/core/settings/base.py 中被逐项解析并映射到 Django 的存储后端配置理解映射关系有助于排查问题环境变量映射目标说明STORAGE_AWS_ACCESS_KEY_IDAWS_ACCESS_KEY_ID仅在显式设置时覆盖默认值STORAGE_AWS_SECRET_ACCESS_KEYAWS_SECRET_ACCESS_KEY同上STORAGE_AWS_BUCKET_NAMEAWS_STORAGE_BUCKET_NAMEbucket 名STORAGE_AWS_REGION_NAMEAWS_S3_REGION_NAME区域默认NoneSTORAGE_AWS_ENDPOINT_URLAWS_S3_ENDPOINT_URL可用于 S3 兼容服务如 MinIOSTORAGE_AWS_FOLDERAWS_LOCATION存储前缀目录STORAGE_AWS_X_AMZ_EXPIRESAWS_QUERYSTRING_EXPIRE签名 URL 有效期默认 86400 秒STORAGE_AWS_S3_USE_SSLAWS_S3_USE_SSL默认TrueSTORAGE_AWS_S3_SIGNATURE_VERSIONAWS_S3_SIGNATURE_VERSION签名版本当STORAGE_TYPEs3时STORAGES[default][BACKEND]会被切换为core.storage.CustomS3Boto3Storage见 label_studio/core/storage.py并开启CLOUD_FILE_STORAGE_ENABLEDTrue。方案三Google Cloud StorageLabel Studio 托管在 GCP 或使用 Docker Compose 时可将 GCS 作为持久化存储。创建 GCS bucket创建 bucket例如命名为heartex-example-bucket-123456选择访问控制方式时使用uniform access control统一访问控制创建一个 IAM Service Account为该服务账号附加预定义的Storage Object Admin角色使其能创建、读取、删除 bucket 中的对象为角色添加条件将权限限定到指定 bucket。两种写法任选其一使用Condition BuilderCondition type 选NameOperator 选Starts withValue 填projects/_/buckets/heartex-example-bucket-123456或使用CELCommon Expression Language表达式resource.name.startsWith(projects/_/buckets/heartex-example-bucket-123456)。配置 GCS bucket 的 CORS同样地使用直接上传并存储音视频、CSV 等媒体时需要配置 CORS。先生成配置文件echo [ { origin: [*], method: [GET,PUT,POST,DELETE,HEAD], responseHeader: [Content-Type,Access-Control-Allow-Origin], maxAgeSeconds: 3600 } ] cors-config.json再通过gsutil应用到 bucket替换YOUR_BUCKET_NAMEgsutil cors set cors-config.json gs://YOUR_BUCKET_NAME连接 GCS bucket创建 bucket 并配置好 IAM 后有三种连接方式Workload Identity推荐、Service Account Key和Docker Compose Service Account Key。方式 AWorkload Identity推荐前提是 GKE 集群已启用 Workload Identity。设置以下环境变量GCP_SA为之前创建的服务账号其余按需替换GCP_SAService-Account-You-Created APP_SAserviceAccount:GCP_PROJECT_ID.svc.id.goog[K8S_NAMESPACE/HELM_RELEASE_NAME-lse-app] WORKER_SAserviceAccount:GCP_PROJECT_ID.svc.id.goog[K8S_NAMESPACE/HELM_RELEASE_NAME-lse-rqworker]为 app 与 rqworker 的 K8s Service Account 创建 IAM policy binding允许它们伪装impersonateGCS 服务账号gcloud iam service-accounts add-iam-policy-binding ${GCP_SA} \ --role roles/iam.workloadIdentityUser \ --member ${APP_SA} gcloud iam service-accounts add-iam-policy-binding ${GCP_SA} \ --role roles/iam.workloadIdentityUser \ --member ${WORKER_SA}在ls-values.yaml中填写projectID、bucketfolder可选默认并给 app 与 rqworker 的 Service Account 加上 GCP 注解global: persistence: enabled: true type: gcs config: gcs: projectID: YOUR_PROJECT_ID bucket: YOUR_BUCKET_NAME folder: app: serviceAccount: annotations: iam.gke.io/gcp-service-account: GCP_SERVICE_ACCOUNT rqworker: serviceAccount: annotations: iam.gke.io/gcp-service-account: GCP_SERVICE_ACCOUNT方式 BService Account Key创建新的 service account key在 GCP 控制台创建并下载服务账号 JSON 密钥文件将 JSON 内容、projectID、bucket写入ls-values.yamlfolder可选global: persistence: enabled: true type: gcs config: gcs: projectID: YOUR_PROJECT_ID applicationCredentialsJSON: YOUR_JSON bucket: YOUR_BUCKET_NAME folder: 使用已有的 Kubernetes Secret 与 key将服务账号 JSON 文件导入 Kubernetes Secretkubectl create secret generic YOUR_SECRET_NAME --from-filekey_jsonPATH_TO_JSON在ls-values.yaml中引用该 Secretglobal: persistence: enabled: true type: gcs config: gcs: projectID: YOUR_PROJECT_ID applicationCredentialsJSONExistingSecret: YOUR_SECRET_NAME applicationCredentialsJSONExistingSecretKey: key_json bucket: YOUR_BUCKET_NAME方式 CDocker Compose下载服务账号 JSON 密钥文件在env.list中写入以下变量STORAGE_GCS_FOLDER可选默认STORAGE_TYPEgcs STORAGE_GCS_BUCKET_NAMEYOUR_BUCKET_NAME STORAGE_GCS_PROJECT_IDYOUR_PROJECT_ID STORAGE_GCS_FOLDER GOOGLE_APPLICATION_CREDENTIALS/opt/heartex/secrets/key.json将 JSON 文件放在env.list同目录在docker-compose.yml的app.volumes中追加挂载- ./service-account-file.json:/opt/heartex/secrets/key.json:roGCS 环境变量的源码映射对应逻辑在 label_studio/core/settings/base.py环境变量映射目标说明STORAGE_GCS_PROJECT_IDGS_PROJECT_IDGCP 项目 IDSTORAGE_GCS_BUCKET_NAMEGS_BUCKET_NAMEbucket 名STORAGE_GCS_FOLDERGS_LOCATION存储前缀目录STORAGE_GCS_EXPIRATION_SECSGS_EXPIRATION签名 URL 有效期默认 86400 秒STORAGE_GCS_ENDPOINTGS_CUSTOM_ENDPOINT自定义端点当STORAGE_TYPEgcs时后端切换为core.storage.AlternativeGoogleCloudStorage。该实现见 label_studio/core/storage.py重写了url()方法以强制使用 IAMsignBlobAPI 生成 v4 签名 URL这样即使没有明文凭据文件只要 Service Account 具备iam.serviceAccounts.signBlob权限即可完成签名——这正是 Workload Identity 模式能在不落盘密钥文件的情况下工作的底层原因。方案四Microsoft Azure StorageLabel Studio 可将 Azure Blob Storage 容器作为持久化存储。创建 Storage 容器创建 Azure 存储账户Storage account。官方特别提示SKU 务必设为Premium_LRSkind 设为BlockBlobStorage。这样底层使用 SSD 而非 HDD若选择 HDD 存储实例可能因性能过低而运行异常获取账户密钥可在 Azure Portal 的Storage accounts Access keys中查看或使用 CLIaz storage account keys list --account-name${STORAGE_ACCOUNT}在存储账户中创建容器可用 Azure Portal 操作或使用 CLIaz storage container create --name YOUR_CONTAINER_NAME \ --account-name YOUR_STORAGE_ACCOUNT \ --account-key YOUR_STORAGE_KEY配置 Azure 容器的 CORS使用直接上传并存储音视频、CSV 等媒体文件时需配置 CORS参考以下规则Cors CorsRule AllowedOrigins*/AllowedOrigins AllowedMethodsGET,PUT,POST,DELETE,HEAD/AllowedMethods AllowedHeadersx-ms-blob-content-type/AllowedHeaders ExposedHeadersx-ms-*/ExposedHeaders MaxAgeInSeconds3600/MaxAgeInSeconds /CorsRule Cors连接 Azure 容器Kubernetes 方式在ls-values.yaml中填写账户名、密钥与容器名folder可选默认global: persistence: enabled: true type: azure config: azure: storageAccountName: YOUR_STORAGE_ACCOUNT storageAccountKey: YOUR_STORAGE_KEY containerName: YOUR_CONTAINER_NAME folder: 可选项使用 Kubernetes Secret 保存凭据kubectl create secret generic YOUR_SECRET_NAME --from-literalstorageaccountnameYOUR_STORAGE_ACCOUNT --from-literalstorageaccountkeyYOUR_STORAGE_KEYglobal: persistence: enabled: true type: azure config: azure: storageAccountNameExistingSecret: YOUR_SECRET_NAME storageAccountNameExistingSecretKey: storageaccountname storageAccountKeyExistingSecret: YOUR_SECRET_NAME storageAccountKeyExistingSecretKey: storageaccountkey containerName: YOUR_CONTAINER_NAMEDocker Compose 方式在env.list中写入STORAGE_AZURE_FOLDER可选默认STORAGE_TYPEazure STORAGE_AZURE_ACCOUNT_NAMEYOUR_STORAGE_ACCOUNT STORAGE_AZURE_ACCOUNT_KEYYOUR_STORAGE_KEY STORAGE_AZURE_CONTAINER_NAMEYOUR_CONTAINER_NAME STORAGE_AZURE_FOLDERAzure 环境变量的源码映射对应逻辑在 label_studio/core/settings/base.py环境变量映射目标说明STORAGE_AZURE_ACCOUNT_NAMEAZURE_ACCOUNT_NAME存储账户名STORAGE_AZURE_ACCOUNT_KEYAZURE_ACCOUNT_KEY账户密钥STORAGE_AZURE_CONTAINER_NAMEAZURE_CONTAINER容器名STORAGE_AZURE_FOLDERAZURE_LOCATION存储前缀目录STORAGE_AZURE_URL_EXPIRATION_SECSAZURE_URL_EXPIRATION_SECS签名 URL 有效期默认 86400 秒后端切换为core.storage.CustomAzureStorage见 label_studio/core/storage.py。源码视角上传与下载的文件分发链路理解持久化存储的底层实现有助于排查「文件 404」「媒体无法加载」等问题。整个链路由三个环节协作完成。1. 存储后端与代理 URL 生成core/storage.py 中的StorageProxyMixin是所有云存储后端的公共基类它重写了url()方法class StorageProxyMixin: def url(self, name, storage_urlFalse, *args, **kwargs): if storage_url is True: return super().url(name, *args, **kwargs) return f{settings.HOSTNAME}/storage-data/uploaded/?filepath{name}也就是说日常业务代码拿到的文件 URL 是指向 Label Studio 自身/storage-data/uploaded/的受保护地址先做鉴权而不是对象存储的公开地址只有需要 nginx 代理时storage_urlTrue才返回真实的对象存储 URL。该路由注册在 label_studio/data_import/urls.py。2. 下载 API 的两种工作模式DownloadStorageData视图label_studio/data_import/api.py是文件分发入口其类文档清晰地描述了两套模式NGINX 模式默认USE_NGINX_FOR_UPLOADSTrue校验用户权限后返回带X-Accel-Redirect头的HttpResponsenginx 拦截该头并直接从对象存储取回文件Django 本身不传输文件字节性能最高Direct 模式USE_NGINX_FOR_UPLOADSFalseDjango 通过RangedFileResponse直接流式返回文件支持 HTTP Range 部分请求206适合无 nginx 的最小化部署与媒体文件的拖动播放。同时该视图对filepath做了双重校验以settings.UPLOAD_DIR开头的上传文件需通过FileUpload.has_permission()校验以settings.AVATAR_PATH开头的头像需校验请求者与被访问用户同属一个组织见 label_studio/data_import/api.py防止跨组织越权访问。3. nginx 的 X-Accel 内部跳转仓库自带的 nginx 配置 deploy/default.conf 中有一段internal的location ~ ^/file_download/(.*?)/(.*?)/(.*)它负责把X-Accel-Redirect指定的「协议 主机 路径」重新拼装成远程对象存储 URL 并代理取回同时做了几点关键处理隐藏 GCSx-goog-*与 AWSx-amz-*的内部响应头避免泄露存储元数据清空转发给对象存储的Authorization与Cookie防止凭据外泄开启proxy_ssl_server_name on与proxy_ssl_name $download_host保证 TLS SNI 正确兼容 CDN/对象存储的虚拟主机访问proxy_max_temp_file_size 0禁止落盘临时文件纯内存转发proxy_intercept_errors on配合error_page 301 302 307 handle_redirect兼容对象存储的临时重定向如 S3 的 307。nginx 容器与 app 容器共同挂载同一个数据卷、通过depends_on关联的编排方式可参考 docker-compose.ymlnginx 暴露 8085/8086 端口app 通过 uwsgi 在 8000 端口提供服务两者共享./mydata:/label-studio/data卷。4. 测试用例佐证仓库提供了针对持久化存储下载链路的完整测试套件 label_studio/tests/data_import/test_persistent_storage_data.py覆盖了鉴权与授权缺失filepath返回 404、非法路径安全检查、FileUpload权限校验、跨组织访问头像被拒403两种文件服务模式NGINX 模式生成X-Accel-Redirect头、Direct 模式走RangedFileResponse并通过USE_NGINX_FOR_UPLOADS开关切换文件类型支持PDF、MP3、MP4、JPG 等格式的 Content-Type 探测与Content-Disposition头处理。这些测试也印证了一个易踩的坑当USE_NGINX_FOR_UPLOADSTrue但存储后端是本地FileSystemStorage时会返回 400 错误提示需要关闭 NGINX 模式或切换到支持代理 URL 的云存储后端S3/GCS/Azure见 label_studio/data_import/api.py。补充MinIO 与 S3 兼容服务对于自建环境仓库还支持通过MINIO_STORAGE_*系列环境变量把默认存储切换到 S3 兼容的 MinIO见 label_studio/core/settings/base.py并提供了开箱即用的 docker-compose.minio.yml 编排文件。这意味着上述 S3 的接入方式尤其是STORAGE_AWS_ENDPOINT_URL自定义端点参数同样适用于任何 S3 协议兼容的对象存储。总结配置核对清单无论选择哪种存储方案部署完成后建议按以下清单核对nginx 是否与 app 一起运行保持USE_NGINX_FOR_UPLOADStrue默认时确认 nginx 容器已启动且default.conf中的/file_download/内部路由存在云存储 CORS 是否已配置使用直接上传并存放音视频/CSV 时三种云存储都要求配置 CORS且MaxAgeSeconds建议 3600权限是否最小化S3 策略仅授权ListBucket 对象级Put/Get/DeleteGCS 角色限定到具体 bucketAzure 使用独立密钥K8s 场景是否满足 ReadWriteManyPVC 方案必须使用 NFS/EFS/Azure Files 等网络存储普通云盘无法支撑多 Pod 读写密钥优先放 SecretS3、GCS、Azure 均支持通过*ExistingSecret参数引用 Kubernetes Secret避免明文密钥进入 values 文件验证媒体可加载上传一张图片或一段音频确认通过/storage-data/uploaded/鉴权后可正常访问导出文件可正常下载。【免费下载链接】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),仅供参考