AWS(EC2/EBS)


##Amazon Elastic Computing Cloud(EC2)
端的に言うと、AWSで数分で起動可能で、１時間または１秒単位での従量課金となる仮想サーバ。従量課金されるのでは起動中(Runningステータス)の時のみ。サーバの追加、削除、スペック変更も柔軟にできる。インスタンスファミリー(例：c5d.xlargeのc)によってCPU、メモリ、ストレージ、ネットワークそれぞれに最適化されたリソースとなる。

検証しないとあまり分からなそうな仕様周りをまとめ。

#バースト可能パフォーマンスインスタンス
CPUクレジットを消費することにより、ベースラインのCPUパフォーマンスに比べて、負荷に応じて高いレベルまでCPU性能がバースト可能(最大100%利用まで)なインスタンス。t2,t3インスタンスが該当する。t2,t3インスタンスは、クレジットと言うものは持っている。

1個の CPU クレジットは、1台の vCPU を使用率 100% で 1分間実行すること。

・初期クレジット値はインスタンスタイプによって異なる
・クレジットは時間とともに１時間ごとに回復。回復量はインスタンスタイプによって異なる
・クレジットの最大蓄積可能数はインスタンスタイプによって異なる
・クレジットはRebootしても不変だが、Stop&Startすると各インスタンスタイプの初期値に戻るので注意.

`詳しくはこちら`
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/burstable-credits-baseline-concepts.html

###プレイスメントグループ

同じプレイスメントグループ内で複数台のEC2を起動できる。利用しない状態よりもEC2間の通信が多い場合にネットワークレイテンシの低下、ネットワークスループットの向上が見込める。ただし、いずれの戦略も筐体にリソースが空いていない可能性もあり、その場合は起動できない。暫く待って再度Tryする必要がある。

- クラスタープレイスメントグループ
    - 単一AZ構成
    - 同じ物理ラックに同一グループ内の全てのEC2を収納
    - 筐体障害に対しては全滅する
    - 性能向上的には一番高い効果を持つ
    - 同一リージョンにある EC2 間のシングルフロー通信 (ポイントツーポイント。1:1の通信) は最大 10Gbps出せる
- パーティションプレイスメントグループ
    - パーティションと呼ばれるグループごとに物理ラックが異なる。同一パーティションであれば物理ラックを共有するが、違うパーティションであれば物理ラックを共有しない
    - 筐体障害に対しては対象パーティションのみ全滅
    - 同じリージョン内の複数のアベイラビリティーゾーンにパーティションを持つことができる
    - クラスターパーティショングループよりも性能は一部軽減されるが、耐障害性は強い
- スプレッドプレイスメントグループ
    - 全てのEC2において物理ラックが異なる。
    - 筐体障害に対しては対象機に乗っているEC2のみ障害
    - 同じリージョン内の複数のアベイラビリティーゾーンに各EC2が乗る筐体を構成できる
    - パーティショングループよりも性能は軽減されるが、耐障害性は最も強い
    
※逆にプレイスメントグループを使わない場合は、同じラックに自分の複数のEC2が乗る可能性もあるし、乗らない可能性もある。


#EBS(Elastic Block Storage)

AWSが提供するEC2インスタンスにファイルシステムとしてマウントすることができるブロックストレージ。
同一AZ内で自動的にレプリケートされることにより可用性を担保、またインスタンスストアのように揮発性のストレージではないためデータの永続性を保証できる。

`詳しくはこちら`
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ebs-volumes.html

大きく分けて現行世代では下記の4種類のストレージタイプが存在。
`参考`
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ebs-volume-types.html

##汎用 SSD (gp2) 
- レイテンシーは 1 桁台のミリ秒
- 1GBあたり3IOPSのサイズ基準のIOPS性能。ただし、33.3GB以下の場合は最小100IOPSは保証。
- ボリュームサイズが1TB以下の場合は、最大3000IOPSまでバースト可能
- ボリュームサイズが1TBより大きな場合はバーストという概念はない。
- ボリュームあたりの最大IOPSは16000IOPS
- スループットの制限は、ボリュームサイズに応じて 128 MiB/秒〜250 MiB/秒
- ボリュームあたりの最大サイズは16TiB

###I/O クレジット及びバーストパフォーマンスに関して
- gp2は、1TB以下の場合はバーストすることにより、IOPSを最大3000IOPSまで確保可能だがバーストできる時間には制限がある。
- 端的に言うと、ベースラインが3IOPSxボリュームサイズは保証され、後はIOクレジットに溜まっている分だけIOPSのバーストが3000IOPSまで可能。
- 初期のIOクレジットは、OS起動時に付与され、5400000IOPSであり、これがIOクレジットの上限。また１秒あたりにIOPSxボリュームサイズ分IOクレジットが補充される仕組み。(逆に言うとOSをstop→startすればフルまで回復する)

![Screen Shot 2020-02-18 at 12.51.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245585/96cb5b62-431b-ab30-3482-ec2304345407.png)

もしも、IOクレジットが常に枯渇している場合は、アプリケーションの属性的にIOを非常に多く使う必要性がある（高負荷なランダムIOなど）のため後述のIO保証型のストレージに変えるか、ボリュームサイズを増やす、またはEBSを複数組み合わせてSW RAIDによりストライピング構成を組むべきだろう。

###スループットパフォーマンスに関して
gp2 ボリュームのスループットは、最大値250 MiB/秒のスループット制限までは以下の計算式で発揮される。

```ruby:
Throughput in MiB/s = ((Volume size in GiB) × (IOPS per GiB) × (I/O size in KiB))
```


###fioコマンドを使ってディスク性能の計測実施

- そもそもストレージの性能とは・・・評価するにあたり以下のような項目がある。

<strong>`(1)IOPS(Input Output Per Second)`</strong>
--->秒間のI/O数
--->IOPS=アームの動く時間+回転待ち時間+読み書き時間
--->IO回数の多さが重要なランダムIOの性能を評価する際はこの値を重要指標にする

<strong>`(2)スループット`</strong>
--->単位時間あたりのI/O量
--->スループットの高さが重要なシーケンシャルIOの性能を評価する際はこの値を重要指標にする

<strong>`(3)レイテンシ`</strong>
--->I/Oリクエストを受信した時刻から I/O がサーバーに戻される時刻までの時間


- fioコマンド実行例

```ruby:EBSに対してIOPSやスループットを計測
###シーケンシャルIO(読み込み)
fio -filename=/tmp/test2g -directory=/tmp -direct=1 -rw=read  -bs=4k -size=5G -numjobs=10 -runtime=10 -group_reporting -name=file1

###シーケンシャルIO(書き込み)
fio -filename=/tmp/test2g -directory=/tmp -direct=1 -rw=write -bs=4k -size=5G -numjobs=10 -runtime=10 -group_reporting -name=file1

###ランダムIO(読み込み)
fio -filename=/tmp/test2g -directory=/tmp  -direct=1 -rw=randread  -bs=4k -size=5G -numjobs=10 -runtime=10 -group_reporting -name=file1

###ランダムIO(書き込み)
fio -filename=/tmp/test2g -directory=/tmp  -direct=1 -rw=randwrite -bs=4k -size=5G -numjobs=10 -runtime=10 -group_reporting -name=file1
```

- fioコマンドオプション
    - filename: 掛るワークロードで利用するファイル
    - direct: バッファを経由せずにディスクに直接IOを行う
    - rw: ベンチマークの内容(read:シーケンシャルread/write:シーケンシャルwrite/randread:ランダムread/randwrite:ランダムwrite)
    - bs: ブロックサイズの指定
    - directory: IOを行うディレクトリ
    - size: 当測定で読み書きするファイルサイズ。
    - numjobs: 同じワークロードを実行するプロセス数
    - runtime：実行最大時間(この時間ずっと同様のワークロードが継続する)
    - name: ジョブ名

- 検証前提

```ruby:gp2ディスク100GBで実施
[root@ip-10-10-3-6 ~]# lsblk -i
NAME    MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
xvda    202:0    0  100G  0 disk 
`-xvda1 202:1    0  100G  0 part /
```

また前提として、EBSへのディスクアクセス専用の帯域を確保したいため`EBS最適化`を有効化すること。
有効化しないと帯域がNWと共有されるため正確なディスク性能が測定できない。

- 検証結果

 * 前提: `dd if=/dev/zero of=/tmp/test2g bs=128M count=40`

####IOPSが最大3000で頭打ちになるか(バースト使い切っていない状態)
上記どのfioパターンで実施しても以下の結果に近い
fio -filename=/tmp/test2g -directory=/tmp -direct=1 -rw=read  -bs=4k -size=2G -numjobs=10 -runtime=10 -group_reporting -name=file1
--->
`抜粋)read : io=131740KB, bw=13170KB/s, iops=3292, runt= 10003msec`
--->
IOPSが約3000で頭打ち。一方でスループットは最大出るはずの128MBまで出ていない。恐らくfioコマンドで指定したブロックサイズが4kのため4kx3000IOPS=約12000KB/s近くまでしかスループットが理論上出ないためか。

####ブロックサイズ指定を大きくしてスループットをあげる(バースト使い切っていない状態)
上記どのfioパターンで実施しても以下の結果に近い
fio -filename=/tmp/test2g -directory=/tmp -direct=1 -rw=read  -bs=64k -size=2G -numjobs=10 -runtime=10 -group_reporting -name=file1
--->
`抜粋)read : io=1409.6MB, bw=144253KB/s, iops=2253, runt= 10006msec`
--->
ブロックサイズを4k→64Kへと16倍してみた。上記では13170KB/sだったので理論的には16倍以上にスループットもなる=上限128Mでるはずと言う想定で実施したところ128MBの上限値が出たのでOK

####バーストクレジット消費済み状態のIOPSがボリュームサイズx3IOPSになるか否か
上記どのfioパターンで実施しても以下の結果に近い
fio -filename=/tmp/test2g -directory=/tmp -direct=1 -rw=read  -bs=4k -size=2G -numjobs=10 -runtime=10 -group_reporting -name=file1
--->
`抜粋)read : io=14148KB, bw=1410.2KB/s, iops=352, runt= 10033msec`
--->
IOPSが約300で頭打ち。それに従ってスループットも落ち目(4kx300IOPS=約1200kb/s)になる。

####1TiB以上のボリュームにした時のIOPSを検証

```ruby:1.5TBの/dataファイルシステム
[root@ip-10-10-3-6 ~]# lsblk -i
NAME          MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
nvme0n1       259:0    0  100G  0 disk 
|-nvme0n1p1   259:1    0  100G  0 part /
`-nvme0n1p128 259:2    0    1M  0 part 
nvme1n1       259:3    0  1.5T  0 disk 
`-nvme1n1p1   259:5    0  1.5T  0 part /data
```

fio -filename=/data/test2g -directory=/tmp -direct=1 -rw=randread  -bs=4k -size=2G -numjobs=10 -runtime=10 -group_reporting -name=file1
--->
`抜粋)read : io=197980KB, bw=19792KB/s, iops=4948, runt= 10003msec`
--->
IOPSが3IOPSx1500GB=約4500IOPS出ているので想定内。

####Linux上でのソフトウェアRAIDによるストライピング構成
100GBのEBSを2つインスタンスに付与して、RAID0を構成
(https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/raid-config.html)

```:RAID0
#RAID0デバイス構成(/dev/nvme1n1,/dev/nvme2n1がそれぞれEBSボリューム)
mdadm --create --verbose /dev/md0 --level=0 --name=MY_RAID --raid-devices=2 /dev/nvme1n1 /dev/nvme2n1

#RAIDデバイス詳細情報
sudo mdadm --detail /dev/md0

#XFSファイルシステム作成
sudo mkfs.xfs -L MY_RAID /dev/md0

#mdadm設定ファイル作成(次回OS起動時に自動でマウントするために)
sudo mdadm --detail --scan | sudo tee -a /etc/mdadm.conf

#新しい RAID 設定のブロックデバイスモジュールを適切に事前ロードする新しいラムディスクイメージを作成
sudo dracut -H -f /boot/initramfs-$(uname -r).img $(uname -r)

#マウントポイント作成
mkdir -p /mnt/raid

#fstabに追記
echo "LABEL=MY_RAID       /mnt/raid   xfs    defaults,nofail        0       0" >> /etc/fstab
```

fio -filename=/mnt/raid/test2g  -direct=1 -rw=read  -bs=4k -size=2G -numjobs=10 -runtime=10 -group_reporting -name=file1
--->
`抜粋)read : io=261184KB, bw=26111KB/s, iops=6527, runt= 10003msec`
--->
3000IOPSまでバーストしているEBSx2個のストライピングにより何と6000IOPSを出せるようになっている。gp2でIOPSを稼ぐことに有用。
使い処としては、
⑴バーストを活用して1TB以下のEBSボリュームを複数組み合わせてRAIDさせることで本来的に出せないはずのIOPSを確保する。あえてEBSを分けることでバーストによる最大3000IOPSxEBSボリューム数のIOPSをバーストバランスが許す限り確保できる。1TB以上のEBSボリュームではバーストが発動しないのでRAID0してもボリューム上限の16000IOPSを超えない場合はわざわざEBSボリュームを2つに分ける意味はない。
(2)ボリューム上限の16000IOPSを超えたい場合

参考
https://blog.ryukiy.net/2015/06/12/premium-storage-part2/
https://www.na3.jp/entry/20120727/p1


####スループットの上限に挑戦
EBSのスループット自体よりも、EC2インスタンス自体のディスクスループット上限が小さい場合、どうなるか検証。

(1) EBS200GBスループット上限256MB/s > EC2インスタンス(c4.large)上限64MiBの場合
 
`fio -directory=/data2 -direct=1 -rw=read  -size=5G -numjobs=1 -runtime=10 -group_reporting -name=file1 -bs=1024k`

```ruby:fio最中のiostat
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
xvda              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
xvdc              0.00     0.00  476.67    0.00 61013.33     0.00   256.00     4.55    9.55    9.55    0.00   2.10  99.87
xvdb              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
```
スループット約64MB/sで頭打ちのため、EC2 インスタンス 自体の上限値に抵触している。IOPS的に3000までバースト可能だがスループットが既に限界のためこれ以上IOPSは増えていない。IOサイズは、fioで、1024kを指定したが実態128kb程度になっている。avgrq-sz(=1ioで確保するセクタ数)が256であることから、512b x 256=128kib


(2) EBS200GBスループット上限256MiB/s < EC2インスタンス(c4.8xlarge)上限500MiBの場合

`fio -directory=/data2 -direct=1 -rw=read  -size=5G -numjobs=1 -runtime=10 -group_reporting -name=file1 -bs=1024k`

```ruby:fio最中のiostat
Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
xvda              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
xvdc              0.00     0.00 2040.00    0.00 261120.00     0.00   256.00     5.16    2.53    2.53    0.00   0.49  99.47
xvdb              0.00     0.00    0.00    0.00     0.00     0.00     0.00     0.00    0.00    0.00    0.00   0.00   0.00
```
スループット約250MB/sで頭打ちのため、EBS 自体の上限値に抵触している。IOPS的に3000までバースト可能だがスループットが既に限界のためこれ以上IOPSは増えていない。IOサイズは、fioで、1024kを指定したが実態128kb程度になっている。avgrq-sz(=1ioで確保するセクタ数)が256であることから、512b x 256=128kib

===>
結論、少なくともEBSで出したいディスク性能（IOPS・スループット）があるならば、それを満たすインスタンスタイプを選択する必要性がある

`参考` インスタンスタイプごとのEBSスループット
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ebs-optimized.html


#IO1ボリュームタイプでのIO保証
実験がてら8000IOPSのIO1タイプのEBSで試した。IOPS:サイズは最大でも50:1の割合以下でなければならないみたいなのでサイズは200GBにする。
fio -filename=/tmp/test2g -directory=/tmp -direct=1 -rw=read  -bs=4k -size=5G -numjobs=10 -runtime=10 -group_reporting -name=file1
--->
`抜粋)read : io=260000KB, bw=25990KB/s, iops=6497, runt= 10004msec`
--->
設定通り8000IOPSが出ていた。gp2では200GBの場合、バーストして最大3000IOPSまでしか出せないが明らかに改善している。それに従いスループットももちろんだが上がっている。