Amazon EBS でスナップショットを定期的に保存しておく方法のまとめ


実はやり方自体は既存の記事で十分まかなえているので、あえて一つ一つの手順まで詳しくは書かない。

# 用語
- [AmazonEBS](https://aws.amazon.com/jp/ebs/)

EC2は、本体に記憶領域（インスタンスストア）を持っているが、こちらの記憶領域は、サーバを停止してしまうと自動的に初期化を行なってしまう。
EC2単体だと、時間起動・停止を行うような使い方をする場合不便。
そこで必要になるのがEBS。前述の通り、EC2に仮想的に外付けされるHDDのような記憶領域で、ここにデータを保存することで、EC2の停止を行なうことができる。

- [EBS snapshot](http://aws.amazon.com/jp/ebs/details/#snapshots)

EBSボリュームの特定時点のスナップショットをS3に保存する機能。
EBSのスナップショット機能を用いると、EBSに含まれるデータが丸ごとS3に複製され、S3へ格納されたスナップショットは新たなEBSとして復元可能。
もしEBS上のデータが破損したりロストしたりしても、スナップショットを取った時点のデータをS3から復元可能。
EBSをデータディスクとして使っていた場合、スナップショットを取得すれば任意の時点のデータバックアップになる。

>EBS ボリュームそのものからデータのコピーを取得し S3 上に保管したものをスナップショットと呼びます。スナップショットには AMI のようなインスタンスを構成するための管理情報は含まれていません。スナップショットの中のデータを利用する場合には、基本的にそのスナップショットを元に EBS ボリュームを作成することになります。

### AMIとEBSの違い
スナップショット = 「EBS ボリュームの中のデータ」を特定のタイミングで取得しS3に保存したもの
AMI  = 「EBS ボリュームの中のデータ(スナップショット) とインスタンスを構成する管理情報」を含む起動テンプレート

<img src="https://qiita-image-store.s3.amazonaws.com/0/56540/c923752c-0fe1-d895-d5e5-8e7492c06910.png" height=200>

要するに、AMIはEBSのsnapshotに加えて+αで管理設定も入っている。

# EBS利用時のポイント
>「スナップショットの保存は差分ベースで行われるものの、最新のスナップショットさえあればボリュームを復元できるようにスナップショット削除プロセスは設計されています。」

→古いスナップショットが不要であれば削除しても大丈夫。過去のものも復元できる。

2回目以降は定期的にsnapshotを保存しまくっても、変更分のみが料金に計上されるので、よっぽど頻繁に中身をいじらなければ料金的にはあまり負担がない。
料金周りの理解には[こちら](http://qiita.com/kaojiri/items/1c4a95c271fb1584476a)

# snapshotをS3に保存する方法
手順は以下のサイトを参考にした
https://blog.supersonico.info/?p=1312
https://ja.amimoto-ami.com/2015/03/04/aws-cli-create-snapshot/

網元AMIさんのサイトがスクリプトまで提供してくれていて優しい。
流れとしては以下のようになる。

1. IAMでポリシー作成
	- 管理画面からポリシー作成画面へ
	- 独自ポリシーを作成
	- ポリシーの設定
2. グループの作成
	- 管理画面からグループ名設定
	- ポリシーをグループに関連付け
3. ユーザの作成
	- ユーザ名設定
	- キー情報の保存
	- 作ったユーザをグループに設定
4. AWS CLIの設定
	- AWS CLIの初期設定
5. 自動スナップショット作成スクリプト設定
	- スクリプトの作成　←　ここ注意
6. cronサーバーにスクリプトを設定

# AWS Lambda を使って面倒なcron処理もAWSにやらせる
[AWS lambda でRDSのsnapshotをとる](http://tkuchiki.hatenablog.com/entry/2015/11/06/124113)

AWS Lambda でスケジュールイベントの設定ができる。
これによりcronで実行するスクリプトをLambdaに移行可能。cronの冗長化は面倒だし、cronのためだけにEC2を起動しなくて良いので便利。
異常時の検出も大事だが、このときにslackに通知したいのでその設定方法も調べてみた。

# lambdaでやってみる
[ドストライクな記事](http://qiita.com/eiroh/items/66bb68d12bd8fdbbd076)が。今回は異常時のアラートとして、メール通知ではなくslackで特定channelへの投稿をさせることに。
異常検知のslackによる通知自体は[すでにやっている](http://qiita.com/kenjiszk/items/77895098a9169220b529)ので真似てみるだけ。
特にCloudWatch→SNS→lambda→slackはこれまた[タイムリーなブログ記事](http://tech.sanwasystem.com/entry/2015/12/22/163130)があるのを発見。

## やりたいこと
- Lambdaのスケジュールイベントを使って自動的にEBSのスナップショットを作成する
	 - 今回使用する言語はPython
- スナップショットの対象は、EC2インスタンスのタグに「Backup-Generation」が付いているもの
- Backup-Generationの値には、0以上の数字を指定
	- スナップショットの保存件数は、Backup-Generationの値
		- 0のときはスナップショットを作成しない
	- スナップショットの保存件数を超えた場合は、作成日付の古いものから削除
- 作成したスナップショットのDescriptionには、「EBSボリュームID」と「EC2インスタンス名」を付与
	- EC2インスタンス名がない場合は、「EBSボリュームID」のみ付与
- 処理が失敗した場合は、CloudWatchの監視によりslackで通知
	- CloudWatchからSNSNotifyを飛ばし、SNSへ通知
	- AWS SNSからlambdaが起動
	- lambdaのスクリプトに従ってslackに通知


```実行手順
- EC2インスタンスに名前とタグをつける
	- 名前をつける
	- タグをつける
- Lambdaの作成
	- Select blueprint
	- Configure function
	- Lambda function code
	- Lambda function handler and role
- IAM Roleの作成
	- Advanced settings
- Lambdaの動作確認 (test-run)
- Lambdaのスケジュールイベントの作成
	- スケジュールイベントの追加 (add event source)
- 処理の失敗をCloudWatchで監視してSNS経由でslackの通知を送る
	- Create Alarm in CloudWatch
	- SNS settings
	- lambda script settings
	- Setup slack webhook
```

## 注意
手順は参考記事の通りなので、以下詰まったポイントについて追記。

- lambdaのスケジュールイベントの作成時
先にAWS SESにおいて、新しくSNS Topic Configurationをcreateする。名前は10文字以内と無茶振りされるので`CWNotify`とでもしておく。
この状態で監視スクリプトのevent source追加のところでは`SNS`を選択。そうすると先に作成したSNS Topicである`CWNotify`が選べるので設定。動作確認をしてから有効化したいのでEnable laterにしておくのを忘れずに。
Detailsではcronを選択し、crontabの書き方にしたがって定期実行のタイミングを指定する。ここではちょっとした注意が必要で、[このように](http://qiita.com/pyr_revs/items/abd29d9472f4cba9032c)、"年"の枠との設定と、曜日のところは *"\*"ではなく"?"を使う* というところだけ気をつける。そしてこれはUTCなので(日本時間 -9時間)で設定。

- slack通知設定
lambdaで `slack_notify` functionを作って、スクリプトを入れてwebhookのurlを入れるところまではいいけど、テストrunした時にずっとエラーが出て詰まった。テスト投稿する際に、configure-test-eventからsample event templateで AWS-SNS を選択する。このテストスクリプトの中の`Messeage: Hello fron SNS!`がslackにテスト投稿される。アホな自分はsample event template にHello World(単なるjsonが渡される)を選んでテスト実行して、うまくいかないな〜と思っていたので、ちゃんと選択するように注意。