如何保持Google Cloud Functions的热启动状态?

42

我知道这可能忽略了使用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)我只能延迟加载有限的依赖项。

另一种方法是避免完全进行冷启动。有什么好的方法或技巧可以保持我的(唯一的)云函数保持热状态?

8个回答

45

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

有方法可以最小化冷启动成本,就像你已经发现的那样,但成本不能被消除。

截至2021年9月,您现在可以指定最小数量的实例保持活动状态。这可以帮助减少(但不能消除)冷启动。请参阅Google Cloud博客文档。对于Firebase,请阅读其文档。请注意,设置最小实例会产生额外的计费 - 保持计算资源处于活动状态并不是一个免费的服务。

如果你绝对需要热服务器来处理24/7的请求,那么你需要管理自己的服务器并让它们24/7运行(并支付这些服务器24/7运行的成本)。正如你所看到的,无服务器的好处在于你不需要管理或扩展自己的服务器,你只为你使用的内容付费,但是你的项目会有不可预测的冷启动成本。这就是权衡。

12
我愿意付费设置一个低延迟的实例,以便云函数始终具有低延迟。在开发和演示新功能时,这总是一个问题。此外,一些函数很少被使用,导致应用在这些区域看起来很慢。 - Oliver Dixon

6

你并不是第一个提出这个问题的人 ;-)

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

从你的问题中不清楚,但我假设你的函数提供了一个HTTP端点。在这种情况下,请找到一个健康检查或定时任务服务,可以配置它每隔x秒|分钟进行一次HTTP调用,并将其指向你的函数。

你可能需要调整时间以找到最佳执行周期,不要太频繁浪费资源,也不要太少以致于函数失效,但这是其他人所做的。


2
谢谢回答!我认为道格(另一个回答者)说得有道理,我引用他的话:“即使您能够通过ping来保持单个实例处于活动状态,系统仍可能启动任意数量的其他实例来处理当前负载。这些新实例将具有冷启动成本。”因此,ping它并不是一个好的解决方案。而且我实际尝试过,冷启动仍然是随机的... - harrisonlo
3
在低(ping)负载量下,服务不太可能尝试通过添加实例来进行扩展。根据您的问题,您似乎对替代解决方案没有兴趣,而是想知道是否有方法可以使实例保持活动状态。目前,这是解决该问题的唯一方法。 - DazWilkin
我已经尝试过这个,但它不起作用。您ping的实例不一定是用户请求发送到的实例。 - stevehs17

5

现在您可以指定MIN_INSTANCE_LIMIT来保持实例一直运行。

Cloud Functions文档:https://cloud.google.com/functions/docs/configuring/min-instances

云函数文档中的示例:

gcloud beta functions deploy myFunction --min-instances 5

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

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

Frank在Twitter上发布的公告:https://twitter.com/puf/status/1433431768963633152

Firebase Functions文档中的示例:

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
    });

需要注意的是,这并不能保证每次都能完全预热实例。它确实减少了冷启动的机会,但仍然可能发生冷启动。特别是,在服务器实例关闭并在处理最大请求数后重新启动时,将会出现冷启动(它们不是“永久实例”)。如果服务器实例分配超出最小数量,也会出现冷启动。如果函数代码崩溃,将始终关闭该实例,可能会出现冷启动。启用最小实例后,您还需要支付空闲CPU时间的费用。 - Doug Stevenson
1
请务必添加某种性能监控或基准测试,以帮助了解此设置是否有益于您的流量模式,并准备好额外的账单以确定其是否值得。 - Doug Stevenson
@DougStevenson 的观点很好。还要注意,在云函数中使用 Firestore SDK 存在冷启动问题,开发人员应该意识到这一点:https://issuetracker.google.com/issues/158014637?pli=1 - Johnny Oshika

2

2

Google刚刚宣布,现在可以设置Cloud Function部署的min-instances。这使您可以设置函数缩小的下限,并最小化冷启动(他们并不保证完全消除)。

保持热实例(空闲时间)会产生一些成本,尽管在撰写本文时,这似乎未在Cloud Functions定价页面中记录。他们说:

如果您设置了一定数量的函数实例,则还将为这些未激活的实例计费。这称为空闲时间,其价格不同。


2

使用Google Scheduler是一个明智的选择,但实际实现并不那么简单。请查看我的文章了解详情。函数示例:

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命令行界面的示例:

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"

1

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

因此,您应该保持云函数代码的简洁和小巧,以执行一个任务。通常这将是后台任务、需要写入某个地方的文件或记录,或需要执行的检查。在这种情况下,如果存在“冷启动惩罚”,那么并不重要。

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

典型的云函数:

enter image description here

典型的温暖云函数:

enter image description here

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

  • 减少依赖和代码量。如前所述,云函数最适合执行单个任务。这将减少在接收请求并执行代码之间加载到服务器的总包大小,从而显着加快速度。
  • 另一种更狡猾的方法是安排一个云计划程序定期向您的云函数发送预热请求。GCP有一个慷慨的免费层,允许3个计划程序和200万次云函数调用(取决于资源使用情况)。因此,根据云函数的数量,您可以轻松地安排每隔几秒钟进行一次HTTP请求。为了清晰起见,我在本帖下面放置了一个片段,其中部署了一个云函数和一个调度程序,用于发送预热请求。

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

  • 我从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

0
为了将冷启动最小化,没有单一的解决方案,而是多种技术的混合。问题更多地是如何使我们的 Lambda 如此快,以至于我们不那么关心冷启动 - 我在谈论 100-500 毫秒 的启动时间范围。 如何使您的 Lambda 更快?
  1. 尽可能保持软件包大小小(删除所有仅使用其中一小部分的大型库)- 将软件包大小保持在最大20 MB。每次冷启动时,都会获取并解压缩此软件包。
  2. 尝试仅在应用程序启动时初始化所需的部分。 Nodejs - https://gist.github.com/Rich-Harris/41e8ccc755ea232a5e7b88dee118bcf5
  3. 如果您的服务使用JVM技术,请尝试将其迁移到Graalvm,其中启动开销降至最低。
    • micronaut + graalvm
    • quarkus + graalvm
    • helidon + graalvm
  4. 使用云基础架构配置来减少冷启动。

在2020年,与几年前相比,冷启动不再是痛点。我想多说一些关于AWS,但我确信以上所有内容对任何云提供商都适用。

在2019年底,AWS推出了Lambda并发预配功能-https://aws.amazon.com/about-aws/whats-new/2019/12/aws-lambda-announces-provisioned-concurrency/,现在你不必再过多关注函数的预热问题了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接