如何让 Google Cloud Functions 保持温暖?

How can I keep Google Cloud Functions warm?

我知道这可能首先忽略了使用 Cloud Functions 的要点,但在我的特定情况下,我正在使用 Cloud Functions,因为这是我可以桥接 Next.js 与 Firebase 托管的唯一方式。我不需要使其具有成本效益等

话虽如此,Cloud Functions 的冷启动时间简直令人无法忍受,而且无法投入生产,我的样板平均需要 10 到 15

我看过 Google (https://www.youtube.com/watch?v=IOXrwFqR6kY) 的这个视频,其中讨论了如何减少冷启动时间。简而言之:1) Trim 依赖项,2) Google 网络上缓存的依赖项版本的试错,3) 延迟加载。

但是 1) 我能做到的只有这么多依赖项 trim。 2)我怎么知道哪个版本缓存更多? 3) 我可以延迟加载的依赖项只有这么多。

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

你不是第一个问的人 ;-)

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

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

您可能需要兼顾时机才能找到金发姑娘时期——不要太频繁以至于你浪费了努力,也不要太频繁以至于它死了——但这就是其他人所做的。

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

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

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

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

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

使用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"

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

如何让你的 lambda 更快?

  1. 使您的包大小尽可能小(删除所有使用了一小部分的大库)- 将包大小保持在最大 20 MB。在每次冷启动时,都会获取并解压缩此包。
  2. 尝试在您的应用程序启动时只初始化您想要的部分。 节点 - 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/,您再也不用担心变暖了。

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

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

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

典型云函数:

典型云函数:

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

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

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

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

片段:

# 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

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

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

If you set a minimum number of function instances, you are also billed for the time these instances are not active. This is called idle time and is priced at a different rate.

您现在可以指定 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

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