はじめに
突然ですが、仮想マシンの中から「自分がロードバランサの負荷分散対象になっているか」を確認できたら便利ではと思いました。サーバの運用担当と Azure の運用担当が分かれており、Azure の状況を知るためには都度都度 Azure の運用担当に聞かなければならないような環境で役に立ちそうです。このような運用担当が分かれている環境、意外とあると思います。
サーバの運用担当に対して権限を限定したロールを付与したアカウントを払い出せばいいだけの話ではあります。ですが、サーバの運用担当が「サーバがロードバランサの負荷分散対象になっているか」を確認したくなるのはトラブルが発生した時だけですし、いつ起こるかわからないトラブルのためだけにアカウントを持ち続けるのは、やりたいことに対して手間が大きい気がします。
というわけで、「アカウント不要で Azure の API を叩けるマネージド ID を利用して何とかしてみました」というのが本エントリーです。
実装
Azure には Azure リソースに対して RBAC の権限を付与するマネージド ID という仕組みがあります。
この仕組みを利用すると、仮想マシン上からのみアクセスできるインスタンスメタデータサービス経由で Azure の API を呼び出すためのトークンを取得できます。サーバの運用担当は仮想マシンにログインできますので、この仕組みをもちろん利用できます。PowerShell でトークンを取得するサンプルは以下の通りです。
$resource = 'https://management.azure.com'
$response = Invoke-RestMethod -Method GET -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=$resource" -Headers @{ Metadata="true" }
$accessToken = $response.access_token
このトークンを利用して、Azure の API から自分がロードバランサの負荷分散対象になっているかを確認します。確認の際には Azure ロードバランサの正常性状態を利用します。
Azure Load Balancer の正常性状態を管理する
なお、API 経由で情報を取得するためには適切な RBAC の設定が必要になります。今回は仮想マシンにシステム割り当てマネージド ID を有効化したうえで、ロードバランサに対して閲覧者権限を付与しました。
この 正常性状態の API は非同期操作になります。API を叩くと情報が返ってくるのではなく、API を叩くと操作の結果を確認できる URL がレスポンスヘッダで返ってくる形です。
$ErrorActionPreference = "stop"
$resource = 'https://management.azure.com'
$response = Invoke-RestMethod -Method GET -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=$resource" -Headers @{ Metadata="true" }
$accessToken = $response.access_token
$header = @{
"Authorization"= "Bearer $accessToken"
}
$res = Invoke-WebRequest -Method POST -Uri 'https://management.azure.com/subscriptions/MY-SUB-ID/resourceGroups/test1106/providers/Microsoft.Network/loadBalancers/lb01/loadBalancingRules/01/health?api-version=2024-03-01&preserve-view=true' -Headers $header
$res.Headers.Location
https://management.azure.com/subscriptions/MY-SUB-ID/providers/Microsoft.Network/locations/japaneast/operationResults/d5346bac-b877-42fb-a4e4-c720e979c236?api-version=2024-03-01&t=638981770882904687&c=MIIHpTCCBo2gAwIBAg(中略)
この URL にアクセスすると、処理が完了している場合には200応答でデータが返ってきます。一方で処理中の場合には202応答でデータは空です。というわけで、200番が返ってくるまでアクセスを繰り返して、202応答の場合には指数関数的バックオフで待ち時間を増やしてリトライすることにします。
$attempt = 1
do {
$res = Invoke-WebRequest $res.Headers.Location -Headers $header
$health = $res.content| ConvertFrom-Json
if($res.StatusCode -eq "202"){
Write-Host "backoff $([Math]::Pow(2, $attempt - 1))"
Start-Sleep -Seconds $([Math]::Pow(2, $attempt - 1))
$attempt++
}
} while($res.StatusCode -ne "200")
応答は以下のようになっています。負荷分散対象になっている/なっていないバックエンドの台数と、各バックエンドの状態を確認できます。
{
"up": 1,
"down": 0,
"loadBalancerBackendAddresses": [
{
"ipAddress": "10.1.0.24",
"networkInterfaceIPConfigurationId": "/subscriptions/MY-SUB-ID/resourceGroups/test1106/providers/Microso
ft.Network/networkInterfaces/iis01169/ipConfigurations/ipconfig1",
"state": "Up",
"reason": "Up_Probe_Success"
}
]
}
以下のようにロードバランサに紐づけた仮想マシンの NIC の IP 設定でフィルタをかければ、自分の結果だけを取得できます。
$ipconfigId = "/subscriptions/MY-SUB-ID/resourceGroups/test1106/providers/Microsoft.Network/networkInterfaces/iis01169/ipConfigurations/ipconfig1"
$myHealth = $health.loadBalancerBackendAddresses | Where-Object {$_.networkInterfaceIPConfigurationId -eq $ipconfigId}
Write-Output "$(Get-Date -Format 'yyyy/MM/dd hh:mm:ss') $($myHealth.state)"
動作確認
いい感じのスクリプトができたので、どれくらいのスピードで正常性状態の API が更新されるのかを確認します。今回のロードバランサのヘルスチェックの設定は以下の通りです。5秒に1回ヘルスチェックを試みて一度でも失敗したらダウンとみなします。
{
"name": "hp",
"id": "/subscriptions/MY-SUB-ID/resourceGroups/test1106/providers/Microsoft.Network/loadBalancers/lb01/probes/hp",
"etag": "W/\"2d64ae21-e390-4e27-a0b4-872453c8899e\"",
"properties": {
"provisioningState": "Succeeded",
"protocol": "Tcp",
"port": 80,
"intervalInSeconds": 5,
"numberOfProbes": 1,
"probeThreshold": 1,
"noHealthyBackendsBehavior": "AllProbedDown",
"loadBalancingRules": [
{
"id": "/subscriptions/MY-SUB-ID/resourceGroups/test1106/providers/Microsoft.Network/loadBalancers/lb01/loadBalancingRules/01"
}
]
},
"type": "Microsoft.Network/loadBalancers/probes"
}
上のスクリプトをループで実行し続けながら、サーバ上で起動している IIS を停止・起動してみます。03秒に IIS を停止したところ13 秒にヘルスチェックが Down に、17秒に IIS を起動したところ24秒にヘルスチェックが Up していますので、少しのラグで情報が反映されていることがわかります。
スクリプトの出力
まとめ
仮想マシンの中から、自分がロードバランサの負荷分散対象になっているかを確認してみました。使いどころは限定的ですが、「アカウントがなくても Azure の API を操作できる」ということは色々な場面で応用できそうです。
ただし、マネージド ID に強い権限を付与してしまうと、サーバが侵害されたときに Azure を操作され放題になってしまいます。付与する権限の強さと、その権限を付与する対象は計画的に。
Note
- 当サイトは個人のブログです。このブログに示されている見解や意見は個人的なものであり、所属組織の見解や意見を表明するものではありません。
- 公開情報を踏まえて正確な情報を掲載するよう努めますが、その内容の完全性や正確性、有用性、安全性、最新性について一切保証しません。
- 添付文章やリンク先などを含む本サイトの内容は作成時点でのものであり、予告なく変更される場合があります。