GCP API - 来自 IAM 和 Ai Platform APIs 的相同 "failed to connect to all addresses" 错误

GCP API - same "failed to connect to all addresses" error from IAM and Ai Platform APIs

我在尝试使用两个不同的 GCP API 库时遇到一个常见错误。上下文是我正在调用以验证是否正确创建了资源——我不是在尝试创建资源,只是在尝试读取它们。对于两者,我将环境变量 GOOGLE_APPLICATION_CREDENTIALS 设置为服务帐户凭据的 json 文件。我确信这意味着我的身份验证或代码中缺少一些我尚未在文档中看到的常见内容。如果有人能指出我正确的方向,那就太棒了。

例子一: 使用 Google.Cloud.Iam.Admin.V1 调用 ListRoles 方法。此代码来自以下示例:

https://cloud.google.com/dotnet/docs/reference/Google.Cloud.Iam.Admin.V1/latest/Google.Cloud.Iam.Admin.V1.IAMClient#Google_Cloud_Iam_Admin_V1_IAMClient_ListRoles_Google_Cloud_Iam_Admin_V1_ListRolesRequest_Google_Api_Gax_Grpc_CallSettings_

代码:

23. // Create client
24. IAMClient iAMClient = IAMClient.Create();
25. // Initialize request argument(s)
26. ListRolesRequest request = new ListRolesRequest
27. {
28.     ParentAsResourceName = new UnparsedResourceName("//bigquery.googleapis.com/projects/MYPROJECTID/datasets/test_hm_bigquery"),
29.     View = RoleView.Basic,
30.     ShowDeleted = false,
31. };
32. // Make the request
33. PagedEnumerable<ListRolesResponse, Role> response = iAMClient.ListRoles(request);
34. 
35. // Iterate over all response items, lazily performing RPCs as required
36. foreach (Role item in response)
37. {
38.     // Do something with each item
39.     Console.WriteLine(item);
40. }

尝试遍历响应对象时执行第 36 行时显示错误。

Grpc.Core.RpcException
  HResult=0x80131500
  Message=Status(StatusCode="Unavailable", Detail="failed to connect to all addresses", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"@1646170431.164000000","description":"Failed to pick subchannel","file":"..\..\..\src\core\ext\filters\client_channel\client_channel.cc","file_line":3159,"referenced_errors":[{"created":"@1646170431.164000000","description":"failed to connect to all addresses","file":"..\..\..\src\core\lib\transport\error_utils.cc","file_line":147,"grpc_status":14}]}")
  Source=System.Private.CoreLib
  StackTrace:
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Grpc.Core.Internal.AsyncCall`2.UnaryCall(TRequest msg)
   at Grpc.Core.Calls.BlockingUnaryCall[TRequest,TResponse](CallInvocationDetails`2 call, TRequest req)
   at Grpc.Core.DefaultCallInvoker.BlockingUnaryCall[TRequest,TResponse](Method`2 method, String host, CallOptions options, TRequest request)
   at Grpc.Core.Interceptors.InterceptingCallInvoker.<BlockingUnaryCall>b__3_0[TRequest,TResponse](TRequest req, ClientInterceptorContext`2 ctx)
   at Grpc.Core.ClientBase.ClientBaseConfiguration.ClientBaseConfigurationInterceptor.BlockingUnaryCall[TRequest,TResponse](TRequest request, ClientInterceptorContext`2 context, BlockingUnaryCallContinuation`2 continuation)
   at Grpc.Core.Interceptors.InterceptingCallInvoker.BlockingUnaryCall[TRequest,TResponse](Method`2 method, String host, CallOptions options, TRequest request)
   at Google.Cloud.Iam.Admin.V1.IAM.IAMClient.ListRoles(ListRolesRequest request, CallOptions options)
   at Google.Api.Gax.Grpc.ApiCall.GrpcCallAdapter`2.CallSync(TRequest request, CallSettings callSettings)
   at Google.Api.Gax.Grpc.ApiCallRetryExtensions.<>c__DisplayClass1_0`2.<WithRetry>b__0(TRequest request, CallSettings callSettings)
   at Google.Api.Gax.Grpc.ApiCall`2.Sync(TRequest request, CallSettings perCallCallSettings)
   at Google.Api.Gax.Grpc.GrpcPagedEnumerable`3.<AsRawResponses>d__4.MoveNext()
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
   at Terraform.Tests.BigQueryDatasetTests.Test() in C:\projects\Terraform.Tests\Terraform.Tests\BigQueryDatasetTests.cs:line 36

例二: 使用 Google.Cloud.AIPlatform.V1。此代码来自

https://cloud.google.com/dotnet/docs/reference/Google.Cloud.AIPlatform.V1/latest/Google.Cloud.AIPlatform.V1.ModelServiceClient#Google_Cloud_AIPlatform_V1_ModelServiceClient_GetModel_Google_Cloud_AIPlatform_V1_ModelName_Google_Api_Gax_Grpc_CallSettings_

19. ModelServiceClient modelServiceClient = ModelServiceClient.Create();
20. ModelName name = ModelName.FromProjectLocationModel("MYPROJECTID", "us-central1", "testhm");
21. Model response = modelServiceClient.GetModel(name);

执行第21行时出现错误

Grpc.Core.RpcException
  HResult=0x80131500
  Message=Status(StatusCode="Unavailable", Detail="failed to connect to all addresses", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"@1646171341.746000000","description":"Failed to pick subchannel","file":"..\..\..\src\core\ext\filters\client_channel\client_channel.cc","file_line":3159,"referenced_errors":[{"created":"@1646171341.746000000","description":"failed to connect to all addresses","file":"..\..\..\src\core\lib\transport\error_utils.cc","file_line":147,"grpc_status":14}]}")
  Source=System.Private.CoreLib
  StackTrace:
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Grpc.Core.Internal.AsyncCall`2.UnaryCall(TRequest msg)
   at Grpc.Core.Calls.BlockingUnaryCall[TRequest,TResponse](CallInvocationDetails`2 call, TRequest req)
   at Grpc.Core.DefaultCallInvoker.BlockingUnaryCall[TRequest,TResponse](Method`2 method, String host, CallOptions options, TRequest request)
   at Grpc.Core.Interceptors.InterceptingCallInvoker.<BlockingUnaryCall>b__3_0[TRequest,TResponse](TRequest req, ClientInterceptorContext`2 ctx)
   at Grpc.Core.ClientBase.ClientBaseConfiguration.ClientBaseConfigurationInterceptor.BlockingUnaryCall[TRequest,TResponse](TRequest request, ClientInterceptorContext`2 context, BlockingUnaryCallContinuation`2 continuation)
   at Grpc.Core.Interceptors.InterceptingCallInvoker.BlockingUnaryCall[TRequest,TResponse](Method`2 method, String host, CallOptions options, TRequest request)
   at Google.Cloud.AIPlatform.V1.ModelService.ModelServiceClient.GetModel(GetModelRequest request, CallOptions options)
   at Google.Api.Gax.Grpc.ApiCall.GrpcCallAdapter`2.CallSync(TRequest request, CallSettings callSettings)
   at Google.Api.Gax.Grpc.ApiCallRetryExtensions.<>c__DisplayClass1_0`2.<WithRetry>b__0(TRequest request, CallSettings callSettings)
   at Google.Api.Gax.Grpc.ApiCall`2.<>c__DisplayClass10_0.<WithCallSettingsOverlay>b__1(TRequest req, CallSettings cs)
   at Google.Api.Gax.Grpc.ApiCall`2.Sync(TRequest request, CallSettings perCallCallSettings)
   at Google.Cloud.AIPlatform.V1.ModelServiceClientImpl.GetModel(GetModelRequest request, CallSettings callSettings)
   at Google.Cloud.AIPlatform.V1.ModelServiceClient.GetModel(ModelName name, CallSettings callSettings)
   at Terraform.Tests.AiPlatformTests.AiModel_Test_Success() in C:\projects\Terraform.Tests\Terraform.Tests\AiPlatformTests.cs:line 21

为了尝试找出问题所在,我还对其进行了调试并使用 Fiddler 查看进出流量。无论哪种方式,我都看不到任何东西。为了确保我正确设置了 Fiddler,我对 github 进行了一个简单的 HTTP 调用,并且看到了正常的条目。所以我对发生的事情有点困惑。

这是一个使用 Cloud (!) IAM Admin 客户端库的示例:

PROJECT=[YOUR-PROJECT]
BILLING=[YOUR-BILLING]
ACCOUNT=[YOUR-ACCOUNT] # Service Account name

gcloud projects create ${PROJECT}
gcloud beta billing projects link ${PROJECT} \
--billing-account=${BILLING}

gcloud services enable iam.googleapis.com \
--project=${PROJECT}

gcloud iam service-accounts create ${ACCOUNT} \
--project=${PROJECT}

gcloud iam service-accounts keys create ${PWD}/${ACCOUNT}.json

EMAIL=${ACCOUNT}@${PROJECT}.iam.gserviceaccount.com

gcloud iam service-accounts keys create ${PWD}/${ACCOUNT}.json \
--iam-account=${EMAIL} \
--project=${PROJECT}

我没有在本地安装 .NET,运行 从容器中安装。当我 运行 容器时,不需要 export GOOGLE_APPLICATION_CREDENTIALS 作为导出值。

云客户端库
podman run \
--interactive --tty --rm \
--volume=${PWD}/cloud:/app \
--volume=${PWD}/${ACCOUNT}.json:/secrets/key.json \
--env=GOOGLE_APPLICATION_CREDENTIALS=/secrets/key.json \
--workdir=/app \
mcr.microsoft.com/dotnet/sdk:6.0 \
bash

NOTE mounts ${PWD}/cloud as /app

using System;

using Grpc.Core;
using Google.Api.Gax;
using Google.Cloud.Iam.Admin.V1;

namespace app {
    class Program {
        static void Main(string[] args) {
            IAMClient client = IAMClient.Create();

            ListRolesRequest rqst = new ListRolesRequest
            {
                View = RoleView.Basic,
                ShowDeleted = false,
            };
            PagedEnumerable<ListRolesResponse, Role> resp = client.ListRoles(rqst);

            foreach (Role item in resp)
            {
                Console.WriteLine(item.Name);
            }
        }
    }
}

这将执行 roles.list,枚举为项目定义的所有角色。

然后:

dotnet new console

dotnet add package Google.Cloud.Iam.Admin.V1
dotnet add package Google.Api.Gax

dotnet run
API 客户端库
podman run \
--interactive --tty --rm \
--volume=${PWD}/api:/app \
--volume=${PWD}/${ACCOUNT}.json:/secrets/key.json \
--env=GOOGLE_APPLICATION_CREDENTIALS=/secrets/key.json \
--workdir=/app \
mcr.microsoft.com/dotnet/sdk:6.0 \
bash

NOTE Mounts ${PWD}/api as /app

然后:

dotnet new console

dotnet add package Google.Apis.Iam.v1

dotnet run

有:

using System;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Iam.v1;
using Google.Apis.Iam.v1.Data;
using Google.Apis.Services;

namespace app
{
    class Program
    {
        const string scope = "https://www.googleapis.com/auth/cloud-platform";
        static void Main(string[] args)
        {
            IamService iamService = new IamService(
                new BaseClientService.Initializer
                {
                    HttpClientInitializer = GetCredential(),
                    ApplicationName = "test",
                });

            var rqst = iamService.Roles.List();
            ListRolesResponse resp = rqst.Execute();

            foreach (Role role in resp.Roles)
            {
                Console.WriteLine(role.Name);
            }
        }
        public static GoogleCredential GetCredential()
        {
            GoogleCredential credential = Task.Run(() => 
                GoogleCredential.GetApplicationDefaultAsync()).Result;

            if (credential.IsCreateScopedRequired)
            {
                credential = credential.CreateScoped(scope);
            }
           return credential;
        }
    }
}
结果

两者都产生:

roles/accessapproval.approver
roles/accessapproval.configEditor
roles/accessapproval.viewer
roles/accesscontextmanager.gcpAccessAdmin
roles/accesscontextmanager.gcpAccessReader
roles/accesscontextmanager.policyAdmin
roles/accesscontextmanager.policyEditor
roles/accesscontextmanager.policyReader
roles/accesscontextmanager.vpcScTroubleshooterViewer
roles/actions.Admin
...

以为我会关闭我提出的这个问题,因为我发现答案是我一直怀疑的东西:正如 Jon Skeet 上面提到的那样,它是网络。客户端的环境需要一个更新的 SSL 证书和几个需要指向它的环境变量。花了一段时间才让客户的合适人员告诉我显示工作站应该如何配置的文档在哪里。我错误地认为这是他们的 IT 人员在设置计算机时完成的(我正在 RDPing 中)。无论如何,一切都好,一切都好。 DazWilkin,感谢你帮助我解决这个问题!