Cloud Functions を使用したサーバーレス ウェブ パフォーマンス モニタリング

このチュートリアルでは、Google Cloud サーバーレス テクノロジーを使用してウェブ パフォーマンス モニタリング アプリを作成する方法について説明します。

パフォーマンスは、ウェブアプリの成功に大きな役割を果たします。サイトのパフォーマンスが低いと、登録数の減少やユーザー維持率の低下を招く恐れがあります。その結果、ビジネス目標に影響する可能性もあります。ウェブアプリを設計、構築、テストするうえでは、パフォーマンスが重要な成功基準となります。

ただしアプリの進化とともに、ページのパフォーマンスも変わる可能性があります。デベロッパーによってイメージやスクリプトが追加または更新されることがあります。アプリ提供の基盤となるインフラストラクチャ自体が変わることもあります。したがって、ページのパフォーマンスを定期的にモニタリングすることが重要です。多くの方がパフォーマンス指標を保存して履歴分析を有効にしています。ページのパフォーマンスが定義済みのしきい値を下回った場合のアラート生成も、一般的に行われています。

目標

  • Cloud ファンクションを作成し、ヘッドレス Chrome を使用してウェブページのパフォーマンス指標を収集します。
  • 収集した指標を Cloud Storage に保存します。
  • Cloud Storage 作成イベントによってトリガーされる別の Cloud 関数を作成して、ページ指標を分析します。
  • 分析結果を Firestore に保存します。
  • Firestore 作成イベントによってトリガーされる別の Cloud 関数を作成して、ページのパフォーマンスが低下した場合に、Pub/Sub にアラートをパブリッシュします。
  • Cloud Scheduler のジョブを作成して、最初の Cloud 関数を定期的にトリガーします。
  • 成功シナリオと失敗シナリオの出力を検証します。

料金

このチュートリアルでは、Google Cloud の課金対象となる以下のコンポーネントを使用します。

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

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを出すことができます。

始める前に

  1. Google アカウントにログインします。

    Google アカウントをまだお持ちでない場合は、新しいアカウントを登録します。

  2. Google Cloud Console の [プロジェクト セレクタ] ページで、Google Cloud プロジェクトを選択または作成します。

    [プロジェクトの選択] ページに移動

  3. Cloud プロジェクトに対して課金が有効になっていることを確認します。プロジェクトに対して課金が有効になっていることを確認する方法を学習する

  4. Cloud Functions, Cloud Scheduler, Pub/Sub, and Cloud Build API を有効にします。

    API を有効にする

アーキテクチャ

通常、ウェブ パフォーマンスのモニタリング オペレーションは生存期間が短くステートレスです。また、イベント ドリブンのオペレーションが多く、スケジュールに沿って発生するか、自動テスト パイプラインなど他のプロセスに付随してトリガーされます。こうした特性により、ウェブ解析アプリの実装にはサーバーレス アーキテクチャが好ましい選択肢であると言えます。

このチュートリアルでは、Cloud Functions、Firestore、Cloud Scheduler、Pub/Sub を含む、Google Cloud サーバーレス スタックのさまざまな部分を使用します。これらのサービスでは、インフラストラクチャの管理が不要で、料金は使用した分のみ発生します。アプリのコアは、Cloud Functions を使用して実装されます。Cloud Functions は、イベント ドリブンでスケーラブルなサーバーレスのランタイム環境です。Cloud Functions を使用すると、独立した疎結合ロジックを使ってアプリを作成、接続できます。

次の図は、このチュートリアルで作成するサーバーレス ソリューションのアーキテクチャを示しています。

サーバーレス ウェブ解析ソリューションのアーキテクチャ

環境の準備

サーバーレス環境を作成する前に、GitHub からコードを取得し、変数を設定して、後ほど行う分析と保存に必要なリソースを準備します。

コードの取得と環境変数の設定を行う

  1. Cloud Console で Cloud Shell を開きます。

    Cloud Shell を開く

  2. このチュートリアルで使用する Cloud Functions のコードを含む、リポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/solutions-serverless-web-monitoring.git
    
  3. Cloud Functions のディレクトリを変更します。

    cd solutions-serverless-web-monitoring/functions
    
  4. 現在のプロジェクト ID とプロジェクト番号を、シェル変数として設定します。

    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 バケットの名前のシェル変数をエクスポートします。バケット名は、グローバルに一意である必要があるため、以下のコマンドでは 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. Cloud Console で Firestore ページに移動します。

    Firestore ページに移動

  2. Firestore データベースを作成したことがない場合は、次の手順を行います。

    1. [ネイティブ モードを選択] をクリックして、Firestore を有効にします。
    2. Cloud Functions を実行するリージョンに近いリージョンのロケーションを選択します。
    3. [データベースを作成] をクリックします。

    構成が完了するまで、少し時間がかかります。

  3. [コレクションを開始] をクリックして、page-metrics にコレクション ID を設定します。

  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 をサポートします。これは、サーバーレス環境で、完全なウェブブラウザの機能を提供します。

Puppeteer は、Chrome DevTools チームが構築した Node.js ライブラリで、ヘッドレス Chrome を制御する高レベルの API を提供します。デフォルトでは、Puppeteer は、最新バージョンのブラウザをライブラリとともにインストールします。そのため、Puppeteer を Cloud ファンクションの依存関係に追加できます。これはヘッドレス Chrome をこのファンクション内で使用する簡単な方法です。

ウェブページのパフォーマンスの測定と分析は、広範かつ複雑な分野です。簡単に行えるように、このチュートリアルでは Puppeteer を使用して、トップレベルのページ パフォーマンス指標を収集します。ただし、Puppeteer と Chrome DevTools Protocol(CDP)を使用して、タイムライン トレースなどの、より詳細な情報も収集できます。また、ネットワークの輻輳をエミュレートし、CPU スロットリングを実行することで、エンドユーザー エクスペリエンスをより適切に再現できます。ウェブページのパフォーマンス分析を初めて導入する場合は、Chrome のウェブ デベロッパー向けサイトを参考にしてください。

ウェブページの読み込み時間は、クライアントのパフォーマンス特性など、多くの要因の影響を受けますので注意してください。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 関数をデプロイします。

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

    この関数のデプロイには数分かかる場合があります。

    デプロイ パラメータでは、この関数には 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
    

指標の分析

通常、ウェブ パフォーマンス モニタリングの実施目標は、定義済みのベンチマークに照らしてパフォーマンスを追跡することです。特定の指標が予想されるしきい値を超える場合、最近のソフトウェア リリースか、基盤となるインフラストラクチャに問題がある可能性があります。

このセクションでは、Python で Cloud Functions の関数を作成してページ指標を解析し、結果を Firestore コレクションに保存します。この関数は、予想されるしきい値に照らして FirstMeaningfulPaint 指標を評価し、しきい値を超えた場合には分析結果に問題があるとしてマークします。FirstMeaningfulPaintユーザー セントリックの指標で、ページがユーザーにとって有用になるタイミングを大まかに示します。指標を含むバケットに新しいファイルが書き込まれるたび、Cloud Storage トリガーを使用して analyze 関数を実行します。

以下の 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 関数をデプロイします。

    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 関数を作成して、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)

ステータスのフィールドが失敗を示す場合にのみ、アラートが送信されることに注意してください。

  • Cloud Functions の alert 関数をデプロイします。

    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 Cloud Firestore コレクション内で、document.create イベントによってトリガーされます。{any} サフィックスは、コレクション内でドキュメントが作成されるたび、関数がトリガーされることを示すワイルドカードです。

分析のスケジューリング

ページのパフォーマンスは、定期的にモニタリングすることをおすすめします。たとえば、特定のページを 1 時間ごと、毎日、または毎週分析したいとします。このセクションでは、Cloud Scheduler のジョブを作成して、定期的に trace 関数をトリガーし、分析パイプラインを実行します。

Cloud Scheduler のジョブは、trace 関数に必要な cloudfunctions.invoker IAM ロールが付与されたサービス アカウントを使用して実行されます。

ウェブページが正しく応答しない場合や、リクエストのタイムアウトが発生した場合、ウェブ解析アプリでの再試行は避けられません。そのため、アプリに再試行ロジックを実装することが重要です。Cloud Functions では、バックグラウンド関数の再試行をサポートしています。

HTTP トリガータイプの Cloud Functions 関数では再試行ができないため、Cloud Functions を使用して trace 関数を再試行することはできません。ただし、Cloud Scheduler は再試行をサポートしています。再試行パラメータの構成の詳細については、RetryConfig のドキュメントを参照してください。

  1. 3 つの Cloud Functions 関数が正しくデプロイされ、ACTIVE ステータスが表示されていることを確認します。

    gcloud functions list
    
  2. Cloud Scheduler のジョブを実行するための ID として使用する新しいサービス アカウントを作成します。

    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 として指定し、関数トリガー URL を uri 値として提供します。分析対象ページ(この場合は www.example.com)は、message-body フラグで提供されます。oidc-service-account-email フラグによって、認証に使用するサービス アカウントが定義されます。このコマンドは max-retry-attempts フラグを使用して行われた再試行回数を示します。また、schedule フラグで渡された値によって、毎日午前 3 時(UTC)に実行スケジュールが設定されます。

結果の検証

このセクションでは、成功条件と失敗条件の両方で、想定された挙動が得られることを確認します。

成功を検証する

Cloud Scheduler のジョブは、次のスケジュールされた時間(この場合は午前 3 時(UTC))まで実行しません。すぐに結果を得るためには、手動で実行をトリガーできます。

  1. Cloud Scheduler のジョブの初期化が完了するまで、90 秒待ちます。
  2. Cloud Scheduler のジョブを手動で実行します。

    gcloud scheduler jobs run traceWithRetry
    
  3. 関数パイプラインが完了するまで、約 30 秒待ちます。

  4. 指標バケットの内容を一覧表示して、ページ指標が収集されたことを確認します。

    gsutil ls -l gs://$METRICS_BUCKET
    
  5. Cloud Console で、Cloud Logging ビューアのページを開きます。

    [ロギング] ページに移動

    3 つの Cloud Functions(traceanalyzealert)それぞれのログメッセージが表示されます。ログの出力に時間がかかることがあるため、必要に応じて [ログ] ペインを更新してください。

    エラーが表示されていない [ロギング] コンソール

  6. Firestore ドキュメント ID をメモしておきます。これは、「Created new Firestore document page-metrics/」という文字列の後に一覧表示されます。

  7. Cloud Console で Firestore ページに移動します。

    Firestore ページに移動

  8. 分析結果を含むドキュメントを調べます。ドキュメントの値は PASS ステータスを示し、最新のページ パフォーマンス指標が含まれます。

  9. Cloud Shell で、サブスクリプションからメッセージを pull して、Pub/Sub トピックにアラート メッセージが送信されていないことを確認します。

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

    リストにアラート メッセージがないことがわかります。

失敗を検証する

  1. トレース機能を、手動でトリガーします。今回は、Google Cloud チュートリアルのページを URL として提供します。このページには多くの動的コンテンツがあり、ページの読み込み時間を、想定された最大しきい値を超えて増大させます。

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

    プロジェクトには Owner または Editor IAM ロールがあるため、関数を呼び出すための十分な権限があります。

  2. 関数パイプラインが完了するまで、約 30 秒待ちます。

  3. 指標バケットの内容を一覧表示して、追加の指標が収集されたことを確認します。

    gsutil ls -l gs://$METRICS_BUCKET
    

    各バケットに、2 つのアイテムが表示されます。

  4. Cloud Console で、Cloud Logging ビューアのページに移動し、Cloud 関数ログをフィルタします。

    [ロギング] ページに移動

    ページの読み込み時間が最大許容値を超えたことを示すエラーが、analyze 関数から表示されます。この場合も、[ログ] ペインを更新して、最新メッセージを表示する必要が生じる可能性があります。

    エラーを示す [ロギング] コンソール

  5. Firestore のドキュメント ID をメモします。

  6. Cloud Console で Firestore ページに移動します。

    Firestore ページに移動

  7. 失敗した分析を記述したドキュメントを見つけます。

    ステータスのフィールドに FAIL とマークされています。

  8. Cloud Shell で、サブスクリプションからメッセージを pull して、Pub/Sub トピックにアラート メッセージが送信されたことを確認します。

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

    この場合、メッセージの内容が表示されます。

クリーンアップ

プロジェクトの削除

  1. Cloud Console で [リソースの管理] ページに移動します。

    [リソースの管理] に移動

  2. プロジェクト リストで、削除するプロジェクトを選択し、[削除] をクリックします。
  3. ダイアログでプロジェクト ID を入力し、[シャットダウン] をクリックしてプロジェクトを削除します。

次のステップ

  • Google Cloud サーバーレス テクノロジーの詳細を確認する。
  • 他の Cloud Functions のチュートリアルを確認する。
  • Puppeteer とヘッドレス Chrome のその他の使用法について説明する、Google I/O 2018 の動画を見る。
  • Google Cloud のその他の機能を試す。チュートリアルをご覧ください。