1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

GCPを使って、できるだけ楽してGo言語のgoroutineのリークを監視する

Posted at

ゴルーチン リーク

Go言語はめちゃめちゃ簡単にゴルーチン(スレッドのようなもの)を作れるのが魅力です。

ゴルーチンはメインフローとは別に切り離されて実行されるので、デッドロックなどでゴルーチンの処理が途中で止まっていても気づかず、「終了したと思っていたが、実は実行中で残っていました」ということがありました。

それ以来、ゴルーチンの起動数を定期的にログに出力して、ゴルーチンのリークが起こっていないかチェックしているのですが、その作業が面倒くさくなってきました。

もっと楽したい

GCPのCloud Monitoringは主に、GCPの各種サービスの状態モニタリングを行う機能なのですが、アプリの状態モニタリングも行えるので、それを使ってゴルーチンの起動数を監視することにしました。

方針

Cloud Monitoringに、Go言語のpprofの値を送るようにして、Cloud Monitoring上でゴルーチンの起動数をグラフ表示します。

プログラムからCloud Monitoringに値を送るには、OpenCensusというオープンソースのライブラリを使うのですが、色々お膳立てが必要で、ちょっとした値を送りたい場合でも、結構コードを書く必要があります。

しかし、OpenCensusのGo言語のライブラリには、pprofの値をよしなに取得して送る機能があり、それを使えば数行のコードでできるので、今回はそれをそのまま使いました。

手順

権限設定

Cloud Monitoringにデータを送るには、「モニタリング管理者」権限が必要なので、アプリに付与しておきます。

サンプルプログラム

package main

import (
	"context"
	"log"
	"os"
	"time"

	"contrib.go.opencensus.io/exporter/stackdriver"
	"go.opencensus.io/metric/metricexport"
	"go.opencensus.io/plugin/runmetrics"
)

func main() {
	//////////////////////////////////////////////////
	// Cloud Monitoringにpprofを送る
	//////////////////////////////////////////////////
	os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "./service-account-key.json")

	// exporter(Cloud Monitoring)
	exporter, err := stackdriver.NewExporter(stackdriver.Options{
		// Cloud Montoringからの検索用文字列
		MetricPrefix: "test-app-go-pprof",
		// データ送信頻度(60秒以上)
		ReportingInterval: 120 * time.Second,
	})
	if err != nil {
		log.Fatal(err)
	}
	defer exporter.Flush()
	exporter.StartMetricsExporter()
	defer exporter.StopMetricsExporter()

	// metrics(pprof)
	err = runmetrics.Enable(runmetrics.RunMetricOptions{
		EnableCPU:    true,
		EnableMemory: true,
	})
	if err != nil {
		log.Fatal(err)
	}

	// metrics -> exporter
	metricexport.NewReader().ReadAndExport(exporter)

	//////////////////////////////////////////////////
	// ゴルーチン リーク テスト
	//////////////////////////////////////////////////
	ctx := context.Background()
	for {
		go func() {
			var a [1000 * 1000 * 10]byte // メモリも消費
			_ = a
			<-ctx.Done() // 停止
		}()
		time.Sleep(60 * time.Second)
	}
}

補足説明

  • OpenCensusはデータ部分(metrics)と送信部分(exporter)に分かれています。
  • exporterをCloud Monitoring(旧名stackdriver)にして、runmetricsでpprofからmetrictを生成するようにします。
  • exporter(Cloud Monitoring)がmetrics(pprof)を読み込んで出力するよう紐付けします。
  • Cloud Monitoringには色んな種類のデータが送られているので、識別しやすいように「MetricPrefix」を付けます。

Cloud Monitoring

プログラムを実行すると、Cloud Monitoringに

custom.googleapis.com/opencensus/test-app-go-pprof/process/cpu_goroutines

という名前で、ゴルーチン数が記録されるので、[Cloud Monitoring]-[Metrics Explorer]で見ることができます。

Art076.png

応用

他のpprofの値も送られるので、ダッシュボードでゴルーチン数とメモリの使用量をならべて表示するといったことも、プログラムの修正無しにできます。

Art078.png

ゴルーチンの数がしきい値を超えたら、Slackに通知などもできます。

out_Art075.png

感想など

これでゴルーチンのリーク監視作業から解放されました。

元記事・関連記事

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?