New Relicでインフラやアプリケーションをモニタリングする

newrelicawsmonitoringgolangfluentd

New RelicはインフラやアプリケーションをモニタリングするSaaSで、同種のサービスとしてはDatadogがある。 2020年に製品や課金体系変わり、転送量とユーザー数に対して課金されるようになったようだ。 ホストや機能に対して課金されるDatadogと比べると、少人数で多くのインスタンスを管理している場合に有利ということになる。また、Datadogの課金体系はやや複雑で頻繁に台数が増減する環境では請求がどの程度になるか読みづらいところがあるが、こちらは分かりやすい。新しい機能を使っても単価が上がることもないのでいろいろと試しやすいのも良いと思う。

一方、大人数で管理していたり、捌くリクエストが膨大な場合APMの転送量がかさむことでDatadogより高くなるケースがある。 多くの場合はユーザー課金の割合が大きくなるので、Full platform userの数を減らして半額のCore userや無料のBasic userにすればコストが抑えられるが、 DashboardやAlertについては全てのユーザーが使える一方APMの画面が見られるのはFull platform userのみだったりするのが悩ましい。 転送量についてはDrop dataすることで減らすことができるが現状画面からは設定できずNerdGraphを用いる必要がある。次はTerraformで設定を行う例。

New RelicのGraphQL API、NerdGraphでリソースを取得する - sambaiz-net

resource "newrelic_nrql_drop_rule" "heavy_path" {
  account_id  = *****
  description = "Drop transactions data"
  action      = "drop_data"
  nrql        = "SELECT * FROM Transaction WHERE `request.uri` = '/heavy_path'"
}

トラブルシューティングは比較的難しく感じていて、なぜかメトリクスが送られないといったことがあったり、古いドキュメントを参照してしまったりしたがライブラリやドキュメントはOSSになっており、いざとなれば実装を読んだり修正のPRを出せば良いのかなと考えている。ただドキュメントに明示されていないことがあったり、されていても見つけられないことがあったりするので、特に初めて使う場合はユーザー単価が上がってしまうがサポートが付くPro以上のプランにすると安心かもしれない。去年買収したCodeStreamを含め、新機能の開発は活発に行われている印象があるので、トラブルシューティングについても改善されると良いなと思っている。

NewRelic CodeStreamでコードの質問やデバッグを効率的に行えるようにする - sambaiz-net

AWS連携

New RelicのアカウントからAssumeRoleするReadOnlyAccessbudgets:ViewBudgetを付与したRoleを作る。

AWSのAssumeRole - sambaiz-net

{
  "Statement": [
    {
      "Action": [
        "budgets:ViewBudget"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ],
  "Version": "2012-10-17"
}

メトリクスの取得は従来の定期的にAPIを呼んでポーリングする方法と、FirehoseにストリーミングするCloudWatch Metric Steamsを用いる方法がサポートされていて、後者が推奨されている。 ただ、Budget など CloudWatch メトリクスにない値はポーリングでないと取得できない。

New RelicでAWSのコストをモニタリングする - sambaiz-net

用意されているCloudFormationのテンプレートにLicense Keyを渡してスタックを作成すると次のリソースが作成され、New Relicの画面上に各種リソースのメトリクスが表示されるようになる。

作成されたMetric Streamsの設定を見るとすべての名前空間のメトリクスが送られるようになっている。

メトリクスが送られない場合はリージョンや誤ったKeyやKey IDをコピーしていないか確認する。

infrastructure monitoring agentのインストール

インスタンスのCPU使用率やネットワークI/Oなどを取得するAgentをインストールする。以下はAmazon Linux 2 (arm64) の例。

$ echo "license_key: ****-" | sudo tee -a /etc/newrelic-infra.yml
$ sudo curl -o /etc/yum.repos.d/newrelic-infra.repo https://download.newrelic.com/infrastructure_agent/linux/yum/amazonlinux/2/aarch64/newrelic-infra.repo
$ sudo yum -q makecache -y --disablerepo='*' --enablerepo='newrelic-infra'
$ sudo yum install newrelic-infra -y
$ sudo systemctl start newrelic-infra

New Relic Flex

/etc/newrelic-infra/integrations.d 下に次のようなyamlファイルを置くとコマンドの出力HTTPのレスポンスファイルの内容をもとにメトリクスを定期的に送ることができる。

Fluentdをモニタリングするのにmonitor_agentの値をTelemetry SDKで送ることもできるが、この機能を用いれば集約サーバーでアプリケーションを動かす必要がない。 ただし設定が多いとデータ量がかなり多くなるので注意。

fluentdのmonitor_agentのデータをGoでGoogle Stackdriverに送って監視する - sambaiz-net

$ cat /etc/newrelic-infra/integrations.d/fluentd.yml
integrations:
- name: nri-flex
  config:
    name: fluentd
    apis:
    - event_type: fluentd
      url: http://localhost:24220/api/plugins.json?@type=forward

次のようなNRQLで値を確認できる。

FROM fluentd SELECT rate(average(buffer_total_queued_size), 1 minute) FACET `config.@id` TIMESERIES 

ログの転送

Infrastructure agent によってインストールされる Fluent Bit ほか自前の Fluentd や Logstash のpluginを用いてログを転送できる。

EMRクラスタのEC2にFluent BitをインストールしログをNew Relicに送信する - sambaiz-net

$ td-agent-gem install fluent-plugin-newrelic
<filter foo>
  @type record_transformer
  <record>
    service_name ${tag}
    hostname "#{Socket.gethostname}"
  </record>
</filter>

<match foo>
  @type newrelic
  api_key ****
</match>

Goの場合logrusのintegrationが提供されていて、これを用いると次のフィールドがログに追加され、リクエストトレースとログを紐づけることができるようになる。同様のことを自前で行えば他のloggerも使える。

func AddLinkingMetadata(m map[string]interface{}, md newrelic.LinkingMetadata) {
	metadataMapField(m, KeyTraceID, md.TraceID)
	metadataMapField(m, KeySpanID, md.SpanID)
	metadataMapField(m, KeyEntityName, md.EntityName)
	metadataMapField(m, KeyEntityType, md.EntityType)
	metadataMapField(m, KeyEntityGUID, md.EntityGUID)
	metadataMapField(m, KeyHostname, md.Hostname)
}

ログを紐づけるとErrors Eventからも飛べるようになるので原因調査の際に役立つ。

CloudWatch Logsを介さずにLambdaのテレメトリを行うnewrelic-lambda-extensionとその仕組み - sambaiz-net

APM

Goroutineの数やGCの実行時間といったランタイム情報や、X-Rayのようなリクエストトレースやそのサマリーを見ることができる。 C, Go, Java, .NET, Node.js, PHP, Python, Rubyのライブラリが提供されている。

$ go get github.com/newrelic/go-agent

echoやginといった主要なWAFにはintegrationが提供されていて、このmiddlewareを使うとリクエストを紐づけたTransactionがcontextに書き込まれ、nrecho.FromContext(c)で取り出せるようになる。 リクエストが紐づいていないTransactionはBackground jobsとして扱われる。

import "github.com/newrelic/go-agent/v3/integrations/nrecho-v4"
app, err := newrelic.NewApplication(
    newrelic.ConfigAppName("app_name"),
    newrelic.ConfigLicense("*****"),
    newrelic.ConfigDistributedTracerEnabled(true),
)
  
http.HandleFunc(newrelic.WrapHandleFunc(app, "/users", usersHandler))
e.Use(nrecho.Middleware(app))

// in middleware
/*
txn.SetWebRequestHTTP(c.Request())
c.Response().Writer = txn.SetWebResponse(rw)
c.SetRequest(c.Request().WithContext(newrelic.NewContext(c.Request().Context(), txn)))
*/

func handler(c echo.Context) error {
	txn := nrecho.FromContext(c)
	return c.String(http.StatusOK, "ok")
}

NewApplication()するとデータを集めて送るgoroutineが動き出すのでShutdown()することなく都度 Application を作成するとgoroutineがリークしてしまう。

Segmentを切るとその区間の実行時間がタイムライン上に表示される。

func do(ctx context.Context) {
  defer newrelic.FromContext(ctx).StartSegment("do").End()
}

HTTPリクエストをwrapすると外部リクエストのSegmentが作られ、Service Map上に各サービスの平均レイテンシなどが表示されるようになる。

client := &http.Client{}
client.Transport = newrelic.NewRoundTripper(client.Transport)

request, _ := http.NewRequest("GET", "http://example.com", nil)
request = newrelic.RequestWithTransactionContext(request, txn)

response, err := client.Do(request)

カスタムメトリクス

app.RecordCustomMetric(key,value)を呼ぶとkeyにCustom/ prefixを付けたメトリクスが送られる。

app.RecordCustomMetric("CustomMetricName", 132)

Data explorerには表示されずAPMのMetrics explorerから値を確認できる。

NRQLでは次のようにnewrelic.timeslice.value参照する

FROM Metric SELECT count(newrelic.timeslice.value) 
  WHERE appName = 'MY APP' 
  WITH METRIC_FORMAT 'Custom/{name}' 
  TIMESERIES FACET name

NewRelicのMetricsとEventsの特性とNRQLによるクエリ - sambaiz-net

アラート

Dashboards の chart などからアラートを飛ばす condition を設定すると、その条件を満たしたときに incident が作成され issue が open する。 issue は policy や condition 単位で作成されるようにすることもでき、その場合は複数の incident が1つの issue に紐づく。 issue のステータスが変わったときに通知が飛ぶ。

通知に付加される condition の custom incident description には {{tag.tags.Name}} のようなテンプレートを用いることができ、これは incident の payload の値を参照するが、 当然 condition の作成時には incident は存在しないのでkeyを確認できず分かりづらい。

デフォルトの Streaming method である Event flow は Delay で設定した時間後にデータが来ないと評価されないので、 不定期に送られる場合は最後にデータが送られてから Delay 時間待ってその Window を評価する Event timer にして必要なら Fill data gaps する。

参考

NewRelic でtd-agent(fluentd) にメトリクスを監視する - y-ohgi’s blog