Go アプリケーションのプロファイリング

このページでは、プロファイリング データを収集し、そのデータを Google Cloud プロジェクトに送信するように Go アプリケーションを変更する方法について説明します。プロファイリングの全般的な情報については、プロファイリングのコンセプトをご覧ください。

Go のプロファイル タイプ:

  • CPU 時間
  • ヒープ
  • 割り当てられたヒープ
  • 競合(Go Mutex)
  • スレッド(Go Goroutine)

サポートされている Go 言語バージョン:

  • 特に明記されていない限り、正式に管理されているすべての Go リリース。詳細については、Go 言語のリリース ポリシーをご覧ください。

サポートされているプロファイリング エージェントのバージョン:

  • エージェントの最新リリースがサポートされています。一般に、1 年を超える期間が経過したリリースはサポートされません。エージェントの最新リリース バージョンを使用することをおすすめします。

サポートされているオペレーティング システム:

  • Linux。Go アプリケーションのプロファイリングは、標準 C ライブラリが glibc または musl で実装されている Linux カーネルでサポートされています。Linux Alpine カーネルに固有の構成情報については、Linux Alpine で実行するをご覧ください。

サポートされる環境:

Profiler API を有効にする

プロファイリング エージェントを使用する前に、基盤となる Profiler API が有効になっている必要があります。Google Cloud CLI または Google Cloud Console のいずれかを使用して、API のステータスを確認し、必要に応じて有効にできます。

gcloud CLI

  1. ワークステーションに Google Cloud CLI がまだインストールされていない場合は、Google Cloud CLI のドキュメントをご覧ください。

  2. 次のコマンドを実行します。

    gcloud services enable cloudprofiler.googleapis.com
    

詳細については、gcloud services をご覧ください。

Google Cloud コンソール

  1. Enable the required API.

    Enable the API

  2. [API が有効です] が表示されている場合、API はすでに有効になっています。そうでない場合は、[有効にする] ボタンをクリックします。

サービス アカウントに IAM ロールを付与する

Google Cloud リソースにアプリケーションをデプロイし、デフォルトのサービス アカウントを使用していて、そのサービス アカウントへのロール付与を変更していない場合は、このセクションをスキップできます。

次のいずれかを行う場合は、サービス アカウントに Cloud Profiler エージェント(roles/cloudprofiler.agentの IAM ロールを付与する必要があります。

  1. デフォルトのサービス アカウントを使用しているものの、ロール付与を変更している。
  2. ユーザーが作成したサービス アカウントを使用している。
  3. Workload Identity を使用している場合は、Kubernetes サービス アカウントに Cloud Profiler エージェントのロールを付与します。

サービス アカウントに IAM ロールを付与するには、Google Cloud コンソールまたは Google Cloud CLI を使用します。たとえば、gcloud projects add-iam-policy-binding コマンドを使用できます。

gcloud projects add-iam-policy-binding GCP_PROJECT_ID \
    --member serviceAccount:MY_SVC_ACCT_ID@GCP_PROJECT_ID.iam.gserviceaccount.com \
    --role roles/cloudprofiler.agent

前のコマンドを使用する前に、次のように置き換えます。

  • GCP_PROJECT_ID: プロジェクト ID。
  • MY_SVC_ACCT_ID: サービス アカウントの名前。

詳細については、プロジェクト、フォルダ、組織へのアクセスを管理するをご覧ください。

Cloud Profiler の使用

サポートされているすべての環境で Profiler を使用するには、アプリケーションにパッケージをインポートし、アプリケーションの早い段階で Profiler を初期化します。

MutexProfiling 構成オプションを true に設定すると、ミューテックス競合プロファイリング(インターフェースでは「競合」)を有効にできます。

構成オプションを含む Profiler API の詳細については、公開 API のドキュメントをご覧ください。

Compute Engine

Compute Engine の場合、profiler.ConfigService にプロファイリング対象のサービスの名前を設定し、必要に応じて ServiceVersion にサービスのバージョンを設定します。


// snippets is an example of starting cloud.google.com/go/profiler.
package main

import (
	"cloud.google.com/go/profiler"
)

func main() {
	cfg := profiler.Config{
		Service:        "myservice",
		ServiceVersion: "1.0.0",
		// ProjectID must be set if not running on GCP.
		// ProjectID: "my-project",

		// For OpenCensus users:
		// To see Profiler agent spans in APM backend,
		// set EnableOCTelemetry to true
		// EnableOCTelemetry: true,
	}

	// Profiler initialization, best done as early as possible.
	if err := profiler.Start(cfg); err != nil {
		// TODO: Handle error.
	}
}

ソースコードの依存関係を手動で取得する場合は、ビルド スクリプトまたは Dockerfile に以下を追加する必要があります。

go get cloud.google.com/go/profiler

GKE

GKE の場合は、profiler.ConfigService にプロファイリング対象のサービスの名前を設定し、必要に応じて ServiceVersion にサービスのバージョンを設定します。


// snippets is an example of starting cloud.google.com/go/profiler.
package main

import (
	"cloud.google.com/go/profiler"
)

func main() {
	cfg := profiler.Config{
		Service:        "myservice",
		ServiceVersion: "1.0.0",
		// ProjectID must be set if not running on GCP.
		// ProjectID: "my-project",

		// For OpenCensus users:
		// To see Profiler agent spans in APM backend,
		// set EnableOCTelemetry to true
		// EnableOCTelemetry: true,
	}

	// Profiler initialization, best done as early as possible.
	if err := profiler.Start(cfg); err != nil {
		// TODO: Handle error.
	}
}

ソースコードの依存関係を手動で取得する場合は、ビルド スクリプトまたは Dockerfile に以下を追加する必要があります。

go get cloud.google.com/go/profiler

App Engine

App Engine フレキシブル環境と App Engine スタンダード環境の場合、コードの追加は Compute Engine と GKE の場合とほぼ同じです。例外が 1 つあります。App Engine 環境では、Service パラメータと ServiceVersion パラメータは環境から取得されるため、これらを指定する必要はありません。


// appengine is an example of starting cloud.google.com/go/profiler on
// App Engine.
package main

import (
	"cloud.google.com/go/profiler"
)

func main() {
	// Profiler initialization, best done as early as possible.
	if err := profiler.Start(profiler.Config{
		// Service and ServiceVersion can be automatically inferred when running
		// on App Engine.
		// ProjectID must be set if not running on GCP.
		// ProjectID: "my-project",
	}); err != nil {
		// TODO: Handle error.
	}
}

アプリケーションをローカルで実行する場合は、ProjectID(Google Cloud プロジェクトの ID)と Service パラメータを profiler.Config に設定します。これらのパラメータは、ローカル環境から取得されません。ServiceVersion を設定する必要はありません。

App Engine スタンダード環境の場合は、Go 1.11 へのアプリの移行で、アプリケーションに必要な変更の詳細を確認してください。また、Google Cloud CLI バージョン 226.0.0 以降を使用する必要があります。Google Cloud CLI を更新するには、次のコマンドを実行します。

gcloud components update

アプリケーションを実行するには:

  1. 依存関係を更新します。

    go get cloud.google.com/go/profiler
    
  2. App Engine フレキシブル環境または App Engine スタンダード環境にアプリケーションをデプロイします。

    gcloud app deploy [DEPLOYMENT]
    

    DEPLOYMENT は構成ファイルのパスです。たとえば、DEPLOYMENTmain/app.yaml になります。

データの分析

Profiler が収集したデータを Profiler のインターフェースに表示し、分析できます。

Google Cloud コンソールで [Profiler] ページに移動します。

[Profiler] に移動

このページは、検索バーを使用して見つけることもできます。

サービス名とバージョンの引数

Profiler エージェントを読み込むときに、service-name 引数とオプションの service-version 引数を指定して構成します。

Profiler は、サービス名で指定されたサービスのすべてのレプリカからプロファイリング データを収集します。Profiler サービスは、サービス バージョンとゾーンの組み合わせに対して、1 つのサービス名について平均で 1 分あたり 1 個のプロファイルを作成します。

たとえば、1 つのサービスの 2 つのバージョンが 3 つのゾーンのレプリカで実行されている場合、Profiler はそのサービスについて 1 分あたり平均で 6 個のプロファイルを作成します。

レプリカで異なるサービス名を使用している場合、サービスが必要以上にプロファイリングされるため、オーバーヘッドが大きくなります。

サービス名を選択する場合は、次の点に注意してください。

  • アプリケーション アーキテクチャでサービスを明確に識別できる名前を選択してください。1 つのサービスまたはアプリケーションしか実行していない場合、どのようなサービス名を選択するかはさほど問題になりません。しかし、アプリケーションが一連のマイクロサービスとして実行されている場合は、名前の選択が重要になります。

  • service-name 文字列で、プロセス ID などのプロセス固有の値を使用しないでください。

  • service-name 文字列は次の正規表現と一致する必要があります。

    ^[a-z0-9]([-a-z0-9_.]{0,253}[a-z0-9])?$

サービス名として imageproc-service のような静的な文字列を使用することをおすすめします。

サービス バージョンは省略可能です。サービス バージョンを指定すると、複数のインスタンスからプロファイリング情報が集約され、バージョンごとに表示されます。これは、複数のバージョンがデプロイされているときの識別に役立ちます。Profiler UI では、データをサービス バージョンでフィルタできます。これにより、コードの新旧バージョンのパフォーマンスを比較できます。

service-version 引数には、自由形式の文字列を指定できますが、通常は、バージョン番号のような値を使用します(例: 1.0.02.1.2)。

エージェント ロギング

プロファイリング エージェントには、ログにデバッグ情報を出力する機能があります。デフォルトでは、エージェント ロギングは無効になっています。

エージェント ロギングを有効にするには、エージェントの起動時に DebugLogging オプションを true に設定します。

profiler.Start(profiler.Config{..., DebugLogging: true});

トラブルシューティング

このセクションでは、Go アプリケーションのプロファイリングに固有の問題について説明します。一般的な問題のヘルプについては、こちらのトラブルシューティングをご覧ください。

動作 原因 解決策
-buildmode=c-archive を使用してビルドされたアプリケーションでは、CPU 時間プロファイルは収集されません。ヒープ、競合、スレッドのプロファイルが収集されます。GitHub の問題 デフォルトでは、-buildmode フラグが c-archive または c-shared の場合、Go アプリケーションに対して CPU プロファイリングが有効になっていません。 profiler.Start を呼び出す前に
signal.Notify(make(
chan os.Signal), syscall.SIGPROF)
への呼び出しを追加します。
GitHub の問題へのレスポンス。

Linux Alpine で実行する

Linux Alpine 用の Go プロファイリング エージェントは、Google Kubernetes Engine の構成でのみサポートされています。

認証エラー

Linux Alpine で Docker イメージ(golang:alpinealpine など)を使用すると、次の認証エラーが表示されることがあります。

connection error: desc = "transport: authentication handshake failed: x509: failed to load system roots and no roots provided"

エラーの詳細を確認するには、エージェント ロギングを有効にする必要があります。 デフォルトでは、Go エージェントはログメッセージを出力しません。

上記のエラーは、Linux Alpine の Docker イメージに、デフォルトでインストールされているはずのルート SSL 証明書がないことを示しています。これらの証明書は、プロファイリング エージェントが Profiler API と通信するために必要になります。このエラーを解決するには、次の apk コマンドを Dockerfile に追加します。

FROM alpine
...
RUN apk add --no-cache ca-certificates

その後、アプリケーションを再度ビルドし、デプロイする必要があります。

次のステップ