弊社では O11yツールとして、NewRelicを使っているのですが、NewRelic Agentの設定からDashBoardの構築までほとんどやり尽くされており、自分で新しく何かを追加したりするということは滅多にないので、今回はISUCON環境でNewRelicをどのように導入していくのか、というところの自信の学びを共有していきたいと思います。
ISUCONとは?
ISUCONとはLINEヤフー株式会社が運営窓口となって開催している、お題となるWebサービスを決められたレギュレーションの中で限界まで高速化を図るチューニングバトルです
https://isucon.net/
概要
やったこととしては、こちらのNewRelic公式の記事をISUCON13の環境で試してみたという形になっています。
https://newrelic.com/jp/blog/how-to-relic/isucon-go-agent
https://newrelic.com/jp/blog/how-to-relic/isucon-infra
前提として、
- EC2インスタンス上にISUCON環境を構築
- 言語はgo
- NewRelicフリープラン使用
としています。
一つ注意点として、NewRelicのフリープランでは提供されている全機能を使うことができますが、対象となるのは1名のみであり、ISUCONチーム内で使いまわしたりできないのが辛いところかなと思っています。
DashBoardを公開することで、チームメンバーにデータを提供することもできますが、そこまでするのはコストなのでは?と思ったので、私はISUCON14本番ではNewRelicは使いませんでした。
APMとInfrastructure Agentの導入
APM(Application Performance Monitoring)は、アプリケーションの性能をリアルタイムで監視し、パフォーマンスの問題を探り出して解決するためのツールのことです。
Infra Agentは、サーバーやインフラストラクチャのパフォーマンスを監視するためのツールです。ハードウェア、OS、アプリケーションの状態をリアルタイムで収集し、分析することができます。
手順としては、サイドバーから「Integrations & Agents」→「Go」を選択します。
あとは、手順に従って進めていくだけではありますが、ところどころ簡単に説明します。
License KeyとUser Keyを作成します(License Keyはあとで使います)
GoのAPM Agent Packageをインストールします。
ISUCON13はEchoを用いているので、「Custom App」を選択。
アプリケーション名を決めます。すると、Go Agentを初期化するコードが出力されるのでそちらをコピーしておきます。
少し下に行くと、標準パッケージに関してどのようにモニタリングを適用させればいいか書かれていますが、今回のケースには当てはまれないので一旦スルーします。
InfraStructure Agentの導入するコマンドが表示されるので、そちらをコピペして、サーバーで実行します。次の「Test Connection」は実行しなくても大丈夫です。
APM Agentの設定
コードを編集してAPMが動くようにしていきます、先ほどコピーしたNR Agentの初期化設定をmain関数に書きます。
func main() {
e := echo.New()
var app *newrelic.Application
var err error
app, err = newrelic.NewApplication(
newrelic.ConfigAppName("isupipe-go"),
newrelic.ConfigLicense("Your Licence Key"),
newrelic.ConfigAppLogForwardingEnabled(true),
)
次にHandlerをNewRelicのAgentでWrapする必要があるのですが、
ISUCON13では、Echoというフレームワークが使われているのでEchoのNR Integrationを入れるとミドルウェアを設定するだけで、簡単にモニタリングができるようになります。
go get -u github.com/newrelic/go-agent/v3/integrations/nrecho-v4
こちらもmain関数のどこかに記述する
cookieStore.Options.Domain = "*.u.isucon.local"
e.Use(session.Middleware(cookieStore))
// e.Use(middleware.Recover())
e.Use(nrecho.Middleware(app)) //これ
これで、APMの設定は完了です。
ベンチを回して負荷をかけてやると、、
Infrastructureでは、CPU、メモリ使用率ネットワークレイテンシが見れたり、、
APMからは、alpのようにAPIごとのレスポンスタイムが見れるようになります。
これでめでたし、と思いたかったのですが一つ大事なことを見落としていました。
スロークエリは??一番大事なとこが計測できていない、、
よく見ると最初に紹介した公式の記事にこう書いてありました。
Go 言語は言語の特性上、自動でDB などへのアクセス処理などの計測ができない言語となっており開発者が自身で計測をするためにコードを追加していく必要があります。
どうやら、公式によるとcreateDataStoreSegmentというnewrelic.DatastoreSegmentを返す関数を作ってやり、クエリ実行コマンドを挟んでやる必要があるらしいです。
func postIsuCondition(c echo.Context) error {
txn := nrecho.FromContext(c)
defer txn.End()
// 略
var count int
select_isu_count_query := "SELECT COUNT(*) FROM `isu` WHERE `jia_isu_uuid` = ?"
select_isu_count := createDataStoreSegment(select_isu_count_query, "isu", "SELECT", jiaIsuUUID)
select_isu_count.StartTime = txn.StartSegmentNow()
err = tx.Get(&count, select_isu_count_query, jiaIsuUUID)
select_isu_count.End()
// 略
}
func createDataStoreSegment(query, collection, operation string, params ...interface{}) newrelic.DatastoreSegment {
mySQLConnectionData = NewMySQLConnectionEnv()
queryParams := make(map[string]interface{})
var i = 0
for _, param := range params {
switch x := param.(type) {
case []interface{}:
for _, p := range x {
queryParams["?_"+strconv.Itoa(i)] = p
i++
}
case interface{}:
queryParams["?_"+strconv.Itoa(i)] = x
i++
default:
//ignore
}
}
return newrelic.DatastoreSegment{
Product: newrelic.DatastoreMySQL,
Collection: collection,
Operation: operation,
ParameterizedQuery: query,
QueryParameters: queryParams,
Host: mySQLConnectionData.Host,
PortPathOrID: mySQLConnectionData.Port,
DatabaseName: mySQLConnectionData.DBName,
}
}
確かに、DatastoreSegmentを使えばスロークエリの情報を取得できることはできましたが、全てのクエリに対して、この処理を加えるのは流石にめんどくさすぎるので、ISUCONのgo実装のモニタリングにはNewRelicは向かないんじゃないかなぁ、ここまで来て思ってしまいました。
PythonとかPHP、NodeならAPM入れるだけでスロークエリとってくれそうなので、それらの言語を使うならNewRelicを入れたいところですね!
最後は尻すぼみな感じで終わってしまいましたが、この記事に書いたようなことを一通り試しただけでもだいぶNewRelicへの理解が深まりました。
ぜひ、ISUCONを通してNewRelicを学んでみてはいかがでしょうか?