ぺい

渋谷系アドテクエンジニアの落書き

Packerで作成されたSnapshotを消す

便利なんだけど放置しがちなAMIたちを消す

f:id:tikasan0804:20180621205204p:plain

Packerはイメージの管理に非常に便利です。弊社でも、AWSのAMIの作成時に使っています。

tikasan.hatenablog.com

ただ、AMIは放置していると無限に作成されてしまうので、いくら安いストレージ料金であっても、結構溜め込んでいるとまあまあな値段になります。そこで、いい感じに消す作業をしたので、その時の作業内容をまとめてみました。
※AMIを消す処理は、正確には登録解除ですが、ここでは面倒なので削除というワードを使っています。

この作業をする上での前提条件

  • インスタンスに紐付いていないAMIは消してよい
  • AMIに紐付いていないスナップショットでも必要なものはある
  • 消すとしてもAMIは保険として、一週間は残したい
  • 今後もPackerでAMIを作成する
  • トライアンドエラーでAMIを何度か作り直すことがある
  • AutoScallingの起動設定に使うAMIはPackerで作っている
  • AutoScallingの起動設定に使っているAMIは消してはいけない
  • スナップショットはAMIに紐付いていると消せない(これはスナップショットの仕様)

他にも細々としたやつはありますが、ざっくり書くと上のような感じ。

Packerの機能でどうにか出来ない?

出来ればPackerで備わっている機能でどうにかしたいよねー。わかるー。ってことで探しました。それっぽいコマンドは見つかりました。

Amazon EBS - Builders - Packer by HashiCorp

  • force_deregister (boolean) - Force Packer to first deregister an existing AMI if one with the same name already exists. Default false.
  • force_delete_snapshot (boolean) - Force Packer to delete snapshots associated with AMIs, which have been deregistered by force_deregister. Default false.

google翻訳にかけると

  • force_deregister(boolean) - 同じ名前の既存のAMIが既に存在する場合、Packerが既存のAMIを最初に登録解除するように強制します。 デフォルトはfalseです。
  • force_delete_snapshot(boolean) - Force_deregisterによって登録解除されたAMIに関連付けられたスナップショットを削除するようにPackerに指示します。 デフォルトはfalseです。

一瞬使えそうと思ったのですが、AMIを新規に作成した時に、必ず正しいとも限らないので、せめて何世代か前のAMIは残しておきたいとか、あとはAutoScallingの起動設定に使っているAMIが消されると色々面倒。そこを自動で頑張るとかも考えたけどコスト高いのでやめ。

Packerのプラグイン

github.com

もしかしたら、Packerプラグインで何か良いのあるか!?って考えていたらありました。最高。このプラグインは、AMIを作成した際に、何世代か前のAMIを自動で削除してくれるというもの。良さそうだ!ってなりましたが・・・。 機能そのものは問題なく動き、素晴らしいものでした。しかしながら、トライアンドエラーとか繰り返している内に、意図せず消えてほしくない世代のものが消えてしまうかもしれないという点がああああというのと、容量食っているスナップショットをどうにかしたい問題もあり、使うのをやめました。

シェルスクリプトでどうにかするか

結局ここに落ち着いてぐぬぬというお気持ちなんですが、Jenkinsのジョブで定期実行するシェルスクリプトを作成しました。(結局それかいってやつ)

ここ、一旦、前提条件をどうやってクリアするかを考えた内容を雑に書く。

インスタンスに紐付いていないAMIを消す

消そうとしているAMI IDが動いているインスタンスに紐付いていないかをチェックすれば良い。

$ aws ec2 describe-instances --filters "Name=instance-state-name,Values=running" "Name=image-id,Values=hoge"

AutoScallingに使っているAMIは消してはいけない

これは、LaunchConfigurationに紐付いているAMI IDを見れば言いわけですが、弊社では使っているAutoScallingは必ず1つ以上のインスタンスが動いていたので、つまり、上のインスタンスに紐付いているかチェックでカバーが出来る。

スナップショットはAMIに紐付いていると消せない

これは、弊社の前提条件ではなく、スナップショットの前提としてあるので、AMIを先に掃除しないと、スナップショットのお掃除は出来ない。

消すとしてもAMIは保険として、一週間は残したい

これ余裕じゃーんと思ったのですが、describe-images には日付フィルタがなかったので、jqコマンドでゴリゴリ頑張ることになった。

delete_date_range=2016-06-12
aws ec2 describe-images --owner self | \
  jq -r ".Images[] |  select(.CreationDate < \"$delete_date_range\")" | \
  jq -r ".ImageId" | tee ami-id.txt

AMIに紐付いていないスナップショットでも必要なものはある

これが地味に面倒な前提条件としてあった。解決策は実に雑ではあるけど、Packerが作成したスナップショットには何も設定していないと、説明の部分に Created by CreateImage(hogehoge) のような内容があり、これが書いてある=Packerが作ったものと判定することにした。
逆に言うと、これから導入を考えている人は、Packerが作ったものと断定出来るTagとかを付与するのが良さそう。

$ aws ec2 describe-snapshots  --owner self --filter "Name=description,Values=Created by CreateImage(*"
  • AutoScallingの起動設定に使うAMIはPackerで作っている
  • 今後もPackerでAMIを作成する

上記は、ここまでの前提条件クリアすればあまり気にしなくて良い事案。

出来たシェルスクリプト

日本語を豊富に使っているけど、まあ良いではないか。tee の部分とかはどっちでも良かったけど、一応出した感じになっている。 以下はジェンキンスのジョブで定期実行しています。また、スクリプトは同一ジョブ内でstepを分けたような感じで管理しています。 パラメーターを色々切り替えれたりとか考えましたが、あまり頑張らない感じで完成とした。

#!/bin/sh
# 動いているインスタンスに紐付きがないかつ、作成から1週間以上のAMIを削除する
delete_day_limit=7
delete_date_range=`date -d "$delete_day_limit day ago" '+%Y-%m-%d'`

echo "AMI ID取得"
aws ec2 describe-images --owner self | \
  jq -r ".Images[] |  select(.CreationDate < \"$delete_date_range\")" | \
  jq -r ".ImageId" | tee ami-id.txt

for deregister_ami_id in `cat ami-id.txt`; do
  if [ `aws ec2 describe-instances --filters "Name=instance-state-name,Values=running" "Name=image-id,Values=$deregister_ami_id" --output=text | wc -l` -eq 0 ]; then
    aws ec2 deregister-image --image-id $deregister_ami_id
    echo "AMI ID = $deregister_ami_id は登録解除しました 稼働しているインスタンスに紐付きがないため"
  else
    echo "AMI ID = $deregister_ami_id はスキップしました 稼働しているインスタンスと紐付きがあったため"
  fi
done
#!/bin/sh
# Packerが作成したAMIとの紐付きがないスナップショットを削除
echo "\nPackerが作成したSnapshot ID取得"
aws ec2 describe-snapshots  --owner self --filter "Name=description,Values=Created by CreateImage(*" | \
  jq -r '.Snapshots[].SnapshotId' | \
  tee snapshot_id.txt

for delete_snapshot_id in `cat snapshot_id.txt`;do
  if [ `aws ec2 describe-images --filter "Name=block-device-mapping.snapshot-id,Values=$delete_snapshot_id" --output=text | wc -l` -eq 0 ]; then
    aws ec2 delete-snapshot --snapshot-id $delete_snapshot_id
    echo "Snapshot ID = $delete_snapshot_id を削除しました AMIとの紐付きがないため"
  else
    echo "Snapshot ID = $delete_snapshot_id をスキップしました AMIとの紐付きがあるため"
  fi
done

結果

キレイさっぱり消えて、そこそこの月々の費用を削減出来た。
消す作業大変だけど、定期的にやっていこう。

ていうか、jq コマンドすごすぎじゃない?