【インスタンスの止め忘れを防ごう】MacのAutomatorとlaunchdで実行中のEC2をリマインドする

Automator

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、虎塚です。

起動したEC2インスタンスをうっかり停止し忘れて、思ったよりも高額の請求がきてしまった……なんてこと、ありませんか?

予期しない出費を避けるために、最近は実行中のEC2インスタンスを自分に対してリマインドするようにしています。今日はその方法をご紹介します。

実現すること

毎日10時と18時に、起動中のEC2インスタンスのインスタンスIDを画面に通知します。動作イメージは次のような感じです。

リマインダーの実行イメージ

動作確認した環境

Mac OS Xが前提です。次の環境で動作確認しました。

  • Mac OS X 10.9.5 (13F1066)
  • Automator 2.4 (381)
  • aws-cli 1.7.14

使用するツールの紹介

手元のツールを組み合わせて、簡単に作ることができます。

Automator
Mac上での作業の自動化を支援するOS標準ツールです。ドラッグアンドドロップで様々なアクションをつなぎあわせて、ワークフローやアプリケーションを作成できます。
launchd
ジョブを定期実行するためのOS標準ツールです。最近のMacでは、cronの代わりにlaunchdを使うことが推奨されています。.plist(プロパティリスト)という設定ファイルにプログラムの実行方法を定義し、launchctlコマンドで読み込んで使います。
.plistファイルは、XMLで記述します。といっても、GUIから設定できるツールもありますので安心してください。でも、今回はテキストエディタで編集します。
スクリプト言語の実行環境
Automatorの「シェルスクリプトを実行」アクションで実行できるスクリプトの実行環境が必要です。シェルスクリプト(bash、csh、ksh、sh、tcsh、zsh)のほかに、Perl、Python、Rubyが利用できます。
今回はzshシェルを使います。

リマインダー作成手順

おおまかな流れは次のとおりです。順に見ていきましょう。

  1. Automatorでアプリケーションを作成する
    • AWS CLIを使って実行中のインスタンスの情報を取得する
    • 実行中のインスタンス情報を画面通知する
  2. 作成したアプリケーションをlaunchdで定期実行する

1. Automatorでアプリケーションを作成する

まず、Automatorを起動します。Launchpadで「Auto」を検索して実行するか、アプリケーションフォルダ直下にあるAutomator.appを実行します。

新規作成のダイアログが開くので(開かない場合は、[ファイル]→[新規作成]を選ぶと開きます)、[書類の種類を選択してください]のボックスで「アプリケーション」をクリックし、[選択]ボタンを押します。

Automatorの新規作成ウィンドウでアプリケーションを選択

次に、アクションを順に並べます。アクション一覧から「シェルスクリプトを実行」を選んで、右ペインにドラッグアンドドロップします。

「シェルスクリプトを実行」をドラッグアンドドロップする

[シェル]のドロップダウンリストで「/bin/zsh」を選んでおきます。初期値として表示されている「cat」を消去して、代わりに次のコマンドを入力します。

aws --output json ec2 describe-instances --filter "Name=instance-state-name,Values=running" | jq '.Reservations[].Instances[].InstanceId'

AWS CLIを実行するために必要なアクセスキー、シークレットアクセスキー、デフォルトのリージョンは、設定ファイルで事前に与えているものとします。このあたりの設定が分からない場合は、「AWSで構築した環境にありがちなシェルスクリプトたち まとめ | Developers.IO」のセットアップの項目を参照してください。

ここで、右上の実行ボタンを押して、「シェルスクリプトを実行」アクションの動作を確認しておきましょう。次のようなログが出力されれば、成功です。

「シェルスクリプトを実行する」アクションを実行してみる

そして、上記のアクションの結果を確認ウィンドウへ渡すために、変数に格納します。アクション一覧から「変数の値を設定」を選んで、右ペインにドラッグアンドドロップします。[変数]ドロップダウンリストに表示されている「新規変数」をクリックします。

変数を新規作成する

すると、[変数オプション]ダイアログが開きます。任意の変数名(instances)を入力して、[完了]ボタンを押します。デフォルトの変数名は「記憶装置」です。

変数に名前をつける

さらに、確認ウィンドウを開くアクションを追加します。アクション一覧から「確認を求める」を選んで、右ペインにドラッグアンドドロップします。[メッセージ]という文字が表示されている欄に、変数ペインから先ほど作成した変数(instance)をドラッグアンドドロップします。

作成した変数を利用する

「通知を表示」アクションでも良いですが、通知は表示領域が小さいことと、数秒たつと自動的に非表示になってしまうため、今回は「確認を求める」アクションにしました。確認ダイアログの方が目立ちますし、ユーザがボタンを押すまでは画面から消えません。さりげないリマインダーをお求めの方は、「通知を表示」を使用してください。

さて、ふたたび右上の[実行]ボタンを押して、「確認を求める」アクションの動作を確認しておきましょう。あらかじめEC2インスタンスを3個起動し、そのうち1つは停止状態にしておきます。作成中のアプリケーションを実行して、次のようにダイアログが表示されると成功です。

作成したアプリケーションを実行してみる

実行中(running)のインスタンスのinstance idだけが表示されていることを確認してください。

[OK]ボタンを押すと、アプリケーションが最後まで実行されます。[キャンセル]ボタンを押すと、このアクションでアプリケーションが終了します。今回はアプリケーションの最後に実行されるアクションが「確認を求める」アクションなので、どちらのボタンを押してもユーザに見える結果は同じです。

最後に、アプリケーションを保存します。[ファイル]→[保存]で[フォーマット]に「アプリケーション」を選んで、任意の場所に保存します。今回は、USER_HOME/bin/automator/aws_check_instance.appとして保存しました。

2. 作成したアプリケーションをlaunchdで定期実行する

手順1で作成したaws_check_instance.appを定期実行するジョブを設定します。

まず、~/Library/LaunchAgents/ディレクトリに、任意の名前(aws.check.instance.plist)でplistファイルを作成します。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>aws.check.instance</string>
  <key>Program</key>
  <string>/Users/{USER_NAME}/bin/automator/aws_check_instance.app/Contents/MacOS/Application Stub</string>
  <key>StartCalendarInterval</key>
  <array>
    <dict>
      <key>Hour</key>
      <integer>10</integer>
      <key>Minute</key>
      <integer>0</integer>
    </dict>
    <dict>
      <key>Hour</key>
      <integer>18</integer>
      <key>Minute</key>
      <integer>0</integer>
    </dict>
  </array>
</dict>
</plist>
Label(必須)
慣習的にplistファイル名と同じ名前を指定します
Program(必須)
実行するプログラムへの絶対パスを指定します。引数を取るプログラムの場合は、ProgramArgumentsという要素を使います。詳しくはマニュアルを参照してください。
アプリケーション(.app)を指定するときは、上のように{APPLICATION_NAME}/Contents/MacOS/Application Stubと指定します。パスに空白を含みますが、ダブルクォートで囲む必要はありません。
StartCalendarInterval
Month、Day、Weekday、Hour、Minuteのkeyを組み合わせて、実行スケジュールを定義します。
keyを指定しない項目は、ワイルドカードとして扱われることに注意してください。たとえば、上のplistでMinuteの設定を除去すると、10時台と18時台の毎分にアプリケーションが実行されます。

動作確認のために、設定から数分後に実行するスケジュールも定義しておくとよいでしょう。

また、今回は使用しませんが、一定時間(秒)ごとに実行するジョブを次のように定義できます。StartIntervalをStartCalendarIntervalの代わりに使用します。

<key>StartInterval</key>
<integer>3600</integer>

上の設定では、1時間に1回ジョブを実行します。

次に、plistをlaunchdに読み込ませます。

launchctl load ~/Library/LaunchAgents/aws.check.instance.plist

何も表示されなければ成功です。登録したジョブの状態は、次のコマンドで確認できます。

% launchctl list aws.check.instance
{
     "Label" = "aws.check.instance";
     "LimitLoadToSessionType" = "Aqua";
     "OnDemand" = true;
     "LastExitStatus" = 0;
     "TimeOut" = 30;
     "Program" = "/Users/{USER_NAME}/bin/automator/aws_check_instance.app/Contents/MacOS/Application Stub";
};

最後に、動作確認をします。定義したスケジュールどおりに確認ダイアログが表示されれば、成功です。

確認ダイアログが表示されない場合は、ジョブの状態をチェックするコマンドを実行してみましょう。LastExitStatusが0以外になっている場合、何らかのエラーが発生しています。

plistファイルにStandardOutPathとStandardErrorPathというkeyを定義することで、デバッグができます。詳しくは、A launchd Tutorialを参照してください。

なお、plistの内容を編集した場合は、次のコマンドで一旦読み込みを解除してから、もう一度読み込んでください。

launchctl unload ~/Library/LaunchAgents/aws.check.instance.plist

注意点

EC2インスタンスの料金は、2015年3月現在、1時間単位の従量課金です。つまり、5分間だけ起動した場合も59分間ずっと起動した場合も、発生する料金は同じです。

そして、1時間のうちに2回起動してしまうと、請求も2倍になることに注意してください。こまめなインスタンス停止は節約になりますが、細かくやりすぎると逆効果です。

参考資料

おわりに

急ごしらえのリマインダーのご紹介でした。これでインスタンスの停止忘れを防いで、無駄遣いを減らせるとうれしいですね。AWS APIが提供されているおかげで、ユーザがこのように対処できるのは大変助かります。

もっとこうするといい感じのリマインダーになるよ!というような情報がありましたら、ぜひお寄せいただければと思います。お待ちしております。

それでは、また。