Webアプリの基礎とさまざまな実行環境を理解する#3(社内勉強会)

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アプリケーションを」「普通の構成で」「普通の作り方で」実装する場合にはマネージドなサービスを組み合わせることで事足りることが多いのですが、業務アプリケーションなどで高可用性、高性能、特別なライブラリを使いたいなどの事情がある場合には自前でサーバーを構築するという選択肢が結果として簡単になるケースが多いです。 morimorihoge注) 例えば以下の様な要件がある(または将来的に要求される可能性がある)場合、PaaS型のマネージドサービスよりも自前でLinuxサーバーを管理できる環境の方が作りやすいことがあります。 特定のソフトウェアを用意して使いたい場合 ImageMagickでの画像変換やffmpegでの動画変換などで新し目の機能・データフォーマットに対応したい場合、PaaSで用意されたものではかゆいところに手が届かないため、自前でコンパイルして使いたいということがあります プロプライエタリなソフトウェアやライブラリを利用したい場合、任意のバイナリを置けないPaaSでは利用できないことがあります 長時間のバッチ処理や大きな一時データを必要とする処理がある場合 PaaS型サービスではサービス側でプログラムの実行時間に制限が付けられているケースがあり、超時間かかるバッチ処理などを知らずに作るとタイムアウトして中断されてしまうことがあります。バッチを小分けにしてQueueに入れて実行するなどで対策は可能ですが、実装時に考慮する一手間がかかりますし、何より根本的に大きなトランザクションのバッチは分割できません(そして業務アプリケーションではこのような要件がままあります) PaaS型サービスはアプリケーションにあまり大きなファイル作成を許していないケースが多いです(大きなファイルを格納する場合にはAWS S3等のオブジェクトストレージを使うことが推奨される)。しかし、例えば動画をアップロードしてオーサリングする様なアプリケーションなどではアプリケーションサーバ側に大きな一時ファイルを置きたいというニーズがありえます 特定のクラウドサービスにロックインされるリスクを嫌う場合 マネージドなサービスを提供するPaaSを利用する場合、当該サービスの運営状況やアップグレードに振り回される可能性があります。採用当時は割安だったサービスが突然値上げしたり、API仕様が変更になりプログラム側の修正が必要になったりするリスクは常にあります。自前でLinuxサーバーを運用する想定の環境であれば、そうしたリスクは最小限にすることが可能です。自前で管理するコストは増えますが、予測&コントロール可能であるということは大事なポイントです。 PaaSを「きちんと」使いたい場合にはそのPaaSの仕様や運用に詳しいエンジニアが必要になります。PaaSは正常系の利用についてはとても便利で誰でも使えるように作られていますが、異常系については当該PaaSに対するある程度深い知識がないとそもそも原因箇所自体が思い当たらなかったり、原因特定しても対策方法が分からなかったりします。ベストエフォート型のアプリケーションであれば問題になることは少ないのですが、重要なデータを取り扱うアプリケーションなどでは異常系の洗い出しや対策が必要になるため自前サーバーの方が全てを掌握できる分安心、というケースはあります。 この辺りの感覚はエンジニアそれぞれによってさじ加減が変わってくる部分ですので、一つの正解があるというよりも、様々な視点でそれぞれ最適な戦略があり、その中でどの戦略がより良いのかを考えていくことになりますね。 AWS EC2 さくらのVPS その他「VPS」「専有」などの名がつくVPS型ホスティングサービス morimorihoge注) 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プロトコル morimorihoge注) 「いや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) TCPの状態遷移図(PDF)より #1: Webのしくみ #2: Webサーバの役割とWebサービスの代表的な形態I〜III #3: Webサービスの代表的な形態IV(Rails)、ステートレスとステートフル(本記事) 関連記事 Rails: Puma/Unicorn/Passengerの効率を最大化する設定(翻訳) 【社内勉強会】バージョン管理の重要性とGitの運用について