为啥在由 Apache 运行时,来自 Google Secret Manager API 的 gRPC 调用会挂起?
Posted
技术标签:
【中文标题】为啥在由 Apache 运行时,来自 Google Secret Manager API 的 gRPC 调用会挂起?【英文标题】:Why does this gRPC call from the Google Secret Manager API hang when run by Apache?为什么在由 Apache 运行时,来自 Google Secret Manager API 的 gRPC 调用会挂起? 【发布时间】:2021-10-26 04:49:13 【问题描述】:简而言之:
我有一个由 Apache 在 Google Compute Engine VM 上提供的 Django 应用程序。
我想在我的 Python 代码中从 Google Secret Manager 访问一个秘密(当 Django 应用程序正在初始化时)。
当我执行“python manage.py runserver”时,已成功检索到密钥。但是,当我让 Apache 运行我的应用程序时,它会在向密钥管理器发送请求时挂起。
太多细节:
我关注了这个问题的答案GCP VM Instance is not able to access secrets from Secret Manager despite of appropriate Roles。我创建了一个服务帐户(不是默认帐户),并为其指定了“云平台”范围。我还在 Web 控制台中为其赋予了“Secret Manager Admin”角色。
在最初遇到问题后,我从 Web 控制台下载了服务帐户的 json 密钥,并将 GOOGLE_APPLICATION_CREDENTIALS env-var 设置为指向它。
当我直接在虚拟机上运行 django 服务器时,一切正常。当我让 Apache 运行应用程序时,我可以从日志中看到服务帐户凭据 json 已成功加载。
但是,当我通过 google.cloud.secretmanager.SecretManagerServiceClient.list_secret_versions 进行第一次 API 调用时,应用程序挂起。我什至没有在浏览器中收到 500 错误,只是一个永恒的加载图标。我追踪了执行情况:
grpc._channel._UnaryUnaryMultiCallable._blocking,第 926 行:'call = self._channel.segregated_call(...'
它永远不会超过那条线。我不知道那个电话的去向,所以我无法进一步检查它。
想法
我不太了解 GCP 服务帐号/API 访问。我不明白为什么 django 开发服务器和 apache 之间会出现这种差异,因为它们都使用来自 json 的相同服务帐户凭据。我也很惊讶该应用程序只是挂在谷歌库中而不是抛出异常。发送请求时甚至还有一个超时选项,但更改它并没有任何区别。
我想知道这是否与我在自己的帐户下运行 django 服务器这一事实有关,但 apache 使用它使用的任何用户帐户?
更新
我尝试更改 apache 运行的用户/组以匹配我自己的。没有变化。
我启用了logging for gRPC itself。使用 apache 和 django 开发服务器运行时有明显的区别。
在 Django 上:
secure_channel_create.cc:178] grpc_secure_channel_create(creds=0x17cfda0, target=secretmanager.googleapis.com:443, args=0x7fe254620f20, reserved=(nil)) init.cc:167] grpc_init(void) client_channel.cc:1099] chand=0x2299b88:为通道堆栈 0x2299b18 创建 client_channel ... timer_manager.cc:188] 休眠 1001 毫秒 ... client_channel.cc:1879]chand=0x2299b88 calld=0x229e440: 创建调用 ... call.cc:1980] grpc_call_start_batch(call=0x229daa0, ops=0x20cfe70, nops=6, tag=0x7fe25463c680, reserved=(nil)) call.cc:1573] ops[0]: SEND_INITIAL_METADATA... call.cc:1573] ops[1]: SEND_MESSAGE ptr=0x21f7a20 ...
因此,创建了一个通道,然后创建了一个调用,然后我们看到 gRPC 开始执行该调用的操作(据我阅读)。
在 Apache 上:
secure_channel_create.cc:178] grpc_secure_channel_create(creds=0x7fd5bc850f70, target=secretmanager.googleapis.com:443, args=0x7fd583065c50, reserved=(nil)) init.cc:167] grpc_init(void) client_channel.cc:1099] chand=0x7fd5bca91bb8:为通道堆栈 0x7fd5bca91b48 创建 client_channel ... timer_manager.cc:188] 休眠 1001 毫秒 ... timer_manager.cc:188] 休眠 1001 毫秒 ...所以,我们创建了一个频道……然后什么也没有。没有电话,没有操作。因此,python 代码就在那里等待 gRPC 进行此调用,而它永远不会这样做。
【问题讨论】:
您可以尝试将GOOGLE_CLOUD_DISABLE_GRPC=true
设置为环境变量吗?
我试过设置,没有任何变化。查看源代码,在我看来,秘密管理器客户端没有非 grpc 实现 - 它允许传输的唯一选项是“grpc”或“grpc_asyncio”。
我也试过从API调用其他方法,结果一样
【参考方案1】:
问题似乎是 Apache 的分叉行为以某种方式破坏了 gRPC。我无法确定确切的原因,但在我开始怀疑分叉是问题之后,我发现 this old gRPC issue 表明分叉是一个有点棘手的领域。
我尝试重新配置 Apache 以使用不同的“多处理模块”,但由于我在这方面的经验有限,我无法让 gRPC 在其中任何一个下工作。
最后,我切换到使用 nginx/uwsgi 而不是 Apache/mod_wsgi,我没有遇到同样的问题。如果您尝试解决此类问题并且必须使用 Apache,我建议您进一步研究 Apache 分叉、gRPC 如何处理分叉以及可用于 Apache 的不同 MPM。
【讨论】:
以上是关于为啥在由 Apache 运行时,来自 Google Secret Manager API 的 gRPC 调用会挂起?的主要内容,如果未能解决你的问题,请参考以下文章
Docker 容器在手动运行容器时可由 localhost 访问,但在由 jenkinsfile 运行时无法访问
如何在运行时在由 android studio 创建的导航抽屉中设置默认片段