Vertex AI中KFP管道使用自定义服务账号的认证异常排查
问题
我希望在Vertex AI中运行KFP管道时使用特定(非默认)服务账号,且无法使用JSON密钥。理想情况下,能按照google.auth用户指南的建议,通过google.auth.default()获取项目ID和凭据。
我已经尝试了三种方式:
- 使用已弃用的
kfp.v2.google.client.AIPlatformClient,指定项目ID实例化客户端,在create_run_from_spec中传入service_account关键字参数运行管道 - 使用
google.cloud.aiplatform.pipeline_jobs.PipelineJob,指定项目ID实例化对象,在submit时传入service_account关键字参数运行管道 - 通过云控制台UI(配合编译管道生成的JSON文件)指定服务账号创建新运行
无论在实际管道(基于自定义构建容器)还是最小可复现示例(使用轻量Python组件)中尝试上述方法,运行creds, project = google.auth.default()并打印项目ID和creds.service_account_email时,得到的都是一个陌生的固定项目ID,且服务账号邮箱为default。看起来传递给管道运行的配置完全未生效,我找不到操作中的错误点。
最小可复现代码
from kfp.v2 import dsl @dsl.component(packages_to_install=['google-auth']) def check_auth(name:str) -> str: import google.auth creds,project = google.auth.default() print(f'Project is: {project}') print(f'Got creds for: {creds.service_account_email}') return project @dsl.pipeline( name='adc-mwe-pipeline' ) def pipeline() -> str: auth_check = check_auth(name='name') return auth_check.output from google.cloud.aiplatform import pipeline_jobs from kfp.v2 import compiler compiler.Compiler().compile(pipeline_func=pipeline, package_path='mwe.json') start_pipeline = pipeline_jobs.PipelineJob( display_name='mwe', template_path='mwe.json', location='some-location', project='my-project', enable_caching=False ) start_pipeline.submit(service_account="my-service-account")
解决方案
你遇到的核心问题是:Vertex AI管道提交时指定的service_account仅用于管道控制层面的操作(比如创建作业、管理资源),组件运行时默认使用的是GKE节点的服务账号,不会自动继承这个指定账号,所以google.auth.default()拿到的是容器环境的默认凭据,而非你指定的服务账号。
下面是两种可行的解决方式:
方式一:在组件中模拟目标服务账号
通过google.auth的身份模拟功能,让组件获取指定服务账号的凭据,无需额外集群配置。
修改组件代码如下:
@dsl.component(packages_to_install=['google-auth']) def check_auth(name:str, target_sa: str) -> str: import google.auth from google.auth import impersonated_credentials # 先获取环境默认凭据,用于发起身份模拟请求 base_creds, _ = google.auth.default() # 模拟目标服务账号 target_creds = impersonated_credentials.Credentials( source_credentials=base_creds, target_principal=target_sa, target_scopes=['https://www.googleapis.com/auth/cloud-platform'], lifetime=3600 ) # 从环境变量获取项目ID(Vertex AI会自动注入该变量) import os project = os.environ.get('CLOUD_ML_PROJECT_ID') print(f'Project is: {project}') print(f'Got creds for: {target_creds.service_account_email}') return project
修改管道定义,添加服务账号参数:
@dsl.pipeline( name='adc-mwe-pipeline' ) def pipeline(target_sa: str) -> str: auth_check = check_auth(name='name', target_sa=target_sa) return auth_check.output
最后提交管道时传入参数:
start_pipeline = pipeline_jobs.PipelineJob( display_name='mwe', template_path='mwe.json', location='some-location', project='my-project', parameter_values={"target_sa": "my-service-account"}, enable_caching=False ) start_pipeline.submit(service_account="my-service-account")
方式二:使用工作负载身份绑定(更优雅的长期方案)
如果你的Vertex AI运行在启用工作负载身份的GKE集群上,可以通过绑定Kubernetes服务账号(KSA)和Google服务账号(GSA),让组件直接使用目标服务账号的凭据。
- 创建Kubernetes服务账号:
kubectl create serviceaccount my-ksa
- 绑定KSA到目标GSA:
gcloud iam service-accounts add-iam-policy-binding my-service-account \ --member="serviceAccount:my-project.svc.id.goog[default/my-ksa]" \ --role="roles/iam.workloadIdentityUser"
- 修改组件指定使用该KSA:
@dsl.component(packages_to_install=['google-auth'], service_account_name='my-ksa') def check_auth(name:str) -> str: import google.auth creds, project = google.auth.default() print(f'Project is: {project}') print(f'Got creds for: {creds.service_account_email}') return project
这样组件运行时会自动获取绑定的GSA凭据,google.auth.default()就能直接拿到目标服务账号的信息。
前置权限要求
无论用哪种方式,都需要确保:
- 目标服务账号拥有
Vertex AI User(roles/aiplatform.user)权限,以及组件运行所需的其他业务权限(比如GCS访问权限) - 提交管道的账号拥有模拟目标服务账号的权限(方式一需要),或者管理工作负载身份绑定的权限(方式二需要)
内容的提问来源于stack exchange,提问作者daniel.young

