如何让 Google Cloud Functions 保持温暖?

Posted

技术标签:

【中文标题】如何让 Google Cloud Functions 保持温暖?【英文标题】:How can I keep Google Cloud Functions warm? 【发布时间】:2019-01-17 20:19:52 【问题描述】:

我知道这可能首先忽略了使用 Cloud Functions 的意义,但在我的具体情况下,我使用的是 Cloud Functions,因为这是我可以将 Next.js 与 Firebase Hosting 连接起来的唯一方法。我不需要让它具有成本效益,等等。

话虽如此,Cloud Functions 的冷启动时间简直令人难以忍受,而且还没有做好生产准备,我的样板文件平均在 10 到 15 秒左右。

我观看了由 Google (https://www.youtube.com/watch?v=IOXrwFqR6kY) 提供的有关如何减少冷启动时间的视频。简而言之:1) 修剪依赖项,2) 用于在 Google 网络上缓存的依赖项版本的试验和错误,3) 延迟加载。

但是 1) 我只能修剪这么多依赖项。 2)我怎么知道哪个版本缓存更多? 3) 我只能延迟加载这么多依赖项。

另一种方法是一起避免冷启动。有什么好方法或技巧可以让我的(一个也是唯一的)云功能保持温暖?

【问题讨论】:

【参考方案1】:

你不是第一个问的 ;-)

答案是配置远程服务以定期调用您的函数,以便单个|唯一实例保持活动状态。

您的问题尚不清楚,但我认为您的 Function 提供了 HTTP 端点。在这种情况下,找到可以配置为每 x 秒|分钟进行一次 HTTP 调用的运行状况检查或 cron 服务,并将其指向您的函数。

您可能需要在时间上进行权衡才能找到“金发姑娘”时期——不要太频繁地浪费精力,也不要太频繁地导致它死去——但这是其他人所做的。

【讨论】:

感谢您的回答!我认为道格(另一个回答者)有一点,我引用:“即使你能够通过 ping 一个实例来保持它处于活动状态,系统也可能会启动任意数量的其他实例来处理当前负载。那些新的实例将具有冷启动成本。”所以ping它不会使它成为一个好的解决方案。而且我其实试过了,冷启动还是随机的…… 在低 (ping) 卷时,服务不太可能尝试使用其他实例进行扩展。您的问题表明您对替代解决方案以及是否有办法让实例保持活动状态不感兴趣。这是目前解决该问题的唯一方法。 我已经尝试过了,但它不起作用。您 ping 的实例不一定是用户请求发送到的实例。【参考方案2】:

对于所有“无服务器”计算提供商,总会有某种形式的冷启动成本是您无法消除的。即使您能够通过 ping 单个实例来保持其处于活动状态,系统也可能会启动任意数量的其他实例来处理当前负载。这些新实例将具有冷启动成本。然后,当负载减少时,不必要的实例将被关闭。

正如您所发现的,有一些方法可以最大限度地降低冷启动成本,但这些成本无法消除。

自 2021 年 9 月起,您现在可以指定保持活动的最小实例数。这有助于减少(但不能消除)冷启动。阅读Google Cloud blog 和documentation。对于 Firebase,请阅读其 documentation。请注意,设置最小实例会产生额外的费用 - 保持计算资源处于活动状态并不是一项免费服务。

如果您绝对要求热服务器 24/7 处理请求,那么您需要管理自己的 24/7 运行的服务器(并支付这些服务器 24/7 运行的成本)。如您所见,无服务器的好处是您无需管理或扩展自己的服务器,您只需为使用的内容付费,但您的项目相关的冷启动成本不可预测。这就是权衡。

【讨论】:

我愿意为低地实例设置付费,以便云功能始终处于低纬度。在开发和演示新功能时,这始终是一个问题。此外,某些功能没有使用太多,导致应用在这些区域看起来很慢。【参考方案3】:

您可以通过 cron 作业触发它,如下所述:https://cloud.google.com/scheduler/docs/creating

【讨论】:

【参考方案4】:

使用 Google Scheduler 是一个明智的解决方案,但实际实施并不那么简单。详情请查看my article。函数示例:

myHttpFunction: functions.https.onRequest((request, response) => 
  // Check if available warmup parameter.                                   
  // Use  request.query.warmup parameter if warmup request is GET.                                   
  // Use request.body.warmup parameter if warmup request is POST.                                   
  if (request.query.warmup || request.body.warmup) 
    return response.status(200).type('application/json').send(status: "success", message: "OK");
  
);
myOnCallFunction: functions.https.onCall((data, context) => 
  // Check if available warmup parameter.
  if (data.warmup) 
    return "success": true;
  
);

gcloud cli 命令示例:

gcloud --project="my-awesome-project" scheduler jobs create http  warmupMyOnCallFuntion --time-zone "America/Los_Angeles" --schedule="*/5 5-23 * * *" --uri="https://us-central1-my-awesome-project.cloudfunctions.net/myOnCallFuntion" --description="my warmup job" --headers="Content-Type=application/json" --http-method="POST" --message-body="\"data\":\"warmup\":\"true\""

gcloud --project="my-awesome-project" scheduler jobs create http  warmupMyHttpFuntion --time-zone "America/Los_Angeles" --schedule="*/5 5-23 * * *" --uri="https://us-central1-my-awesome-project.cloudfunctions.net/myHttpFuntion?warmup=true" --description="my warmup job" --headers="Content-Type=application/json" --http-method="GET"

【讨论】:

【参考方案5】:

为了将冷启动保持在最低限度,没有单一的解决方案,它是多种技术的混合。问题更多的是如何让我们的 lambda 变得如此之快,以至于我们不太关心冷启动 - 我说的是 100-500 毫秒范围内的启动时间。

如何让你的 lambda 更快?

    尽量减小包大小(删除所有使用了一小部分的大型库)- 将包大小保持在最大 20 MB。每次冷启动时都会提取并解压缩此包。 尝试在您的应用程序启动时只初始化您想要的部分。 Nodejs-https://gist.github.com/Rich-Harris/41e8ccc755ea232a5e7b88dee118bcf5 如果您的服务使用 JVM 技术,请尝试将它们迁移到 Graalvm,从而将启动开销降至最低。 micronaut + graalvm quarkus + graalvm helidon + graalvm 使用云基础架构配置来减少冷启动。

2020 年的冷启动已经没有几年前那么痛苦了。关于 AWS,我想说的更多,但我确信以上所有内容都适用于任何云提供商。

在 2019 年底 AWS 引入了 Lambda 并发配置 -https://aws.amazon.com/about-aws/whats-new/2019/12/aws-lambda-announces-provisioned-concurrency/,您不需要不得不再关心变暖了。

【讨论】:

【参考方案6】:

云功能通常最适合只执行一项(小)任务。我经常遇到想要在一个云功能中完成所有事情的人。说实话,这也是我开始开发云功能的方式。

考虑到这一点,您应该保持云函数代码简洁且只执行一项任务。通常这将是后台任务、需要写入某处的文件或记录,或者必须执行的检查。在这种情况下,是否有冷启动惩罚并不重要。

但现在,包括我自己在内的人们都依赖云功能作为 API GatewayCloud Endpoints 的后端。在这种情况下,用户访问一个网站,该网站向云功能发送后端请求以获取一些附加信息。现在云函数充当 API,用户正在等待它。

典型的云功能:

典型的云功能:

有几种方法可以解决冷启动问题:

减少依赖和代码量。正如我之前所说,云功能最适合执行单个任务。这将减少在接收请求和执行代码之间必须加载到服务器的整体包大小,从而显着加快处理速度。 另一种更老套的方法是安排云调度程序定期向您的云函数发送预热请求。 GCP 有一个慷慨的免费层,它允许 3 个调度程序和 200 万个云函数调用(取决于资源使用情况)。因此,根据云功能的数量,您可以轻松地每隔几秒安排一次 http 请求。为了清楚起见,我在这篇文章下方放置了一个 sn-p,它部署了一个云功能和一个发送预热请求的调度程序。

如果您认为您已经调整了冷启动问题,您还可以采取措施加快实际运行时间:

我从 Python 切换到 Golang,这让我在实际运行时方面获得了两位数的性能提升。 Golang 的速度可与 Java 或 C++ 媲美。 在全局级别声明变量,尤其是 GCP 客户端,如存储、发布/订阅等 (source)。这样,您的云函数的未来调用将重用该对象。 如果您在云函数中执行多个独立操作,可以将它们设为异步。 同样,干净的代码和更少的依赖项也改善了运行时。

片段:

# Deploy function
gcloud functions deploy warm-function \
  --runtime=go113 \
  --entry-point=Function \
  --trigger-http \
  --project=$PROJECT_ID \
  --region=europe-west1 \
  --timeout=5s \
  --memory=128MB

# Set IAM bindings
gcloud functions add-iam-policy-binding warm-function \
  --region=europe-west1 \
  --member=serviceAccount:$PROJECT_ID@appspot.gserviceaccount.com \
  --role=roles/cloudfunctions.invoker

# Create scheduler
gcloud scheduler jobs create http warmup-job \
  --schedule='*/5 * * * *' \
  --uri='https://europe-west1-$PROJECT_ID.cloudfunctions.net/warm-function' \
  --project=$PROJECT_ID \
  --http-method=OPTIONS \
  --oidc-service-account-email=$PROJECT_ID@appspot.gserviceaccount.com \
  --oidc-token-audience=https://europe-west1-$PROJECT_ID.cloudfunctions.net/warm-function

【讨论】:

【参考方案7】:

Google has just announced 能够为您的 Cloud Function 部署设置 min-instances。这使您可以设置缩小函数的下限并最大限度地减少冷启动(它们不承诺会消除它们)。

保持温暖实例(空闲时间)的成本很小 - 尽管在撰写本文时,Cloud Functions pricing page 上似乎没有记录。他们说:

如果您设置了函数实例的最小数量,您也需要付费 暂时这些实例不活跃。这称为空闲时间 并以不同的价格定价。

【讨论】:

【参考方案8】:

您现在可以指定 MIN_INSTANCE_LIMIT 以保持实例始终运行。

云函数文档:https://cloud.google.com/functions/docs/configuring/min-instances

文档中的 Cloud Functions 示例:

gcloud beta functions deploy myFunction --min-instances 5

通过指定minInstances,它也可以在 Firebase Functions 中使用:

Firebase 函数文档:https://firebase.google.com/docs/functions/manage-functions#min-max-instances

弗兰克在 Twitter 上宣布:https://twitter.com/puf/status/1433431768963633152

文档中的 Firebase 函数示例:

exports.getAutocompleteResponse = functions
    .runWith(
      // Keep 5 instances warm for this latency-critical function
      minInstances: 5,
    )
    .https.onCall((data, context) => 
      // Autocomplete a user's search term
    );

【讨论】:

请务必注意,这并不能保证 100% 的时间完全加热实例。它确实减少了冷启动的机会,但冷启动仍然会发生。特别是,服务器实例在处理最大数量的请求(它们不是“永久实例”)后关闭并重新启动后会出现冷启动。如果分配的服务器实例超出最小数量,则会出现冷启动。如果函数的代码崩溃,则可能会出现冷启动,这总是会关闭该实例。您还需要为启用最少实例的空闲 CPU 时间付费。 请务必添加某种性能监控或基准,以帮助了解此设置是否有利于您的流量模式,并为额外的计费做好准备以确定是否值得。 @DougStevenson 优点。另请注意,在 Cloud Functions 中使用 Firestore SDK 时存在固有的冷启动问题,开发人员应注意:issuetracker.google.com/issues/158014637?pli=1

以上是关于如何让 Google Cloud Functions 保持温暖?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Google Cloud Function 上的 Spring Cloud 函数中获取 Pub/Sub 事件的元数据

如何解决 Google Cloud Function 上的“No module named 'frontend'”错误消息

如何在 Python 中运行 Google Cloud Function 中的子进程

如何从 Firebase Cloud Function 在 Google Pub/Sub 中发布消息?

如何从Google Cloud Function(Cheerio,Node.js)发出多个http请求

如何让 Google Cloud Functions 保持温暖?