EBSのsnapshotの差分サイズを計算する

# エピローグ

昔昔あるところに、写真家がいました。

写真家のもっているカメラは、「昨日から変化した部分」しか映りませんでした。

ある写真家は、毎日写真を撮り、
ある写真家は一週間ごとに写真を撮っていました。

このカメラで撮影したときにかかる金額を、写真家は知りませんでした。

フィルムの枚数に課金されるのではないようです。


# モチベーション

EBSの金額を浮かしたい！
金額に対するスナップショット数を最大化して環境復旧などをいい感じにしたい！





# 最初にあった疑問

最初にあった疑問は以下です。

:::note info
【疑問1】
差分スナップショットとは何か。どこに課金されるのか？
どうやら、差分、のサイズはスナップショットのサイズではなさそう。
では、差分とはどこにあるの？
:::


:::note info
【疑問2】
差分に課金される（＝取得回数に課金されるのではない）ならば、
1週間に1回EBSスナップショットを取得するのと、
毎日EBSスナップショットを取得するのでは、
料金の差は大きくはならないのではないか？
:::

:::note info
【疑問3】
差分スナップショットの古いものを消したら、
そこにしか含まれていない差分は消失してしまうのではないか？
:::

:::note warn
疑問２について補足です。

 [毎日の差分の積み重ね] > [1週間分の差分] 

ではあるが、

[毎日の差分の積み重ね] - [1週間分の差分] = 重複している差分量（複数回更新されているブロックのみカウント）

であるなら、複数回更新されているブロック数の多寡によっては、
この値は小さくなるのではないか。

まとめると、
ブロック数が増える→わかる。
毎日内容が変わるブロックがある→そんなにないのでは？

てことは毎日撮るのありかも？
:::

# 結果

:::note info
疑問１：
→スナップショットごとの差異をAPI（後述）で比較できる。
その差異のあるブロック数が、差分スナップショットとなる。
:::

:::note info
疑問２：
→重複する差分、を求める方法はなく不明であった。
あくまで概算での週次と日次の比較となった。
まったく同じデータの更新があるような、ミラー環境を作ればできるかもしれない。
:::

:::note info
疑問３：
→EBSスナップショットは一番古いのを消すと、消されたあとの中で一番古いスナップショットの中に、消されたスナップショットに一意な部分が移るという動きをする。
:::



# 差分計算の解説

・スナップショット間の差分を比較するAPI [ebs list-changed-blocks](list-changed-blocks)を使う。
これにより、変化があるブロック数だけがリストとして表示される。

・カウントする。ただし、pagenationがあり、pageがなくなるまでこの処理を繰り返す必要がある。

以下の手順になる。

```
//①snapshot間で差をカウントする。
aws ebs list-changed-blocks --first-snapshot-id snap-XXXX --second-snapshot-id snap-XXXX --profile profile | grep BlockIndex | wc -l

//②末尾を見て、次のブロックにnext-tokenがあるかチェック
aws ebs list-changed-blocks --first-snapshot-id snap-XXXX --second-snapshot-id snap-XXXX --profile profile | tail

//③next-tokenを指定して次のブロックをカウントする。
aws ebs list-changed-blocks --first-snapshot-id snap-XXXX --second-snapshot-id snap-XXXX --next-token nexttoken-id --profile profile | grep BlockIndex | wc -l

//④末尾を見て、次のブロックにnext-tokenがあるかチェック。あれば、③に戻る
aws ebs list-changed-blocks --first-snapshot-id snap-XXXX --second-snapshot-id snap-XXXX --next-token nexttoken-id --profile profile |tail

//⑤カウントを合計すればincliment量が出る。
```

繰り返しなのでshellにしました。
以下のshellで引き数3つ与えれば、差分のブロック数を算出できます。

``` ebs_incliment_count.sh
#!/bin/bash
FIRST_SNAPSHOT_ID=${1:? snapshot-id-1 is not given. Please add this parameter.}
SECOND_SNAPSHOT_ID=${2:? snapshot-id-2 is not given. Please add this parameter.}
AWS_PROFILE=${3:? aws-profile is not given. Please add this parameter.}
EBS_BLOCK_COUNT=$(aws ebs list-changed-blocks --first-snapshot-id ${FIRST_SNAPSHOT_ID} --second-snapshot-id ${SECOND_SNAPSHOT_ID} --profile ${AWS_PROFILE} | grep BlockIndex | wc -l)

FIRST_NEXT_TOKEN=$(aws ebs list-changed-blocks --first-snapshot-id ${FIRST_SNAPSHOT_ID} --second-snapshot-id ${SECOND_SNAPSHOT_ID} --profile ${AWS_PROFILE} --query NextToken --output text)

EBS_INCLIMENT_COUNT=$(aws ebs list-changed-blocks --first-snapshot-id ${FIRST_SNAPSHOT_ID} --second-snapshot-id ${SECOND_SNAPSHOT_ID} --next-token ${FIRST_NEXT_TOKEN} --profile ${AWS_PROFILE} | grep BlockIndex | wc -l)

EBS_BLOCK_COUNT=${EBS_BLOCK_COUNT}+${EBS_INCLIMENT_COUNT}

NEXT_TOKEN=$(aws ebs list-changed-blocks --first-snapshot-id ${FIRST_SNAPSHOT_ID} --second-snapshot-id ${SECOND_SNAPSHOT_ID} --next-token ${FIRST_NEXT_TOKEN} --profile ${AWS_PROFILE} --query NextToken --output text)

while [ -n ${NEXT_TOKEN} ]
do
    EBS_INCLIMENT_COUNT=$(aws ebs list-changed-blocks --first-snapshot-id ${FIRST_SNAPSHOT_ID} --second-snapshot-id ${SECOND_SNAPSHOT_ID} --next-token ${NEXT_TOKEN} --profile ${AWS_PROFILE} | grep BlockIndex | wc -l)

    EBS_BLOCK_COUNT=${EBS_BLOCK_COUNT}+${EBS_INCLIMENT_COUNT}

    NEXT_TOKEN=$(aws ebs list-changed-blocks --first-snapshot-id ${FIRST_SNAPSHOT_ID} --second-snapshot-id ${SECOND_SNAPSHOT_ID} --next-token ${NEXT_TOKEN} --profile ${AWS_PROFILE} --query NextToken --output text)

done

```

実行は、
```
bash -ex ebs_incliment_count.sh 一つ目のsnapshot-id 2つ目のsnapshot-id aws-profile
```
ですね。
差分のあるブロックをカウントできたら、
それに、512KiBをかければ、差分サイズになります。

追記：以下を使って足した結果を出力することもできる
https://kagasu.hatenablog.com/entry/2017/11/13/160045


# EBSについての解説

・ブロックインデックスとは、
基本的に512KiBブロック単位の論理インデックスです。ブロックインデックスは、論理ボリューム内のデータの論理オフセットをブロックサイズで割ることで特定します（データの論理オフセット／524288）。データの論理オフセットは512KiBアライメントである必要があります。
※1

・各ブロックのサイズ
各ブロックのサイズは512KiBである。
※2

・差分スナップショットは動的に変更される。
なので、一番古いスナップショットが削除されても、それによって復旧ができなくなるということはない。
※3

・APIで比較するが、pagenationがある。なので、最後のページまで、確認して比較する必要がある。

# まとめ

・price calcuratorは、最初のバックアッププランを算定する際には、使えるが、
他のプランとの比較や、特定のスナップショットの保持期間を延長した際の金額算出には使えない。

・差分量はAPIを利用して導出できる。一方で、pagenationがあるので、シェルを作成して、
導出するのが良い。


・算出した差分量のユースケースとして、以下が挙げられる。
①特定のスナップショットの保持期間を延長させた際の、金額量を算出する。
例.1か月前と2か月前の差分、2か月前と3か月前の差分量を算出し、平均的な1か月での差分量を導出。それをもとに、特定のスナップショットの保持期間を1か月延長した際のコストを算出する。
②世代数を増やした際にかかる料金の概算を出す。（差分量をいくつか算出して、平均値などを出すことで、一か月増やした場合の推定ができる）






# おまけ：過程にあったトラップたち。

(飛ばしていただいて結構です。）トラップだらけでした。

・毎月のAWSからの請求を見ればわかると思ったが、EBSスナップショット、
とまとめられており、スナップショット種別ごとの、金額の比較ができなかった。

・最初は、差分スナップショットだから、
スナップショット＝差分
と思っていて、スナップショットのブロック数を計算する[API list-snapshot-blocks](https://docs.aws.amazon.com/cli/latest/reference/ebs/list-snapshot-blocks.html)
で、計算していた。
また、このときはpagenationの概念を知らなかったので、list-snapshot-blocksでカウントしても、差が出ていた（本来ならここで出力されるindex数は同じ）

・AWSのコンソールの[Price calculator](https://calculator.aws/#/addService/EBS)で、
計算したら楽勝と思ったが、
世代数指定の計算ができない。（日次、なら日次で毎日撮る結果しか出ず、4日後削除する、
とかが考慮されていない）
実際は、保存期間を設定していると思うので、それを考慮した計算のコンソールが欲しかった。

・[Price calculator](https://calculator.aws/#/addService/EBS)は、
差分ブロックサイズ、を入力させるが、
差分ブロックサイズは、日ごとに変わる値である。
なので、そこに入力すべきはその平均値であるが、
そのような値を求めるには毎日分計算する必要がある。

・pagenationのことを知らず、最後のページまでめくらずに、
比較スコアを出していた。

・色々なEBSボリュームがあって、コンソールから該当のボリュームを検索して見つけ出すのが大変。


# 最後に
何か良い方法や、間違っている点などあればコメントしていただけると幸いです。

# 参考文献
1.block index について
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-accessing-snapshot.html#ebsapi-block-indexes

2.block size について
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-accessing-snapshot.html#ebsapi-block-indexes

3.how inclimental snapshot work
https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSSnapshots.html#how_snapshots_work
図解で説明されていて非常にわかりやすいです。