#22 性能比較 - PostgreSQL on Rook/EBS

本記事は[PostgreSQL on Kubernetes Advent Calendar 2018](https://qiita.com/advent-calendar/2018/postgresonk8s)の22日目です。昨日は「[EBSでも出来るよ-PostgreSQL on k8s-](https://qiita.com/tzkoba/items/984fb628fced79813d91)」として、EBSを使ったActive-StandbyのDBクラスタを構築してみました。

今日はRookとEBSそれぞれの上に構築したPostgreSQLの性能比較をしてみたいと思います。

# TL;DR

+ Pg-RookとPg-EBSで性能比較をしてみた。
+ 予想ではディスク3本分のIO性能が出るはずのPg-Rookが有利なはずだったが。
+ 結果はPg-EBSの圧勝。gp2のバーストが影響したか。


# 性能比較時の構成
前回、PostgreSQL on Kubernetes(EBS)の構成を作ったことでPostgreSQL on Rookと共存する以下のような検証環境が出来上がりました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/54853/2d6c864e-d385-17bb-0345-2240fb7fbd22.png)

こちらの図には下記の2つのStatefulSetが含まれ、それぞれActive-StandbyのDBクラスタとなっています。

+ **Pg-Rook**：PostgreSQL on Rookの構成、ストレージノードは3台
+ **Pg-EBS**：PostgreSQL on KubernetesでストレージにEBSを使った構成

これら2つのStatefulSetに含まれるPostgreSQLインスタンスに、[#6](https://qiita.com/tzkoba/items/f2d04827ab3e5f0cc440)で紹介した手順でpgbenchによるベンチマークを実行していきます。

## テスト結果の予想
今回の検証ではEC2インスタンスとしては、以下のように処理性能がかなり低いものを使っています。

| ノード | 台数|type | vCPU | Mem | EBS | IOPS |
| :-: | :-:|:-: | :-:| :-:| :-:| :-: |
| DB|1|t2.medium|2|4G|gp2,40GB|120|
| ストレージ|3|t2.medium|2| 4G|gp2,60GB|180|

DBノードはActive-Standbyなので、性能テストという観点では正系1台のみの稼動になります。そして、Pg-EBSから利用するボリュームもgp2：60GBとしているため、ストレージサーバ1台分のIO性能を持っていることになります。

ここまでの構成から予想される性能テストの結果は下記です。

+ ストレージ性能が低いため、そこがボトルネックになる。
+ Pg-EBSではIOPS 180がボトルネックとなり、そこまでで頭打ち。
+ Pg-RookではIOPS 180*3=540 から Cephのオーバーヘッドを引いたIO性能で頭打ち。
+ ストレージ・ノードの台数が3台と少ないが、IO性能は2台分以上は出るはずで今回の構成ではPg-Rook > Pg-EBSとなる。

# ベンチマークの取得
Pg-EBSとPg-Rookのそれぞれでpgbenchによるベンチマークを取得していきます。

## Pg-EBSのベンチマーク結果
まず、以下でPg-EBSのPostgreSQLインスタンスを初期化します。
[#6](https://qiita.com/tzkoba/items/f2d04827ab3e5f0cc440#%E3%83%86%E3%82%B9%E3%83%88%E7%94%A8%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E5%88%9D%E6%9C%9F%E5%8C%96)で書いたように、-s 100を指定することで1000万件のデータを用意しています。

```terminal:Pg-EBS初期化
$ kubectl exec -it pgtools-75fb6bdb95-kcqzc -c pgbench -- /usr/pgsql-10/bin/pgbench -h pg-ebs-sf.default.svc -U bench -i -s 100
```

ベンチマークを取得していきます。

```terminal:pgbenchの実行
$ kubectl exec -it pgtools-75fb6bdb95-kcqzc -c pgbench -- /usr/pgsql-10/bin/pgbench -h pg-ebs-sf.default.svc -U bench -c 2 -T 180 -l --aggregate-interval=60 bench
Password:
starting vacuum...end.
transaction type: <builtin: TPC-B (sort of)>
scaling factor: 100
query mode: simple
number of clients: 2
number of threads: 1
duration: 180 s
number of transactions actually processed: 74560
latency average = 4.828 ms
tps = 414.217027 (including connections establishing)
tps = 414.231408 (excluding connections establishing)
```

pgbenchの引数 **-c 2** がコネクション2本での実行を指していますが、これを変えて合計3回のベンチマークを取得した結果が下記となります。

|コネクション数| tps | レイテンシ(avg) |
|:-:|--: |--:|
|2| 414 | 4.8 ms |
|4| 708 | 5.6 ms|
|8| 1,079 | 7.4 ms |

## Pg-Rookのベンチマーク結果
上記のPg-Rookと同じように初期データを用意し、テストケースも同じく2/4/8コネクションの3ケースでベンチマークを取得した結果が以下のようになりました。

|コネクション数| tps | レイテンシ(avg) |
|:-:|--: |--:|
|2| 186 | 10.7 ms |
|4| 275 | 14.5 ms|
|8| 398 | 20.1 ms |

# 結果を分析してみる
Pg-EBS、Pg-Rookのベンチマークをグラフにしてみると一目瞭然ですが、予想と大きく異なってPg-EBSの性能がいずれのケースでも2倍以上良い結果となりました。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/54853/91ba336e-2e35-b564-63b9-a360f6192b71.png)

何故こうなったのか、DBノードのIOを確認してみます。

まず、DBノードでどのデバイスがPg-EBSとPg-Rookでマウントされているものなのかを確認します。全てのファイルシステムを確認できるようにsudoを付けてdf -hを実行しましょう。

```terminal:DBノードのマウント状況
$ sudo df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/xvda1       40G  7.4G   33G  19% /
（一部略）
/dev/xvdba       59G  5.0G   51G   9% /var/lib/kubelet/plugins/kubernetes.io/aws-ebs/mounts/aws/us-east-2a/vol-05264b66cd595cf9b
/dev/xvdbs       59G  2.6G   54G   5% /var/lib/kubelet/plugins/kubernetes.io/aws-ebs/mounts/aws/us-east-2a/vol-07f10f99376e73895
/dev/rbd0       5.0G  385M  4.7G   8% /var/lib/kubelet/plugins/ceph.rook.io/rook-ceph-system/mounts/pvc-0f0dcad3-06b9-11e9-9405-02ef0b761764
/dev/rbd1        10G  2.8G  7.3G  28% /var/lib/kubelet/plugins/ceph.rook.io/rook-ceph-system/mounts/pvc-0f0aa023-06b9-11e9-9405-02ef0b761764
```

この情報とAWSのEBS情報、KubernetesのPersistent Volume情報をあわせると以下の状況であることが分かります。

+ /dev/xvdbsがPg-EBSのデータ領域、/dev/xvdbaがアーカイブログ領域
+ /dev/rbd1がPg-Rookのデータ領域、/dev/rdb0がアーカイブログ領域

そして、テスト時のそれぞれのデバイスのiostatを確認します。

### Pg-EBSのiostat結果
下記がコネクション8本の時のPg-EBSのベンチマーク時のiostatになります。

```
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          34.85    0.00   35.04   12.31    1.33   16.48

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    9.33    0.00   565.33     0.00   121.14     0.01    1.46    1.46    0.00   0.25   0.23
xvdba             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
xvdbs             0.00    10.67 1434.67 2230.00 14665.33 21892.00    19.95     3.96    1.10    0.84    1.26   0.18  67.17
rbd1              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
rbd0              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
```

データディレクトリ（xvdbs）にRead(IOPS)：1,434、Write(IOPS)：2,230と高い負荷が掛かっています。しかし、%util（ディスク利用率）は67%とまだ余裕があり、%iowaitも12%と落ち着いています。

### Pg-Rookのiostat結果
一方でPg-Rookの場合、コネクション2本の時から%utilが90%近くまで行っており、8本の時は以下のようになっています。

```
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          14.02    0.00   18.32   36.64    1.31   29.72

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   78.33    0.67  1969.33     4.67    49.97     0.54    6.78    6.83    1.00   0.09   0.70
xvdba             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
xvdbs             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
rbd1              1.00     0.00  444.00  626.67  6952.00  9456.00    30.65    16.55   14.72    2.65   23.27   0.90  96.70
rbd0              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
```

こちらはデータディレクトリがrbd1となりますが、Read(IOPS)：444、Write(IOPS)：626とPg-EBSに比べれば負荷が低いにも関わらず、%util（ディスク利用率）は96%を超えています。当然、%iowaitも上がり36%と完全にIOネックを示しています。

### IOPS制限はどうなった？
テスト結果の予想で示したようにIOPSは180で制限が掛かるはずです。しかし、結果を見ると以下のようになっています。

+ Pg-EBSでは全くIOPS制限が効いていない。想定の数十倍のIO性能が出ている。
+ Pg-Rookでは書込みで600-800IOPSが出ているが、そこで律速されているように見え、IOPS制限が効いている可能性が高い。

これはもう少し検証をしないと正確には言えないのですが、インスタンスタイプのt2.mediumやEBSのgp2で**バースト**という考え方があり、今回のベンチマークはその範囲内で取得されたと考えられそうです。
しかし、Pg-Rookの構成でもバーストして良いはずなのにそうなっていないということは何か環境差異があるか、ネットワーク帯域など他の要因が隠れている可能性もあります。

予想とは大きく異なる結果となってしまっているため、以下のような方法で追加検証が必要そうです。

+ バーストのないio1などのEBSに変更する。
+ gp2でもベースライン性能になるまで長時間のベンチマークを取得する。
+ ローカルディスクのPostgreSQLインスタンスで計測をしてみる。


#まとめ
Amazon EC2ではバースト・ベースラインという考え方があり、性能検証時はそれらを考慮する必要があります。今回のベンチマークではその辺りの問題にはまってしまいました。

時間があれば、ちょっと視点を変えて追加検証をしてみたいと思います。

よろしくお願いします。