時々あなたはそれを自分でしなければならないだけです。 プロプライエタリアプリケーションを管理する場合、利用できるユーティリティのスイートはありません。独自のソリューションを実装する必要がある可能性があります。 ただし、カスタムソリューションには引き続き監視が必要です。 このブログでは、LogicMonitorのTechOpsが一部の内部ツールのカスタム監視をどのように実装したかを示します。
XNUMX月に、データを保存するための独自の時系列データベース(TSDB)のリリースを発表しました。 私達 しませんでした 新しいTSDBをサポートするために必要なすべての内部ツールを発表します。 開発は優れたTSDBを提供しましたが、本番環境で実行するために必要なツールは提供しませんでした。そのため、TechOpsチームは、新しいTSDBアーキテクチャからユーザーデータをバックアップするための運用インフラストラクチャの構築に着手しました。
すべてのTSDBデータをバックアップするためのシステムが整ったら、そのシステムを監視する必要がありました。 詳細には触れませんが、TSDBバックアップシステムには、メタデータデータベース、外部ストレージ、バックアップエージェント、一元化されたバックアップスケジューラなど、監視が必要ないくつかのコンポーネントと、ネットワークスループットなどのパフォーマンスメトリックがあります。 CPU使用率、メモリ使用率、およびディスクパフォーマンス。
これらのほとんどは、LogicMonitorを使用して箱から出してすぐに監視するための簡単なものですが、特注のバックアップエージェントとスケジューラは両方とも Go、Googleの効率的でスケーラブルなオープンソースプログラミング言語であり、本質的に監視には向いていませんでした。 このブログの残りの部分では、これらのGoアプリケーションからカスタムメトリックを公開し、LogicMonitorでそれらを監視することがいかに簡単であったかについて説明します。
概要
Goを使用すると、アプリケーション内に単純なHTTPサーバーを作成することが信じられないほど簡単になります。 この機能と組み合わせて LogicMonitorのWebページデータ収集方法、コードからカスタムメトリックをすばやく公開し、そのデータを使用してインフラストラクチャを監視できるようになりました。
アプリケーションメトリクスの公開
プロセスの最も難しい部分は、コードから公開するメトリックを正確に決定することです。 システムとアプリケーションはそれぞれ異なるため、基本から始めて、Goランタイムライブラリを使用してGoランタイム自体に関するパフォーマンスメトリックを公開する例を示します。
インポート( 「エンコーディング/ json」 "ログ" 「net / http」 "ランタイム" ) func Performance(w http.ResponseWriter、req * http.Request){ 結果:= make(map [string] float32) //ゴルーチンの数を取得します // https://golang.org/pkg/runtime/#NumGoroutine numRoutines:= runtime.NumGoroutine() results ["GoRoutines"] = float32(numRoutines) //メモリ統計を取得します // https://golang.org/pkg/runtime/#MemStats var memStats runtime.MemStats runtime.ReadMemStats(&memStats) //割り当てられ、まだ解放されていないバイト results ["MemAlloc"] = float32(memStats.Alloc) //無料の数 results ["MemFrees"] = float32(memStats.Frees) //割り当てられ、まだ解放されていないバイト results ["MemHeapAlloc"] = float32(memStats.HeapAlloc) //アイドルスパンのバイト results ["MemHeapIdle"] = float32(memStats.HeapIdle) //非アイドルスパンのバイト results ["MemHeapInUse"] = float32(memStats.HeapInuse) //割り当てられたオブジェクトの総数 results ["MemHeapObjects"] = float32(memStats.HeapObjects) //システムから取得したバイト results ["MemHeapSys"] = float32(memStats.HeapSys) // mallocの数 results ["MemMallocs"] = float32(memStats.Mallocs) //ガベージコレクションの総数 results ["MemNumGc"] = float32(memStats.NumGC) //ガベージコレクタがプログラムを一時停止した合計時間 results ["MemPauseTotalNs"] = float32(memStats.PauseTotalNs) //システムから取得したバイト 結果["MemSys"] = float32(memStats.Sys) resp、err:= json.Marshal(results) if err!= nil { log.Printf( "エラー:キューメトリックをjsonにマーシャリングできませんでした") w.WriteHeader(http.StatusInternalServerError) 場合} else { w.Write(resp) } }
このコードブロックは、次の例に示すように、Goランタイムに関するメトリックを含むJSON文字列を返します。
{"GoRoutines":56、 "MemAlloc":6986048、 "MemFrees":950790800、 "MemHeapAlloc":6986048、 "MemHeapIdle":34209790、 "MemHeapInUse":13205504、 "MemHeapObjects":33145、 "MemHeapSys":47415296 MemMallocs ":950823940、" MemNumGc ":142465、" MemPauseTotalNs ":40569120000、" MemSys ":52869370}
公開できる指標は、想像力によってのみ制限されます。 アプリケーションから特定のメトリックを取得し、それをJSONに変換して応答するために必要な、あらゆるコードを記述できます。 基本的な概要は次のとおりです。
import( "encoding / json" "log" "net / http")func FooMetric(w http.ResponseWriter、req * http.Request){//ここでデータポイントを計算または取得しますresults:= getFooMetric()resp、err:= json.Marshal(results)if err!= nil {log.Printf( "エラー:キューメトリックをjsonにマーシャリングできませんでした")w.WriteHeader(http.StatusInternalServerError)} else {w.Write(resp)}}
アプリケーションメトリクスの提供
必要なメトリックをコンパイルするためにいくつかの関数を組み立てたら、それは 単純なHTTPサーバーを起動し、アプリからメトリックを公開するのは簡単です。
import( "log" "net / http")func StartServer()error {// https://golang.org/pkg/net/http //リクエストルーティングを処理するマルチプレクサを作成しますh:= http.NewServeMux()/ /リクエストをルーティングするためのリソースハンドラーの追加//最初の引数 'pattern'は、リクエストパスを照合し、//そのリクエストを// 8080番目の引数 'handler'で指定された関数に転送するために使用されます// https:// golang.org/pkg/net/http/#HandleFunc // //この場合、URLを// https:// localhost:8080 / stats / performance //上記で作成した関数にマッピングしています。マッピング// https:// localhost:8080 / stats / fooから関数FooMetric h.HandleFunc( "/ stats / performance"、Performance)h.HandleFunc( "/ stats / foo"、FooMetric)// HTTPサーバーを作成します、目的のリスナーポート、//マルチプレクサ、およびいくつかのタイムアウト構成を渡す// https://golang.org/pkg/net/http/#Server srv:=&http.Server {Addr:10、Handler:h、ReadTimeout :10 * time.Second、WriteTimeout:XNUMX * time.Second、} // HTTPサーバーを起動します// https://golang.org/pkg/net/http/#Server.ListenAndServe log.Printf( "info:Stats server Started on localhost" + statsPort)log.Fatal(srv.ListenAndServe())return nil}
ボーナス
Goを使用すると、次の例に示すように、リアルタイムのスタックトレースを非常に簡単に出力できます(これは、LogicMonitorでアプリケーションを使用する場合は特に適用できませんが、共有しないと便利です)。
import( "net / http" "runtime")func Stacktrace(w http.ResponseWriter、req * http.Request、){buf:= make([] byte、1 << 16)runtime.Stack(buf、true)w .Write(buf)}
それでおしまい! これで、HTTPサーバーからこの関数を提供し、ブラウザーでスタックトレースを表示できます。 追加するだけです h.HandleFunc(“ / stacktrace”、Stacktrace) HTTPサーバーハンドラーに。
すべてをまとめる
コードを既存のアプリケーションに簡単に挿入できるように、すべてを構造体にまとめてから、このHTTPサーバーをアプリケーションのスタートアップに含める方法を示します。
完成した構造体は次のとおりです。
package app import ( "encoding/json" "log" "net/http" "runtime" ) const statsPort = ":8080" type StatsServer struct {} func s *StatsServer) Performance( w http.ResponseWriter, req *http.Request, ) { results := make(map[string]float32) // get number of Goroutines // https://golang.org/pkg/runtime/#NumGoroutine numRoutines := runtime.NumGoroutine() results["GoRoutines"] = float32(numRoutines) // get memory stats // https://golang.org/pkg/runtime/#MemStats var memStats runtime.MemStats runtime.ReadMemStats(&memStats) // bytes allocated and not yet freed results["MemAlloc"] = float32(memStats.Alloc) // number of frees results["MemFrees"] = float32(memStats.Frees) // bytes allocated and not yet freed results["MemHeapAlloc"] = float32(memStats.HeapAlloc) // bytes in idle spans results["MemHeapIdle"] = float32(memStats.HeapIdle) // bytes in non-idle span results["MemHeapInUse"] = float32(memStats.HeapInuse) // total number of allocated objects results["MemHeapObjects"] = float32(memStats.HeapObjects) // bytes obtained from system results["MemHeapSys"] = float32(memStats.HeapSys) // number of mallocs results["MemMallocs"] = float32(memStats.Mallocs) // total number of garbage collections results["MemNumGc"] = float32(memStats.NumGC) //total time that the garbage collector has paused the program results["MemPauseTotalNs"] = float32(memStats.PauseTotalNs) // bytes obtained from system results["MemSys"] = float32(memStats.Sys) resp, err := json.Marshal(results) if err != nil { log.Printf("error: couldn't marshal queue metrics to json") w.WriteHeader(http.StatusInternalServerError) } else { w.Write(resp) } } func (s *StatsServer) Stacktrace( w http.ResponseWriter, req *http.Request, ) { buf := make([]byte, 1<<16) runtime.Stack(buf, true) w.Write(buf) } func s *StatsServer) StartServer() error { // https://golang.org/pkg/net/http // Create a multiplexer to handle request routing h := http.NewServeMux() // Add resource handlers to route requests // The first argument, 'pattern', is used to match a request path // and forward that request to the function specified by the // second argument, 'handler' // https://golang.org/pkg/net/http/#HandleFunc // // In this case, we're mapping the URL // https://localhost:8080/stats/performance // to the function we created above name Performance and mapping // https://localhost:8080/stats/foo to the function FooMetric h.HandleFunc("/stats/performance", Performance) h.HandleFunc("/stats/foo", FooMetric) // Create the HTTP server, passing in desired listener port, // our multiplexer, and some timeout configurations // https://golang.org/pkg/net/http/#Server srv := &http.Server{ Addr: 8080, Handler: h, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, } // Start the HTTP server // https://golang.org/pkg/net/http/#Server.ListenAndServe log.Printf("info: Stats server started on localhost" + statsPort) log.Fatal(srv.ListenAndServe()) return nil }
これで、HTTPサーバーをアプリに含めるのは、アプリケーションのスタートアップ関数に以下の行を追加するのと同じくらい簡単です。
//監視メトリックサーバーを初期化します//注:サーバーをgo関数として起動するか、ListenAndServe //それ以上のコード実行をブロックしますgofunc(){stats:= StatsServer {} stats.StartServer()}()
HTTPリクエストをアプリケーションに送信することで、指標を表示できるようになりました。
> curl https:// localhost:8080 / stats / performance | jq。 {"GoRoutines":56、 "MemAlloc":6986048、 "MemFrees":950790800、 "MemHeapAlloc":6986048、 "MemHeapIdle":34209790、 "MemHeapInUse":13205504、 "MemHeapObjects":33145、 "MemHeapSys":47415296 MemMallocs ":950823940、" MemNumGc ":142465、" MemPauseTotalNs ":40569120000、" MemSys ":52869370}
アプリがデータを提供しているので、監視を開始します。
LogicMonitor内ですべてをまとめる
公開されたメトリクスの消費は、 Webページのデータソース.
たとえば、次のパフォーマンスを監視するようにデータソースを構成した方法は次のとおりです。 TSDBバックアップスケジューラ:
Applies-Toの値system.hostnameは、このアプリケーションを実行している可能性のあるすべてのサーバーと一致するように構成されています。
GoHTTPサーバーで構成されたリソースエンドポイントを使用していることに注意してください。
最後のステップとして、JSONデータポイントをデータソースに追加する例を次に示します。
JSON応答内の特定のデータポイントへのパスに一致するように、JSONパスフィールドを更新してください。
それでおしまい! LogicMonitorは現在、アプリケーションからデータを収集しています。 メトリックは、監視対象のデバイスに移動してデータソースを見つけることで表示できます。
Goコードで公開されているランタイムメトリックに対応するデータポイントに注意してください。
これで、何らかの理由で顧客データのバックアップがタイムリーに完了しなかった場合でも、運用チームがタイムリーにそれを知ることができると確信できます。 結局のところ、カスタムアプリケーションをインストルメント化していない場合でも、問題について通知するXNUMXつの監視システム(顧客と上司)があります。 これらのXNUMXつのシステムがトリガーされる前に、問題を認識し、対処できることを確認したいと思います。