一定時間利用されていないEC2インスタンスを自動でstopする


プライベートのOSS開発向けに、一定時間利用されていないAWSのEC2インスタンスを自動でstopする仕組みを作った。プライベート用なのでちょっと雑かも。

最初は【AWS】CloudWatch入門/使っていないEC2を自動シャットダウンしよう のように CloudWatch で CPU 使用率を監視すれば良いかと思ったが、やってみると vim を開いて編集しているだけの時間が案外長く、利用中なのにstopされてしまうことが多かったのでもう少し工夫した。

要件

  • 一定時間利用されていないAWSのGPUインスタンスを自動でstopしたい
  • ただし、vim を開いて編集しているだけのような CPU を喰わない時間帯も利用中なので stop したくない
  • ただし、GPUを使って deep learning の学習を走らせているような時間帯も利用中なので stop したくない
  • tmux や screen を使うので、余計なプロセスは生き続けている可能性があることも注意

設計

  1. ssh セッションが生きて入れば ssh して作業中なので「active」と定義する
  2. CPU 使用率が 2% より大きければなにか処理中なので「active」と定義する
  3. cron に仕込んで AWS CloudWatch に CustomMetrics として active か否かの情報を投げる
  4. AWS CloudWatch で一定時間 Non Active なら EC2 インスタンスを stop するようにアラームを作る

ポイント

  • CloudWatch を使わずに cron で毎分 1 or 2 の判定をして Non Active だったら即座に shutdown -h now するような仕組みでも、大体は機能すると思うが、EC2インスタンス起動直後に shutdown される可能性があるため避けた。後はちょっとコーヒーブレークで離席して ssh セッション切れたらすぐ stop されそうなので避けた。
  • EC2上でログを吐いて、自前でログを舐めて一定時間(20分とか) Non Active なら shutdown するような仕組みにしても良いが、ログの rotate などを考えると面倒なので、AWS CloudWatch にデータを蓄積するようにした。
  • AWS CloudWatch は 2018/09 現在、複合メトリクス(AND, OR)を条件にしたアラームを作れないので、スクリプト側で 1 or 2 の判定を複合して「active か否か」の情報を投げるようにした。
  • GPU だけ使って CPU 全く使わないアプリケーションは存在しないので、CPU使用率だけ見れば良いことにした。GPU使用率も見た方が良いのは確かで、これはただの手抜き。

実装

is-active.sh
#!/bin/bash

# The first report of vmstat gives averages since the last reboot, drop it.
cpu_usage_usr=$(vmstat 1 2 | tail -1 | awk '{print $13}')
ssh_session_count=$(ss | grep '[s]sh' | wc -l)
if [ $cpu_usage_usr -gt 1 -o $ssh_session_count -gt 0 ]; then
  echo 1
  exit 0
fi
echo 0
exit 1
aws-put-mon-is-active.sh
#!/bin/bash
REGION=${1:-us-west-2}

SCRIPT_DIR=$(dirname "$0")
instance_id=$(curl --silent http://169.254.169.254/latest/meta-data/instance-id/)

value=$("$SCRIPT_DIR"/is-active.sh)
aws cloudwatch put-metric-data --namespace 'EC2/Custom' --metric-name 'IsActive' \
  --value "$value" --dimensions InstanceId="$instance_id" --region "$REGION"
/etc/cron.d/aws-put-mon-is-active
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
MAILTO=root
HOME=/home/sonots

* * * * * root /home/sonots/scripts/aws-put-mon-is-active.sh

あとは 【AWS】CloudWatch入門/使っていないEC2を自動シャットダウンしよう を参考に CPU 使用率の代わりに、今回作った EC2/Custom IsActive メトリクスを見るようなアラームを作って、一定時間(20分とか) Non Active なら stop するように設定。

なお、AWS CloudWatch アラームの EC2 アクションを選べるようにするには、カスタムメトリクスに InstanceId ディメンジョンを設定しなければならない(ので、してある)。