Azure スポット仮想マシン(AWS用語では「スポットインスタンス」と呼称)は余剰リソースを利用した仮想マシンです。余剰リソースのため、SLAを保証された仮想マシンよりも割安な傾向がありますが、その代わりAzure全体のリソース不足の場合は停止してしまうデメリットがあります。再実行が許されるバッチ処理や検証用途などに使用すると上手に費用削減できるでしょう。
このページではAzure スポット仮想マシンの使い方をまとめます。
最も安価なBシリーズの仮想マシンはAzure Spot Virtual Machinesを使用できません。
前提
公式ドキュメント
参考になる公式ドキュメントを以下に示します。
- Azure Spot Virtual Machines を使用する
- Azure CLI を使用して Azure Spot Virtual Machines をデプロイする
- Azure 小売価格の概要
- クエリ パラメーターを使用して応答をカスタマイズする
事前設定
以下、リソースグループを作成します。
az group create --name MyResourceGroup --location japaneast
スポットインスタンスの作成
価格指定なしのスポットインスタンス
仮想マシン作成時に引数priorityににSpotを指定すると、Azure Spot Virtual Machinesを作成する事ができます。操作例は以下の通りです。
az vm create \ --resource-group MyResourceGroup \ --name linux010 \ --image UbuntuLTS \ --size Standard_D1_v2 \ --admin-username azureuser \ --ssh-key-values ~/.ssh/authorized_keys \ --priority Spot
価格指定なしのスポットインスタンス
引数max-priceを指定すると、指定した価格を下回る場合のみ仮想マシンを起動するようになります。なお、max-priceは「ドル/時間」の単位で指定してください。
az vm create \ --resource-group MyResourceGroup \ --name linux020 \ --image UbuntuLTS \ --size Standard_D1_v2 \ --admin-username azureuser \ --ssh-key-values ~/.ssh/authorized_keys \ --priority Spot \ --max-price 0.5
立ち退き(eviction)時の挙動
Azureのリソース逼迫によってAzure Spot Virtual Machinesが停止した時に、ディスク等のリソース含めて削除するのか、それとも割り当て解除のみでVirtual Machineの再起動が可能な状態にするかは選択できます。
引数eviction-policyに「Deallocate(割り当て解除)」か「Delete(削除)」を指定する事で、立ち退き(eviction)時の挙動を明示的に定義できます。設定例は以下の通りです。
az vm create \ --resource-group MyResourceGroup \ --name linux030 \ --image UbuntuLTS \ --size Standard_D1_v2 \ --admin-username azureuser \ --ssh-key-values ~/.ssh/authorized_keys \ --priority Spot \ --eviction-policy Delete
スポットインスタンスの作成
削除予定の通知
Azure Spot Virtual Machinesは突然削除される事はなく、削除がスケジュールされた後に削除処理が実行されます。言い換えれば、スケジュールを事前に検知できれば、削除前にアプリケーションが正常終了するための処理を実行できます。
削除のスケジュールはIMDS(Azure Instance Metadata Service) APIを介して情報収集できます。各仮想マシンにログインし、169.254.169.254宛にHTTP requestを送信すると、その仮想マシンに関する情報を入手する事ができます。例えば、スケジュールに関する情報を取得するならば、以下のように操作します。
スケジュール以外の情報収集方法は「Azure Instance Metadata Service (Linux) – エンドポイント カテゴリ」等の資料を参照ください。
curl \ -H Metadata:true \ http://169.254.169.254/metadata/scheduledevents?api-version=2019-08-01
操作ログは以下の通りです。初回実行のみ2分程度の時間を要します。
jqコマンドがインストールされていない場合は「python -m json.tool」で代用する事もできます。
[azureuser@linux010 ~]$ curl \ > -H Metadata:true \ > http://169.254.169.254/metadata/scheduledevents?api-version=2019-08-01 \ > | python -m json.tool % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 37 0 37 0 0 0 0 --:--:-- 0:00:55 --:--:-- 12 { "DocumentIncarnation": 0, "Events": [] }
削除の擬似テスト
以下のようなazコマンドでAzure Spot Virtual Machinesの削除を擬似できます。
az vm simulate-eviction \ --resource-group MyResourceGroup \ --name linux010
削除がスケジュールされると、IMDS(Azure Instance Metadata Service) APIで得たスケジュールに、NotBeforeの時刻が記述されています。以下の場合、1分程度しか猶予はありませんが、NotBeforeで指定された時刻より前に仮想マシンが削除される事はないので、それまでの時間を使ってアプリケーションが正常終了するための処理を遂行できます。
[azureuser@linux010 ~]$ date 2022年 3月 7日 月曜日 13:58:37 UTC [azureuser@linux010 ~]$ [azureuser@linux010 ~]$ [azureuser@linux010 ~]$ curl \ > -H Metadata:true \ > http://169.254.169.254/metadata/scheduledevents?api-version=2019-08-01 \ > | python -m json.tool % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 278 100 278 0 0 19868 0 --:--:-- --:--:-- --:--:-- 21384 { "DocumentIncarnation": 1, "Events": [ { "Description": "", "EventId": "9EC3D2A6-E1E3-4472-8B57-F5C0502179DD", "EventSource": "Platform", "EventStatus": "Scheduled", "EventType": "Preempt", "NotBefore": "Mon, 07 Mar 2022 13:59:48 GMT", "ResourceType": "VirtualMachine", "Resources": [ "linux010" ] } ] } [azureuser@linux010 ~]$
価格調査
APIを使った価格調査
Azure Spot Virtual Machinesの価格はAPIを使った調査が可能です。先頭100件に絞り込まれてしまいますが、特段のクエリなしに全量を表示するならば、以下のHTTP requestを送付します。
https://prices.azure.com/api/retail/prices
最近のブラウザはJSONも綺麗に表示してくれますので、上記URLをブラウザに入力しただけでも、以下のように表示されます。
もし、絞り込みをしたいならば、クエリ文字列を使って絞り込みが可能です。例えば、Virtual Machineのみを表示したいならば、以下URLをブラウザに入力します。
APIの仕様は「Azure 小売価格の概要」「クエリ パラメーターを使用して応答をカスタマイズする」等のドキュメントを参照ください。
https://prices.azure.com/api/retail/prices?$filter=serviceName eq 'Virtual Machines'
コードで記述する場合は、URLエンコーディングやエスケープに注意しつつ実装します。bashでcurlコマンドを使用する例ならば以下のようになります。
QUERY_STR="\$filter=serviceName eq 'Virtual Machines'" QUERY_STR=${QUERY_STR}" and armRegionName eq 'japanwest'" QUERY_STR=${QUERY_STR}" and contains(skuName,'Spot')" QUERY_STR=${QUERY_STR}" and armSkuName eq 'Standard_D1_v2'" curl 'https://prices.azure.com/api/retail/prices' \ --get \ --data-urlencode "${QUERY_STR}" | jq
実行例は以下の通りです。
admin@mac19 ~ % curl 'https://prices.azure.com/api/retail/prices' \ --get \ --data-urlencode "${QUERY_STR}" | jq % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1860 100 1860 0 0 1099 0 0:00:01 0:00:01 --:--:-- 1107 { "BillingCurrency": "USD", "CustomerEntityId": "Default", "CustomerEntityType": "Retail", "Items": [ { "currencyCode": "USD", "tierMinimumUnits": 0, "retailPrice": 0.01223, "unitPrice": 0.01223, "armRegionName": "japanwest", "location": "JA West", "effectiveStartDate": "2022-03-01T00:00:00Z", "meterId": "45d942dd-a2b4-53c2-834a-9e75e0e62726", "meterName": "D1 v2/DS1 v2 Spot", "productId": "DZH318Z0BQ34", "skuId": "DZH318Z0BQ34/02GT", "productName": "Virtual Machines Dv2 Series", "skuName": "D1 v2 Spot", "serviceName": "Virtual Machines", "serviceId": "DZH313Z7MMC8", "serviceFamily": "Compute", "unitOfMeasure": "1 Hour", "type": "Consumption", "isPrimaryMeterRegion": true, "armSkuName": "Standard_D1_v2" }, { "currencyCode": "USD", "tierMinimumUnits": 0, "retailPrice": 0.01223, "unitPrice": 0.01223, "armRegionName": "japanwest", "location": "JA West", "effectiveStartDate": "2022-03-01T00:00:00Z", "meterId": "c298287a-838a-5daf-99ba-c918f597bb92", "meterName": "D1 v2/DS1 v2 Spot", "productId": "DZH318Z0BPVM", "skuId": "DZH318Z0BPVM/01HL", "productName": "Virtual Machines Dv2 Series Windows", "skuName": "D1 v2 Spot", "serviceName": "Virtual Machines", "serviceId": "DZH313Z7MMC8", "serviceFamily": "Compute", "unitOfMeasure": "1 Hour", "type": "DevTestConsumption", "isPrimaryMeterRegion": true, "armSkuName": "Standard_D1_v2" }, { "currencyCode": "USD", "tierMinimumUnits": 0, "retailPrice": 0.02641, "unitPrice": 0.02641, "armRegionName": "japanwest", "location": "JA West", "effectiveStartDate": "2022-03-01T00:00:00Z", "meterId": "c298287a-838a-5daf-99ba-c918f597bb92", "meterName": "D1 v2/DS1 v2 Spot", "productId": "DZH318Z0BPVM", "skuId": "DZH318Z0BPVM/01HL", "productName": "Virtual Machines Dv2 Series Windows", "skuName": "D1 v2 Spot", "serviceName": "Virtual Machines", "serviceId": "DZH313Z7MMC8", "serviceFamily": "Compute", "unitOfMeasure": "1 Hour", "type": "Consumption", "isPrimaryMeterRegion": true, "armSkuName": "Standard_D1_v2" } ], "NextPageLink": null, "Count": 3 }
エラーメッセージによる調査
やや乱暴な方法ですが、エラーメッセージから調査する方法もあります。以下のようにmax-priceに極端に小さな値を指定すると、エラーメッセージで最小価格を教えてもらえます。実行例は以下の通りです。
$ az vm create \ --resource-group MyResourceGroup \ --name linux050 \ --image UbuntuLTS \ --admin-username azureuser \ --ssh-key-values ~/.ssh/authorized_keys \ --priority Spot \ --max-price 0.01 Argument '--max-price' is in preview and under development. Reference and support levels: https://aka.ms/CLI_refstatus It is recommended to use parameter "--public-ip-sku Standard" to create new VM with Standard public IP. Please note that the default public IP used for VM creation will be changed from Basic to Standard in the future. {"status":"Failed","error":{"code":"DeploymentFailed","message":"At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.","details":[{"code":"Conflict","message":"{\r\n \"error\": {\r\n \"code\": \"OperationNotAllowed\",\r\n \"message\": \"Unable to perform operation 'Create VM' since the provided max price '0.01 USD' is lower than the current spot price '0.01885 USD' for Azure Spot VM size 'Standard_DS1_v2'. For more information, see http://aka.ms/AzureSpot/errormessages.\"\r\n }\r\n}"}]}}