永続的リクエストで起動された EC2 スポットインスタンスを停止後に開始しようとすると IncorrectSpotRequestState エラーが出た時の対処方法

2022.12.30

困っていた内容

永続的リクエストで起動された EC2 スポットインスタンスを AWS CLI の stop-instances コマンドで停止後、start-instances コマンドで開始しようとすると、下記のエラーが発生し開始することができません。

An error occurred (IncorrectSpotRequestState) when calling the StartInstances operation: You can't start the Spot Instance 'i-xxxxxxxxxxxxxxxxx' because the associated Spot Instance request is not in an appropriate state to support start.

インスタンスが停止していることは、describe-instances コマンドで確認しています。 このエラーを解消するにはどうすればいいでしょうか。

どう対応すればいいの?

このエラーは、EC2 スポットインスタンスを起動したスポットリクエストの状態が "disabled" ではないために発生しています。 スポットリクエストの状態が "disabled" となったことを確認してから、EC2 スポットインスタンスの開始をお試しください。

やってみた

まず、下記のブログの手順で、マネジメントコンソールから EC2 スポットインスタンスを永続的リクエストで起動します。

停止・開始のできるスポットインスタンス(EC2)の作り方

なお、永続的リクエストで起動された EC2 スポットインスタンスは、停止・開始ができるのですが、以下の制約や条件があります。

スポットインスタンスを停止する

制約事項

  • スポットインスタンスを停止できるのは、そのインスタンスが、persistent なスポットインスタンスリクエストから起動された場合だけです。
  • 関連するスポットインスタンスリクエストがキャンセルされている場合は、スポットインスタンス を停止することはできません。スポットインスタンスリクエストがキャンセルされた場合は、スポットインスタンスを終了することのみ可能です。
  • フリート、起動グループ、またはアベイラビリティーゾーングループの一部であるスポットインスタンスは停止できません。

スポットインスタンスを開始する

前提条件

スポットインスタンスは、次の場合にのみ開始できます。

  • スポットインスタンスを手動で停止している。
  • スポットインスタンスが EBS-backed インスタンスである。
  • スポットインスタンスに使用可能な容量がある。
  • スポット料金が上限価格より低くなっている。

また、スポットインスタンスリクエストは、下記の状態をとります。

スポットインスタンスリクエストの状態

スポットインスタンスリクエストは、次に示すいずれかの状態を取ります。

  • open – リクエストは受理されるまで待機状態です。
  • active – リクエストは受理されており、関連付けられたスポットインスタンスが存在します。
  • failed – リクエストの 1 つ以上のパラメータが正しくありません。
  • closed – スポットインスタンスは中断または終了されました。
  • disabled – スポットインスタンスがユーザーにより停止されました。
  • cancelled – このリクエストはユーザーによりキャンセルされたか、リクエストの有効期限が切れました。

そうこうするうちにインスタンスが起動しました。元気そうです。

スポットリクエストの状態は「active」になっています。

さて、ここから前記エラーが発生するときのインスタンスの状態やスポットリクエストの状態を確認していきますが、そのために下記のようなスクリプトを作ってみました。

#!/bin/bash

# インスタンスの状態とスポットリクエストの状態を出力する関数
function output-state() {
    # インスタンスの状態出力
    echo "インスタンスの状態"
    aws ec2 describe-instances --instance-ids $1 \
        --query 'Reservations[0].Instances[0].{InstanceId: InstanceId, State: State.Name}' \
        --output table

    # スポットリクエストの状態出力
    echo "スポットリクエストの状態"
    aws ec2 describe-spot-instance-requests --spot-instance-request-ids $2 \
        --query 'SpotInstanceRequests[0].{InstanceId: InstanceId, SpotInstanceRequestId: SpotInstanceRequestId, State: State}' \
        --output table
}

# 引数として渡されたインスタンス ID とスポットリクエスト ID を変数に格納
instance_id=$1
spot_request_id=$2

# 日時出力
date

output-state ${instance_id} ${spot_request_id}

# インスタンスを停止する
echo "インスタンス停止"
aws ec2 stop-instances --instance-ids ${instance_id} \
    --query 'StoppingInstances[0].{InstanceId: InstanceId, CurrentState: CurrentState.Name, PreviousState: PreviousState.Name}' \
    --output table

# 停止するまで待つ
echo "停止中..."
aws ec2 wait instance-stopped --instance-ids ${instance_id}
echo -e "停止完了!\n"

while :
do
# 日時出力
date

output-state ${instance_id} ${spot_request_id}

# インスタンスを開始する
echo "インスタンス開始"
aws ec2 start-instances --instance-ids ${instance_id} \
    --query 'StartingInstances[0].{InstanceId: InstanceId, CurrentState: CurrentState.Name, PreviousState: PreviousState.Name}' \
    --output table

# start-instances コマンドの終了コードを変数に格納
result=$?

# 終了コードが 0 (= start-instances がエラーになってない) ならループから抜ける
if [ $result -eq 0 ]; then
       break
fi

# 改行する
echo -e

# 1秒待つ
sleep 1s

done

# インスタンスの状態が running になるまで待つ
echo "開始中..."
aws ec2 wait instance-running --instance-ids ${instance_id}
echo -e "開始完了!\n"

output-state ${instance_id} ${spot_request_id}

では、スクリプトを実行してみます。

まず、インスタンスの状態が "running" で、スポットリクエストの状態が "active" のときにスポットインスタンスを停止します。

instance_stop

インスタンスが停止しました。

このときのインスタンスの状態は "stopped"、スポットリクエストの状態は "active" であり、この状態でインスタンスを開始しようとするとエラーが発生します。

instance_stopped_request_active

インスタンスが停止しているだけでは開始できないようです。

このあとしばらく同じ状況が続きます。

instance_start_error

状況が変わったのは、スポットリクエストの状態が "disabled" になったときです。

instance_start_success

インスタンスの開始が成功し、インスタンスの状態が "running" になりました。

まとめ

このように、永続的リクエストで起動された EC2 スポットインスタンスを開始するには、インスタンスの状態が  "stopped" になっているだけではなく、スポットリクエストの状態が "disabled" になっている必要があります。

参考資料

スポットリクエストステータス