はじめに
もし、アプリケーションに実装できるならそれが良いです。独自に実装などせずにエンドポイントにて500 Internal Server Error
が多発していればアラートをすれば良いので...。
こちらの続編になります。 syu-m-5151.hatenablog.com
本エントリーでは、GolangでEchoフレームワークを使用し、Prometheus ExporterをMiddlewareとして実装する方法について説明します。Prometheus Middlewareは、自動でMetrics を生成します。これにより、アプリケーションのパフォーマンス監視や問題解析が容易になります。
利用しているコードはこちら
コードを解説するんじゃい
シンプルだけど解説をします。環境構築などは前回のエントリーに任せます。 Echoフレームワークを使ってGolangでシンプルなWebアプリケーションを作成し、Prometheus Exporterをミドルウェアとして実装する例です。
package main import ( "math/rand" "net/http" "time" "github.com/labstack/echo-contrib/prometheus" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" prom "github.com/prometheus/client_golang/prometheus" ) // Prometheus のメトリクスを定義しています。 // これらのメトリクスは、3-shake.com への外部アクセスの情報を収集するために使用されます。 var ( externalAccessDuration = prom.NewHistogram( prom.HistogramOpts{ Name: "external_access_duration_seconds", Help: "Duration of external access to 3-shake.com", Buckets: prom.DefBuckets, }, ) lastExternalAccessStatusCode = prom.NewGauge( prom.GaugeOpts{ Name: "last_external_access_status_code", Help: "Last status code of external access to 3-shake.com", }, ) ) // init 関数内で、メトリクスを Prometheus に登録しています。 func init() { prom.MustRegister(externalAccessDuration) prom.MustRegister(lastExternalAccessStatusCode) } // 3-shake.com の外部アクセスを計測するミドルウェアを作成します。 func measureExternalAccess(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { // HTTP クライアントを作成し、タイムアウトを 10 秒に設定します。 client := &http.Client{Timeout: 10 * time.Second} // 現在の時刻を取得し、アクセス開始時間として保持します。 startTime := time.Now() // 3-shake.com に対して HTTP GET リクエストを送信します。 resp, err := client.Get("https://3-shake.com") // アクセス開始時間から現在の時刻までの経過時間を計算し、duration に格納します。 duration := time.Since(startTime) // エラーが発生しない場合(リクエストが成功した場合) if err == nil { // アクセス時間(duration)をヒストグラムメトリクスに追加します。 externalAccessDuration.Observe(duration.Seconds()) // ステータスコードをゲージメトリクスに設定します。 lastExternalAccessStatusCode.Set(float64(resp.StatusCode)) // レスポンスのボディを閉じます。 resp.Body.Close() } // 次のミドルウェアまたはハンドラ関数に処理を移します。 return next(c) } } func unstableEndpoint(c echo.Context) error { // 0 から 4 までのランダムな整数を生成します。 randomNumber := rand.Intn(5) // 生成された整数が 4 の場合、HTTP ステータスコード 500 を返します。 if randomNumber == 4 { return c.String(http.StatusInternalServerError, "Something went wrong!") } // それ以外の場合、HTTP ステータスコード 200 を返します。 return c.String(http.StatusOK, "Success!") } func main() { e := echo.New() // ミドルウェアの設定 e.Use(middleware.Logger()) e.Use(middleware.Recover()) // Prometheus ミドルウェアを有効にします。 p := prometheus.NewPrometheus("echo", nil) p.Use(e) // 3-shake.com への外部アクセスを計測するミドルウェアを追加します。 e.Use(measureExternalAccess) // ルートのエンドポイントを設定します。 e.GET("/", func(c echo.Context) error { return c.String(http.StatusOK, "Hello, World!") }) // /unstable エンドポイントを設定します。 // 20% の確率で HTTP ステータスコード 500 を返します。 e.GET("/unstable", unstableEndpoint) // サーバーを開始します。 e.Start(":2121") }
var
var
で3-shake.com への外部アクセスの情報を収集するための Prometheus メトリクスを定義していきます。
var ( externalAccessDuration = prom.NewHistogram( prom.HistogramOpts{ Name: "external_access_duration_seconds", Help: "Duration of external access to 3-shake.com", Buckets: prom.DefBuckets, }, ) lastExternalAccessStatusCode = prom.NewGauge( prom.GaugeOpts{ Name: "last_external_access_status_code", Help: "Last status code of external access to 3-shake.com", }, ) )
init
init
関数でメトリクスを Prometheus に登録します。
func init() { prom.MustRegister(externalAccessDuration) prom.MustRegister(lastExternalAccessStatusCode) }
measureExternalAccess
measureExternalAccess
関数で3-shake.com への外部アクセスを計測するミドルウェアを定義します。こちらの方がEcho Likeな定義の仕方だと思うので好きです。Echo のカスタムミドルウェアで、リクエストが処理される前に 3-shake.com への外部アクセスを計測する役割を持っています。
func measureExternalAccess(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { // HTTP クライアントを作成し、タイムアウトを 10 秒に設定します。 client := &http.Client{Timeout: 10 * time.Second} // 現在の時刻を取得し、アクセス開始時間として保持します。 startTime := time.Now() // 3-shake.com に対して HTTP GET リクエストを送信します。 resp, err := client.Get("https://3-shake.com") // アクセス開始時間から現在の時刻までの経過時間を計算し、duration に格納します。 duration := time.Since(startTime) // エラーが発生しない場合(リクエストが成功した場合) if err == nil { // アクセス時間(duration)をヒストグラムメトリクスに追加します。 externalAccessDuration.Observe(duration.Seconds()) // ステータスコードをゲージメトリクスに設定します。 lastExternalAccessStatusCode.Set(float64(resp.StatusCode)) // レスポンスのボディを閉じます。 resp.Body.Close() } // 次のミドルウェアまたはハンドラ関数に処理を移します。 return next(c) } }
unstableEndpoint
ちゃんと、メトリクス値が取得できているか確認したいのでunstableEndpoint
というエンドポイントを追加し、リクエストのうち約 5 回に 1 回失敗するように実装しました。このエンドポイントは、リクエストが成功した場合には HTTP ステータスコード 200 を返し、失敗した場合には HTTP ステータスコード 500 を返します。
func unstableEndpoint(c echo.Context) error { // 0 から 4 までのランダムな整数を生成します。 randomNumber := rand.Intn(5) // 生成された整数が 4 の場合、HTTP ステータスコード 500 を返します。 if randomNumber == 4 { return c.String(http.StatusInternalServerError, "Something went wrong!") } // それ以外の場合、HTTP ステータスコード 200 を返します。 return c.String(http.StatusOK, "Success!") }
curl してくるワンライナー(fish)も用意しましたので思う存分curl してください。
for i in (seq 1 50); curl http://localhost:2121/unstable; echo ""; end
Middlewareを適用
寂しいのでPrometheus 以外のMiddlewareをEchoインスタンスに適用しました。
middleware.Logger()
: リクエストのログを出力するMiddlewaremiddleware.Recover()
: パニックを回復してアプリケーションがクラッシュしないようにするMiddleware
e.Use(middleware.Logger()) e.Use(middleware.Recover())
Prometheus Middlewareを適用
これだけなんです。 Echo用の新しいPrometheus Middlewareインスタンスを作成します。作成したPrometheus MiddlewareインスタンスをEchoインスタンスに適用します。
// Prometheusミドルウェアを適用する p := prometheus.NewPrometheus("echo", nil) p.Use(e)
EchoのMiddlewareについて
Middlewareは、リクエストとレスポンスの処理の前後にカスタムロジックを実行するための仕組みを提供しています。EchoのMiddlewareは、コードの再利用性、可読性、そして機能の分離を向上させるために役立ちます。
Echo Echo Middleware の特徴
Middlewareは、複数のMiddleware関数を組み合わせて実行することができます。これにより、機能を組み合わせてカスタム処理パイプラインを構築することができます。これらは登録された順序で実行されます。これにより、処理の流れを明確にし、簡単に制御できるようになります。また、Echoでは、これらをグローバルに適用することも、特定のルートに適用することもできます。これにより、アプリケーション全体または特定のエンドポイントに対してカスタム処理を適用できます。Echoは、いくつかの組み込みミドルウェアを提供していますが独自のカスタムミドルウェアを作成してアプリケーションに適用することもできます。
e := echo.New() e.Use(middleware.Logger()) # 登録された順序で実行されるぞ e.GET("/",getHallo,middleware.Recover()) # e.GET("/", <Handler>, <Middleware...>) で特定のルートにだけMiddlewareを登録できる e.Use(LoggingMiddleware) # 独自で実装するカスタムミドルウェア
再び、Docker Compose での実行するんじゃろがい
完全に同じことやってるのでこちらを参考にしてください
見れたぞぉおおおお
http://localhost:2121/metrics の結果もこちらに記載しておきます
Golang のEcho でMiddlewareを使ってアプリケーションのPrometheus Exporter を実装することができました。アラートの設定方法については他のブログを参照してください。
さいごに
以上で、Echo フレームワークを使って、Prometheus メトリクスを追加し、さらに不安定なエンドポイントを作成する方法を解説しました。この知識を活かして、みなさんのアプリケーションにメトリクスを取得する機能を追加して、可観測性を向上させましょう!
全然、関係ないけど翻訳に携わってコンテナセキュリティのブラックボックス感が多少薄まるのでみんな読んでくれ...