Google Cloud Platform

Compute Engine インスタンスの起動を高速化する 3 つのステップ

Google Cloud Platform(GCP)の美点の 1 つは、何百万ものリクエストに対応して難なくスケーリングできることです。Compute Engine を使用すれば、インスタンス グループ負荷分散のような技術により、いとも簡単にスケーリングを行えます。ただし、VM ベースのアプリケーションでは、利用の急増に合わせてスケーリングを行うときにインスタンスの起動時間が問題となることがあります。

起動時間のせいでアプリケーションにトラブルが発生するのを避けるため、この投稿では 3 つのシンプルなステップを紹介します。これらのステップを踏めば、起動プロセスのどの部分が最も時間がかかるのか、どうすれば起動時間を短縮できるのかという問いの答えを見つけることができます。

どこに時間がかかるのか

起動時のパフォーマンスを最適化するうえで最も重要な最初のステップの 1 つは、起動の各段階について包括的なプロファイリングを行うことです。これにより、Compute Engine がインスタンスの作成に費やしている時間と、コードの実行にかかっている時間がわかります。

Google の正式なドキュメントでは、起動プロセスをプロビジョニング(provisioning)、ステージング(staging)、実行(running)の 3 段階に分けています。ですが、リクエスト(request)、プロビジョニング(provisioning)、ブート(booting)の 3 段階に分けてパフォーマンス テストを行うほうが簡単です。各段階にかかる時間を外部(具体的には Cloud Shell)から測定できるからです。

  • リクエスト : VM を要求してから、Create Instance API からの「VM の要求を受けた」との応答を得るまでの時間です。この段階は、インスタンスを挿入する REST コマンドに GCP が応答する時間を測定することで直接プロファイリングできます。
  • プロビジョニング : Compute Engine が、要求された VM のスペースをアーキテクチャ上で見つけるのにかかる時間です。このスペースは、Get Instance API を定期的にポーリングすることで見つかります。見つかると、“status” フラグが “provisioning” から “running” に変わります。
  • ブート : スタートアップ スクリプトなどのカスタム コードが実行され、インスタンスが利用可能になるまでの時間です。私の同僚で Google Cloud のデベロッパー アドボケートである Terry Ryan は、エンドポイントに繰り返しポーリングを行い、500、400、200 番台のステータス コードをそれぞれ受け取った場合の時間の違いを測定することで、この段階をプロファイリングすることを好みます。

rZyj8q6rj58zLVcwgk_bvrkhp-gvXKHMP70zLTi2lKKJP3y7OcF3vGB4ZS-RoXiwOpGspXk7nn4nDm42u42dp_ycpDxndVRj3CqgDHwnr6kIHrWlT3uwlTXB7nJUHTQcO1gaVwwP3xrc.PNG
リクエスト、プロビジョニング、ブートの各段階に要する時間の測定結果を示すグラフの例(測定回数 : 183 回)

スタートアップ スクリプトのプロファイリング

予期せぬ状況を除けば、インスタンスの起動時間は通常、インスタンスがスタートアップ スクリプトを実行するブート段階が全体の半分以上を占めています。そのため、どの段階がパフォーマンスのボトルネックになっているかを調べるうえでは、ブート スクリプトのプロファイリングを行うことが極めて有益です。

スタートアップ スクリプトの実行時間を測定することは、一見するよりも少し厄介です。コードを非常に強力なツール システム(Stackdriver Custom Metric APIstatsdbrubeck など)に統合すれば、プロファイリングやパフォーマンス監視に役立つかもしれません。しかし、スタートアップ スクリプトにこうしたツールを適用すると、複雑なやり取りが行われ、ブート時間のオーバーヘッドが発生することがあります。それがプロファイリング結果をゆがめるおそれがあり、そうなればテストが無意味になってしまいます。

気の利いたプロファイリングの 1 つのやり方は、スタートアップ スクリプトの各セクションを SECONDS コマンドでラップして(Linux ビルドの場合)、各セクションにかかった時間をファイルに追記し、要求に応じてそのファイルを提供するように新しいエンドポイントをセットアップすることです。

こうすることで、外部の場所からエンドポイントをポーリングしてデータを得ることができ、大がかりな開発やサービスに対する変更は必要なくなります。また、スクリプトのどのセクションがブート時間の中で最も大きな割合を占めているかもわかります。

HYf_GoRISUMMfseG1u6l0VmbNsQVzs4k7wFghDtoDiS9rCPgw30yrOJZu7P5CrHseMWMSGNWhF6m6F0VLBmLLrzAM3mE5a24qEFdv1Kiut2j1sygIxFi12SpSJN_pjC5L6oHiSLB28bm.PNG
Linux スタートアップ スクリプトの各セクションに要する時間の測定結果を示すグラフの例

カスタム イメージへの移行

ほとんどの開発者にとって、スタートアップ スクリプトの中で最も実行時間が長いのは、サービスを適切に稼働させるためのパッケージの取得とアプリケーションのインストールです。これは、インスタンスの多くが公開イメージ(OS とブート ローダの事前設定済みの組み合わせ)で作成されるためです。

これらのイメージは、迅速に立ち上げて運用したいときに重宝します。ですが、本番レベルのシステムを構築し始めると、ブート時間で大きな割合を占めるのは OS の起動ではなく、ユーザーが実行するスタートアップ シーケンスで行われる、パッケージおよびバイナリの取得と初期化だということがわかります。

この問題には、インスタンスのカスタム イメージを作成することで対処できます。カスタム イメージを作成するには、ホストのディスク情報(起動およびインストール後)のスナップショットを作成し、配布場所に保存します。後でターゲット インスタンスが起動されると、このイメージ情報がハード ドライブにコピーされます。これは、ルート永続ディスクを作成して特定の状態に変更し、その状態を保存して新規インスタンスで再利用したい場合に理想的です。また、大規模ライブラリやソフトウェア群のインストール(やコンパイル)がお客様のセットアップに多く含まれる場合にも適しています。

tWjypqS8eA0mxAzb2PKQSt24-8KS_pogJpYWBUGLCrW8xOH79xhA801HyW6hSzfsGVBeN5F9qHlyZwSXokcZh4dQbrOjzvK0UtKr1wwvMw78CQPcG1vQBCMSo5YGzfH83ZQ9dzgjr0kp.PNG
インスタンスのスタートアップの各段階に要する時間の測定結果を示すグラフの例。右のグラフは 1 秒未満の単位であることに注意

1 ミリ秒ずつの短縮が大切

毎秒数百万のリクエストをさばくために数千インスタンスのサービスをスケーリングする場合、起動時間の小さな変化が、コストや応答時間、そして何よりも重要なユーザーの体感パフォーマンスに大きな影響をもたらします。

Google Cloud アプリケーションの最適化方法をもっと知りたい方は、Google Cloud Performance Atlas のブログ動画をご覧ください。パフォーマンスの向上には 1 ミリ秒ずつの短縮が大切なのです。


* この投稿は米国時間 7 月 26 日、Developer Advocate である Colt McAnlis によって投稿されたもの(投稿はこちら)の抄訳です。

- By Colt McAnlis, Developer Advocate