Azure Container Instancesでアウトバウンド時のIPアドレスを固定する
日次で外部サービスにアクセスするプログラムをAzure Container Instances(ACI)を使って動かす際に、静的IPアドレスを固定する必要があり、ひと工夫したのでそれの備忘録。
以下のドキュメントにも記載のある通り、Azure FireWall( +ルートテーブル・パブリックIPアドレス)というサービスを利用し、ACIをデプロイしているサブネットから外へのルーティングをAzure Firewallに向けてあげることで、必ず静的IPアドレスを利用して外部にアクセスすることができます。
ただし、Azure Firewallはリソースを作っておくだけで1時間あたり140円ほどかかり、1ヶ月で見ると約10万程度かかってしまう。 azure.microsoft.com
今回ACIは毎日決まった時間に1回動かせればよく、Azure Firewallが必要な時間は1日1時間にも満たないので割りに合わず・・・。
そこで、Azure Automationを利用し、ACIを実行をさせる前後で、Azure Firewallのデプロイ・削除を行わせることで、お金をかけずに日次バッチ処理をさせることにしました。実行スケジュールもいい感じに設定できます。
Azure Automationでは、PowerShellなどを利用してAzureリソースの作成・実行・削除などの命令を行うことができます。 事前準備として、VNet・サブネット(ACI配置用とAzure Firewall配置用)・パブリックIPアドレス・ACI(サブネットに配置して)をドキュメントを参考に作成してください。
Azure AutomationのRunbookには以下の様なコードを記述します。
# 認証関連の設定
$connection = Get-AutomationConnection -Name AzureRunAsConnection$ErrorActionPreference = 'Stop'
$logonAttempt = 0
while(!($connectionResult) -And ($logonAttempt -le 10))
{
$LogonAttempt++
$connectionResult = Connect-AzAccount `
-ServicePrincipal `
-Tenant $connection.TenantID `
-ApplicationId $connection.ApplicationID `
-CertificateThumbprint $connection.CertificateThumbprint
Start-Sleep -Seconds 30
}
# Azure Firewallの作成
$Azfw = New-AzFirewall -ResourceGroupName <リソースグループ名> -Name testFW -Location "Japan East" -VirtualNetworkName <ACIとAzure Firewallが存在するVnet名> -PublicIpName <パブリックIPアドレス名>
#Azure FirewallのプライベートIPアドレスを取得
$AzfwPrivateIP = $Azfw.IpConfigurations.privateipaddress
# ルートテーブルを作成
$routeTableDG = New-AzRouteTable `
-ResourceGroupName <リソースグループ名> `
-Name Firewall-rt-table `
-location "Japan East" `
-DisableBgpRoutePropagation
#ルートテーブルのルールを作成
Add-AzRouteConfig `
-Name DG-Route `
-RouteTable $routeTableDG `
-AddressPrefix 0.0.0.0/0 `
-NextHopType VirtualAppliance `
-NextHopIpAddress $AzfwPrivateIP `
| Set-AzRouteTable
# ACIを配置しているサブネットの情報を取得
$networkProfile = Get-AzVirtualNetwork -ResourceGroupName <リソースグループ名> -Name <ACIとAzure Firewallが存在するVnet名>
$subnetProfile = Get-AzVirtualNetwork -ResourceGroupName <リソースグループ名> -Name <ACIとAzure Firewallが存在するVnet名> | Get-AzVirtualNetworkSubnetConfig -Name <ACIを配置しているサブネット名>
$delegation = Get-AzDelegation -Subnet $subnetProfile
# ルートテーブルとサブネットの関連付け
Set-AzVirtualNetworkSubnetConfig `
-VirtualNetwork $networkProfile `
-Name <ACIを配置しているサブネット名> `
-AddressPrefix 10.3.1.0/24 `
-Delegation $delegation `
-RouteTable $routeTableDG | Set-AzVirtualNetwork
# Azure FirewallのSNATルールを作成
$AppRule1 = New-AzFirewallApplicationRule -Name Allow -SourceAddress <ACIを配置しているサブネットのアドレス空間> `
-Protocol http, https -TargetFqdn <接続先のFQDN>
$AppRuleCollection = New-AzFirewallApplicationRuleCollection -Name App-Coll01 `
-Priority 200 -ActionType Allow -Rule $AppRule1
$Azfw.ApplicationRuleCollections.Add($AppRuleCollection)
Set-AzFirewall -AzureFirewall $Azfw
# ACI実行
Invoke-AzResourceAction -ResourceGroupName <リソースグループ名> -ResourceName <ACI名> -Action Start -ResourceType Microsoft.ContainerInstance/containerGroups -Force
# ACI停止待ち
while ($true)
{
Start-Sleep -Seconds 30
$resultContainerStatus = Get-AzContainerGroup -ResourceGroupName <リソースグループ名> -Name<ACI名>
if(($resultContainerStatus.State -eq 'Failed') -Or ($resultContainerStatus.State -eq 'Succeeded'))
{
break
}
}
# ACIサブネットとルートテーブルの関連付けを解除
Set-AzVirtualNetworkSubnetConfig `
-VirtualNetwork $networkProfile `
-Name <ACIを配置しているサブネット名> `
-AddressPrefix<ACIを配置しているサブネットのアドレス空間> `
-Delegation $delegation `
-RouteTable $null | Set-AzVirtualNetwork
# ルートテーブル削除
Remove-AzRouteTable -ResourceGroupName <リソースグループ名> -Name Firewall-rt-table -Force
# Azure Firewall削除
Remove-AzFirewall -ResourceGroupName <ACI名> -Name testFW -Force
Azure Automationの他に、Logic Appsという同じ様にAzureリソースの管理・スケジュール実行できるサービスがあり、こちらのほうが使いやすかったりしそうなのですが、 当時はAzure Firewallのデプロイができなさそうだったため、Automationを利用しました。(現時点では不明)