使用 Cloud Functions 进行无服务器 Web 性能监控


本教程介绍了如何使用 Google Cloud 无服务器技术创建 Web 性能监控应用。

性能对于任何 Web 应用的成功都起着非常重要的作用。如果您的网站性能不佳,则注册人数会比较少,用户留存率会比较低,这可能会影响您的业务目标。在设计、构建和测试 Web 应用时,性能应该是衡量成功的一个关键标准。

不过,随着应用的发展,网页性能也可能会随时间发生变化。开发者可能会添加或更新映像和脚本,或者底层应用服务基础架构本身可能会发生变化。因此,定期监控网页性能非常重要。通常,您需要存储性能指标以便进行历史记录分析。此外,您通常还需要设置一些提醒消息,以便系统在网页性能低于某些定义的阈值时收到提醒。

目标

  • 创建一个使用无头 Chrome 来收集网页性能指标的 Cloud Functions 函数
  • 将指标存储在 Cloud Storage 中。
  • 创建另一个由 Cloud Storage 创建事件触发的 Cloud Functions 函数,以分析网页指标。
  • 将分析结果存储在 Firestore 中。
  • 创建另一个由 Firestore 创建事件触发的 Cloud Functions 函数,以便在网页性能不佳时向 Pub/Sub 发布提醒消息。
  • 创建 Cloud Scheduler 作业以定期触发第一个 Cloud Functions 函数。
  • 验证成功和失败情况下的输出。

费用

本教程使用 Google Cloud 的以下收费组件:

  • Cloud Functions
  • Cloud Scheduler
  • Cloud Storage
  • Firestore
  • Pub/Sub
  • Container Registry
  • Cloud Build

您可使用价格计算器根据您的预计使用情况来估算费用。

准备工作

  1. 登录您的 Google Cloud 账号。如果您是 Google Cloud 新手,请创建一个账号来评估我们的产品在实际场景中的表现。新客户还可获享 $300 赠金,用于运行、测试和部署工作负载。
  2. 在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目

    转到“项目选择器”

  3. 确保您的 Google Cloud 项目已启用结算功能

  4. 启用 Cloud Functions, Cloud Scheduler, Pub/Sub, and Cloud Build API。

    启用 API

  5. 在 Google Cloud Console 中的项目选择器页面上,选择或创建一个 Google Cloud 项目

    转到“项目选择器”

  6. 确保您的 Google Cloud 项目已启用结算功能

  7. 启用 Cloud Functions, Cloud Scheduler, Pub/Sub, and Cloud Build API。

    启用 API

架构

Web 性能监控操作通常是无状态的,而且持续时间很短。它们通常也是由事件驱动的 - 要么按时间表执行,要么作为其他某个过程(例如自动化测试流水线)的一部分触发。这些特性使得无服务器架构成为实现网络分析应用的理想选择。

在本教程中,您将使用 Google Cloud 无服务器栈的各种组件,包括 Cloud Functions、Firestore、Cloud Scheduler、Pub/Sub。您不必管理这些服务的基础架构,并且只需为所用的资源付费。应用的核心是使用 Cloud Functions 实现的,Cloud Functions 提供了一个可扩缩的事件驱动型无服务器执行环境。借助 Cloud Functions,您可以使用松散耦合的独立逻辑块创建和连接应用。

下图展示了您在本教程中创建的无服务器解决方案的架构。

无服务器网络分析解决方案的架构

准备环境

在创建无服务器环境之前,您需要从 GitHub 获取代码、设置变量,以及准备稍后进行分析和存储所需的资源。

获取代码并设置环境变量

  1. 在 Google Cloud Console 中,打开 Cloud Shell。

    打开 Cloud Shell

  2. 克隆包含本教程中使用的 Cloud Functions 函数代码的代码库:

    git clone https://github.com/GoogleCloudPlatform/solutions-serverless-web-monitoring.git
    
  3. 切换到 functions 目录:

    cd solutions-serverless-web-monitoring/functions
    
  4. 将当前项目 ID 和项目编号设置为 shell 变量:

    export PROJECT=$DEVSHELL_PROJECT_ID
    export PROJECT_NUM=$(gcloud projects list \
        --filter="$PROJECT" \
        --format="value(PROJECT_NUMBER)")
    
  5. 设置 Cloud Functions 的默认部署区域。以下示例将区域设置为 us-east1,但您可以将此区域更改为可以使用 Cloud Functions 的任何区域

    export REGION=us-east1
    gcloud config set functions/region $REGION
    

创建 Cloud Storage 存储分区

在本部分中,您将创建一个 Cloud Storage 存储分区以存储收集的网页性能数据。您可以选择任何位置或存储类别,但最好是在将要使用相应存储分区的 Cloud Functions 函数所在的位置创建存储分区。

  1. 在 Cloud Shell 中,导出将要存储指标的 Cloud Storage 存储分区的名称的 shell 变量。存储分区名称必须是全局唯一的,因此以下命令将您的 Google Cloud 项目编号用作存储分区名称的后缀。

    export METRICS_BUCKET=page-metrics-$PROJECT_NUM
    
  2. 使用 gsutil 工具创建存储分区:

    gsutil mb -l $REGION gs://$METRICS_BUCKET
    
  3. 使用存储分区名称更新 env-vars.yaml 文件。此文件包含您稍后将传递给 Cloud Functions 函数的环境变量。

    sed -i "s/\[YOUR_METRICS_BUCKET\]/$METRICS_BUCKET/" env-vars.yaml
    

创建 Firestore 集合

在稍后的部分中,您将分析网页性能指标。在本部分中,您将创建一个 Firestore 集合来存储每项分析的结果。

  1. 在 Google Cloud Console 中,前往 Firestore 页面。

    转到 Firestore 页面

  2. 如果您之前从未创建过 Firestore 数据库,请执行以下步骤:

    1. 点击选择原生模式以激活 Firestore。
    2. 选择靠近您的 Cloud Functions 函数运行区域的区域位置。
    3. 点击创建数据库

    完成配置需要一些时间。

  3. 点击开始收集,然后将集合 ID 设置为 page-metrics

  4. 点击保存

创建 Pub/Sub 主题和订阅

如果分析表明网页性能不佳,您通常需要通知相关系统和各方。在本部分中,您将创建 Pub/Sub 主题,其中将包含描述任何不佳性能的消息。

  1. 在 Cloud Shell 中,创建名为 performance-alerts 的 Pub/Sub 主题:

    gcloud pubsub topics create performance-alerts
    
  2. 创建对该主题的订阅。您可以使用该订阅来验证提醒消息是否会发布到该主题。

    gcloud pubsub subscriptions create performance-alerts-sub \
        --topic performance-alerts
    

收集网页性能指标

很多网站都会使用 JavaScript 来动态呈现网页内容。这会使性能分析变得更复杂,因为客户端需要模拟浏览器才能完全加载网页。适用于 Cloud Functions 的 Node.js 运行时环境支持无头 Chrome,无头 Chrome 可以在无服务器环境中提供完整网络浏览器的功能。

Puppeteer 是由 Chrome DevTools 团队构建的 Node.js 库,用于提供高级 API 来控制无头 Chrome。默认情况下,Puppeleer 会安装最新版本的浏览器和库。因此,您可以将 Puppeteer 作为依赖项添加到 Cloud Functions 函数中,以便在该函数中使用无头 Chrome。

衡量和分析网页性能是一项复杂的大型工作。为简单起见,在本教程中,您将使用 Puppeteer 收集一些顶级网页性能指标。不过,您也可以使用 Puppeteer 和 Chrome DevTools Protocol (CDP) 收集更详细的信息,例如时间轴跟踪记录。您还可以通过模拟网络拥塞和执行 CPU 节流来更好地展现最终用户体验。如需查看有关如何分析网页性能的详细介绍,请参阅 Chrome Web 开发者网站

请注意,影响网页加载时间的因素有很多,包括客户端的性能特征。请务必使用 Cloud Functions 函数的 CPU 和 RAM 配置来建立基准。

tracer/index.js 文件中的以下代码段展示了如何使用 Puppeteer 加载网页:

// launch Puppeteer and start a Chrome DevTools Protocol (CDP) session
// with performance tracking enabled.
browser = await puppeteer.launch({
  headless: true,
  args: ['--no-sandbox']
});
const page = await browser.newPage();
const client = await page.target().createCDPSession();
await client.send('Performance.enable');

// browse to the page, capture and write the performance metrics
console.log('Fetching url: '+url.href);
await page.goto(url.href, {
  'waitUntil' : 'networkidle0'
});
const performanceMetrics = await client.send('Performance.getMetrics');
options = createUploadOptions('application/json', page.url());
await writeToGcs(metricsBucket, filename, JSON.stringify(performanceMetrics), options);
  • 在 Cloud Shell 中,部署 trace Cloud Functions 函数:

    gcloud functions deploy trace \
        --trigger-http \
        --runtime nodejs10 \
        --memory 1GB \
        --source tracer \
        --env-vars-file env-vars.yaml \
        --quiet
    

    部署 Cloud Functions 函数可能需要几分钟的时间。

    部署参数指定函数应具有 HTTP 触发器、应使用 Node.js 10 运行时环境、应具有 1 GB 内存。为了运行无头 Chrome,至少必须具有此内存量。环境变量是通过 env-vars.yaml 文件提供给函数的。

默认情况下,HTTP 触发的 Cloud Functions 函数允许未经身份验证的调用。因此,您必须保护跟踪记录函数。

  • 移除 allUserscloudfunctions.invoker IAM 角色:

    gcloud beta functions remove-iam-policy-binding trace \
        --member allUsers \
        --role roles/cloudfunctions.invoker
    

分析指标

Web 性能监控练习的一个典型目标是根据一些定义的基准来跟踪性能。如果特定指标超出预期阈值,则可能表示最近的软件版本存在问题或者底层基础架构存在问题。

在本部分中,您将使用 Python 创建 Cloud Functions 函数以解析网页指标并将结果保存到 Firestore 集合中。该函数会根据预期阈值评估 FirstMeaningfulPaint 指标;如果超出阈值,则该函数会将分析结果标记为有问题。FirstMeaningfulPaint以用户为中心的指标,用于广泛描述网页何时对用户有用。每当新文件写入包含指标的存储分区时,您都可以使用 Cloud Storage 触发器执行分析函数。

analyzer/main.py 文件中的以下代码段展示了函数逻辑:

def analyze(data, context):
  """Function entry point, triggered by creation of an object in a GCS bucket.

  The function reads the content of the triggering file, analyses its contents,
  and persists the results of the analysis to a new Firestore document.

  Args:
    data (dict): The trigger event payload.
    context (google.cloud.functions.Context): Metadata for the event.
  """
  page_metrics = get_gcs_file_contents(data)
  max_time_meaningful_paint = int(os.environ.get('MAX_TIME_MEANINGFUL_PAINT'))
  analysis_result = analyze_metrics(data, page_metrics,
                                    max_time_meaningful_paint)
  docref = persist(analysis_result, data['name'])
  logging.info('Created new Firestore document %s/%s describing analysis of %s',
               docref.parent.id, docref.id, analysis_result['input_file'])
  • 部署 analyze Cloud Functions 函数:

    gcloud functions deploy analyze \
        --trigger-resource gs://$METRICS_BUCKET \
        --trigger-event google.storage.object.finalize \
        --runtime python37 \
        --source analyzer \
        --env-vars-file env-vars.yaml
    

    该函数由指标存储分区中的 finalize 事件触发,该事件会在存储分区中每次创建对象时发送。该函数使用 Python 3.7 运行时环境。

在出现故障时发出提醒消息

如果指标分析表明网页性能不佳,您通常需要采取措施。

在本部分中,您将创建 Cloud Functions 函数以便在网页性能不佳时向 Pub/Sub 主题发送消息。每次在 Firestore 集合中创建文档时,系统都会触发该函数。相关各方可以订阅 Pub/Sub 主题并采取适当的措施。例如,支持应用可以订阅 Pub/Sub 消息,以及发送电子邮件、触发支持网页调度程序或提交错误。

alerter/main.py 文件中的以下代码段展示了函数逻辑:

def generate_alert(data, context):
  """Cloud Function entry point, triggered by a change to a Firestore document.

  If the triggering document indicates a Failed status, send the document to
  configured PubSub topic.

  Args:
    data (dict): The event payload.
    context (google.cloud.functions.Context): Metadata for the event.
  """
  doc_fields = data['value']['fields']
  status = doc_fields['status']['stringValue']
  if 'FAIL' in status:
    global publish_client
    if not publish_client:
      publish_client = pubsub.PublisherClient()

    logging.info('Sending alert in response to %s status in document %s',
                 status, context.resource)
    project = os.environ.get('GCP_PROJECT')
    topic = os.environ.get('ALERT_TOPIC')
    fqtn = 'projects/{}/topics/{}'.format(project, topic)
    msg = json.dumps(data['value']).encode('utf-8')
    publish_client.publish(fqtn, msg)

请注意,只有在状态字段指示失败时,系统才会发送提醒消息。

  • 部署 alert Cloud Functions 函数:

    gcloud functions deploy alert \
        --trigger-event providers/cloud.firestore/eventTypes/document.create \
        --trigger-resource "projects/$PROJECT/databases/(default)/documents/page-metrics/{any}" \
        --runtime python37 \
        --source alerter \
        --env-vars-file env-vars.yaml \
        --entry-point generate_alert
    

    该函数由 page-metrics Firestore 集合中的 document.create 事件触发。{any} 后缀是一个通配符,它表示,在该集合中创建文档的任何时候,系统都应触发该函数。

安排分析

定期监控网页性能是一种不错的做法。例如,您可能希望每小时、每天或每周分析某个特定网页。在本部分中,您将创建 Cloud Scheduler 作业,以便通过触发 trace 函数来定期运行分析流水线。

Cloud Scheduler 作业是使用服务账号执行的,该服务账号已获得调用 trace 函数所需的 cloudfunctions.invoker IAM 角色。

有时候,网页没有正确响应或请求超时,于是网络分析应用不得不重试。因此,您的应用务必要有重试逻辑。Cloud Functions 支持后台函数重试。

HTTP 触发的 Cloud Functions 函数无法重试,因此您无法使用 Cloud Functions 重试 trace 函数。不过,Cloud Scheduler 支持重试。如需详细了解如何配置重试参数,请参阅 RetryConfig 文档。

  1. 验证三个 Cloud Functions 函数是否已正确部署并显示 ACTIVE 状态:

    gcloud functions list
    
  2. 创建新的服务账号,该账号将用作执行 Cloud Scheduler 作业的身份:

    gcloud iam service-accounts create tracer-job-sa
    
  3. 向新服务账号授予 trace 函数的 cloudfunctions.invoker IAM 角色:

    gcloud beta functions add-iam-policy-binding trace \
        --role roles/cloudfunctions.invoker \
        --member "serviceAccount:tracer-job-sa@$PROJECT.iam.gserviceaccount.com"
    
  4. 创建 Cloud Scheduler 作业:

    gcloud scheduler jobs create http traceWithRetry \
        --uri="https://$REGION-$PROJECT.cloudfunctions.net/trace" \
        --http-method=POST \
        --message-body="{\"url\":\"http://www.example.com\"}" \
        --headers="Content-Type=application/json" \
        --oidc-service-account-email="tracer-job-sa@$PROJECT.iam.gserviceaccount.com" \
        --schedule="0 3 * * *" \
        --time-zone="UTC" \
        --max-retry-attempts=3 \
        --min-backoff=30s
    

    由于该作业将调用 HTTP 触发的 trace 函数,因此该命令将作业类型指定为 http,并提供函数触发器网址作为 uri 值。待分析的网页(在本示例中为 www.example.com)是在 message-body 标志中提供的。oidc-service-account-email 标志定义用于身份验证的服务账号。该命令使用 max-retry-attempts 标志指示重试次数,而通过 schedule 标志传递的值则将运行时间设置为每天凌晨 3:00(世界协调时间 (UTC))。

验证结果

在本部分中,您将验证成功和失败情况下的行为是否符合预期。

验证成功时的行为

Cloud Scheduler 作业将在下一个安排的时间(在本示例中为世界协调时间 (UTC) 凌晨 3:00)开始运行。如需立即查看结果,您可以手动触发运行。

  1. 等待 90 秒,让调度器作业完成初始化。
  2. 手动运行 Cloud Scheduler 作业:

    gcloud scheduler jobs run traceWithRetry
    
  3. 等待大约 30 秒,让函数流水线执行完毕。

  4. 列出指标存储分区的内容以显示网页指标已收集:

    gsutil ls -l gs://$METRICS_BUCKET
    
  5. 在 Google Cloud 控制台中,打开 Cloud Logging 查看器页面:

    转到 Logging 页面

    您会看到来自以下三个 Cloud Functions 函数的日志消息:traceanalyzealert。系统可能需要一些时间才能显示日志,因此您可能需要刷新日志窗格。

    没有显示任何错误的 Logging 控制台

  6. 记下 Firestore 文档 ID,该 ID 列在文本 Created new Firestore document page-metrics/ 后面

  7. 在 Google Cloud 控制台中,转到 Firestore 页面:

    转到 Firestore 页面

  8. 检查包含分析结果的文档。文档值表示 PASS 状态,并包含最新的网页性能指标。

  9. 在 Cloud Shell 中,通过尝试拉取订阅的消息来验证系统是否没有向 Pub/Sub 主题发送任何提醒消息:

    gcloud pubsub subscriptions pull \
        projects/$PROJECT/subscriptions/performance-alerts-sub \
        --auto-ack
    

    您不会看到有任何项列出。

验证失败时的行为

  1. 手动触发跟踪记录函数。此时,以网址形式提供 Google Cloud 教程页面。此页面包含大量动态内容,导致页面加载时间超过预期的最大阈值。

    gcloud functions call trace \
        --data='{"url":"https://cloud.google.com/docs/tutorials"}'
    

    由于您具有项目的 OwnerEditor IAM 角色,因此有足够的权限来调用该函数。

  2. 等待大约 30 秒,让函数流水线执行完毕。

  3. 列出指标存储分区的内容以验证是否已收集其他指标:

    gsutil ls -l gs://$METRICS_BUCKET
    

    现在,您会在每个存储分区中看到两项内容。

  4. 在 Google Cloud 控制台中,转到 Cloud Logging 查看器页面并过滤出 Cloud Functions 函数日志:

    转到 Logging 页面

    您会看到来自 analyze 函数的错误,它指示该网页的加载时间超出了允许的最长加载时间。同样,您可能需要刷新日志窗格以查看最新的消息。

    显示了错误的 Logging 控制台

  5. 记下 Firestore 文档 ID。

  6. 在 Google Cloud 控制台中,转到 Firestore 页面:

    转到 Firestore 页面

  7. 找到描述失败分析的文档。

    状态字段会被标记为 FAIL

  8. 在 Cloud Shell 中,通过拉取订阅的消息来验证系统是否向 Pub/Sub 主题发送了提醒消息。

    gcloud pubsub subscriptions pull \
        projects/$PROJECT/subscriptions/performance-alerts-sub \
        --auto-ack
    

    这一次,您会看到消息的内容。

清除数据

删除项目

  1. 在 Google Cloud 控制台中,进入管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

后续步骤

  • 详细了解 Google Cloud 无服务器技术。
  • 浏览其他 Cloud Functions 教程
  • 观看 Google I/O '18 大会的视频,其中介绍了 Puppeteer 和无头 Chrome 的其他用途。
  • 探索有关 Google Cloud 的参考架构、图表和最佳做法。查看我们的 Cloud Architecture Center