1. HOME
  2. テックブログ
  3. コマンドラインでEC2を管理してみる

コマンドラインでEC2を管理してみる

2023/06/28 テクノロジー

AWSで複数のEC2のインスタンスを管理しています。しかも、起動させっぱなしではなく、使う時に起動して使い終わったら停止させます。
停止はEC2インスタンスの利用者がやっておけばいいんですが、まれに忘れてしまうこともあり、思わぬコストが発生したりすることがあります。
また、EC2インスタンス起動の「AWSの管理コンソールにログインしてEC2のページを開いて対象のEC2インスタンスを選択してインスタンスを開始する」という操作がちょっと面倒になってきました。

何か楽にやる方法はないかと考えていたら、「そういえばAWS CLIでEC2インスタンスの操作ができるな。ただ、いちいちあのコマンドを打つのもアレだから、シェルスクリプトあたりでちょっと組んでみるか。」という結論に至り、実際に組んでみることにしました。

要件

要件としては

  • 現在のステータスが一覧表示できる
  • Nameタグを指定してインスタンスの起動・停止ができる
  • 全インスタンス一斉に起動・停止ができる

というところでしょうか。

まずはawsコマンドの動作確認をします。

一覧表示

一覧表示は「aws ec2 describe-instances」です。「–output」で出力フォーマットの指定、「–query」で出力のフィルタリングができます。
ちょっと試してみます。

% aws ec2 describe-instances \
% --output=json \
% --query 'Reservations[].Instances[].{InstanceId:InstanceId,State:State.Name,Name:Tags[?Key==`Name`].Value|[0]}'
[
    {
        "InstanceId": "i-10101010101010101",
        "State": "stopped",
        "Name": "Bergkamp"
    },
    {
        "InstanceId": "i-14141414141414141",
        "State": "stopped",
        "Name": "Henry"
    },
    {
        "InstanceId": "i-88888888888888888",
        "State": "stopped",
        "Name": "Ljungberg"
    },
    {
        "InstanceId": "i-77777777777777777",
        "State": "stopped",
        "Name": "Pires"
    }
]

実際に運用する時はjson形式よりテーブルにした方が見やすそうですね。

% aws ec2 describe-instances \
% --output=table \
% --query 'Reservations[].Instances[].{InstanceId:InstanceId,State:State.Name,Name:Tags[?Key==`Name`].Value|[0]}'
-------------------------------------------------
|               DescribeInstances               |
+----------------------+-------------+----------+
|      InstanceId      |    Name     |  State   |
+----------------------+-------------+----------+
|  i-10101010101010101 |  Bergkamp   |  stopped |
|  i-14141414141414141 |  Henry      |  stopped |
|  i-88888888888888888 |  Ljungberg  |  stopped |
|  i-77777777777777777 |  Pires      |  stopped |
+----------------------+-------------+----------+

インスタンスの起動

インスタンスの起動は「aws ec2 start-instances」です。インスタンスの指定はインスタンスIDでしかできなさそうです。

% aws ec2 start-instances --instance-ids i-14141414141414141
{
    "StartingInstances": [
        {
            "CurrentState": {
                "Code": 0,
                "Name": "pending"
            },
            "InstanceId": "i-14141414141414141",
            "PreviousState": {
                "Code": 80,
                "Name": "stopped"
            }
        }
    ]
}
%
% aws ec2 describe-instances \
% --output=table \
% --query 'Reservations[].Instances[].{InstanceId:InstanceId,State:State.Name,Name:Tags[?Key==`Name`].Value|[0]}'
-------------------------------------------------
|               DescribeInstances               |
+----------------------+-------------+----------+
|      InstanceId      |    Name     |  State   |
+----------------------+-------------+----------+
|  i-10101010101010101 |  Bergkamp   |  stopped |
|  i-14141414141414141 |  Henry      |  running |
|  i-88888888888888888 |  Ljungberg  |  stopped |
|  i-77777777777777777 |  Pires      |  stopped |
+----------------------+-------------+----------+

インスタンスの停止

インスタンスの停止は「aws ec2 stop-instances」です。こちらもインスタンスの指定はインスタンスIDです。

% aws ec2 stop-instances --instance-ids i-14141414141414141
{
    "StoppingInstances": [
        {
            "CurrentState": {
                "Code": 64,
                "Name": "stopping"
            },
            "InstanceId": "i-14141414141414141",
            "PreviousState": {
                "Code": 16,
                "Name": "running"
            }
        }
    ]
}
%
% aws ec2 describe-instances \
% --output=table \
% --query 'Reservations[].Instances[].{InstanceId:InstanceId,State:State.Name,Name:Tags[?Key==`Name`].Value|[0]}'
-------------------------------------------------
|               DescribeInstances               |
+----------------------+-------------+----------+
|      InstanceId      |    Name     |  State   |
+----------------------+-------------+----------+
|  i-10101010101010101 |  Bergkamp   |  stopped |
|  i-14141414141414141 |  Henry      |  stopped |
|  i-88888888888888888 |  Ljungberg  |  stopped |
|  i-77777777777777777 |  Pires      |  stopped |
+----------------------+-------------+----------+

この辺りを組み合わせていけば作れそうですね。
ただ、インスタンスIDは覚えてられないので、スクリプトではNameタグで指定できるようにしたいです。
一覧を取得するときのフォーマットをテキスト形式(タブ区切りになります)にしてNameタグでgrepしてawkで抜き出してみます。

% aws ec2 describe-instances \
% --output=text \
% --query 'Reservations[].Instances[].{InstanceId:InstanceId,State:State.Name,Name:Tags[?Key==`Name`].Value|[0]}' \
% | grep $'\t'Ljungberg$'\t' | awk '{print $1}'
i-88888888888888888

スクリプトでNameタグを受け取る→descibe-instancesでインスタンスIDを調べる→そのインスタンスIDでインスタンスを起動・停止するという流れはいけそうです。

作成したスクリプトの動作確認

で、組んでみました。

% ./ec2.sh
Usage: ./ec2.sh [-l] [-s state (-n name|-a)]
 
Options
-l          一覧表示
-s state    'start' or 'stop'
-n name     Nameタグを指定
-a          全てのEC2

一覧表示してみます。
複数のインスタンスタイプを使っていたりスポットインスタンスにしていたりするので、その辺も出力するようにしてあります。

% ./ec2.sh -l
------------------------------------------------------------------------------
|                              DescribeInstances                             |
+----------------------+---------------+------------+------------+-----------+
|      InstanceId      | InstanceType  | LifeCycle  |   Name     |   State   |
+----------------------+---------------+------------+------------+-----------+
|  i-10101010101010101 |  t3.small     |  spot      |  Bergkamp  |  stopped  |
|  i-14141414141414141 |  t3.small     |  spot      |  Henry     |  stopped  |
|  i-88888888888888888 |  t3.small     |  None      |  Ljungberg |  stopped  |
|  i-77777777777777777 |  t2.micro     |  spot      |  Pires     |  stopped  |
+----------------------+---------------+------------+------------+-----------+

Henryを起動します。

% ./ec2.sh -n Henry -s start
------------------------------------------------------------------------------
|                              DescribeInstances                             |
+----------------------+---------------+------------+------------+-----------+
|      InstanceId      | InstanceType  | LifeCycle  |   Name     |   State   |
+----------------------+---------------+------------+------------+-----------+
|  i-10101010101010101 |  t3.small     |  spot      |  Bergkamp  |  stopped  |
|  i-14141414141414141 |  t3.small     |  spot      |  Henry     |  pending  |
|  i-88888888888888888 |  t3.small     |  None      |  Ljungberg |  stopped  |
|  i-77777777777777777 |  t2.micro     |  spot      |  Pires     |  stopped  |
+----------------------+---------------+------------+------------+-----------+
% ./ec2.sh -l              
------------------------------------------------------------------------------
|                              DescribeInstances                             |
+----------------------+---------------+------------+------------+-----------+
|      InstanceId      | InstanceType  | LifeCycle  |   Name     |   State   |
+----------------------+---------------+------------+------------+-----------+
|  i-10101010101010101 |  t3.small     |  spot      |  Bergkamp  |  stopped  |
|  i-14141414141414141 |  t3.small     |  spot      |  Henry     |  running  |
|  i-88888888888888888 |  t3.small     |  None      |  Ljungberg |  stopped  |
|  i-77777777777777777 |  t2.micro     |  spot      |  Pires     |  stopped  |
+----------------------+---------------+------------+------------+-----------+

この状態で、全て起動させてみます。
既に起動しているインスタンスはメッセージを表示して処理をスルーするようにしてあります。

% ./ec2.sh -a -s start
Henry は既に起動しています。
------------------------------------------------------------------------------
|                              DescribeInstances                             |
+----------------------+---------------+------------+------------+-----------+
|      InstanceId      | InstanceType  | LifeCycle  |   Name     |   State   |
+----------------------+---------------+------------+------------+-----------+
|  i-10101010101010101 |  t3.small     |  spot      |  Bergkamp  |  running  |
|  i-14141414141414141 |  t3.small     |  spot      |  Henry     |  running  |
|  i-88888888888888888 |  t3.small     |  None      |  Ljungberg |  running  |
|  i-77777777777777777 |  t2.micro     |  spot      |  Pires     |  pending  |
+----------------------+---------------+------------+------------+-----------+
% ./ec2.sh -l        
------------------------------------------------------------------------------
|                              DescribeInstances                             |
+----------------------+---------------+------------+------------+-----------+
|      InstanceId      | InstanceType  | LifeCycle  |   Name     |   State   |
+----------------------+---------------+------------+------------+-----------+
|  i-10101010101010101 |  t3.small     |  spot      |  Bergkamp  |  running  |
|  i-14141414141414141 |  t3.small     |  spot      |  Henry     |  running  |
|  i-88888888888888888 |  t3.small     |  None      |  Ljungberg |  running  |
|  i-77777777777777777 |  t2.micro     |  spot      |  Pires     |  running  |
+----------------------+---------------+------------+------------+-----------+

では、まとめて停止します。

% ./ec2.sh -a -s stop
-------------------------------------------------------------------------------
|                              DescribeInstances                              |
+----------------------+---------------+------------+------------+------------+
|      InstanceId      | InstanceType  | LifeCycle  |   Name     |   State    |
+----------------------+---------------+------------+------------+------------+
|  i-10101010101010101 |  t3.small     |  spot      |  Bergkamp  |  stopping  |
|  i-14141414141414141 |  t3.small     |  spot      |  Henry     |  stopping  |
|  i-88888888888888888 |  t3.small     |  None      |  Ljungberg |  stopping  |
|  i-77777777777777777 |  t2.micro     |  spot      |  Pires     |  stopping  |
+----------------------+---------------+------------+------------+------------+
% ./ec2.sh -l       
------------------------------------------------------------------------------
|                              DescribeInstances                             |
+----------------------+---------------+------------+------------+-----------+
|      InstanceId      | InstanceType  | LifeCycle  |   Name     |   State   |
+----------------------+---------------+------------+------------+-----------+
|  i-10101010101010101 |  t3.small     |  spot      |  Bergkamp  |  stopped  |
|  i-14141414141414141 |  t3.small     |  spot      |  Henry     |  stopped  |
|  i-88888888888888888 |  t3.small     |  None      |  Ljungberg |  stopped  |
|  i-77777777777777777 |  t2.micro     |  spot      |  Pires     |  stopped  |
+----------------------+---------------+------------+------------+-----------+

良さそうですね。
要件に上げていたものは一通りクリアできてます。
これで面倒な作業から少しだけ開放されそうです (^^)v

今回作成したスクリプトは要望があれば何らかの形で公開できればと思います。

P.S.
EC2のNameタグとインスタンスIDを見てピンときた方は仲間です。

ファブリカコミュニケーションズで働いてみませんか?

あったらいいな、をカタチに。人々を幸せにする革新的なサービスを、私たちと一緒に創っていくメンバーを募集しています。

ファブリカコミュニケーションズの社員は「全員がクリエイター」。アイデアの発信に社歴や部署の垣根はありません。

“自分から発信できる人に、どんどんチャンスが与えられる“そんな環境で活躍してみませんか?ご興味のある方は、以下の採用ページをご覧ください。

◎ 新卒採用の方はこちら
◎ キャリア採用の方はこちら

この記事を書いた人

きむら
プロダクト開発本部 インフラチーム
きむら

おすすめの記事