Azure Load Balancer の正常性関連メトリックを使いこなす

公開:2020/10/03
更新:2020/10/03
15 min読了の目安(約13900字TECH技術記事

はじめに

Azure Load Balancer (ALB) はシンプルな L4 ロードバランサーであり、「正常性プローブ」と呼ばれる ALB からの死活監視パケットへの応答状況に応じて、負荷分散の振り分けを実施します。

  • 具体的には、新規のコネクション要求 (e.g. TCP SYN パケット) を ALB のフロントエンドで受信した際、それに紐づく (i.e. 負荷分散規則で関連付けられた) バックエンド サーバーの "正常" なサーバーをリストアップします。そして、パケット ヘッダーのハッシュ値に基づき、正常なサーバーの中から負荷分散先を決定します。
  • 言い換えると、任意の時刻において、ALB はバックエンド サーバーが "正常" または "異常" であると認識しており、この ALB としての "正常性の認識" が負荷分散に大きく影響を与えるということです。

この記事では、メトリックと呼ばれる Azure の機能を使って、Azure Load Balancer が認識しているサーバー正常性等を確認する方法を紹介したいと思います。

メトリックとは何か?

まず最初に、メトリックについてざっくりおさらいしておきましょう。

  • メトリックとは Time Grain を代表するある実数値 (あるいはその値を取得するための Azure Monitor サービスの機能) のこと。
    • ここで、Time Grain とは時間の間隔 (interval) を表すパラメータです。TimeGrain には、1 分、5 分、1 時間、1 日など、キリの良い時間が選べるようになってます。
    • Time Grain とメトリックの取得期間は異なる点に注意しましょう。例えば、Time Grain が 10 分で、メトリック取得期間が現在から過去 1 時間であれば、メトリックは 6 個取れる計算。
  • Time Grain の中で内部的に複数回ログをサンプリングしていることもある (し、実際そういうメトリックが多い)。
    • その場合、複数の内部ログを何らかの形 (e.g. Average) で集約して代表値を取得する。結局、取得される値は Time Grain につき一つだけという点に変わりはない。
    • 集約方法は、{平均、最小、最大、合計、カウント} 等が用意されているが、闇雲に設定するのではなくメトリックの推奨集約方法を使うのが良い。
    • 内部ログにはディメンション (dimension) と呼ばれる属性が存在していることがあり、ディメンションを使って集約対象を絞りこんだり、ディメンション値ごとに集約値を取得できたりする。前者の操作をフィルター、後者を分割と呼びまうs。
  • 様々なインターフェイスを通してメトリックが取得できます。

ちなみに公式ドキュメントはここにあります。

Azure Load Balancer メトリック

メトリック機能を使えるのは Standard SKU の Azure Load Balancer だけです。Basic SKU では取得できません。とはいっても SLA が保証されているのも Standard SKU だけですし、Control Plane 操作も Basic SKU より段違いに早いので、Standard SKU はおすすめです。

メトリック、アラート、リソース正常性を使用した診断 - Azure Standard Load Balancer | Microsoft Docs

そして、Azure Load Balancer の正常性に関係するメトリックは 2 つです。

  • Health Probe Status: バックエンド サーバーの正常性を表すメトリック。正常性プローブにバックエンド サーバーが期待通り応答しているかいなかが反映されていて、Azure Load Balancer 自身が収集しているログです。
  • Data Path Availability: Azure Load Balancer を経由するデータパスが生きているかを表すメトリック。これは、Azure Load Balancer ではない第三者が収集しているログで、Azure データセンター內部のどこかしらから定期的にエンドポイントを監視している内容を反映してます (ドキュメントの図がわかりやすい)。

なんとなくで使うぶんには適当に使えちゃうのですが、ディメンションの指定を忘れたり、集約方法ってなんじゃい?ってなりがちなので、今回記事にまとめてみました。以下では、まずはじめに主な監視シナリオと使い方だけざっくり説明した後に、メトリック定義を詳しく確認していくことにします。

監視シナリオ別のつかいかた

バックエンド サーバー毎の正常性を確認したい

  • 対象 ALB の [メトリック] 画面を開く。
  • メトリックで Health Probe Status を、集約で 平均 を選択する。
  • 以下のディメンションでフィルターを追加する (対象のエンドポイントを絞り込む)。
    • FrontendIPAddress
    • FrontendPort
    • Protocol
  • BackendIPAddress ディメンションで分割指定する。

集約値 x 結果
x = 100 Time Grain の期間中、サーバーはずっと正常だった
0 < x < 100 Time Grain の期間中、サーバーが正常だった時間の割合が x % だった
x = 0 TimeGrain の期間中、サーバーは常に異常だった

※ Azure Portal のメトリック エクスプローラーは、メトリック収集期間の長さによって Time Grain を自動的に調節します。TIme Grain はグラフ上のポイントの両隣が何分離れているかで判断できるほか、Azure Portal が裏で叩いてる REST API の interval Query Parameter をブラウザの開発者ツール等で見ても確かめられます。

バックエンド プールの正常なサーバーの台数を知りたい

正常系の台数は算出できないですが、正常系の割合なら取得できます。

  • 対象 ALB の [メトリック] 画面を開く。
  • メトリックで Health Probe Status を、集約で 平均 を選択する。
  • 以下のディメンションでフィルターを追加する (対象のエンドポイントを絞り込む)。
    • FrontendIPAddress
    • FrontendPort
    • Protocol

集約値 x 結果
x = 100 TimeGrain の期間中、すべてのサーバーが正常
0 < x <100 TimeGrain の期間中、正常だったサーバーの割合が x %
x = 0 TimeGrain の期間中、すべてのサーバーが異常

ALB 自体の正常性を監視したい

Data Path Availability だけだとバックエンド サーバーが全台異常になった場合と ALB 自体に問題が発生した場合が見分けられません。なので、 Helath Probe Status の両方を見る必要があります。

  • 対象 ALB の [メトリック] 画面を開く。
  • メトリックで Data Path Availability を、集約で 平均 を選択する。
  • 以下のディメンションでフィルターを追加する (対象のエンドポイントを絞り込む)。
    • FrontendIPAddress
    • FrontendPort

集約値 x 結果
x = 100 データパスに問題はなく、エンドポイントに TCP SYN パケットを投げると 1 台以上の正常なサーバーに振り分けが実施される状態だった。
x < 100 データパスが利用不可な状態が存在した。つまり ALB 自体に異常があったか、バックエンド プールに正常なサーバーが 1 台も存在しなかった状態があった。さらに Health Probe Status でバックエンド プールの正常性を確認して (もちろんエンドポイントのフィルター付きで)、もし正常なサーバーが 1 台以上あったら ALB 自体に異常があったと判断できる。

確認方法はわかったからアラートを構成したい

  • これまでのシナリオ例のように、Azure Portal の GUI で興味のあるメトリックを見れる状態をつくる。
  • [新しいアラート ルール] を押して "条件" を構成する。
  • その他のアラート関係の設定方法は 公式ドキュメント を見ながらすすめる。

条件設定は、例えば、Health Probe Status なら次のような感じ。

ポイントは 3 つある。

  • 適宜ディメンションによる条件指定をおこなうこと。ただし、分割と書いているが、実際はフィルターの設定なので注意。この例だとエンドポイント (10.0.0.100 の 80/tcp) で絞ることで、この背後のバックエンド プールのすべてのサーバー正常性を集約対象としている (さらに、集約方法は平均を選んでるので『バックエンド プールの正常なサーバーの台数を知りたい』の状態)。
  • 集約値を正しく解釈して、適切な 演算子集約方法しきい値 を選ぶこと。この例だと集約値はバックエンド プールの正常系の割合 (%) となるので、それが 80 % を下回った場合にアラートを発火させるようにしている。
  • 評価基準の 集約粒度 は「過去何分の内部ログを対象とするか」、評価の頻度 は「何分ごとにアラートを評価するか」の設定であることに注意する。この例だと 5 分ごとにアラート評価のタイミングが訪れて、その度に過去 5 分ぶんのログを見ている。

JSON でメトリックを取得したい

Azure PowerShell / Azure CLI / REST API の出番です。ですが、Powershell の Get-AzMetricDefinition は条件指定が弱い (e.g. 複数の集約方法での値を同時にとってこれない) 等の不便さがあり、REST API はクエリ パラメータを書いたり Bearer Token をとってくるのが面倒なので、Azure CLI が圧倒的におすすめです。

az monitor metrics | Microsoft Docs

export resource="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/ilb-standard-rg/providers/Microsoft.Network/loadBalancers/ilb-standard"

# ベース
az monitor metrics list --resource "$resource" --metric DipAvailability

# 一番週度の細かいメトリック取得
az monitor metrics list --resource "$resource" --metric DipAvailability \
	--dimension FrontendIPAddress FrontendPort ProtocolType BackendIPAddress BackendPort \
	--aggregation Average Minimum Maximum Total Count \
	--interval PT1M

# Time Grain の指定 (e.g. 15 分)
az monitor metrics list --resource "$resource" --metric DipAvailability \
	--interval PT15M

# 対象期間の指定
az monitor metrics list --resource "$resource" --metric DipAvailability \
	--start-time 2020-10-02T14:00:00Z \
	--end-time 2020-10-02T14:15:00Z
	
# ディメンション フィルター (e.g. フロントエンド 10.0.0.100 の 80/tcp ポートのバックエンド正常性)
az monitor metrics list --resource "$resource" --metric DipAvailability \
	--filter "FrontendIPAddress eq '10.0.0.100' and FrontendPort eq '80' and ProtocolType eq 'Tcp'"

# ディメンション分割 (e.g. エンドポイント毎の正常性)
az monitor metrics list --resource "$resource" --metric DipAvailability \
	--dimension FrontendIPAddress FrontendPort ProtocolType

# 集約の指定 (e.g. 平均の集約値と內部ログの数を同時に取得)
az monitor metrics list --resource "$resource" --metric DipAvailability \
	--aggregation Average Count

メトリックの定義

メトリックの定義 (メタデータ) の確認には Get-AzMetricDefinitionaz monitor metrics list-definitions コマンドが活用できます。ここでも、JSON で出力が返ってくる Azure CLI のほうが圧倒的におすすめ。

$resource = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/ilb-standard-rg/providers/Microsoft.Network/loadBalancers/ilb-standard"

Select-AzSubscription -Subscription $resource.Split('/')[2]
Get-AzMetricDefinition -ResourceId $resource
export resource="/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/ilb-standard-rg/providers/Microsoft.Network/loadBalancers/ilb-standard"

az login
az account set --subscription $(echo "$resource" | cut -d'/' -f3)
az monitor metrics list-definitions --resource "$resource"

特定のメトリックのみにターゲットを絞るには、jq を使って絞り込めば見やすい。以下は、Health Probe Status (条件指定に canonical なメトリック名 DipAvailability を使ってます) の例。

az monitor metrics list-definitions --resource "$resource" | jq '.[] | select (.name.value == "DipAvailability")'

このまま Health Probe Status を例にとって、メトリック定義の見方を以下で説明します。

Name / Description

  "name": {
    "localizedValue": "Health Probe Status",
    "value": "DipAvailability"
  },
  "displayDescription": "Average Load Balancer health probe status per time duration",

name はメトリック名に関するメタデータです。

  • localizedValue が Portal 上やドキュメント上の表記で、人間用の名前です。スペースとかバンバン入ってますね。
  • value がシステム内部で扱われている正式名称です。az monitor metrics list コマンド等で指定するときは value のほうを使います。

また displayDescription にはメトリックが表すログの意味定義が入っているので、ここからもメトリックの意味を知れます。

Retention / TimeGrain

  "metricAvailabilities": [
    {
      "retention": "93 days, 0:00:00",
      "timeGrain": "0:01:00"
    },
    ...
    {
      "retention": "93 days, 0:00:00",
      "timeGrain": "1 day, 0:00:00"
    }
  ],

metircAvailabilities からはリテンション期間 (過去にさかのぼってメトリックを取得できる最大期間) と、Time Grain (取得できるログの粒度) が確認できます。ALB の Health Probe Status の場合は、リテンションは 93 日間で、1 分間隔から 1 日間隔で状態を取得できるようですね。

なお、Azure Monitor メトリックは 基本的に 93 日のリテンション期間が設けられてます

Aggregation Type

  "primaryAggregationType": "Average"
  "supportedAggregationTypes": [
    "None",
    "Average",
    "Minimum",
    "Maximum",
    "Total",
    "Count"
  ],

指定可能な集約方法 (aggregation type) についての情報です。集約方法は意外と多くの人が躓くパラメータなので、なんでこんなパラメータがあるのか少し補足します。端的に言えば、「Time Grain で指定した期間内で記録された内部ログを、一つの代表値にまとめるため」に存在します。

例えば、Health Probe Status だと最小の Time Grain は 1 分ですが、正常性プローブの間隔が 12 秒に設定されてる場合だと、1 分間に 5 回くらいは内部的にログが取れてそうですね。そうした 5 回分の内部ログをどう集約するかというのが "集約方法" です。

Health Probe Status の内部ログは、プローブ失敗時に 0 を取り、プローブ成功時に 100 を取ります。なので先の例だと、各集約方法で次のような情報が得られます。

  • Average: Time Grain での内部ログの平均値 (0 から 100 までの値を取る実数値)
  • Mnimum: Time Grain での內部ログの最小値 (0 または 100)
  • Maximum: Time Grain での內部ログの最大値 (0 または 100)
  • Total: Time Grain での内部ログの合計値 (0 から 500 までの値をとる整数値)
  • Count: Time Grain での内部ログの数 (つまり 5)

そして皆さんもお気付きの通り、最も意味のあるのは Average で集約したときの代表値 (i.e. 平均値) なんですね。なので、推奨値が Average になっているわけです。推奨集約方法は primaryAggregationType から確認できますが、Azure Monitor メトリックの公式ドキュメント にも書いてあります。

ちなみに、この記事で「内部ログ」と呼んでるものは、公式には「サンプルの測定値」と呼ばれています。

Azure Monitor では、すべてのメトリックを 1 分刻みの間隔で保存します。 ただし場合によっては、メトリックを 1 分間に複数回サンプリングする必要があります。

Dimension

  "dimensions": [
    {
      "localizedValue": "Protocol Type",
      "value": "ProtocolType"
    },
    {
      "localizedValue": "Backend Port",
      "value": "BackendPort"
    },
    {
      "localizedValue": "Frontend IP Address",
      "value": "FrontendIPAddress"
    },
    {
      "localizedValue": "Frontend Port",
      "value": "FrontendPort"
    },
    {
      "localizedValue": "Backend IP Address",
      "value": "BackendIPAddress"
    }
  ],

ディメンション (dimension) は、メトリックの内部ログに付与される属性のことです。例えば mpstat や Windows Resource Monitor で CPU ごとの使用率が計測できるのは、「CPU コア」というディメンションを內部で意識しているためです。そして、
ディメンションが複数存在するメトリックを 多次元メトリック (multi-dimensional metrics) と呼びます。

Health Probe Status は多次元メトリックです。多次元メトリックの場合、ディメンションを意識せずにログを取得すると大局的な傾向しかつかめなくなるため、必ずディメンションに気をつけてください。

ディメンションの使い方は 2 通りで、分割 (split)フィルター (filter) です。分割は SQL でいうところの GROUP BY で、分割に指定したディメンションの値ごとに內部ログが集約されます。フィルターは、その名前の通り、特定の条件を満たす內部ログのみを集約の対象とします。

  • 個人的には、Health Probe Status だと FrontendIPAddressFrontendPOrtProtocol の 3 つをフィルター指定して、BackendIPAddress で分割指定したほうが良い と思います。フィルターに使う 3 つのディメンションは、ALB を介して提供されるサービスのエンドポイントを表すので、監視したいエンドポイント (サービス) を間違う心配がなくなります。また BackendIPAddress で分割すると、バックエンド サーバーごとに正常性状況をつかめるようになります。
  • また、特定のエンドポイントに対しての監視目的ではない場合は、フィルターはかけず、すべての dimension を分割指定する のがおすすめです。なぜなら、こうすることで最も粒度の細かいログが手に入り、後の分析で如何様にでも集約できるからです。

Azure Portal でメトリックを確認する場合もフィルターと分割を適宜追加しましょう。以下の画像のように、エンドポイントでフィルターした後 BackendIPAddress で分割すると、いい感じに各バックエンド サーバーの応答状況が確認できます。

Unit

  "unit": "Count"

単位 (unit) は、メトリック (の內部ログ) の単位を表します。ドキュメント を見るといろんな単位があることがわかります。

  • カウント (Count)
  • バイト (Bytes)
  • ミリ秒 (Milliseconds)
  • ...

さて、Health Probe Status の単位は「カウント」なのですが、誤解を生みがちな単位なので注意が必要です。というのも、Azure Monitor メトリックでの「カウント」は、単に内部ログが整数値であることを言っているだけで、日本語でいうところの個数 / カウントとはニュアンスが異なるからです。例えば、

  • DDoS 攻撃中を受けているときに 1 の値を取り、そうでなければ 0 の値を取る IfUnderDDoSAttack というメトリックも単位がカウントです。個人的にはこれは Binary または Boolean という単位とすべきだと思います。
  • Azure Load Balancer で観測された TCP SYN パケット数を計測する SynCount も、単位がカウントです (個人的には、Count は無限にインクリメントできる 0 以上の整数を表すものだと思うので、これが「カウント」としてしっくり来ます)。

単位がカウントになっていたら、内部ログが整数値なんだ~、と思ってください。

なお、単位 (unit) はあくまで內部ログの単位なので、Count の集約方法を選択した場合はもはや意味を成しません (Count は、TimeGrain に含まれる內部ログの数を数える集約方法である点を思い出してください)。