#23 PostgreSQL on k8sで性能比較(もうちっとだけ続くんじゃ)

本記事は[PostgreSQL on Kubernetes Advent Calendar 2018](https://qiita.com/advent-calendar/2018/postgresonk8s)の23日目です。昨日は「[性能比較 - PostgreSQL on Rook/EBS](https://qiita.com/tzkoba/items/d214fe3bc8499544262c)」として、Rook/CephによるPostgreSQL構成とEBSを使った構成の性能比較をしてみました。

想定とは異なり、EBS1本を使ったPostgreSQLの性能が良く、IOPSが180で上限のところも統計値上で超えた値を示していたので、今日はその辺りをもう少し調べてみようと思います。

# TL;DR

+ EBSのボリュームタイプをio1に変えて、性能変動の要素を排除。
+ しかし、そもそもCephは書込み量が多く、3ノード構成では単一EBSに勝てていなかった。
+ 12ノード（！）までストレージを増やしたら、性能は上回ったよ。

# gp2（SSD）のバースト性能について
まず、gp2のバースト時の性能を確認しておきます。
[こちらの記事](https://dev.classmethod.jp/cloud/aws/ebs-gp2-burstbalance/)によると、gp2のIOPS（ベースライン）を100に設定していても、2,000秒（約33分）は最大3,000IOPSでのIOが可能なようです。

前回のiostatを見直してみると、確かにRead・Writeともに1,000-2,000を超えているのに%utilは60%程度と余裕があり、最大**3,000IOPS**ということの納得性はあります。

```terminal: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
```

一方でストレージノードのIOPSがバーストしない原因は現時点で分かっていません。この後も見ていきますが、ストレージ以外のボトルネックがあるから、と考えるのが妥当なように見えます。


# EBSをio1に変更し、もう一度ベンチマーク

## Pg-EBS（バーストなし）のベンチマーク結果
[pg-ebs-storageclass.yaml](https://github.com/tzkoba/postgres-on-k8s/blob/master/postgres-install/pg-ebs/pg-ebs-storageclass.yaml)のtypeをgp2からio1に変更して、Pg-EBSを再作成します。この際、IOPSが100になるよう、必要に応じてAWSのクラウドコンソールから変更を行います。

io1（IOPS:100）で作成したPg-EBSのベンチマーク結果は以下となりました。
※8コネクションではベンチが詰まるため、ケースはコネクション1/2/4の3つにしています。

|コネクション数| tps | レイテンシ(avg) |
|:-:|--: |--:|
|1| 42 | 23.8 ms |
|2| 61 | 33.0 ms|
|4| 58 | 68.7 ms |

やはり100IOPS固定のストレージですと、性能が顕著に下がりました。
コネクション2本でデータボリューム（xvdcl）の%utilが100%近くに行っているため、これ以上は増やしても性能が落ちてしまいます。

```terminal:Pg-EBSのコネクション2本時のiostat
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           2.20    0.00    1.53   47.97    0.17   48.14

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
xvdbr             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
xvdcl             0.00     1.00    0.00  103.33     0.00   832.00    16.10   100.16  700.96    0.00  700.96   9.68 100.00
```

このとき、WriteのIOPSが100を超えており、想定どおりにここで律速されていることがわかります。

## Pg-Rook（バーストなし）のベンチマーク結果
こちらは前回のgp2利用でもバーストがなかったと想定されるPg-Rookですが、念のため、io1にボリュームタイプを変更してベンチマークを取り直します。

こちらは前回のgp2のベンチマークとも比較しておきましょう。

|コネクション数| tps | レイテンシ(avg) | *gp2でのtps* |
|:-:|--: |--:| --: |
|1| 46 | 21.5 ms | - |
|2| 43 | 46.0 ms| *186* |
|4| 49 | 81.7 ms | *275* |

ベンチマーク結果が顕著に劣化しました。今回はIOPSをgp2の180からio1の100に下げているのですが、その幅以上にTPSが落ちていることから、Pg-Rookの構成でもgp2のバーストが効いていたと思われます。

iostatを見ると、コネクション2本で既に限界が来ています。

```terminal:Pg-Rookコネクション2本時のiostat
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           3.32    0.00    3.14   46.07    0.52   46.95

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
rbd1              0.00     0.00    0.00    1.67     0.00  1706.67  2048.00     0.06 1097.20    0.00 1097.20   6.80   1.13
rbd0              0.00     0.00    3.00   86.33    42.67  1856.00    42.51     1.00   12.78    1.67   13.17  11.00  98.27
```

IOPSはやはり100前後で%utilが100%近くに達し、ノード3台分（100*3=300 IOPS）には大きく及びません。

### 根本的な違いは何か？
ようやく条件がそろったので、iostatの結果をつぶさに見てみます。
以下でコネクション2本の際のPg-RookとPg-EBSのデータディレクトリのiostatを比較しています。

```shell:iostat比較
# Pg-EBSの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
xvdcl             0.00     1.00    0.00  103.33     0.00   832.00    16.10   100.16  700.96    0.00  700.96   9.68 100.00
# Pg-Rookのiostat
rbd0              0.00     0.00    3.00   86.33    42.67  1856.00    42.51     1.00   12.78    1.67   13.17  11.00  98.27
```

大きく違うのは以下の点で、Pg-Rookの性能を下げてしまっているのはIOPSネックではなく、Cephでレプリカを行うことによる**書込み量の増加**、そしてそれに伴う帯域等のボトルネック発生が予想されます。

+ Pg-RookのwkB/sとavgrq-szがEBSのPg-EBSの2倍以上。つまり、同じIOPSでも1IOあたりのサイズがPg-Rookのほうが大きく、帯域ネックとなる可能性がある。
+ Pg-EBSのavgqu-szとw_awaitが非常に高い。一方でsvctmは同じぐらいなので、書込み性能というより書込み待ち時間はEBS単体のほうが大きい。

※IOPSの見方は[こちら](https://yohei-a.hatenablog.jp/entry/20111203/1322909311)が非常にわかりやすいです。

## （おまけ）Rook/Cephのノード数を増やしてベンチマーク
予想を大きく裏切り、Rook/Cephによる分散ストレージ構成が単一ディスク構成に負けるのは非常に残念な結果です。しかし、分散ストレージであれば台数を増やせば性能も（リニアではないにせよ）伸びるはず。**ならば、増やしましょう！**

+ **Rook/CephでOSDを配置するストレージノードを3台から12台に増やす。**

DBノード2台、ストレージノード12台の結果を含めたベンチマークの比較はこちら。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/54853/17498885-edcb-c0db-8966-c630a299cfaa.png)

12台もストレージノードに使ってそもそもコスト面の問題もありますが、処理性能はPg-EBSを上回りました。WriteのIOPSも**250-300**まで伸びるようになっていました。

# まとめ
昨日に続き、Rook/CephとEBSにのせたPostgreSQLインスタンスの性能比較を深堀りしてみました。Pg-Rookという観点で見ると、まだまだ残念な結果ですが、インスタンスタイプも変えてNW帯域を変えるなどで性能が伸びる余地はありそうです。

性能については当然データベースで最も気になる要素になりますので、今後もいろいろと検証をしていきたいと思います。

よろしくお願いします。