ヒントとアドバイス
このドキュメントでは、Cloud Run functions を設計、実装、テスト、デプロイする際のベスト プラクティスについて説明します。
正確性
このセクションでは、Cloud Run functions を設計および実装するための一般的なベスト プラクティスについて説明します。
べき等関数を作成する
関数は、何回呼び出されても結果が同じになることが必要です。これにより、前の呼び出しがコードの途中で失敗した場合は、呼び出しを再試行できます。詳細については、イベント ドリブン関数の再試行をご覧ください。
HTTP 関数が HTTP レスポンスを送信することを確認する
関数が HTTP トリガーの場合、次のように、HTTP レスポンスを送信します。通知を行わないと、タイムアウトまで関数の実行が継続する可能性があります。その場合、タイムアウト時間全体が課金されます。タイムアウトにより予期しない動作が発生することもあります。また、後続の呼び出しでコールド スタートが発生し、予期しない結果や余分なレイテンシが発生する可能性があります。
Node.js
Python
Go
Java
C#
Ruby
PHP
バックグラウンド アクティビティを開始しない
バックグラウンド アクティビティは関数の終了後に発生します。関数の呼び出しの終了とは、関数が結果を返したときです。または、Node.js イベント ドリブン関数で callback
引数を呼び出すなどして完了のシグナルを発生させることもあります。正常に終了した後は、あらゆるコードは CPU にアクセスできず、処理を続行できません。
加えて、同じ環境で次の呼び出しが実行されると、バックグラウンド アクティビティが再開され、新しい呼び出しが中断されます。これが、予期せぬ動作や診断が難しいエラーにつながる可能性があります。関数の終了後にネットワークにアクセスすると、通常は接続がリセットされます(ECONNRESET
エラーコード)。
個々の呼び出しのログで呼び出し完了を示す行の後を見ると、バックグラウンド アクティビティが記録されていることがよくあります。特に、コールバックやタイマーなどの非同期処理が存在する場合は、バックグラウンド アクティビティがコードの中に埋もれている可能性があります。 コードを確認し、関数の終了前にすべての非同期処理が完了するようにしてください。
一時ファイルを常に削除する
一時ディレクトリ内のローカル ディスク ストレージは、メモリ内ファイル システムです。書き込んだファイルは関数で使用できるメモリを消費し、呼び出し後も維持される場合があります。これらのファイルを明示的に削除しないと、最終的にメモリ不足エラーにつながり、その結果コールド スタートが発生する可能性があります。
個々の関数で使用されるメモリの量を確認するには、Google Cloud コンソールの関数リストで関数を選択してから、[メモリ使用量] プロットを選択します。
一時ディレクトリ以外への書き込みはしないでください。また、プラットフォームや OS に依存しない方法でファイルパスを構築してください。
パイプラインを使用して、サイズの大きいファイルを処理する際のメモリ要件を減らすことができます。たとえば、Cloud Storage でファイルを処理するために、読み取りストリームを作成し、これをストリームベースのプロセスに渡してから、出力ストリームを Cloud Storage に直接書き込むことができます。
Functions Framework
関数をデプロイすると、Functions Framework がその現在のバージョンを使用して、自動的に依存関係として追加されます。異なる環境間で同じ依存関係が一貫してインストールされるように、関数を Functions Framework の特定のバージョンに固定することをおすすめします。
これを行うには、該当するロックファイル(たとえば、Node.js の場合は package-lock.json
、Python の場合は requirements.txt
)に優先バージョンを含めます。
ツール
このセクションでは、ツールを使用して Cloud Run functions を実装、テスト、操作する方法を説明します。
ローカルでの開発
関数のデプロイには時間がかかるため、多くの場合、関数のコードをローカルでテストするほうが時間を短縮できます。
エラー報告
例外処理を使用する言語では、捕捉されない例外をスローしないでください。これは、以降の呼び出しでコールド スタートが強制されるためです。エラーを適切に報告する方法については、Error Reporting ガイドをご覧ください。
手動で終了しない
手動で終了すると、予期しない動作が発生する可能性があります。手動で終了せずに、次の言語別のイディオムを使用してください。
Node.js
process.exit()
を使用しない。HTTP 関数は res.status(200).send(message)
でレスポンスを送信する必要があります。また、イベント ドリブン関数は、(明示的か暗黙的かを問わず)値が返されると終了します。
Python
sys.exit()
を使用しない。HTTP 関数は、レスポンスを文字列として明示的に返す必要があります。また、イベント ドリブン関数は、(明示的か暗黙的かを問わず)値が返されると終了します。
Go
os.Exit()
を使用しない。HTTP 関数は、レスポンスを文字列として明示的に返す必要があります。また、イベント ドリブン関数は、(明示的か暗黙的かを問わず)値が返されると終了します。
Java
System.exit()
を使用しない。HTTP 関数は response.getWriter().write(message)
でレスポンスを送信する必要があります。また、イベント ドリブン関数は、(明示的か暗黙的かを問わず)値が返されると終了します。
C#
System.Environment.Exit()
を使用しない。HTTP 関数は context.Response.WriteAsync(message)
でレスポンスを送信する必要があります。また、イベント ドリブン関数は、(明示的か暗黙的かを問わず)値が返されると終了します。
Ruby
exit()
または abort()
は使用しないでください。HTTP 関数は、レスポンスを文字列として明示的に返す必要があります。また、イベント ドリブン関数は、(明示的か暗黙的かを問わず)値が返されると終了します。
PHP
exit()
または die()
は使用しないでください。HTTP 関数は、レスポンスを文字列として明示的に返す必要があります。また、イベント ドリブン関数は、(明示的か暗黙的かを問わず)値が返されると終了します。
SendGrid を使ってメールを送信する
Cloud Run functions はポート 25 でのアウトバウンド接続を許可しないため、保護されていない接続では SMTP サーバーに接続できません。メールを送信するには、SendGrid を使用することをおすすめします。メールの送信に関するその他のオプションについては、Google Compute Engine のインスタンスからのメールの送信のチュートリアルをご覧ください。
パフォーマンス
このセクションでは、パフォーマンスを最適化するためのベスト プラクティスについて説明します。
依存関係を適切に使用する
関数はステートレスであるため、多くの場合、実行環境はゼロから初期化されます(これをコールド スタートといいます)。コールド スタートが発生すると、関数のグローバル コンテキストが評価されます。
関数によってモジュールがインポートされる場合、それらのモジュールの読み込み時間は、コールド スタート時の呼び出しレイテンシに加算されます。依存関係を正しく読み込み、関数が使用しない依存関係を読み込まないようにすることで、このレイテンシと関数のデプロイに必要な時間を短縮できます。
グローバル変数を使用して将来の呼び出しでオブジェクトを再利用する
Cloud Run 関数の状態は、将来の呼び出しのために必ずしも保持されるわけではありません。しかし、Cloud Run functions の関数が以前の呼び出しの実行環境をリサイクルすることはよくあります。変数をグローバル スコープで宣言すると、その値は再計算せずに後続の呼び出しで再利用できるようになります。
この方法では、関数の呼び出しごとに再作成するためコストが高くなりがちなオブジェクトをキャッシュに保存できます。このようなオブジェクトを関数の本文からグローバル スコープに移動して、パフォーマンスを大幅に向上することができます。次の例では、heavy オブジェクトを関数のインスタンスにつき 1 回だけ作成し、指定されたインスタンスに到達するまですべての関数呼び出しで共有します。
Node.js
Python
Go
Java
C#
Ruby
PHP
ネットワーク接続、ライブラリ参照、および API クライアント オブジェクトをグローバル スコープでキャッシュに保存することが非常に重要です。例については、ネットワークの最適化の記事をご覧ください。
グローバル変数の遅延初期化を行う
グローバル スコープの変数を初期化する場合、初期化コードは常にコールド スタート呼び出しによって実行され、関数のレイテンシが長くなります。特定の場合では、try
/ catch
ブロックで適切に処理されないと、これによって呼び出されるサービスの断続的なタイムアウトが発生します。一部のコードパスでのみ使用されるオブジェクトについては、必要に応じて遅延させて初期化することを検討してください。
Node.js
Python
Go
Java
C#
Ruby
PHP
PHP 関数は、リクエスト間で変数を保持できません。前述のスコープのサンプルでは、遅延読み込みを使用してファイル内のグローバル変数の値をキャッシュに保存します。
これは特に、複数の関数を 1 つのファイルに定義し、関数ごとに異なる変数を使用する場合に重要です。遅延初期化を使用しないと、初期化されたが使用されない変数にリソースを浪費することがあります。
インスタンスの最小数を設定してコールド スタートを減らす
デフォルトでは、Cloud Run functions は受信リクエスト数に基づいてインスタンス数をスケールします。このデフォルトの動作は、Cloud Run functions がいつでもリクエストを処理できるために必要なインスタンスの最小数を設定して変更できます。インスタンスの最小数を設定すると、アプリケーションのコールド スタートが減少します。レイテンシの影響を受けやすいアプリケーションの場合は、インスタンスの最小数を設定することをおすすめします。
インスタンスの最小数を設定する方法については、最小インスタンスの使用をご覧ください。
参考情報
パフォーマンスの改善について詳しくは、「Google Cloud Performance Atlas」の動画 Cloud Run functions Cold Boot Time をご覧ください。