クラウドネイティブ時代のWebサービスのキャパシティ管理について、小売店を例に説明する

[Ateam Group Manager ＆ Specialist Advent Calendar 2020](https://qiita.com/advent-calendar/2020/ateam) の19日目は、株式会社エイチーム EC事業本部のリードエンジニア @ihsiek が担当します。

## はじめに

クラウドネイティブな時代になって、サーバレスやコンテナでの運用が一般的になり、キャパシティ管理の考え方も変化しています。

**「サーバレスになるとキャパシティって管理しなくて良いよね」** という話もたまに耳にしますが、実はこれ**「サーバレスになると(インフラで)キャパシティって管理しなくて良いよね」** っていう意味なんですよね。
キャパシティ管理の責任がインフラレイヤーからアーキテクチャやアプリケーションのレイヤーに移っているので、この発言にはちょっとした怖さがあります。

兎にも角にもキャパシティの概念をイメージできないことには対策が取れませんし、上記の内容もいまいちピンとこないと思うので、多くの人が利用したことがあるであろう小売店(スーパーマーケット)を例に説明してみます。


## 考慮する要素

スーパーマーケットをそのままWebサービスに置き換えると無理があるので、私が思う主要な要素に絞って考えることにします。

* 店舗
広さに応じて収容できる客の人数や設置できるレジの数、陳列できる商品数などが制限される。

* レジ
来店客が購入する商品を決済する役割。
不足するとレジ待ちの行列が長くなり、商品を購入するという来店客の目的を達成するのに時間がかかる。

* 補充
バックヤードから在庫を陳列する役割。
不足すると在庫が切れて、来店客が欲しいタイミングで商品を手に取れなくなる。


実際のスーパーマーケットの要素としては明らかに不足していますが、キャパシティを考えるだけなら上記で十分だと思います。

ここからは早速、各要素とWebサービスとの対応についてみていきます。

## スーパーマーケットとWebサービス

### 店舗

店舗の広さ以上には客を呼び込むことができませんし、レジも設置できず、商品も陳列できません。
商いの上限を決めてしまう要素です。
Webサービスに置き換えるとアプリケーションが動作するサーバのスペックに該当するかと思います。

現実世界のスーパーマーケットでは、一度建物を作ると **延べ床面積を広げることは難しく** なります。
これはオンプレ運用で、 **購入している物理サーバ以上のスペックを用意することが難しい** のと似ています。
ただし、昨今AWSやGCPなどPaaSの台頭で、クラウドネイティブな環境にはこの制約がほとんどなくなっています。(アカウントごとのリソース上限などがあるため、無制限ではありませんが…)

オンプレ運用ではこの部分がキャパシティの上限を決めてしまっていましたが、クラウドネイティブな環境ではあまり考えなくてよくなりました。

### レジ

スーパーマーケットの客のほとんどは、商品を購入する目的で来店しているハズです。
つまり、商品を購入できないと来店の目的を達成できないし、レジで待たされることをストレスに感じる人も少なくないのではないでしょうか。
これはWebサービスに置き換えると動的コンテンツを生成するPumaやTomcatなどのアプリケーションサーバソフトウェアに該当すると思います。

商用のWebサービスを運用している場合、ユーザーが目的を達成するタイミングで動的な処理が必要になることが多いと思います。
ECサイトであれば在庫の確保や決済で動的な処理が必要になりますし、シミュレーションサイトであれば複雑な計算をするためにバックエンド側で処理させることがあります。
サーバレス化やコンテナ化でキャパシティを考えなくてよいと言われているのは、主にこの部分です。

来店客に応じてレジが柔軟に増減するのであれば、確かにキャパシティを考えなくていいのですが、 **例えばアプリケーションサーバにPumaのコンテナを用いる場合、worker数がCPUコア数×1.5倍が適正値であることを考慮してコンテナを定義しないとパフォーマンスを引き出せないことになります。**

CPUコアが2つ割り当てられているのに、Pumaのworker数に1しか割り当てていないとするとCPUコアが1つ余ってしまいますし、worker数を4割り当てたとするとCPUコア1つあたり2workerを受け持つことになってしまいます。

スーパーマーケットの例に戻って、レジ1列に2機のレジスターが設置されているのにレジ係が1名しかいなくて捌けていない状態や、レジ係が4名いて持て余している状態をイメージすると理解しやすいのではないでしょうか。

### 補充

来店客は購入したい商品を見比べたり、手に取ったりして、購入前の検討をします。
欲しい商品が陳列されていないと機会損失になりますし、タイムセールなどで客が殺到すると補充が追いつかなくなります。
これはWebサービスに置き換えると…、とその前に補充には商品の性質によって2種類あると思います。
1つは同一商品によって性質が大きく変動しない既製品(グループA)、もう1つは野菜・肉・魚など性質が一定でない商品(グループB)です。
グループAは商品サンプルなどのダミーを用意することができ、グループBはダミーで代用することが困難です。

改めてWebサービスに置き換えると、グループAは静的コンテンツを配信するNginxやApache HTTPサーバなどのWebサーバソフトウェアに、グループBはデータベースになると思います。

#### 補充(グループA)

先の例でも触れましたが、グループAは実際の商品を並べなくても来店客を捌くことができます。
これは、Webサーバソフトウェアの代用としてCDNやミドルウェアのキャッシュが使えることに似ています。
本来はキャパシティ管理に大きく影響する要素ですが、キャッシュ技術の向上やCDNサービスにアウトソースすることでWebサーバソフトウェアのレイヤーでキャパシティを考慮する比重はかなり下がっていると思います。

#### 補充(グループB)

一方、グループBは来店客が目利きをした商品をレジに持っていくのを考えるとダミーで対応するのが難しいです。
データベースにもキャッシュ機構は存在するものの、Create/Update/Deleteを考慮するとキャッシュだけではすべての流入を捌くことができないのと似てますよね。

加えて、AWS Lambdaを一般的なRDBMSと組み合わせた場合、相性が悪いことが知られています。
これは、コネクション確立のオーバーヘッドが大きいことや、RDBMSの最大同時接続数に到達しやすいことが理由で、データベースのキャパシティが飽和する事象につながりかねません。

そのような背景からAWS LambdaやCloud Functionsなどのサーバレスアーキテクチャでは、NoSQLを用いることが一般的です。また、AWSに限れば、Amazon RDS Proxyを用いることでRDBMS製品であるAmazon RDSであっても前述の問題を回避できます。
知っていれば当然ですが、アーキテクチャを設計する際にデータベースのキャパシティを考慮しておくことはやはり大事です。

## まとめ

スーパーマーケットはWebサービスと同様に、来客(流入)に対してサービスを提供しますが、補充が適切に行えて、レジが十分に機能するからこそストレスなく買い物ができるようになっています。

キャパシティ管理の責任がアプリケーションやアーキテクチャのレイヤーに移ったことを意識して開発すると、よりユーザー体験のよいサービスになるのではないでしょうか。

## おわりに

私は会社でスペシャリストの役割をもらっていますが、特定の技術にスペシャリティを発揮するだけでなく、周囲のメンバーに自分の知見を伝えたり、エンジニアではない人たちに技術をわかりやすく伝えることもスペシャリストとしての大事な役割だと考えています。

普段から身近な事象に例えて技術を説明することが多いので、そのまま記事にしたら面白いかもと思い、寄稿してみましたがいかがでしたでしょうか。

[Ateam Group Manager ＆ Specialist Advent Calendar 2020](https://qiita.com/advent-calendar/2020/ateam) の20日目は、株式会社エイチームライフスタイルの @matsuken3 さんがお送りします。
きっといいこと書いてくれるはず！！期待してます💖