AWSのテスト環境リソースを定期自動削除するようにした話

皆様こんにちは、インフラの中嶋です。
今回はAWSテスト環境のリソースを自動で削除する仕組みを作りましたのでそのお話となります。

背景

  • AWS環境で自由にリソースの作成・検証を行うことができる環境をAWS利用者に提供している。
  • 今までは請求アラート(稟議額を超えそうになった際にメールにて通知)が来たタイミングで利用料金の高いものを優先してそのインスタンスの作成者に削除可能かの確認作業を行っていた。
    (大抵はインスタンス名・タグに自分の苗字を入れてくれてるが、そのように容易に特定できない場合は全体に告知しなければいけないので大変)
  • 検証環境はインスタンスの自動削除を実装しよう!という話になった。

狙い

  • 検証後にもかかわらずリソースを利用し続けてしまっていることで無駄にかかっている料金の削減
  • 年に何度か訪れる不定期の棚卸作業を自動化することでそこにかかっている工数の削減

結果

  • 不定期でやってくる棚卸がなくなって快適になった。
  • 想像よりも大きな利用料の削減になった。
  • チームの人に「いいね」と言ってもらえて感謝された ※重要

 

対象サービスと仕様

  • 利用にお金がかかるものに対して自動削除を実装する。
  • 基本、作成から90日を経過した時点でSlackに削除告知を出す。(一部例外有)
  • 告知から1週間の猶予期間の後(猶予期間も毎営業日告知は流れる)、対象のサービスを削除。
    ※その間にタグとしてExFlg:Trueが設定されていた場合は更に90日の延長対象となる。
サービス 精査方法
EC2 作成後90日以上経過しているインスタンスを1週間後に削除。
タグでExFlgの値にTrueが設定されていた場合は180日まで延長できる。
ElasticIP インスタンスに関連付けられていないElasticIPを削除する。(紐づけられていない=利用のないIPである為、即時削除)
EBSスナップショット 作成後90日以上経過しているインスタンスを1週間後に削除。
タグでExFlgの値にTrueが設定されていた場合は180日まで延長できる。
ELB 作成後90日以上経過しているインスタンスを1週間後に削除。
タグでExFlgの値にTrueが設定されていた場合は180日まで延長できる。
S3 作成後90日以上経過しているインスタンスを1週間後に削除。
タグでExFlgの値にTrueが設定されていた場合は180日まで延長できる。
RDS 作成後90日以上経過しているインスタンスを1週間後に削除。
タグでExFlgの値にTrueが設定されていた場合は180日まで延長できる。
NATゲートウェイ 作成後90日以上経過しているインスタンスを1週間後に削除。
タグでExFlgの値にTrueが設定されていた場合は180日まで延長できる。

AWS側の環境構築

  • スクリプトを実行するサーバはインフラ用アカウント上にあるEC2(スクリプトをまとめて置いてあるサーバ)にて行う。
  • 検証用アカウントにてIAMロールを作成(EC2,RDS,ELB,S3に対する読み込み・削除権限を付与)し、信頼関係に上記インフラ用アカウントのアカウントIDを信頼されたエンティティとして追加
  • インフラ用アカウントのEC2上よりAWS-CLIで一連の操作ができることを確認。

スクリプトの仕様

スクリプト名 内容 実行頻度 通知場所
サービス名-check.sh 該当サービスの利用状況を下記に記載の精査方法と照らし合わせ、合致するものを「slackNotice-サービス名.sh」により通知する。 毎営業日(12:30) -
サービス名-delete.sh 上記スクリプトからさらに1週間経過したものに対して削除処理を行うスクリプト。
削除した対象とともに「slackNotice-delサービス名.sh」により通知する。
毎営業日(17:00) -
slackNotice-サービス名.sh サービス名-check.shに呼び出され、対象サービスの削除対象を通知する。 サービス名-check.shに呼び出された時 Skackの通知用チャンネル
slackNotice-delサービス名.sh サービス名-delete.shに呼び出され、対象サービスの削除結果を通知する。 サービス名-delete.shに呼び出された時 Skackの通知用チャンネル

コード

  • 前提として、上記AWS側の環境構築にてIAMロールが適用されており、検証アカウントリソースの操作権限があるかの確認が終わっている状況であること。
  • 削除時のスクリプトのみ掲載。スクリプトから削除用に用意している列を消したりすると通知に引用できる。

サンプルコード:ec2-delete.sh

#!/bin/sh -x

# リージョンの設定
export AWS_DEFAULT_REGION="ap-northeast-1"
# 操作したいAWSアカウントのメールアドレスを指定
export AWS_PROFILE=xxxxxxxx@yyy.zzz

ago=90
date -v-${ago}d > /dev/null 2>&1 && DATE=`date -v-${ago}d +%Y-%m-%d` || DATE=`date --date="-${ago} days" +%Y-%m-%d`

# 作成後90日以上経過しており、稼働中でかつ、ExFlgが未設定のインスタンスを検索し、結果をec2listに格納
aws ec2 describe-instances --query "Reservations[].Instances[?(LaunchTime<='$DATE' && State.Name == 'running' && (Tags[?Key=='ExFlg'] | [0].Value == None ))][].{ID:InstanceId, Tags:Tags[?Key=='Name'] | [0].Value, ExFlg:Tags[?Key=='ExFlg'] | [0].Value, Created:LaunchTime, SG:SecurityGroups[0].GroupName, State:State.Name }" --output table > ec2list

# 削除用にインスタンスIDのみの簡易リストを作成 こちらはテーブルではなくテキスト形式での出力
aws ec2 describe-instances --query "Reservations[].Instances[?(LaunchTime<='$DATE' && State.Name == 'running' && (Tags[?Key=='ExFlg'] | [0].Value == None ))][].{ID:InstanceId}" --output text > ec2dellist

# 削除リストを読み込み、インスタンスを削除していく
while read line
do
        echo $line
        aws ec2 stop-instances --instance-ids $line
done < "./ec2dellist"
./slackNotice-delec2.sh ${ago}

ago=180
#~~~90日の際の処理とほぼ同じため、省略~~~

サンプルコード:slackNotice-delec2.sh

#!/bin/bash

set -eu

msg="*awsアカウント名 リソース削除*\n\`\`\`"
i=0
# ファイルを読み込む
FILE="./ec2list"
if [ ! -s $FILE ]; then
   exit
fi

while read line
do
   if [ $i -lt 2 ]; then
      i=$((i+1))
      continue
   fi
   msg=$msg+$line+"\n"
done < $FILE

msg+="\`\`\`\n上記リソースは作成されてから${1}日以上経過しましたので削除いたしました。"
echo $msg

#SlackのWebHookURLを指定
webhook="https://hooks.slack.com/services/xxxx/yyyy/zzzzzzzzz"
payload="payload={\"text\":\"${msg}\"}"

curl -s -S -X POST --data-urlencode "${payload}" ${webhook}  > /dev/null

サンプルコード:その他のサービス

# EBSスナップショットの一覧表示
aws ec2 describe-snapshots --owner self

# EBSスナップショットの削除
aws ec2 delete-snapshot --snapshot-id スナップショットIDを指定

---

# ElasticIPの一覧表示
aws ec2 describe-addresses

# ElasticIPの便利な検索 インスタンスに関連付けられてないかつ、プライベートIPアドレスが割り当てられていない場合
aws ec2 describe-addresses --query "Addresses[?InstanceId==None && PrivateIpAddress==None]"

# ElasticIPの削除
aws ec2 release-address --allocation-id AllocationIdを指定

---

# ELBの一覧表示
aws elbv2 describe-load-balancers

# ELBの削除
aws elbv2 delete-load-balancer --load-balancer-arn LoadBalancerArnを指定

---

# NatGatewayの一覧表示
aws ec2 describe-nat-gateways

# NatGatewayの便利な検索 ステートがavailableのもののみ表示
aws ec2 describe-nat-gateways --query "NatGateways[?(State=='available')]"


# NatGatewayの削除
aws ec2 delete-nat-gateway --nat-gateway-id NatGatewayIdを指定

---

# RDSの一覧表示
aws rds describe-db-instances

# RDSの削除
aws rds delete-db-instance --db-instance-identifier DBInstanceIdentifierを指定 --skip-final-snapshot

---

# S3の一覧表示
aws s3api list-buckets

# S3バケットの削除 ※バケットのバージョニングが有効になっている場合は削除できないため、別途処理の追加が必要
aws s3 rb s3://バケット名 --force

 

シェルの自動実行

  • crontabを設定し、定期自動実行するように設定する。
    • sudo crontab -u ユーザ名 -e
    • ※ユーザ名の部分はスクリプトをおいており、実行するユーザを入力する。

自動実行したいスクリプトの設定を追記する。 ※下記は一例

# AWS検証環境自動削除 チェック処理 平日毎日12時半に起動
30 12 * * 1-5 cd /home/ユーザ名/作業ディレクトリ(スクリプトが置いてあるディレクトリ/ ; ./ebssnp-check.sh
30 12 * * 1-5 cd /home/ユーザ名/作業ディレクトリ(スクリプトが置いてあるディレクトリ/ ; ./ec2-check.sh
30 12 * * 1-5 cd /home/ユーザ名/作業ディレクトリ(スクリプトが置いてあるディレクトリ/ ; ./elb-check.sh
30 12 * * 1-5 cd /home/ユーザ名/作業ディレクトリ(スクリプトが置いてあるディレクトリ/ ; ./rds-check.sh
30 12 * * 1-5 cd /home/ユーザ名/作業ディレクトリ(スクリプトが置いてあるディレクトリ/ ; ./s3-check.sh
30 12 * * 1-5 cd /home/ユーザ名/作業ディレクトリ(スクリプトが置いてあるディレクトリ/ ; ./natgateway-check.sh

# AWS検証環境自動削除 リソース削除処理 平日毎日17時に起動
0 17 * * 1-5 cd /home/ユーザ名/作業ディレクトリ(スクリプトが置いてあるディレクトリ/ ; ./ebssnp-delete.sh
0 17 * * 1-5 cd /home/ユーザ名/作業ディレクトリ(スクリプトが置いてあるディレクトリ/ ; ./ec2-delete.sh
0 17 * * 1-5 cd /home/ユーザ名/作業ディレクトリ(スクリプトが置いてあるディレクトリ/ ; ./eip-delete.sh
0 17 * * 1-5 cd /home/ユーザ名/作業ディレクトリ(スクリプトが置いてあるディレクトリ/ ; ./elb-delete.sh
0 17 * * 1-5 cd /home/ユーザ名/作業ディレクトリ(スクリプトが置いてあるディレクトリ/ ; ./rds-delete.sh
0 17 * * 1-5 cd /home/ユーザ名/作業ディレクトリ(スクリプトが置いてあるディレクトリ/ ; ./s3-delete.sh
0 17 * * 1-5 cd /home/ユーザ名/作業ディレクトリ(スクリプトが置いてあるディレクトリ/ ; ./natgateway-check.sh

 

稼働チェック

実際に運用している画像です。
緊急性の低いアラートを集約しているSlackチャンネルが有り、下記のように通知をしてます。

チェック時の通知
f:id:AdwaysEngineerBlog:20211203101117p:plain

削除時の通知
f:id:AdwaysEngineerBlog:20211203101127p:plain


 

さいごに

結局いくら削減できたの?

  • 毎月の平均利用額と自動削除スクリプト設置後の平均利用額を比較したところ、月当たり5万円、年間で60万円程度の削減となることがわかりました。
  • 各部署のマネージャーとの調整など大変でしたが、今後のメンテナンス作業が削減できたことを考えるととてもよい取り組みだったと思います。

やってみて

  • 正直もっと最適化(コード内容だったり他サービスも対象とする等)できる部分はあると思うので今後時間があったら触りたいです!
  • 思った以上に経費の削減に繋がったみたいでうれしいです。
  • 初めてAWS-CLIを使いましたが、便利なので皆さんも使ってみてください!