michimani.net

AWS CLI で複数の EC2 インスタンスをまとめて起動・停止する

2020-10-15

前回の JAWS-UG CLI 専門支部で、シェルの for 文と AWS CLI コマンドを組み合わせた操作があったので、同じパターンを使って複数の EC2 インスタンスを起動・停止してみます。

目次

概要

AWS CLI を使って複数の EC2 インスタンスを起動・停止します。 “複数の” というのは、今回は “特定のタグキーと値を持つ EC2 インスタンス” ということにします。

使用する AWS CLI のバージョンは、現時点 (2020/10/15) で最新の 2.0.56 です。

$ aws --version
aws-cli/2.0.56 Python/3.7.4 Darwin/19.6.0 exe/x86_64

やってみる

早速やってみます。手順としては、まず特定のタグキーと値を持つ EC2 インスタンスを複数準備して、その後 まとめて起動と停止を実施します。

EC2 インスタンスの作成

EC2 インスタンスの作成には、 ec2 run-instances コマンドを使います。 今回 “特定のタグキーと値” として指定するのは StartStopByCLI というタグキーと TRUE という値とします。

AMI イメージは Amazon Linux 2 を指定するので、最新のイメージ ID を取得します。

$ EC2_IMAGE_ID=$(aws ec2 describe-images \
--owners amazon \
--filters 'Name=name,Values=amzn2-ami-hvm-2.0.*-x86_64-gp2' \
--query 'reverse(sort_by(Images,&CreationDate))[1].ImageId' \
--output text) \
&& echo ${EC2_IMAGE_ID}
ami-0053d11f74e9e7f52

ami-0053d11f74e9e7f52 という AMI イメージ ID が取得できました。この AMI イメージで複数の EC2 インスタンスを作成します。

インスタンスタイプは、時間あたりのコストが低い t3a.nano を指定します。

$ EC2_INSTANCE_TYPE="t3a.nano"

タグで指定する文字列もあらかじめ変数に保持しておきます。

$ RESOURCE_TAG_SPEC="ResourceType=instance,Tags=[{Key=StartStopByCLI,Value=TRUE}]"

これらの条件で、 3 つのインスタンスを作成してみます。

$ aws ec2 run-instances \
--image-id ${EC2_IMAGE_ID} \
--instance-type ${EC2_INSTANCE_TYPE} \
--tag-specifications ${RESOURCE_TAG_SPEC} \
--count 3

ステータスを確認します。

$ aws ec2 describe-instances \
--filters Name=tag-key,Values=StartStopByCLI Name=tag-value,Values=TRUE \
--query 'Reservations[*].Instances[].{InstanceID:InstanceId,Status:State.Name}'
[
    {
        "InstanceID": "i-0a7f9d2965a2177f6",
        "Status": "running"
    },
    {
        "InstanceID": "i-044041fb5a5d40623",
        "Status": "running"
    },
    {
        "InstanceID": "i-0d2ab038f784f9156",
        "Status": "running"
    }
]

これで対象のインスタンスが作成できました。ちなみに、それ以外のインスタンスも含めてステータスを確認してみると、

$ aws ec2 describe-instances \
--query 'Reservations[*].Instances[].{InstanceID:InstanceId,Status:State.Name,LaunchTime:LaunchTime}'
[
    {
        "InstanceID": "i-0539ee02588c12499",
        "Status": "stopped",
        "LaunchTime": "2020-09-10T09:03:47+00:00"
    },
    {
        "InstanceID": "i-0a7f9d2965a2177f6",
        "Status": "running",
        "LaunchTime": "2020-10-15T01:44:26+00:00"
    },
    {
        "InstanceID": "i-044041fb5a5d40623",
        "Status": "running",
        "LaunchTime": "2020-10-15T01:44:26+00:00"
    },
    {
        "InstanceID": "i-0d2ab038f784f9156",
        "Status": "running",
        "LaunchTime": "2020-10-15T01:44:26+00:00"
    }
]

今回作成したインスタンスの他に、一つ停止しているインスタンスが存在しているという状態です。

まとめて起動・停止する

では、先ほど作成したインスタンスを対象にして、 CLI コマンドでまとめて起動・停止してみます。現時点でインスタンスは起動しているので、まずはまとめて停止させてみます。

EC2 インスタンスの停止

EC2 インスタンスを停止するには、 ec2 stop-instances コマンドを使います。その際にインスタンス ID を指定する必要があるので、 ec2 describe-instances コマンドで取得したインスタンス ID を渡します。これを、シェルの for 文を使って一気にやってしまいます。

対象のインスタンス ID のリストは次のコマンドでします。

$ aws ec2 describe-instances \
--filters Name=tag-key,Values=StartStopByCLI Name=tag-value,Values=TRUE \
--query 'Reservations[*].Instances[].InstanceId' \
--output text
[
    "i-0a7f9d2965a2177f6",
    "i-044041fb5a5d40623",
    "i-0d2ab038f784f9156"
]

これを for 文で回して、各インスタンスに対して ec2 stop-instance を実行します。

$ for i in $(
  aws ec2 describe-instances \
  --filters Name=tag-key,Values=StartStopByCLI Name=tag-value,Values=TRUE \
  --query 'Reservations[*].Instances[].InstanceId' \
  --output text
); do
  aws ec2 stop-instances \
  --instance-ids ${i}
done

ステータスを確認してみます。

$ aws ec2 describe-instances \
--filters Name=tag-key,Values=StartStopByCLI Name=tag-value,Values=TRUE \
--query 'Reservations[*].Instances[].{InstanceID:InstanceId,Status:State.Name}'
[
    {
        "InstanceID": "i-0a7f9d2965a2177f6",
        "Status": "stopped"
    },
    {
        "InstanceID": "i-044041fb5a5d40623",
        "Status": "stopped"
    },
    {
        "InstanceID": "i-0d2ab038f784f9156",
        "Status": "stopped"
    }
]

停止できてますね。

EC2 インスタンスの起動

では、今度は対象のインスタンスをまとめて起動してみます。起動するには ec2 start-instances コマンドを使います。

$ for i in $(
  aws ec2 describe-instances \
  --filters Name=tag-key,Values=StartStopByCLI Name=tag-value,Values=TRUE \
  --query 'Reservations[*].Instances[].InstanceId' \
  --output text
); do
  aws ec2 start-instances \
  --instance-ids ${i}
done

今度は全インスタンスのステータスを確認してみます。

$ aws ec2 describe-instances \
--query 'Reservations[*].Instances[].{InstanceID:InstanceId,Status:State.Name,LaunchTime:LaunchTime}'
[
    {
        "InstanceID": "i-0539ee02588c12499",
        "Status": "stopped",
        "LaunchTime": "2020-10-08T09:02:05+00:00"
    },
    {
        "InstanceID": "i-0a7f9d2965a2177f6",
        "Status": "running",
        "LaunchTime": "2020-10-15T02:01:28+00:00"
    },
    {
        "InstanceID": "i-044041fb5a5d40623",
        "Status": "running",
        "LaunchTime": "2020-10-15T02:01:30+00:00"
    },
    {
        "InstanceID": "i-0d2ab038f784f9156",
        "Status": "running",
        "LaunchTime": "2020-10-15T02:01:31+00:00"
    }
]

対象のインスタンスのみが起動しています。

for 文使わなくてもいいのでは

途中で気付きましたが、インスタンスの起動・停止をするコマンドは *-instances という形で複数形になっているんです。そして、インスタンス ID を指定するオプションも --instance-ids と、複数形になっています。つまり、今回のように for 文でインスタンスを一つずつ処理する必要はなかったわけです。

つまり、次のようにすれば起動・停止のコマンドは一回実行するだけでいいんですよね。

$ TARGET_INSTANCES=$(
  aws ec2 describe-instances \
  --filters Name=tag-key,Values=StartStopByCLI Name=tag-value,Values=TRUE \
  --query 'Reservations[*].Instances[].InstanceId'
) \
&& aws ec2 stop-instances \
--instance-ids ${TARGET_INSTANCES}

{
    "StoppingInstances": [
        {
            "CurrentState": {
                "Code": 64,
                "Name": "stopping"
            },
            "InstanceId": "i-044041fb5a5d40623",
            "PreviousState": {
                "Code": 16,
                "Name": "running"
            }
        },
        {
            "CurrentState": {
                "Code": 64,
                "Name": "stopping"
            },
            "InstanceId": "i-0a7f9d2965a2177f6",
            "PreviousState": {
                "Code": 16,
                "Name": "running"
            }
        },
        {
            "CurrentState": {
                "Code": 64,
                "Name": "stopping"
            },
            "InstanceId": "i-0d2ab038f784f9156",
            "PreviousState": {
                "Code": 16,
                "Name": "running"
            }
        }
    ]
}

まとめ

AWS CLI を使って特定のタグキーと値を持つ EC2 インスタンスをまとめて起動・停止してみた話でした。

当初はシェルの for 文で各インスタンスごとに起動・停止を実行する想定でしたが、それぞれのコマンドのドキュメントを見れば複数インスタンスに対して同時に起動・停止を行うことができることがわかりました。一回の CLI コマンド実行で複数のインスタンスを処理できるのであればそのほうがコマンドの実行回数が少なくなるのでシンプルですが、 for を使った再帰処理の復習になったので、良かったことにします…。

最後に使用したインスタンスを削除しておしまいです。

$ TARGET_INSTANCES=$(
  aws ec2 describe-instances \
  --filters Name=tag-key,Values=StartStopByCLI Name=tag-value,Values=TRUE \
  --query 'Reservations[*].Instances[].InstanceId'
) \
&& aws ec2 terminate-instances \
--instance-ids ${TARGET_INSTANCES}

ちなみに、同じようなことを以前に AWS SDK for Python (boto3) で実行した話については下記の記事で書いてます。


comments powered by Disqus