BPS社内勉強会のmorimorihogeさんのスライド「Webアプリの基礎とさまざまな実行環境を理解する」の第3回です。
- #1: Webのしくみ
- #2: Webサーバの役割とWebサービスの代表的な形態I〜III
- #3: Webサービスの代表的な形態IV(Rails)、ステートレスとステートフル(本記事)
IV. Webサーバ+アプリケーションサーバ
いよいよRailsにつながる話になります。
これは前回のI.とIII.のハイブリッドと言えます。ハイブリッドすなわち「いいとこ取り」で、静的なファイルやHTTPの処理はWebサーバに任せ、プログラム処理が必要なリクエストだけをアプリケーションサーバに送ることでI.とIII.のメリットを同時に得ることができます。
現在最も一般的なRails環境はこれに該当します。Java serverでもこの形式が使われることはありました(Apache + Tomcatなど)が、Railsの登場で一気に普及した感があります。
現在のBPSでは「Nginx + Puma」か「Apache + Puma」が標準のRails実行環境です。Webサーバは以前はUnicornでしたが今はPumaが標準になりました。
メリット
- Webサーバの軽さとアプリケーションサーバの柔軟性を同時に得られる
- アプリケーションの実装を軽減できる: WebサーバでできることはWebサーバに任せる
- レスポンスが速い: III.のアプリケーションサーバ単体構成と比べ、静的ファイルはアプリケーションサーバに問い合わせずすぐに返せるため、一般に高速になります
デメリット
- 設定が増える: 運用するサーバプログラムが2つになるため、2つの設定を管理する必要がある
- 監視ポイントが増える: 障害発生のポイントが増えるため
Webサーバ+アプリケーションサーバの実際のサービス例(専門サービスの場合)
以下はRailsを前提とした話です。
- Heroku: IIIの形式でも公開できますが、Nginxを組み合わせて運用することも可能です
- 他にもいくつか
HerokuはRailsアプリケーションのホスティングサービスの典型です。Heroku独自の設定やルールがある点に注意しましょう。
以前はニフティクラウドC4SAもRailsアプリのホスティングサービスを行っていましたが、現在はサービス終了しています。
使いどころ
- Railsサーバを立てたいけどインフラエンジニアがいないとき
- とにかく(エンジニア人件費的に)安く(リリース準備的に)早く立てたいとき
- Linuxサーバー側のセキュリティを考えずに済ませたいとき
ただし、Rails本体や組み合わせるミドルウェアのセキュリティ問題については当然考慮が必要です。
Webサーバ+アプリケーションサーバの実際のサービス例(自前で立てる場合)
マネージドなサービスではなく自前でサーバーを構築することで、サーバー要件に対する自由度が高くなります。
「普通のWebアプリケーションを」「普通の構成で」「普通の作り方で」実装する場合にはマネージドなサービスを組み合わせることで事足りることが多いのですが、業務アプリケーションなどで高可用性、高性能、特別なライブラリを使いたいなどの事情がある場合には自前でサーバーを構築するという選択肢が結果として簡単になるケースが多いです。
VPSはVirtual Private Serverの略で、「物理ホストは多重化されている」が、VM技術等を利用して「論理ホストとしては完全に管理権限の取れる」サーバーが提供される環境のことです。
BPSでは現在はお客様側からの指定が無い限り、AWS EC2を利用しています。
Webサービスで押さえておくべきその他の要素
こうした要素が加わると考慮すべき点が変わってきます。詳しくは今後の勉強会で。
まとめ
基本的に、どのようなWebサービスでもここで紹介した構成(またはそのサブセット)で構成されています。これらの基本構成を理解しておくことで、より複雑なWebサービス構成の把握に役立ちます。
Web環境構築は現在は主にインフラエンジニアの仕事ではありますが、自分の書いたプログラムがどのような環境で動作しているかを知ることは、トラブルシューティングはもちろん、設計にも役立ちます。
⚓補講: ステートレスとステートフル
複数回の連続した通信を行う場合において「ステートレス」「ステートフル」という二種類の戦略があります。
ステート(state)は日本語では「状態」を意味し、状態を保存するか、という話題です。
ステートレス(stateless)
最大の特徴は「1つ1つの往復通信が独立・完結している」ことです。たとえばリクエストA => リクエストB => リクエストCの順にHTTPでの通信を行ったとすると、AとBとCの通信の間には何のつながりもありません。これを指して「状態がない」すなわちステートレスと呼びます。以下はステートレスなプロトコルの例です。
- IPプロトコル
- HTTP
- memcachedプロトコル
「いやWebサイトはログイン状態をリクエスト間で保持したりするじゃないか」という人はスルドイです。
HTTPにはプロトコル上ステートを維持するための機能はありません。多くのサイトではCookieに保存したセッションIDを使ってログイン状態を維持しますが、あれはHTTPに用意されたCookie機能を使うことでステートレスプロトコル上でステートフルな通信をアプリケーション層で実現しているのです。
ステートフル(stateful)
「複数回行われる通信を1つのまとまりとして扱う」のが特徴です。たとえばA => B => Cの順に通信を行ったとすると、AとBとCの通信に関連や順序・状態をもたせることができます。
例えば「初期化 -> データ転送 -> 終了確認」という3回のメッセージに分けて順番に通信を行うステートフルプロトコルでは「初期化」せずに「データ転送」を行おうとすると「データ転送」の処理に失敗します。
これは、サーバー側で「初期化」したかどうかという状態を持っており、初期化されていないデータ通信は拒否するという状態に応じた処理が行われているわけです。
以下はステートフルなプロトコルの例です。
- TCPプロトコル
- 決済APIなど(「トランザクション開始」「与信」「売上」などを順番に呼び出す必要がある)
ステートレスとステートフルの使い分け
実用上はステートフルプロトコルの方が以前の状態を前提とした通信ができるため、通信上は効率が良くなります。しかし、ステートフルプロトコルは一般に「状態」を保持するためにサーバー側のリソースを消費します。
サーバー側のリソースを消費するということはサーバー1台辺りで処理できるリクエスト数が減るということになるので、たくさんのリクエストを捌くという観点から見ると都合が良くありません。
そのため、不特定多数のリクエストを受け付けるWebで使うHTTPはステートレスプロトコルが選ばれています。
例えば、仮に100万人のユーザーがいるWebサーバへの通信がステートフルだとすると、Webサーバは100万人分のステートを保持し続けないといけなくなり、スケールしなくなってしまいます。ステートレスであれば、サーバは通信が終わったらその通信のことを忘れてもよいので、サーバの処理がずっと軽くなります。
なお、ステートフルプロトコルにおいてステートをどのように管理するのかは、プログラムの設計においても重要なポイントのひとつになります。
参考: TCPの状態遷移図(PDF)
- #1: Webのしくみ
- #2: Webサーバの役割とWebサービスの代表的な形態I〜III
- #3: Webサービスの代表的な形態IV(Rails)、ステートレスとステートフル(本記事)
例えば以下の様な要件がある(または将来的に要求される可能性がある)場合、PaaS型のマネージドサービスよりも自前でLinuxサーバーを管理できる環境の方が作りやすいことがあります。
この辺りの感覚はエンジニアそれぞれによってさじ加減が変わってくる部分ですので、一つの正解があるというよりも、様々な視点でそれぞれ最適な戦略があり、その中でどの戦略がより良いのかを考えていくことになりますね。