こんにちは、サイオステクノロジー武井です。今回はApplication Insightsを使って本格的なアプリケーションのパフォーマンス管理をやりたいと思います。
あ、またAzureの話って思って他のサイトに行かないでください(^_^;) 後に詳しくお話しますが、Application Insightsの監視対象はオンプレミスや他のクラウドもOKなので、Azurer(アジュラー→Azureを使う人の総称)じゃない人ももうちょっと読んで頂けますと幸いですーm(_ _)m
APMってなに?
Application Performance Managementの略です。一般的にはアプリケーションの性能を管理・監視することを指します。昨今になってこの言葉が巷を騒がせていますが、なぜでしょうか?
昔とくらべて、アプリケーションの構成は複雑化するばかりです。その最たるものとして「マイクロサービス」があげられるわけですが、マイクロサービスは複数の小さなプロセスが総合に連携しあって、一つの大きなシステムを構成します。そうなると、アプリケーションの問題がどこで起こっているのかが把握しにくくなります。単純に「パフォーマンスが遅い」と言っても、多数あるマイクロサービスのどこに問題があるのかというのは、ログを丁寧に追っていくしかありません。しかし、通常のログだけでは、マイクロサービス同士の関連までも追っていくことは難しいと思います。
例えばあるユーザーのアクセスのログは、マイクロサービスAではこのログ、マイクロサービスBではこのログ、、、といった感じで解析するためには、一つのHTTPリクエストに対して一意な識別子を全てのマイクロサービスのログに埋め込むしかありません。これを実装するのはかなり骨が折れるわけですが、こういうのを手助けしてくれるのがAPMなのです。
ということで、APMがどのように手助けしてくれるかを本ブログで順を追って説明したいと思います。
Application Insightsってなに?
Application Insightsとは、アプリケーションのパフォーマンスを管理できるAzureのサービスになります。アプリケーションのパフォーマンス管理とは具体的にいいますと、アプリケーションのレスポンス速度を常に計測して規定の速度を下回ったらアラートを上げたり、アプリケーションのパフォーマンスが出ないときの原因を突き止めたり、様々なメトリック(CPU使用率やネットワークI/O)を取得して週次のレポートにして分析したりします。分析とは例えば、最近CPU使用率高くなってきたから、インスタンス数を増やそうかなーとかそういう判断をします。
Application Insightsの素晴らしいところは、コードを一切変更せずに、既存のアプリケーションのパフォーマンス監視ができるところです。.NETやJavaは専用のエージェントを組み込むことにより、これを実現します。他の言語(Python等)は若干変更しなければならないのですが。。。
そして、Application Insightsの監視対象は、Azure上で動くアプリケーションだけではなく、オンプレミスや他のクラウドサービスで動くアプリケーションも含むことができます。
Application Insightsのしくみ
Application Insightsのしくみについて説明します。
Application Insightsのデータの流れ
Application Insightsのデータの流れについて、以下の図をベースに説明します。
まず、Application Insightsにはログとメトリクスを格納するデータベースがあります。ログとは、アプリケーションが出力するアクセスログやエラーログというような、いわゆる「ログ」です。メトリクスはCPU使用率やメモリなど、システムの状態を表す数値データになります。Application Insightsはログとメトリクスのデータをもとに様々な分析やアクションをします。
では、そのログやメトリクスは主に2つのところから集められます。一つはApplication Insights SDK、そしてもう一つは、ブJavaScript SDKです。
まずは、Application Insights SDKの方について説明します。
Javaや.NETに、Application Insights専用のSDKを埋め込む(埋め込むといった表現が適切かどうかはわかりませんが)ことで、そのSDKがアプリケーションの内部の挙動を検知して、アプリケーションのログ、他のシステムへのアクセス、メモリやプロセスの使用率などをApplication Insightsに送ってくれます。このApplication Insightsに送られるデータのことを「テレメトリ」と呼びます。
そして、そのようにして集められたテレメトリは、Application Insightsのデータベースに格納されて、あらかじめ定められた条件(ある数値が一定のしきい値を超えるなど)に合致するとシステム管理者にメールで通知されたり、システム管理者がテレメトリを分析することができます。また、先の条件によって、イベントを起動することもできます。HTTPやAzure Functions、Logic Appsなどを起動することができるので、Slackへ通知したりとか、仮想マシンをシャットダウンしたりとか、それはもう色んなことができます。
ところで、全くコードを変更することなく、様々な情報をApplication Insightsに収集するってどういうしくみなのか気になりますよね。ここでは、Javaの場合にフォーカスして説明します。おそらく.NETもそうしくみは変わらないと思います。
JavaにはJavaエージェントというしくみがあり、既存アプリケーションのJavaのコードを上書きしたり挙動を変えることのできる機能があります。Application Insightsでは、Application Insights専用のJavaエージェント(先ほどからSDKと呼称しているものです)を指定して、既存アプリケーションを実行することにより、ログを取得するライブラリ(Logback)はHTTPクライアントライブラリのクラスを変更して、Application Insightsにデータが流れるようにします。Javaエージェントの仕組みについては、以下のブログを参考にしていただければと思いますヮ(゚д゚)ォ!
次に、Java Script SDKについて説明します。
テレメトリは、アプリケーションに埋め込んだApplication Insights SDKだけではなく、ブラウザからも集めることができます。その場合のテレメトリの収集方法は、HTML内に特殊なJava Scriptを埋め込むだけでOKです。ブラウザから集められたデータによって、クライアントサイドでしかわからない情報を収集することができます。例えばHTMLのレンダリングのスピードなどです。昨今クライアントサイドはJava Scriptが複雑化して、HTMLのレンダリングやDOMの解析などの処理も結構すごくて、これが原因で全体的なパフォーマンス低下につながる可能性もあります。そういった原因も解析することが可能です。
先のApplication Insightsの構成図にもあるように、「Application Insights SDK」と「JavaScript SDK」の違いは、そのデータ収集範囲です。Application Insights SDKは、そのSDKが埋め込まれているアプリケーション及びアプリケーションがアクセスする他のシステム、JavaScript SDKはブラウザからアプリケーションの手前までになります。
Application Insightsのデータの保存
先ほどの図で、Application Insightsは「ログ」「メトリック」というデータ保存場所に保存されると説明しました。今回はこのデータ保存場所に焦点を当てて説明します。
「メトリック」はCPU使用率や空きメモリなど、「数値」に関する情報が保存される場所で、「ログ」はその名のとおり、リクエストや例外のログなど、テキストベースの情報が保存されるところです。
そして、Application Insightsにはグラフやマトリックスなど色々な可視化ツール、アラート機能がありますが、全てこの「ログ」「メトリクス」に保存されているデータをベースにしています。
ログの保存
特に重要なのがログで、ログはLog Analyticsワークスペースというリソース内にある複数のテーブルに分けて出力されます。これらのテーブルの中身は簡単に見ることができます。Application Insightsのリソースにアクセスして、左部メニューの「ログ」をクリックします。
「availabilityResults」「browserTimings」など様々なテーブルがあるのがわかります。これらのテーブルにSDKから送信されたテレメトリが格納されます。では、各テーブルにどんな情報が格納されているのかを表にしてみました。
availabilityResults | 可用性テスト(後に説明する外部からの接続確認テスト)の結果が格納される |
browserTimings |
ブラウザに埋め込んだJavaScriptから送信されたテレメトリ情報が格納されるテーブル。ブラウザ側の挙動(レンダリングにかかった時間など)を取得することができる。 |
customEvents |
コード内で意図的に発生させたイベントが記録されるテーブル。 |
customMetrics | SDK独自のメトリックが記録されるテーブル。例えばJavaのSDKだったらヒープ使用量など。 |
dependencies |
他のシステムを呼び出したログが格納されるテーブル。先の図で「他システムへのアクセス」と書いてあるところ。 |
exceptions | アプリケーションの例外を記録するテーブル。 |
performanceCounters | CPU使用率やメモリ使用量などのデータが記録されるテーブル。メトリクスというデータベースにも同様のデータが記録されるが、こちらのテーブルに記録されるデータのほうがより詳細な情報があり分析に向いている。つまりメトリクスに記録されるのはリアルタイムの処理向けで、ログに記録されるのは統計や分析に向いている。 |
requests | アプリケーションへのアクセスログが記録されるテーブル。browserTimingsにも同様のログが記録されるが、browserTimingsの方はブラウザからアプリケーションまでのアクセスログであり、ブラウザからアプリケーションまでの送受信速度など詳しいデータが取れる。requestsはあくまでアプリケーションがリクエストを受信したというだけのログになる。 |
traces | アプリケーションが出力したログが記録されるテーブル。JavaのSDKを使った場合には、logbackのライブラリにて出力されたログがこのテーブルに記録される。 |
メトリクスの保存
メトリクスが内部的にどのように保存されているかをここで説明致します。メトリクスがAzure Monitorに送信されてくると、「合計」「カウント(送信されたメトリクスの数)」「最小値」「最大地」「平均」を1分単位で集計します。そしてさらに今度は5分ごとに同様の集計を行い、さらに15分ごと、30分ごと、1時間毎・・・と同様の集計を行ってデータを保存します。このようにAzure Monitorにメトリクスが送信されたら、都度データを集計して、集計済みのデータを保存しておくことにより、都度集計しなくてすむので、高速なグラフの生成などが可能になります。
標準的なメトリックとログベースのメトリック
さらにメトリックは「標準的なメトリック」と「ログベースのメトリック」の2種類があります。
「標準的なメトリック」の方は、取得データがログベースのメトリックより少ないので、アラートメールの送信などリアルタイム処理に向いています。
一方で「ログベースのメトリック」はメトリックでありながら、そのベースとなるデータはログです。その分情報量は多いのですが、リアルタイム処理が苦手で、主にグラフなどの分析処理に向いています。
まずはやってみよう!!
Application Insightsの詳細な仕様や機能は後ほど説明します。まずはApplication Insightsの雰囲気を掴むため、超簡単なアプリで単純なメトリックスを取得して、その内容を確認したいと思います。
Application Insightsの作成
まずはログやメトリックスのデータを格納するためのAzureリソース「Log Analyticsワークスペース」を作成します。Azureポータル上部のテキストボックスに「log」と入力すると、「Log Analyticsワークスペース」と表示されるので、それをクリックします。
「+作成」をクリックします。
「サブスクリプション」「リソースグループ」は適宜環境に合ったものを入力してください。「名前」はLog Analyticsワークスペースを識別する任意の名称、「地域」はLog Analyticsワークスペースを作るリージョンを選択します。最後に「確認および作成」をクリックします。
内容に問題なければ「作成」をクリックします。これでしばらくするとLog Analyticsワークスペースが出来上がります。
次にApplication Insightsのリソースを作成します。Azureポータル上部のテキストボックスに「Application」と入力すると、「Application Insights」と表示されるので、それをクリックします。
「+作成」をクリックします。
以下の要領で入力してください。
- サブスクリプション:あなたの環境にあったもの
- リソースグループ:あなたの環境にあったもの
- 名前:Application Insightsのリソースを識別する任意の名称
- 地域:Application Insightsのリソースを作るリージョン
- リソースモード:ワークスペースベース
- Log Analyticsワークスペース:先ほど作成したLog Analyticsワークスペース
最後に「確認および作成」をクリックします。
内容に問題がなければ「作成」をクリックします。
監視対象サンプルアプリケーションの作成
Spring Boot公式のサンプルアプリを使うこととします。以下のリポジトリをCloneしてください。
$ git clone https://github.com/spring-guides/gs-spring-boot.git
ビルドをします。サンプルアプリ本体はinitialディレクトリにあるので、そこに移動してmavenでビルドします。
$ cd gs-spring-boot/initial $ mvn clean package
とりあえず稼働確認します。
$ java -jar target/spring-boot-0.0.1-SNAPSHOT.jar
https://localhost:8080/にアクセスして、ブラウザに「Greetings from Spring Boot!」と表示されれば成功です!!では、次に、このサンプルアプリケーションに、Application Insights SDKとJavaScript SDKのの両方を使ってテレメトリを収集してみたいと思います。
Application Insights SDKを使ってテレメトリを収集する
以下のURLからApplication InsightsのSDKをダウンロードして、gs-spring-boot/initial/libディレクトリに配置してください。
https://docs.microsoft.com/en-us/azure/azure-monitor/app/java-in-process-agent
Application Insightsのリソースの左メニュー部「概要」をクリックします。右側に「接続文字列」というのがありますので、これをコピーします。これは、Application Insightsを識別するインストルメンテーションキーを含む接続文字列になり、Application Insightsにテレメトリを送信する際に必要になります。データベースへの接続情報みたいなものです。
Application InsightsのSDKを配置したのと同じディレクトリにapplicationinsights.jsonという名前のファイルを作成し、以下の内容を追加してください。
{ "connectionString": "[先ほどコピーした接続文字列]" }
先ほどのSDKをJavaエージェントとして指定して、Javaアプリケーションを起動します。
$ java -javaagent:lib/applicationinsights-agent-3.0.2.jar -jar target/spring-boot-0.0.1-SNAPSHOT.jar
そして何度かhttps://localhost:8080/にアクセスしてみます。次にApplication Insightsのリソースにアクセスして、左部メニューのログをクリックします。クエリの入力欄に「requests」と入力して、「実行」をクリックします。後ほど詳細は説明しますが、このrequestsというテーブルにはアクセスログが記録されます。見てみますと、なんとhttps://localhost:8080/宛のアクセスログが記録されているのがわかりますでしょうか!!他にも色んなログが記録されるのですが、それは後ほど。。。
ということでとっても簡単に、そして既存のコードを変更することなくアプリケーションの情報を取得することができました。
JavaScript SDKを使ってテレメトリを収集する
今度はJavaScript SDKを使ってテレメトリを収集してみます。テレメトリを収集したい対象のWebページ全てに、以下のように<script>〜</script>で囲っている部分を<head>〜</head>の間に挿入します。「XXXXXXXXXXXX」はインストルメンテーションキーを入力します。
<!DOCTYPE html> <html> <head> <title>JavaScript SDK Test</title> <meta charset="utf-8" /> <script type="text/javascript"> !function(T,l,y){var S=T.location,k="script",D="instrumentationKey",C="ingestionendpoint",I="disableExceptionTracking",E="ai.device.",b="toLowerCase",w="crossOrigin",N="POST",e="appInsightsSDK",t=y.name||"appInsights";(y.name||T[e])&&(T[e]=t);var n=T[t]||function(d){var g=!1,f=!1,m={initialize:!0,queue:[],sv:"5",version:2,config:d};function v(e,t){var n={},a="Browser";return n[E+"id"]=a[b](),n[E+"type"]=a,n["ai.operation.name"]=S&&S.pathname||"_unknown_",n["ai.internal.sdkVersion"]="javascript:snippet_"+(m.sv||m.version),{time:function(){var e=new Date;function t(e){var t=""+e;return 1===t.length&&(t="0"+t),t}return e.getUTCFullYear()+"-"+t(1+e.getUTCMonth())+"-"+t(e.getUTCDate())+"T"+t(e.getUTCHours())+":"+t(e.getUTCMinutes())+":"+t(e.getUTCSeconds())+"."+((e.getUTCMilliseconds()/1e3).toFixed(3)+"").slice(2,5)+"Z"}(),iKey:e,name:"Microsoft.ApplicationInsights."+e.replace(/-/g,"")+"."+t,sampleRate:100,tags:n,data:{baseData:{ver:2}}}}var h=d.url||y.src;if(h){function a(e){var t,n,a,i,r,o,s,c,u,p,l;g=!0,m.queue=[],f||(f=!0,t=h,s=function(){var e={},t=d.connectionString;if(t)for(var n=t.split(";"),a=0;a<n.length;a++){var i=n[a].split("=");2===i.length&&(e[i[0][b]()]=i[1])}if(!e[C]){var r=e.endpointsuffix,o=r?e.location:null;e[C]="https://"+(o?o+".":"")+"dc."+(r||"services.visualstudio.com")}return e}(),c=s[D]||d[D]||"",u=s[C],p=u?u+"/v2/track":d.endpointUrl,(l=[]).push((n="SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details)",a=t,i=p,(o=(r=v(c,"Exception")).data).baseType="ExceptionData",o.baseData.exceptions=[{typeName:"SDKLoadFailed",message:n.replace(/\./g,"-"),hasFullStack:!1,stack:n+"\nSnippet failed to load ["+a+"] -- Telemetry is disabled\nHelp Link: https://go.microsoft.com/fwlink/?linkid=2128109\nHost: "+(S&&S.pathname||"_unknown_")+"\nEndpoint: "+i,parsedStack:[]}],r)),l.push(function(e,t,n,a){var i=v(c,"Message"),r=i.data;r.baseType="MessageData";var o=r.baseData;return o.message='AI (Internal): 99 message:"'+("SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details) ("+n+")").replace(/\"/g,"")+'"',o.properties={endpoint:a},i}(0,0,t,p)),function(e,t){if(JSON){var n=T.fetch;if(n&&!y.useXhr)n(t,{method:N,body:JSON.stringify(e),mode:"cors"});else if(XMLHttpRequest){var a=new XMLHttpRequest;a.open(N,t),a.setRequestHeader("Content-type","application/json"),a.send(JSON.stringify(e))}}}(l,p))}function i(e,t){f||setTimeout(function(){!t&&m.core||a()},500)}var e=function(){var n=l.createElement(k);n.src=h;var e=y[w];return!e&&""!==e||"undefined"==n[w]||(n[w]=e),n.onload=i,n.onerror=a,n.onreadystatechange=function(e,t){"loaded"!==n.readyState&&"complete"!==n.readyState||i(0,t)},n}();y.ld<0?l.getElementsByTagName("head")[0].appendChild(e):setTimeout(function(){l.getElementsByTagName(k)[0].parentNode.appendChild(e)},y.ld||0)}try{m.cookie=l.cookie}catch(p){}function t(e){for(;e.length;)!function(t){m[t]=function(){var e=arguments;g||m.queue.push(function(){m[t].apply(m,e)})}}(e.pop())}var n="track",r="TrackPage",o="TrackEvent";t([n+"Event",n+"PageView",n+"Exception",n+"Trace",n+"DependencyData",n+"Metric",n+"PageViewPerformance","start"+r,"stop"+r,"start"+o,"stop"+o,"addTelemetryInitializer","setAuthenticatedUserContext","clearAuthenticatedUserContext","flush"]),m.SeverityLevel={Verbose:0,Information:1,Warning:2,Error:3,Critical:4};var s=(d.extensionConfig||{}).ApplicationInsightsAnalytics||{};if(!0!==d[I]&&!0!==s[I]){var c="onerror";t(["_"+c]);var u=T;T=function(e,t,n,a,i){var r=u&&u(e,t,n,a,i);return!0!==r&&m["_"+c]({message:e,url:t,lineNumber:n,columnNumber:a,error:i}),r},d.autoExceptionInstrumented=!0}return m}(y.cfg);function a(){y.onInit&&y.onInit(n)}(T[t]=n).queue&&0===n.queue.length?(n.queue.push(a),n.trackPageView({})):a()}(window,document,{ src: "https://js.monitor.azure.com/scripts/b/ai.2.min.js", // The SDK URL Source // name: "appInsights", // Global SDK Instance name defaults to "appInsights" when not supplied // ld: 0, // Defines the load delay (in ms) before attempting to load the sdk. -1 = block page load and add to head. (default) = 0ms load after timeout, // useXhr: 1, // Use XHR instead of fetch to report failures (if available), crossOrigin: "anonymous", // When supplied this will add the provided value as the cross origin attribute on the script tag // onInit: null, // Once the application insights instance has loaded and initialized this callback function will be called with 1 argument -- the sdk instance (DO NOT ADD anything to the sdk.queue -- As they won't get called) cfg: { // Application Insights Configuration instrumentationKey: "XXXXXXXXXXXX" /* ...Other Configuration Options... */ }}); </script> </head> <body> <h1>hoge</h1> </body> </html>
そして上記のWebページにアクセスしてから、Application InsightsのログのbrowserTimingsというテーブルにアクセスしてみます。
以下のようなログが記録されています。
特に重要と思われるのが、name、url、networkDuration、sendDuration、receiveDuration、processingDuration、totalDurationになります。それぞれの内容は以下のとおりです。
name |
JavaScript SDKを埋め込んだWebページのタイトル |
url |
JavaScript SDKを埋め込んだWebページのURL |
networkDuration |
HTTPリクエストを送信するまでの前処理で、DNSによる名前解決やTCP接続などにかかった時間 |
sendDuration | HTTPリクエストの送信完了までにかかった時間 |
receiveDuration |
HTTPレスポンス受信までにかかった時間 |
processingDuration | WebページのDOM解析やレンダリング処理にかかった時間 |
totalDuration | networkDuration + sendDuration + サーバーサイドの処理時間 + receiveDuration + processingDuration |
上記のなんとかDurationは、以下のような図にしてみると、その関係がわかりやすいかと思います。
これはまだまだ序章ですよ。これからもっとゴイスーな機能を紹介します。
Application Insightsのゴイスーな機能の数々
本章では、Application Insightsの中でも、利用頻度の高いと思われる便利機能をピックアップして解説します。
アプリケーションマップ
アプリケーションマップとは、アプリケーション同士の依存関係を可視化したものです。依存関係ってなんか難しい言い方ですが、要は、アプリケーションが他のシステムにアクセスしていた場合、その関係を可視化するのがアプリケーションマップです。以下はその例です。
上図には丸で囲まれたものが5個ありますが、これを「ノード」と呼びます。緑の丸で囲まれた部分は「コンポーネント」と呼ばれるもので、Application InsightsのSDKが組み込まれているアプリケーションそのものです。マイクロサービスで言えば、個別にデプロイ可能な部分となります。
上図は、以下の内容を可視化したものになります。
- webという名前のアプリケーション(上図一番左端の緑丸)にはApplication InsightsのSDKが組み込まれている。
- webは3つのサービスを呼び出しており、その3つとはそれぞれ「api.github.comというエンドポイントを持つGitHubのRestAPI」、「todoという名称のMySQL」「apiという名前のWeb API」
- apiはコンポーネントであり、Application InsightsのSDKが組み込まれている。
- apiはAzure Storage用のSDKにて、Azure Storageにアクセスしている。
もちろん上図のシステムはApplication Insights用に作ったテスト用アプリケーションで、処理内容に意味はありません(´・ω・`)
ここでちょっとアプリケーションマップの使い方の簡単な一例を上げてみたいと思います。
例えば、上図のGitHubのAPIを呼び出すのに失敗してしまった場合に、アプリケーションマップを使ってどのようにトラブルシューティングをするかを実践してみたいと思います。
まず、GitHubのAPIへのアクセスに失敗してしまって、エラー画面が返ってきてしまったとユーザーからシステム管理者に報告があったとしてます。もちろん、この時点ではユーザー及びシステム管理者はエラーの原因がGitHubのAPIへのアクセスに失敗下ということを知りません。
そこで、システム管理者はアプリケーションマップを見てみます。おや、webからapi.github.comまでの線が何やら赤くなっています。これは、アクセスに失敗したことを表しています。どうやらここに原因がありそうです。更に詳細に原因を解析するために、webのノードからapi.github.comのノードまで伸びている赤い線をクリックしてみましょう。
下図のようなブレードが新たに表示されます。何やら403のエラーがでているようです。さらに詳細を調査するために「エラーの調査」をクリックします。
下図のようなグラフが表示されます。これは後ほど解説しますが、例外が発生した数などをグラフ化したものです。画面右部「全般」の「上位10応答コード」の「403・・・」をクリックしてみます。
「提案」に表示されているサンプルをクリックしてみます。
以下のような画面が表示されます。これは、あるHTTPリクエストがどのようなメソッドを呼び出し、それにどのくらい時間がかかっているかを階層化して表したものです。以下は「localhost:8080/HelloWorld02」というりHTTPリクエストを呼び出したときに、クラス「HelloController02」のメソッド「helloworld02」を呼び出し、さらにそのメソッド内でapi.github.comに対してHTTPリクエスト(メソッドはGET)を発行していますよという図になります。「EXCEPTION」はコード内で例外が発生したことを表すものです。api.github.comへのリクエストの直後に例外が発生しています。これは、この説明のために作成したサンプルコードで、api.github.comでHTTPステータスコードが200以外の場合は例外を発生させるとともに、そのレスポンスを例外のメッセージとして返すように仕込んだものです。このEXCEPTIONをクリックしてみます。
すると例外のメッセージが表示されます。おや、そのメッセージを見てみますと、どうやらapi.github.comに一度に大量のリクエストを送ったことで403が返ってきてしまったようですね。
こんな感じで、アプリケーションマップから効率的にエラーの原因を探ることができるのです( ̄ー ̄)bグッ!
ライブメトリック
リクエストの状況をリアルタイムで出力します。
各グラフの説明は以下のとおりです。
Request Rate | 秒間あたりのリクエスト数を表示します。 |
Request Duration |
HTTPリクエストを処理するのにかかった時間を表示します。これは、クライアントがHTTPリクエストを発行してからHTTPレスポンスを受け取るまでにかかった時間ではなく、サーバー側がHTTPリクエストを受信して、処理を行い、応答を返す準備が整うまでにかかった時間になります。 |
Request Failure Rate |
リクエストが失敗した数を表示します。 |
Dependency Call Rate | 秒間あたりの、他システムへアクセスをした回数を表示します。 |
Dependency Call Duration |
他のシステムへアクセスした際の、応答までに要した時間を表示します。 |
Dependency Call Failure Rate | 他システムへのアクセスに失敗した回数を表示します。 |
Commited Memory | 使用メモリを表示します。 |
CPU Total (%) | CPU使用率を表示します。 |
Exception Rate | システム全体の、秒間の例外発生を表示します。 |
トランザクションの検索
トランザクションの検索ができます。トランザクションとは、サーバー側でHTTPリクエストが受信されてから完了するまでの一連の処理の流れを表します。
トランザクションとは?
トランザクションの検索について説明する前に、まずもうちょっと具体的に「トランザクション」とは何者かということについて説明します。以下のようなシステムがあるとします。
上図のシステムは以下の流れで処理が行われます。
- ユーザーは、Webというアプリケーション(以降、Webと呼称)の/Web01というURLにアクセスします。
- Webは、APIというアプリケーション(以降、APIと呼称)の/apiというURLにアクセスします。
- APIは、Java用のSDKでAzure Blob Storageにアクセスします。
- Webは、MySQLにアクセスします。
ちなみに1の処理は、Webを構成するJavaアプリケーションのクラスWebController01のweb01メソッドで行われいまして、2の処理は、APIを構成するJavaアプリケーションのクラスAPIController01のapiメソッドで行われています。
Application Insightsの世界では、1から4までのそれぞれの処理を「イベント」といい、その一連の関連性のあるイベント(1〜4)をひとまとめにしたものを「トランザクション」といいます。Application InsightsのSDKを仕込んでいないシステムでは、このトランザクションを可視化することは不可能です。それぞれのログは何の関連性もありません。ただし、Application InsightsのSDKを仕込んでいると、先の1〜4までの処理を以下のようにトランザクションとして可視化することが可能となります。下図1〜4までの番号は、先に説明した1〜4までの処理に対応しています。
まず1ですが、demowebapp:8080といイベントを見てみると、/Web01というURLにアクセスしていることがわかります。
WebContorller01.web01は、これはクラス名とメソッドを表していて、WebContorllerというクラスのweb01メソッドをコールしていることを表しています。
次に2の処理です。地球儀のアイコンの横にapi:8080と書いてあるものですが、これはapi:8080というホスト名をGETメソッドで呼び出したことを意味します。
雲のアイコンの横にapi:8080と書いてあるものは、/apiというロケーションのURLを呼び出したことを意味しています。
ApiContoroller.apiは、これはクラス名とメソッドを表していて、APiContorllerというクラスのapiメソッドをコールしていることを表しています。
次に3の処理です。appinsightsdemoは、Azure Blob Storageへのアクセスを表しています。
最後の4の処理ですが、MySQLにアクセスしていることを表しています。
画面右部のバーは、処理にかかった時間を表しており、上図からわかるのはAzure Blob Storageへのアクセスが全体の処理時間の大半を示すということです。
分散トレース
先に説明したように、一連の独立したイベントをトランザクションとしてひとまとめにして、それらの以前関係を可視化するなんていうことが、どのようにして実現できるのでしょうか?それは「分散トレース」という技術に基づくものとなります。
分散トレースはW3C等で標準化がなされていますが、今回はそういった難しい話ではなく、そもそもゆるふわな感じで分散トレースについて簡単に説明致します。
冒頭でご説明したように、昨今のアプリケーションはマイクロサービスの隆盛などにより、かなり複雑化しております。アプリケーションは細かい機能に分断され、その機能同士がメッセージキューやgRPCなどで複雑に連携しあっており、そのおかげで修正の影響を局所化できるなどのメリットがある反面、アプリケーションの動きがトレースしにくいという実態もあります。例えば、アプリケーションAとアプリケーションBとアプリケーションCがあり、アプリケーションA→アプリケーションB→アプリケーションCの順にアクセスして連携を取るものとします。それぞれのアプリケーションはログを出力していれば詳細な解析は可能ですが、アプリケーション同士の関連を追うのが難しくなります。
もうちょっと説明をわかりやすくするために、まず分散トレースのない世界を考えてみたいと思います。下図の例は3つのアプリA、アプリB、アプリCがあり、アプリA→アプリB→アプリCの順に連携して動作するものとします。
アプリCでエラーが発生したとします。このエラーがどのユーザーで発生したかを調べるべくアプリAを調べても、アプリCのログと関連性を示すものがまったくないので、わかりません(´・ω・`)
そして次に分散トレースのある世界のお話をします。ここで全てのアプリにまたがって同じユーザーがした操作にトレースIDという一意の識別子をつけることとします。すると、先程とは違い、ユーザーの操作が追跡できるようになります!!アプリCで発生したエラーには「ohNg8ora」というトレースIDがついているので、そのトレースIDをもとにアプリAのログを調べればユーザーAがログインしているので、ユーザーAが操作したというのがわかりますね!!
分散トレースの便利さがわかったとして、これをApplication Insightsで実現するにはどうすればいいのでしょうか?「トランザクションとは?」で紹介した以下の機能を持つアプリケーションを例に考えてみたいと思います。
上図のアプリケーションは「トランザクションとは?」でも説明したように、以下の流れで処理が行われます。
- ユーザーは、Webというアプリケーション(以降、Webと呼称)の/Web01というURLにアクセスします。
- Webは、APIというアプリケーション(以降、APIと呼称)の/apiというURLにアクセスします。
- APIは、Java用のSDKでAzure Blob Storageにアクセスします。
- Webは、MySQLにアクセスします。
この処理の流れでどのように分散トレースが実現されるかを見てみます。
「Application Insightsのデータの保存」でも説明したように、Application Insightsはのログはそれぞれ用途別に用意されたテーブルに保存されます。実は分散トレースで重要な「トレースID」もこのテーブルに保存されます。では、上図をもとにその流れを見ていきます。
①では、ユーザーはWebというアプリケーションにアクセスして、その結果をApplication Insights SDKがrequestsテーブルにログを登録します。その際、先程説明したトレースIDに相当するOperation IDというものを生成して、requestsテーブルのoperation_idというフィールドに登録します。
②では、WebはAPIという他のシステムにアクセスしているので、その依存関係を記録するために、dependenciesというテーブルにログを登録します。その際に①で生成されたOperation IDをoperation_idフィールドに登録します。
③では、②と同様にMySQLという他のシステムにアクセスしているので、その依存関係を記録するために、dependenciesというテーブルにログを登録します。その際に①で生成されたOperation IDをoperation_idフィールドに登録します。
④では、APIというアプリケーションに組み込まれているApplication Insights SDKが、①で生成されたOperation IDをrequestsテーブルのoperation_idというフィールドに登録します。ここで、疑問なのですが、①〜③までは、Webというアプリケーションのなかの同じプロセスで動作していることなので、Operation IDを各テーブルに登録するのはたやすいことです。でも、APIというアプリケーションは、Webとは別のアプリケーションになります。ではWebからAPIへのOperation IDの受け渡しはどのように行っているのでしょう?実は、ここからがキモなのですが、上図からわかりますように、WebからAPIにアクセスするときRequest-IdというHTTPヘッダにOperation IDを乗っけて渡しています。この動作は実は開発者が特別にコーディングする必要はなく、Application Insightsが知らないところで勝手にやってくれているのです。「Application Insightsのデータの流れ」で説明したように、javaagentのしくみによりApplication Insights SDKがHTTPクライアント関連のクラスを書き換え、HTTPヘッダにOperation IDを埋め込むようにしているからなのです。
超便利ですよね。本来であれば分散トレースを実現するのは超大変なのですが、Application Insights SDKに任せておけば、ぜーんぶ自動的にやってくれるのですヮ(゚д゚)ォ!
⑤では、②と同様にAzure Blob Storageという他のシステムにアクセスしているので、その依存関係を記録するために、dependenciesというテーブルにログを登録します。その際に、①で生成され、Application Insights SDKによってHTTPヘッダーに乗せられて運ばれてきたOperation IDをoperation_idフィールドに登録します。
ここで①〜⑤までの一連の動作でrequestsテーブルやdependenciesテーブルにOperation IDというものを記録しました。つまり、WebやAPIなど別々に動作するアプリケーションで発生したイベントに、Operation IDという共通のIDが付与されたことになります。このIDに基づいて、トランザクションを生成するのです。
いかがでしょうか?ちょっとややこしい話になりましたが、トランザクションを生成するしくみに関する説明でした。
トランザクションを検索してみる
前置きが長くなりましたが、トランザクションを検索してみましょう。
トランザクションは7個のイベント(Availability、Request、Exception、Page View、Trace、Custom Event、Dependency)から構成されています。
上の図のグラフが表すのは、横軸で指定されている時間帯に発生したイベントの種類(Availability、Request、Exception、Page View、Trace、Custom Event、Dependency)を棒グラフにて表しています。イベントの種類の内容はそれぞれ以下のとおりです。
Availability | 可用性テストの回数 |
Request |
サーバーが受信したHTTPリクエストの数 |
Exception | サーバーで発生した例外の数 |
Page View | ブラウザから送信されたHTTPリクエストの数 |
Trace | アプリケーションが出力したログの数 |
Custom Event | コード内で意図的に発生させたイベントの数 |
Dependency | 他のシステムを呼び出した回数 |
「イベントの種類」をクリックすると、発生したイベントでフィルターすることができます。例えば「Exception」をチェックすると、例外のイベントのみに絞ることができます。「結果」に一覧表示されているイベントをクリックしてみます。
すると、そのイベントが発生したトランザクションが表示されます。
可用性
外部からWebアプリケーションのURLに対して接続を行い、その接続の成功と失敗をログに記録することができます。そのテスト方法は以下の2つの方法があります。
URLのpingテスト | 指定したURLに対してHTTPリクエストを発行し、その結果に応じてテストの成功・失敗を判別します。 |
複数ステップのWebテスト | Visual Studioで作成したWebのシナリオテストをもとにテストを実行します。 |
ちなみに「複数ステップのWebテスト」で利用できるシナリオを作成できるVisual Studioは2019までだそうで、それ以降は作ることができなくなるそうです(こちらの記事参照)。
ということで、今回はURLのpingテストに的を絞って説明致します。
では早速作ってみましょう。Application Insightsのリソースにアクセスして、左部メニューの「可用性」をクリックします。次に「+テストの追加」をクリックします。
必要な項目を入力します。たくさんあるので、順を追って説明していきます。
テスト名 | テストを一意に識別する任意の名称を入力します。 |
テストの種類 |
先程説明した2種類のテスト「URLのpingテスト」「複数ステップのWebテスト」から選択します。 |
URL |
テストをする対象のURLを入力します。 |
従属解析の要求 |
これをチェックすると、「URL」で指定したURLに含まれる画像やスクリプトなどもダウンロードします。つまり、それだけレスポンスが返ってくるのに時間がかかることとなり、テストの結果はその分の時間も考慮しなくてはならなくなります。 |
可用性テストが失敗した場合の再試行を有効にします。 |
これをチェックすると、テストが失敗した場合、20 秒後に再び実行されます。失敗は、3 回連続で発生した場合にのみ記録されます。 |
テストの頻度 |
文字通りテストの頻度です。ここで指定した間隔で定期的にテストを実施します。 |
テストの場所 | テストを実施する場所を指定します。ここで指定した地域から、先程指定したURLに対してHTTPリクエストを発行してその応答をチェックします。最低でも5ヶ所からテストすることが推奨されており、複数指定すればリージョン障害なども検知できます。 |
テストのタイムアウト | 「URL」で指定されたURLにテストした結果、応答が返ってこなくてエラーになるまでの秒数です。 |
HTTP応答 |
これにチェックを入れると、その後に指定する「期待される状態コード」に設定したHTTPステータスコードが返って来ないとエラーになります。 |
期待される状態コード |
「HTTP応答」にチェックを入れた場合に、ここで指定したHTTPステータスコードが返って来ないとエラーになります。 |
コンテンツの一致 |
これにチェックを入れると、その後に指定する「コンテンツに含める必要がある対象」に設定した内容がHTTPレスポンスの中に含まれないとエラーになります。 |
コンテンツに含める必要がある対象 | 「コンテンツの一致」にチェックを入れた場合に、ここで指定した内容が、HTTPレスポンスに含まれないとエラーになります。 |
アラートの状態 |
これにチェックを入れると、自動的にアラート(後述)が追加され、可用性テストで一定の回数失敗するとアラートが上がるようになります。 |
必要な項目を入力して「作成」をクリックするとテストが出来上がります。下の方に「WebTest」というのが見えますね。これが作成したテストです。
そして上のグラフを見ますと、横軸に時間、縦軸にパーセンテージがありますでしょう。実行したテストが時間あたりにどれだけ成功したかを表すもので、100%となっているときはもちろんすべて成功しています。100%を下回ると、テスト失敗しているということなので、ちょっとやばみな感じです。
可用性テストの結果ももちろんログに記録されます。先のグラフもこのログに記録されたデータを元にしたものです。「ログ」の「availabilityResults」というテーブルを見てみます。
以下のようなログが記録されています。これは先程定義したテストが1回行われるたびに一つのログが記録されていきます。
availabilityResultsのテーブルに記録されるログで特筆すべき重要な項目を以下に記載します。
name | テストを作成するときに「テスト名」の項目に設定したテストの名前です。 |
location |
テストを作成するときに「テストの場所」で指定したもので、例えばここに「East Asia」と記載されていると、このテストはEast Asiaからテストしたものということになります。 |
success |
テストの結果です。成功だと1、失敗だと0が入ります。 |
ところで、可用性テストはテストを追加しただけでは、アラートは上がりません。エラーが出ても粛々と記録し続けるだけです。なのでこの後説明する「アラート」で別途アラートを設定しないとけないのですが、実はアラート作成時に「アラートの状態」を「有効」に設定すると自動的にアラートが作成されます。ちょっと見てみましょう。
作成したテストの右端にある「・・・」をクリックして、「規則(アラート)ページ」をクリックします。
以下のようにアラートが作成されているのがわかります。アラートをクリックしてみます。
以下のように設定されております。詳細は後ほど説明しますが、以下のアラートは同じ場所からのテストが直近5分以内の、1分間での失敗回数の平均が2以上の場合にアラートが上がります。ただし、アクションが未設定ですので、メールは飛びません。メールを飛ばしたりなどの通知を行うためには、改めてアクションを設定する必要があります。
失敗
HTTPステータスコード400系や500系などのエラー、発生した例外など様々なエラーの記録を見ることができます。
ではそれぞれを詳細に見てみましょう。
操作 | Application Insights SDKを組み込んだアプリケーションで発生したHTTPエラー(HTTPステータスコードが500系や400系など)をグラフに表示します。 |
依存関係 |
Application Insights SDKを組み込んだアプリケーションから他のシステムへアクセスした際に発生したエラーを表示します。例えばBlob Storageへのアクセスに失敗した場合のエラーがグラフに表示されます。 |
例外 |
Application Insights SDKを組み込んだアプリケーションで発生した例外を表示します。Javaでいえば、Exceprionなどです。 |
ロール |
??? |
画面の右端には発生したエラーの一覧が表示されています。
それぞれのエラーの種別の説明は下記のとおりです。
上位3応答コード | Application Insights SDKを組み込んだアプリケーションから返ってきたHTTPステータスコードの上位3つです。 |
上位3例外の種類 |
Application Insights SDKを組み込んだアプリケーションから返ってきた例外の種類の上位3つです。 |
上位3失敗した依存関係 |
Application Insights SDKを組み込んだアプリケーションから他システムにアクセスした際に発生した例外の上位3つです。 |
パフォーマンス
アプリケーションの応答速度を見ることができます。
ではそれぞれを詳細に見てみましょう。
操作 | Application Insights SDKを組み込んだアプリケーションがリクエストを受け取ってからレスポンスを返すまでの応答速度を表示します。 |
依存関係 |
Application Insights SDKを組み込んだアプリケーションから他のシステムへリクエストを送りレスポンスが返ってくるまでの応答速度を表示します。 |
ロール |
??? |
「サーバー|ブラウザ」「操作|依存関係」を選択したときの、それぞれ取得出来るレスポンス応答時間の範囲を以下の図にまとめました。
アラート
文字通りアラートを上げるための設定です。監視を行う場合には、これまでにご説明したログやグラフをちょくちょく見て、何が起こっているかを調べるのも辛いので、何らかのしきい値を設定して、それを超えたらアラートを上げるようにするのが一般的です。例えば、例外の発生した数が5分間で10回以上あったらメールで通知するなどです。その通知を受けて、初めてApplication Insightsのポータルでログやグラフなどを見て解析するっていうのが通常の流れです。
ということでアラートの設定方法をこれよりご説明します。アラートを設定するためには、「アラートルール」を設定する必要がありますが、このアラートルールは「スコープ」「条件」「アクショングループ」「通知」「アクション」の5つの要素から成り立ちます。それぞれの詳細は以下のとおりです。
スコープ | アラートを上げる対象です。Azureのリソースを指定します。例えば仮想マシンのCPU使用率でアラートを上げたい場合は、対象の仮想マシンのリソースを指定します。 |
条件 |
アラートを上げるための条件を指定します。指定した条件によって、選択できる条件が異なります。スコープで仮想マシンのリソースを選択するとCPU使用率がでてきたり、Application Insightsのリソースを指定すると可用性テストの結果であったりです。 |
アクショングループ |
後に説明する通知とアクションをグループ化したものです。 |
通知 |
条件に合致した場合の通知先です。メールアドレス、SMS、Azureアプリへのプッシュ通知、音声から選択ができます。 |
アクション |
条件に合致した場合のアクションです。Azure FunctionsやLogic Appsを呼び出したりすることができますので、例えば条件に合致した場合に特定の仮想マシンを止めるとか、Slackに通知するとかいろんなことができます。 |
それぞれのパーツがわかったところで、アラートを上げるための大まかな流れについてご説明いたします。まず、監視対象であるスコープを決定します。すると、設定することのできる条件が決まるので、条件を設定します。最後に条件に合致した場合の通知とアクションを設定したアクショングループを指定します。アクショングループには、複数の通知先と複数のアクションをグループ化できます。さらに、条件に合致した場合のアクショングループも複数の指定が可能です。
と、説明はここまでにしておきまして、ここからは実際にアラートを上げるための設定をしてみましょう。以下の条件でアラートが上がるようにします。
1分間隔でログをチェックして、直近5分以内に例外が一つでも上がっていたらメールで通知する。
左部のメニューから「アラート」をクリックし、「+新しいアラートルール」をクリックします。
「スコープ」の部分にて、監視対象のApplication Insightsリソースが選択されていることを確認します。ここは任意のリソースを選択できますが、Application Insightsのリソースからアラートを作成しようとすると、デフォルトでそのApplication Insightsのリソースが選択済みになるようになっています。
「条件」を追加します。「条件の追加」をクリックします。
シグナルを選択します。シグナルは監視対象の値です。メモリの空き容量であったりCPU使用率であったりです。ここでは例外の発生数を指標にアラートを上げたいので「Exceptions」を指定します。
ちなみに、各シグナルが何を表しているかについては、ドキュメントがあったりなかったりです。「サービスの監視」が「プラットフォーム」については、以下のURLにドキュメントがあるようですが、それ以外については現在ありません。名前から類推するしかない(´・ω・`)
https://docs.microsoft.com/ja-jp/azure/azure-monitor/app/standard-metrics
下までスクロールして「アラートロジック」を表示します。
以下の要領で設定して下さい。最後に「完了」をクリックします。
しきい値(1つ目) | Staticはあらかじめ定められたしきい値を基準にアラートをあげます。動的は機械学習によりしきい値を自動算出してくれます。ここではStaticを選択します。 |
演算子 |
後に設定する「しきい値」と比較してどういう状態であったときにアラートを上げるかを決めます。例えば今回のケースで言えば、発生した例外が1以上なので「しきい値」は1、演算子は「次の値以上」になります。 |
カウント | 監視対象のデータの集計方法です。「最小」「最大」「平均」「合計」「カウント」があります。例えば今回のケースで言えば、直近5分以内の例外の数を数えて一つ以上あればアラートを上げるので、「カウント」になります。また別のケースで、直近5分以内のCPU使用率の最大値をしきい値にしたい場合は「最大」を選択します。 |
しきい値(2つ目) |
「演算子」のところで大体説明してしまいましたが、今回のアラートを挙げる条件は「1分間隔でログをチェックして、直近5分以内に例外が一つでも上がっていたら」ですが、この「一つ」の部分がしきい値となります。 |
単位 |
「しきい値」 カウント(B) → しきい値 x1000x1000x1000 |
これでアラートルールが出来上がりました。次はアクションやアクショングループを作成します。「アクショングループの追加」をクリックします。
「アクショングループの作成」をクリックします。
アクショングループを一意に識別する「アクショングループ名」とその表示名である「表示名」を入力して、「次へ:通知 >」をクリックします。
「通知の種類」から通知先を選択します。「Azure Resource Managerのロールへのメール」はAzureのリソースに権限を与えるためのロールである「所有者」や「共同者」などに所属しているユーザー全員のメールアドレスにメールで通知が送られます。「電子メール/SMSメッセージ/プッシュ/音声」はその名のとおりです。今回はこちらを選択します。
いろんな通知手段がありますが、今回は電子メールを選択します。「電子メール」にチェックをして、通知したい宛先を入力して、「OK」をクリックします。
再び「通知」タブの画面に戻りますので、通知を一意に識別する「名前」を入力します。複数の通知を作りたい場合には、この手順を繰り返します。「次へ: アクション>」をクリックします。
次にアクションを選択します。Azure FunctionsやLogic Appsを呼ぶことができますが、ここでは指定しません。「確認および作成」をクリックします。
内容に問題ないことを確認して、最後に「作成」をクリックします。
次にアラートルールの詳細を指定します。
各項目の入力内容の詳細は以下のとおりです。
アラートルール名 | アラートルールを一意に識別する値になります。 |
説明 |
アラートルールの詳細な説明になります。 |
リソースグループにアラートルールを保存します。 | アラートルールを保存するリソースグループを指定します。 |
重要度 | 後ほどご説明するアラートの一覧にて、ここで定義した重要度でアラートがグルーピングされます。 |
作成時にアラートルールを有効にする |
「アラートルールの作成」をクリックした瞬間にこのアラートルールが有効になります。 |
Automatically resolve alerts |
アラートは状態をもっており、「Fired」と「Resolved」です。Firedはまだアラートのもととなる原因が解決していない状態、Resolvedは解決した状態です。このチェックをオンにしておくと、アラートのもととなる原因が解消されると自動的にアラートの状態がReslovedになります。 |
これでアラートルールの作成は完了です。
ではアプリケーションになにか意図的に例外を発生さたときの状態を見てみます。
まず先程指定した宛先あてにメールが届きます。
Azureポータルの左部メニューの「アラート」をクリックして、アラートの一覧を見てみます。
おや、何やら重要度が「情報」の「アラートの合計数」が2になっているものがありますね。この「重要度」は、アラートルールを作成したときに定義した重要度そのもので、重要度ごとにこの画面でグルーピングされます。やばいアラートは重要度を「重大」にしておくと、ヤバさが可視化できていい感じになります。
「新規」「確認済み」「クローズ済み」は、アラートの状態を表すものです。状況に応じて手動で変更します。アラートが上がった直後の状態は「新規」になり、後は状況に応じて「確認済み」「クローズ済み」にします。例えばアラートが上がって、そのアラートの対応に着手したら「確認済み」にして、そのアラートに関する問題が解決したら「クローズ済み」にするのがよいかもです。
ということでエラーが出ている重要度である「情報」をクリックしてみます。モニターの状態が「Fired」になっているのが、新しくアラートが上がったものですね。このアラートの名前の部分をクリックしてみます。
例外が発生した時間とその数のグラフが表示されます。このアラートに関する問題が解決したら、つまり例えばこのアラートが何らかのバグが起因して発生しているとして、そのバグを改修してビルド・リリースして正常な状態に戻ったら、「アラートの状態」を「解決済み」にします。「アラートの状態の変更」をクリックします。
「アラートの状態を選択します」のプルダウンを表示し「クローズ済み」を選択して、「保存」をクリックします。
アラートの一覧を見てみますと、全て「クローズ済み」となりました。こんな感じでアラートの管理をします。
メトリック
こちらでも説明したように「メトリック」は、CPU使用率やネットワークの入出力バイト数などの数値データになります。これと対極をなすのが「ログ」なのですが、これは後ほど説明します。
で、このメトリックでは、メトリックに基づきグラフを生成してくれます。早速やってみましょう。
グラフの生成
ではまず簡単なグラフを作ってみましょう。HTTPのリクエストに失敗したグラフを作ってみます。Application Insightsのリソースのトップ画面左部メニューの「メトリック」をクリックします。
「スコープ」にはApplication Insightsのリソース名、「メトリック名前空間」は「Application Insights標準のメトリック」、「メトリック」は「Failed requests」、「集計」には「カウント」を選択します。これらの項目の意味については後述しますので、取りあえずここではざっくりグラフを作ってみましょう。
するとこんなグラフが出来上がります。横軸は時間、縦軸は失敗したHTTPリクエストの数です。
では、このグラフからHTTPリクエストコードが500の失敗のリクエストのみに絞ってみます。「フィルターの追加」で実現が可能です。ということで、「フィルターの追加」をクリックして、「プロパティ」に「Result Code」、「演算子」に「=」を選択して、「値」に「500」をチェックしてみましょう。以下のように結果がだいぶ絞られます。これはフィルターの追加で指定したようにHTTPステータスコードが500のみに絞られたグラフになります。
では次に、HTTPステータスコード別のHTTPリクエスト失敗数を出してみます。その前に先程追加したフィルターを削除します。以下の「Result code = 500」の横にある「X」ボンタンをクリックします。
「分割を適用する」をクリックして、「値」で「Result Code」を選択します。するとHTTPステータスコードごとのHTTPリクエストのエラー数のグラフが以下のように表示されます。
ログ
こちらでも説明しましたようにApplication Insightsのデータは、「ログ」「メトリック」というデータ保存場所に保存されます。この「ログ」というメニューではログの中身を見ることができます。Application Insightsのリソースにアクセスして、左部メニューの「ログ」をクリックします。クエリを入力して「実行」をクリックするとログを見ることができます。例えばrequestsテーブルのログを見たければ「requests」というクエリを実行します。
以下のクエリはもっと凝ったことをしています。requestsというテーブルから、timestamp、url、resultCodeというフィールドだけを表示し、さらにresultCodeが404のレコードを抽出して、その結果をtimestampの降順に並べ直しています。こんな感じでSQLとLinuxのパイプの合の子みたいな記述で高度な検索ができるのも、Application Insightsのログ分析の特徴です。
この記述はKusto クエリ言語 (KQL)と呼ばれており、もっと極めたい方は以下のURLのリファレンスを参照下さい。
https://docs.microsoft.com/ja-jp/azure/data-explorer/kusto/query/
Workbooks
いわゆるレポートの機能になります。案ずるより産むが易しということで以下の内容をまとめた週次レポートを作ってみます。
- 例外の発生数
- URLごとの応答時間(降順でソート)
Application Insightsのリソースの左部メニューの「Workbooks」をクリックして、パブリックテンプレートから「空にする」をクリックします。レポートはすでに作成済みの様々なテンプレートをもとに作成できますので、それ使うと楽ちんなのですが、今回はあえてまっさらのテンプレートから作ってみます。
まずレポートのタイトルを付けたいので、「+追加」→「テキストの追加」をクリックします。
レポートのタイトルを入力します。マークダウンが使えます。完了したら「編集が完了しました」をクリックします。
以下のようにレポートのタイトルが完成しました。次は、例外の発生数をグラフにします。「+追加」→「メトリックの追加」の順にクリックします。
「メトリックの追加」をクリックします。
「名前空間」に「ログベースのメトリック」を選択します。これにより「メトリック」で選択するメトリックをフィルタします。「メトリック」では「Exceptions」を選択します。「集計」は「最小」「最大」「平均」「合計」「カウント」から選択できますがExceptionsについては「カウント」しか選択できません。これは他の項目が意味を成さないからです。例外については最小も最大も取れませんし。「最小」「最大」「平均」「合計」「カウント」の種別の詳細については「メトリクスの保存」の章をご参考下さい。で、最後に「保存」をクリックします。
以下のようなレポートが出来上がります。
次にURLごとの応答時間を表示します。「追加」→「クエリの追加」の順にクリックします。
「リソースの種類」で「Application Insights」を選択し、「リソース」では今回作成したApplication Insightsのリソース名を選択します。
「Application Insightsログクエリ」というテキストボックスの中に以下のクエリを入力して、「クエリの実行」をクリックします。
// requestsというテーブルを検索対象とする requests // URL(operation_Name)ごとの応答時間(duration)の平均を求める | summarize avg(duration) by operation_Name // 応答時間の平均の降順でソートする | order by avg_duration desc
「列の設定」をクリックします。
「列」で「avg_duration」を選択して、「列レンダラー」にて「棒(下)」を選択します。これで値の下に棒グラフがでるようになります。最後に「保存して閉じる」をクリックします。
以下のようにURLごとの応答時間のグラフが表示されます。応答時間の降順でソートされています。「編集が完了しました」をクリックします。
目的のレポートが完了しました!!
レポートを保存します。フロッピーディスクのマークをクリックして「タイトル」にレポートの名前を入れて「保存」をクリックします。
このレポートを他の人と共有するには、以下の赤枠の部分をクリックします。
レポートを共有したい人に「共有するリンク」に記載のリンクをお知らせします。共有相手は、このApplication Insightsのリソースにアクセス出来る権限を持っている必要があります。
ユーザー
接続したユーザーを表示させることができます。これは、「JavaScript SDKを使ってテレメトリを収集する」にて紹介したJava Scriptを埋め込むことで可能となります。Cookieを生成し、それをAzure Monitorに送信することによってユーザーの一意性を識別しています。なので、例えば同じAさんでも違うPCでアクセスしたりとか、プライベートブラウズにしたりとか、異なるブラウザでアクセスしたりすると、複数ユーザーとしてカウントされてしまいます。
実戦的な監視をやってみる
本章では、実際に発生しそうなユースケースをもとに、実戦的な監視をしてみたいと思います。
アプリケーションで例外が発生した場合
アプリケーションに例外が発生した場合の監視を実戦してみます。「トランザクションとは?」でも例に上げた以下のシステムを例にしてみます。
上図のシステムは以下の流れで処理が行われます。
- ユーザーは、Webというアプリケーション(以降、Webと呼称)の/Web01というURLにアクセスします。
- Webは、APIというアプリケーション(以降、APIと呼称)の/apiというURLにアクセスします。
- APIは、Java用のSDKでAzure Blob Storageにアクセスします。
- Webは、MySQLにアクセスします。
で、上図のシステムで何らかの例外が発生したとして、この例外を解決するために、以下のような運用フローを想定します。
- 例外が発生したことがメールで通知される。
- 通知を受けて例外の解析を行う。
ということで、まずは例外が発生したらメールで通知されるようにしましょう。
左部のメニューから「アラート」をクリックし、「+新しいアラートルール」をクリックします。
「スコープ」の部分にて、監視対象のApplication Insightsリソースが選択されていることを確認します。ここは任意のリソースを選択できますが、Application Insightsのリソースからアラートを作成しようとすると、デフォルトでそのApplication Insightsのリソースが選択済みになるようになっています。
「条件」を追加します。「条件の追加」をクリックします。
シグナルを選択します。例外の発生数を指標にアラートを上げたいので「Exceptions」を指定します。
下までスクロールして「アラートロジック」を表示します。
以下の要領で設定して下さい。最後に「完了」をクリックします。
しきい値(1つ目) | Staticはあらかじめ定められたしきい値を基準にアラートをあげます。動的は機械学習によりしきい値を自動算出してくれます。ここではStaticを選択します。 |
演算子 |
後に設定する「しきい値」と比較してどういう状態であったときにアラートを上げるかを決めます。例えば今回のケースで言えば、発生した例外が1以上なので「しきい値」は1、演算子は「次の値以上」になります。 |
カウント | 監視対象のデータの集計方法です。「最小」「最大」「平均」「合計」「カウント」があります。例えば今回のケースで言えば、直近5分以内の例外の数を数えて一つ以上あればアラートを上げるので、「カウント」になります。 |
しきい値(2つ目) |
今回のアラートを挙げる条件は「1分間隔でログをチェックして、直近5分以内に例外が一つでも上がっていたら」ですが、この「一つ」の部分がしきい値となります。 |
単位 |
「しきい値」 カウント(B) → しきい値 x1000x1000x1000 |
これでアラートルールが出来上がりました。次はアクションやアクショングループを作成します。「アクショングループの追加」をクリックします。
「アクショングループの作成」をクリックします。
アクショングループを一意に識別する「アクショングループ名」とその表示名である「表示名」を入力して、「次へ:通知 >」をクリックします。
「通知の種類」から通知先を選択します。「Azure Resource Managerのロールへのメール」はAzureのリソースに権限を与えるためのロールである「所有者」や「共同者」などに所属しているユーザー全員のメールアドレスにメールで通知が送られます。「電子メール/SMSメッセージ/プッシュ/音声」はその名のとおりです。今回はこちらを選択します。
いろんな通知手段がありますが、今回は電子メールを選択します。「電子メール」にチェックをして、通知したい宛先を入力して、「OK」をクリックします。
再び「通知」タブの画面に戻りますので、通知を一意に識別する「名前」を入力します。複数の通知を作りたい場合には、この手順を繰り返します。「次へ: アクション>」をクリックします。
次にアクションを選択します。Azure FunctionsやLogic Appsを呼ぶことができますが、ここでは指定しません。「確認および作成」をクリックします。
内容に問題ないことを確認して、最後に「作成」をクリックします。
次にアラートルールの詳細を指定します。
各項目の入力内容の詳細は以下のとおりです。
アラートルール名 | アラートルールを一意に識別する値になります。 |
説明 |
アラートルールの詳細な説明になります。 |
リソースグループにアラートルールを保存します。 | アラートルールを保存するリソースグループを指定します。 |
重要度 | 後ほどご説明するアラートの一覧にて、ここで定義した重要度でアラートがグルーピングされます。 |
作成時にアラートルールを有効にする |
「アラートルールの作成」をクリックした瞬間にこのアラートルールが有効になります。 |
Automatically resolve alerts |
アラートは状態をもっており、「Fired」と「Resolved」です。Firedはまだアラートのもととなる原因が解決していない状態、Resolvedは解決した状態です。このチェックをオンにしておくと、アラートのもととなる原因が解消されると自動的にアラートの状態がReslovedになります。 |
これでアラートの設定は完了です。
ではここからは実際にアラートのメールを受け取ってから、例外の解析を行うまでを見てみたいと思います。
例外が発生すると以下のようなメールが届きます。
メールを受けて例外の解析に着手します。まずは、「失敗」の画面を開いて以下のような操作をします。
発生したエラーの一覧が表示されます。何か一つピックアップしてクリックします。
エラーが発生したイベントを含むトランザクションの詳細画面が表示されます。ApiConrollerクラスのapiメソッドで発生したEXCEPTIONをクリックしてみます。
おや、何やらIllegalArugmentExceptionが発生していますね。不正な引数をSDKか何かに渡しているのでしょうか???ってこれから先は、ソースコードの解析になるので割愛しますが、こんな感じで、「失敗」のメニューから分析します。
アプリケーションのレスポンスが遅い場合
アプリケーションのレスポンスが遅いという事象が発生した場合の解析をApplication Insightsでしてみます。以下のようなシステムがあったとします。これは「トランザクションとは?」でも例に上げたシステムです。
上図のシステムは以下の流れで処理が行われます。
- ユーザーは、Webというアプリケーション(以降、Webと呼称)の/Web01というURLにアクセスします。
- Webは、APIというアプリケーション(以降、APIと呼称)の/apiというURLにアクセスします。
- APIは、Java用のSDKでAzure Blob Storageにアクセスします。
- Webは、MySQLにアクセスします。
ちなみに1の処理は、Webを構成するJavaアプリケーションのクラスWebController01のweb01メソッドで行われいまして、2の処理は、APIを構成するJavaアプリケーションのクラスAPIController01のapiメソッドで行われています。
このシステムを利用するお客様から「XX時ごろなんかレスポンス遅かったよ!!」と連絡があったとします。そんな場合は、さっきのグラフの画面にアクセスします。
おや?お客様から報告のあった時間帯の応答時間がポンッと突き抜けてますね。もっと表示時間帯の範囲を狭めてみます。グラフでズームアップしたい時間帯の範囲をドラッグします。
画面下の「サンプル」をクリックします。
すると個別の応答時間のリストが表示されます。5.1秒と結構長いレスポンス時間がありますね。これをクリックします。
すると明らかにapi:8080宛へのリクエストの処理時間が大半を締めていることがわかります。すると何やらAPIに問題がありそうだと言うことがわかります。
こんな感じでパフォーマンス解析をして、どんどん問題を絞っていきます。
まとめ
いかがでしょうか?今までのように、みやみやたらにログと格闘することなく、スマートにアプリケーションの問題を解決できることがおわかりになったかと思います。これで、アプリケーションの障害をさらっと解決して、ハナキンはベルサッサできますね。No Application Insights, No Life!!