Elastic APM 7.4.0リリース記念 Angular SPAと.NETCoreアプリにAPMを仕込む

概要

10月2日、Elasticの各種プロダクトの7.4.0がリリースされました。

APM(Application Performance Monitoring)も7.4.0が出ており、ブログも書かれております。

www.elastic.co

この中で2つ注目するところがありました。

  1. We have also further strengthened our support for Single Page Applications (SPA) by adding out of the box Angular instrumentation.
  2. .NET agent full framework support

AngularのSPA向けサポート強化と、.NETのAgentのことが入っています。

2019年5月末のElastic{ON} Tour Tokyoでは、今年中に.NETのAgent出るかもね、と話されていたように思いますが、やはり年内に出てきていましたね。

ちょうど手元にある環境が、フロントがAngular8のSPAで、サーバ側が.NETCore2.2という環境があったので、APMを試してみた記録の記事です。

Angularに仕込む

Angular SPAへの組み込み方は、この本家の記事が一番参考になります。

www.elastic.co

npm install

npm install @elastic/apm-rum-angular --save

何はなくとも、npm installでangular用のライブラリを入れます。

app_module.tsへの適用

お手持ちのapp_module.tsに対して設定を入れていきます。

import { Routes, RouterModule } from '@angular/router'
import { ApmService } from '@elastic/apm-rum-angular'

const routes: Routes = [
  { path: 'contact', component: ContactListComponent },
  { path: 'contact/:id', component: ContactDetailComponent }
]

@NgModule({
  imports: [BrowserModule, RouterModule.forRoot(routes)],
  declarations: [AppComponent, ContactListComponent, ContactDetailComponent],
  providers: [ApmService],
  bootstrap: [AppComponent]
})
export class AppModule {
  constructor(service: ApmService) {
    // API is exposed through this apm instance
    const apm = service.init({
      serviceName: 'angular-app',
      serverUrl: 'http://localhost:8200'
    })

    apm.setUserContext({
      'username': 'foo',
      'id': 'bar'
    })
  }
}

まず大事な設定の箇所を確認します。

apmサーバの指定

    // API is exposed through this apm instance
    const apm = service.init({
      serviceName: 'angular-app',
      serverUrl: 'http://localhost:8200'
    })

ここでは、serviceNameとserverUrlを書いていますが、serverUrlはAPMサーバの場所を指定します。
serviceNameは、KibanaのAPM画面で、ここで指定したサービス名が表示されることになります。

routeの設定

const routes: Routes = [
  { path: 'contact', component: ContactListComponent },
  { path: 'contact/:id', component: ContactDetailComponent }
]

pathとcomponentを対にして設定していますが、いろんなコンポーネントから成り立つようなSPAを作っている場合、すべてのrouteをここで書いておくのは難しいかと思います。

そういう場合は、const routesの部分を丸っと消して、NgModuleのimport箇所をこう書き換えてしまいましょう。

@NgModule({
  imports: [BrowserModule, RouterModule],
  declarations: [・・・],
})

ここまでのまとめ

ここまでにやったことは大きく2つです。

  1. npm installでエージェントを入れる
  2. app_module.tsにAPMの設定を足す

念のためここでビルドエラーが出ないかを確認し、npm startできるかどうか確かめておきましょう。

.NET CoreアプリのAPMを仕込む

nugetでインストール

本家のドキュメントを確認します。

www.elastic.co

今回の.NETCoreアプリは、EntityFrameworkCoreも入っているため、Elastic.Apm.NetCoreAllをnugetで選択して入れます。
この時点でのバージョンは、1.1.0でした。

f:id:tsgkdt:20191002221248p:plain

設定ファイルを書く

appsettings.jsonAPMの設定を追加します。

{
  "ConnectionStrings": {
    "Default": "Data Source=*****************;Initial Catalog=*************;Integrated Security=True;Pooling=False;Connect Timeout=30"
  },
  "AllowedHosts": "*",
  "ElasticApm": {
    "LogLevel": "Debug",
    "ServerUrls": "http://localhost:8200",
    "TransactionSampleRate": 1.0,
    "ServiceName":  "server-app"
  }
}

やはり肝心なのは、ServerUrlsでAPMサーバの場所を指定するのと、ServiceNameで分かりやすい名前を設定しておきましょう。

何も指定しない場合は、プロジェクト名がServiceNameとなって表示されます。

Startup.csに追加する

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseStaticFiles();
    app.UseAllElasticApm(Configuration);  // ← これ

Configurationを引数で渡しておくことで、先ほど設定したappsettings.jsonの設定を使ってくれます。

もし、引数無しの app.UseElasticApm(); とした場合については、ドキュメントにこう書かれています。

By simply calling app.UseElasticApm() without the overload, the agent will read configurations only from environment variables.

設定ファイルを読まずに環境変数を使うとのことです。
例えばAzureでAppServiceを使っている人などは、設定ファイルより環境変数を使うことが多いかと思うので、その用途に向いていそうです。

APMサーバの設定

忘れてはならないAPMサーバの設定を2つほど紹介しておきます。

APMサーバの中にある設定ファイル、apm-server.ymlを編集します。

  # Enable Real User Monitoring (RUM) Support. By default RUM is disabled.
  rum:
    enabled: true

    # Comma separated list of permitted origins for real user monitoring.
    # User-agents will send an origin header that will be validated against this list.
    # An origin is made of a protocol scheme, host and port, without the url path.
    # Allowed origins in this setting can have * to match anything (eg.: http://*.example.com)
    # If an item in the list is a single '*', everything will be allowed.
    allow_origins : ['*']

まず1点目です。

rum.enabledがデフォルトでは、falseになっています。
今回はRUMを使いたいので、これをtrueにします。

rum:
 enabled: true

次に2点目です。

CORS設定をしておかないと、APMサーバとフロントエンドのAngularのSPAが動くところが違うなどした場合、データを送れないため、これを許可するようにしておきます。

allow_origins: ['*']

KibanaのAPM画面で確認

f:id:tsgkdt:20191002222808p:plain

Angular側の見え方

f:id:tsgkdt:20191003000959p:plain

Transactionsのところを見ると、SPAのRouteのところが表示されています。
リンクになっているので、選択すると詳細画面に遷移します。

f:id:tsgkdt:20191003001156p:plain

home/system/logが、外部にPOSTでアクセスしていて、そこで3秒近くかかっていることが見て取れます。
また、赤枠で囲ったuser: barとなっているところを見てください。

これは、app_module.tsでAPMの設定をいれたときにid:bar といれたところからきています。

TimelineではなくMetadataのタブに遷移すると、usernameも確認することができます。

f:id:tsgkdt:20191003001459p:plain

.NETCore側の見え方

今度は、.NETCoreで作られたサーバ側の方を見てみましょう。

f:id:tsgkdt:20191003001620p:plain

GETやらPOSTやらRESTのエンドポイントになっているところがリストアップされています。

Transactionのリンク部分をクリックして、詳細を見てみます。

f:id:tsgkdt:20191003001820p:plain

発行されているSQL(EntityFrameworkCoreを使っているところ)の呼び出しと処理時間がTimelineとして表示されています。

遅いな?と思ったとき、SQLを投げている回数が多いのか、SQLそのものの処理に時間がかかっているのか、一目で確認することができますね。

まとめ

たとえば、このようなアプリケーションをAzure上のPaaSにのせて動かしていたことを考えますと、何もElasticのAPMじゃなくてもAzure Monitorで足りるじゃないか、という考え方もできます。

一方で、ElasticのAPMの良さを考えてみますと次のように言えそうです。

  1. オンプレでもどこのベンダーのクラウドでも、同じようにアプリケーションのパフォーマンスを監視できる
  2. 各種のメトリクスやアプリケーションのログの監視とツールを統合することによる学習コスト低減が実現できる
  3. 監視する対象の台数で課金されない

機械学習との連携も組み込まれていますが、そんな本格的な利用でなくても、ある時点で組み込んでパフォーマンスの調査をしてみる、という用途でも十分に活用できるかと思います。