Azureでリージョンをまたいでアクセスを分散させて耐障害性向上あるいは負荷分散などを実現できるサービス「Traffic Manager」。これをAzureポータル(GUI)での手動操作ではなく、スクリプトで自動的にデプロイ(生成)できるようにするARM(Azure Resource Manager)テンプレートについて説明する。
対象:Azure Traffic Manager、ARMテンプレート(Bicep)
Azureでリージョンをまたいでアクセスを分散させて耐障害性向上や負荷分散をしたい場合、DNS ベースのトラフィックロードバランサーの「Traffic Manager」を利用することが候補の一つになるだろう。
このTraffic Managerは比較的シンプルなサービスとはいえ、複数生成しようとすると、AzureポータルのGUIではなかなかに面倒だ。ウィザードでうっかり選択肢を間違えることもあるだろう。
そこで本Tech TIPSでは、ARM(Azure Resource Manager)テンプレートを使ってこのTraffic Managerを一発で生成する方法を紹介する。
Traffic ManagerをデプロイするためのARMテンプレートファイル(traf.azuredeploy.bicep)が長いため、以下では複数のセクションに分けて説明する。
最初のセクションでは、Traffic Managerの名前やルーティング、DNSレコードに関する設定をパラメーターで受け取る。
// ファイル: traf.azuredeploy.bicep(1/6)
////////// Traffic Managerの名前やルーティング、DNSレコードを設定する //////////
param trafName string // Traffic Managerプロファイル名
///// ルーティング方法の選択
@allowed([
'Geographic' // 地域
'MultiValue' // 複数値
'Performance' // パフォーマンス
'Priority' // 優先度
'Subnet' // サブネット
'Weighted' // 重み付け
])
param trafficRoutingMethod string
///// DNSレコードに関する設定
// Traffic ManagerプロファイルのDNS名のサブドメイン部分
param relativeName string = '${trafName}-${uniqueString(utcNow())}'
// デフォルトは、プロファイル名とハイフン、一意な13桁文字列をつなげた名前
@minValue(0)
param dnsTTL int = 60 // DNSレコードのTTL(キャッシュ保持可能時間)。秒単位
// DNS設定まとめ
var dnsConfig = {
relativeName: relativeName
ttl: dnsTTL
}
パラメーター「trafficRoutingMethod」で指定しているルーティングについては、Traffic Managerが複数のエンドポイント(オリジンサーバ)に対して振り分ける方式を6つの選択肢から選ぶ必要がある。耐障害性向上なら「Priority(優先度)」、負荷分散なら「Weighted(重み付け)」などが選択肢として挙がるだろう。ただ、「MultiValue(複数値)」については、Azureのサービスではない外部エンドポイントでしか利用できない、といった制限もあるので注意が必要だ。ルーティングの詳細については、Microsoft Learn「Traffic Manager のルーティング方法」「Traffic Manager についてよく寄せられる質問 (FAQ)」が参考になる。
パラメーター「relativeName」には、生成するTraffic Managerプロファイルに付けられるドメイン名の「<サブドメイン>.trafficmanager.net」のうち、<サブドメイン>名を指定する。上記リストでは、デフォルトでTraffic Managerプロファイル名とユニークな13桁文字列をハイフンでつなげた名称にしている。
パラメーター「dnsTTL」には、Traffic Managerプロファイルを指し示すDNSレコードのTTL(Time To Live:レコードをキャッシュに保持できる時間)を秒単位で指定する。これが長くなるほど、エンドポイントの切り替えにかかる実質的な時間が長くなる(障害発生時のダウンタイムが長くなる)。短くすると、エンドユーザーのデバイス(端末)でDNSへの名前解決要求が増える。未指定だと0秒、つまりキャッシュ禁止になる。
次のセクションでは、エンドポイントが正常か、それともダウンしていて応答できない異常な状態かを判定する「モニタリング(監視)」について設定する。
// ファイル: traf.azuredeploy.bicep(2/6)
////////// エンドポイントのモニタリング(監視)を設定する //////////
// プローブ: 正常かどうかを調べるためにエンドポイントへ送信されるリクエストのこと
param healthCheckPath string // 監視するファイルのパス
@allowed([
'HTTP'
'HTTPS'
'TCP'
])
param monitorProtocol string = 'HTTPS' // 監視に用いるプロトコル
param monitorPort int = 443 // 監視に用いる通信ポート
@allowed([
10
30
])
param intervalInSeconds int = 30 // 監視の間隔。秒単位
@minValue(0)
@maxValue(9)
param toleratedNumberOfFailures int = 5 // 障害と判断する最小発生回数
@minValue(5)
param timeoutInSeconds int = 7 // 障害と判断する最短応答時間。秒単位
// intervalInSecondsより5秒以上、短くする必要あり
// モニタリング(監視)の設定まとめ
var monitorConfig = {
protocol: monitorProtocol
port: monitorPort
path: healthCheckPath
intervalInSeconds: intervalInSeconds
toleratedNumberOfFailures: toleratedNumberOfFailures
timeoutInSeconds: timeoutInSeconds
}
パラメーター「healthCheckPath」では、例えば「/health.html」というように、監視するファイルのパスを指定する。デフォルトでは、このファイルへのリクエストに対してHTTPステータスコード200が返されれば、正常と診断される。また、このファイルにアクセスする際のプロトコルと通信ポート番号は、パラメーター「monitorProtocol」「monitorPort」でそれぞれ指定する必要がある。
監視中に障害が発生したかどうか、その判断を左右するのがパラメーター「intervalInSeconds」「toleratedNumberOfFailures」「timeoutInSeconds」の3つだ。これらの設定値はエンドポイントの性能(監視するファイルの平均応答速度など)に依存するので、調整が必要になるだろう。詳細はMicrosoft Learn「Traffic Manager エンドポイントの監視」が参考になる。
次のセクションでは、エンドポイント(オリジンサーバ)を設定する。ここでは、同じAzureのApp Serviceを東日本リージョンと西日本リージョンにそれぞれ1台ずつ配置済みとし、それらをエンドポイントとして登録している。
// ファイル: traf.azuredeploy.bicep(3/6)
////////// エンドポイントを設定する //////////
// エンドポイントの定義。ここではApp Serviceを前提としている
param endpoints array = [
{
// 1つ目のApp Service
name: '<1つ目のApp Serviceの名前>'
type: 'azureEndpoints' // Azureのサービスの場合
// 外部エンドポイントなら「externalEndpoints」を指定
endpointStatus: 'Enabled'
resourceGroup: '<1つ目のApp Serviceのリソースグループ名>'
FQDN: '<1つ目のApp ServiceのFQDN>' // 名前解決に利用
priority: 2 // 優先順位。小さいほど優先される
weight: 1 // 重み。大きいほどアクセスが増える
alwaysServe: 'Disabled' // Enabledにすると、障害を検知しても切り替えない
}
{
// 2つ目のApp Service
name: '<2つ目のApp Serviceの名前>'
type: 'azureEndpoints'
endpointStatus: 'Enabled'
resourceGroup: '<2つ目のApp Serviceのリソースグループ名>'
FQDN: '<2つ目のApp ServiceのFQDN>'
priority: 4
weight: 1
alwaysServe: 'Disabled'
}
]
// エンドポイントであるApp Serviceの既存リソース
resource webApp 'Microsoft.Web/sites@2022-03-01' existing = [
for endpoint in endpoints: {
name: endpoint.name
scope: resourceGroup(endpoint.resourceGroup)
}
]
パラメーター「endpoints」内のハッシュの「FQDN」キーには、そのエンドポイントに到達できるFQDNを指定する。App Serviceならデフォルトの「<App Service名>.azurewebsites.net」を指定すればよい。
パラメーター「trafficRoutingMethod」でルーティング方式として「Priority」を選択した場合には、各エンドポイントに優先度を数値で指定する必要がある。それが「priority」キーである。この数値が最も小さいエンドポイントが、正常時に100%のアクセスが振り向けられる。例えば、普段は東日本リージョンのエンドポイントにアクセスを振り向けつつ、東日本側が停止した場合は西日本リージョンに振り向けたい場合は、東日本側のpriorityを小さくし、西日本側のpriorityはそれより大きくする必要がある。
「weight」キーは「重み付け」のルーティングをする場合に、各エンドポイントへのアクセス数を配分するのに用いられる。この数値が大きいほどアクセスが増える。デフォルトでは全エンドポイントに「1」が設定される。
「alwaysServe」キーを「Enabled」に設定すると、プローブが無効になり、そのエンドポイントには障害発生時でも必ずルーティングに従ってアクセスが配分されるようになる。
上記リストで「webApp」という既存リソースを定義しているのは、次のセクションでエンドポイントのリソースIDが必要になるからだ(後述)。
次のセクションでTraffic Managerプロファイルを実際に生成する。
// ファイル: traf.azuredeploy.bicep(4/6)
////////// Traffice Managerプロファイルのリソースを生成する //////////
resource trafficManagerProfile 'Microsoft.Network/trafficmanagerprofiles@2022-04-01' = {
name: trafName
location: 'global' // 必ず「global」を指定
properties: {
profileStatus: 'Enabled'
trafficRoutingMethod: trafficRoutingMethod
dnsConfig: dnsConfig
monitorConfig: monitorConfig
endpoints: [
// ループでエンドポイントを1つずつ設定
for (endpoint, i) in endpoints: {
name: 'tme-${endpoint.name}'
type: 'Microsoft.Network/trafficManagerProfiles/${endpoint.type}'
properties: {
endpointStatus: endpoint.endpointStatus
targetResourceId: webApp[i].id
target: endpoint.FQDN
priority: endpoint.priority
weight: endpoint.weight
endpointLocation: webApp[i].location
alwaysServe: endpoint.alwaysServe
}
}
]
trafficViewEnrollmentStatus: 'Disabled'
}
}
注意すべきは「location」に必ず「global」すなわちグローバルリージョンを指定することだ。東日本などその他のリージョンを指定すると、エラーが発生して生成に失敗する。従ってリソースグループの配置先リージョンとは必ず異なるので、デプロイのスクリプトなどでは例外として扱わなければならない場合もあるだろう。
次のセクションでは、Traffic ManagerのログをAzure Log Analyticsで分析したり、ストレージアカウントに自動保存したりするための診断設定を追加する。
// ファイル: traf.azuredeploy.bicep(5/6)
////////// 診断設定をTraffic Managerに加える //////////
param diagLogs array = [
// 診断に加えるログ一覧
'ProbeHealthStatusEvents' // Traffic Manager Probe Health Results Event
]
param diagMetrics array = [] // 診断に加えるメトリック一覧
// ログ用ストレージアカウントの既存リソース
param logStorageRG string
param logStorageName string
resource logStgAccount 'Microsoft.Storage/storageAccounts@2023-01-01' existing = {
name: logStorageName
scope: resourceGroup(logStorageRG)
}
// Log Analytics Workspaceの既存リソース
param logAnalyticsRG string
param logAnalyticsName string
resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
name: logAnalyticsName
scope: resourceGroup(logAnalyticsRG)
}
// 診断設定の拡張リソース生成
resource diagSetting 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
name: 'diag-${trafName}'
scope: trafficManagerProfile // 診断対象のTraffic Managerプロファイルのリソース
properties: {
storageAccountId: logStgAccount.id
workspaceId: logAnalytics.id
logs: [
for log in diagLogs: {
category: log
enabled: true
}
]
metrics: [
for metric in diagMetrics: {
category: metric
enabled: true
}
]
}
}
Log Analyticsワークスペースとストレージアカウントは、あらかじめ別途生成しておき、それらの名前とリソースグループをパラメーターで指定する必要がある。
執筆時点でTraffic Managerのログは「ProbeHealthStatusEvents」(Azureポータルでは「Traffic Manager Probe Health Results Event」)という、プローブに異常が生じたときや正常に戻ったときに生じるイベントを記録するログだけだ。詳細はMicrosoft Learn「Microsoft.Network/trafficManagerProfiles でサポートされているログ」が参考になる。
最後のセクションでは、メトリックアラート(アラートルール)をTraffic Managerに追加する。
// ファイル: traf.azuredeploy.bicep(6/6)
////////// メメトリックアラートをTraffic Managerに加える //////////
param endpointStatus int = 1 // Endpoint Status by Endpoint(1を切ったら異常)
var metricAlertParams = [
{
name: 'Endpoint Status Falls Down at ${trafName}'
description: '${trafName} で「Endpoint Status」の最小値が ${endpointStatus} を下回りました'
severity: 2
evaluationFrequency: 'PT1M'
windowSize: 'PT5M'
metricTitle: 'metric_min_endpoint_status_lt_${endpointStatus}_in_1m'
threshold: endpointStatus
metricName: 'ProbeAgentCurrentEndpointStateByProfileResourceId'
operator: 'LessThan'
timeAggregation: 'Minimum'
}
]
// モジュール: メトリックアラートのリソース生成
param monitorActionGroupRG string
param monitorActionGroupName string
module MetricAlertsModule './metricalert.azuredeploy.bicep' = [
for (metricAlert, i) in metricAlertParams: {
name: '${deployment().name}-metricAlert-${i+1}'
params: {
parent: {
id: trafficManagerProfile.id
name: trafficManagerProfile.name
type: trafficManagerProfile.type
}
ma: metricAlert
monitorActionGroupRG: monitorActionGroupRG
monitorActionGroupName: monitorActionGroupName
}
}
]
上記リストでは、モジュールでパラメーターを指定しつつ、この後に記している「metricalert.azuredeploy.bicep」という別のBicepファイルを呼び出して、実際にアラートルールを生成させている。この生成の詳細については、Tech TIPS「【Azure】大量のメトリックアラートを一発で作る方法(リソーステンプレート編)」を参照していただきたい。
また通知のためのMonitorアクショングループは、あらかじめ別途生成しておき、その名前とリソースグループをパラメーターで指定する必要がある。Monitorアクショングループの生成についても、前出のTIPSを参照していただきたい。
// ファイル: metricalert.azuredeploy.bicep
////////// メトリックアラートを実際に生成する //////////
param parent object // 親リソース(メトリック提供)の情報
param ma object // メトリックアラートの主要なパラメーターを収めたオブジェクト
///// Monitorのアクショングループの既存リソース
param monitorActionGroupRG string
param monitorActionGroupName string
resource monitorActionGroup 'Microsoft.Insights/actionGroups@2023-01-01' existing = {
name: monitorActionGroupName
scope: resourceGroup(monitorActionGroupRG)
}
///// アラートルールのリソース生成
resource metricAlert 'Microsoft.insights/metricalerts@2018-03-01' = {
name: ma.name
location: 'global' // 必ず「global」にする
properties: {
description: ma.description
severity: ma.severity
enabled: true
scopes: [parent.id]
evaluationFrequency: ma.evaluationFrequency
windowSize: ma.windowSize
criteria: {
allOf: [
{
threshold: ma.threshold
name: ma.metricTitle
metricNamespace: parent.Type
metricName: ma.metricName
operator: ma.operator
timeAggregation: ma.timeAggregation
criterionType: 'StaticThresholdCriterion'
}
]
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
}
autoMitigate: true
targetResourceType: parent.Type
actions: [
{
actionGroupId: monitorActionGroup.id
}
]
}
}
上記リストのファイル「metricalert.azuredeploy.bicep」は、前述の「traf.azuredeploy.bicep」と同じフォルダに配置する必要がある。
■関連リンク
Copyright© Digital Advantage Corp. All Rights Reserved.