[翻訳]Webアプリを10倍速くする10のtips(上)

2017-11-05



元記事: 10 Tips for 10x Application Performance
https://www.nginx.com/blog/10-tips-for-10x-application-performance/

Webアプリのパーフォンス改善はますます重要になっているよね。経済活動においてインターネットが占める割合はどんどん増えてるしさ。先進国の経済なんて5%以上がオンラインで動いてるくらいだ。それに伴ってWeb速度に対するユーザの期待もガンガン上がってしまってるよ。もしも、あなたのところのWebサイトやアプリがすぐ反応しなかったら、すぐに見限られて商売敵のところへ移っちゃうだろうさ。

例えばAmazonが10年くらい前に行った調査だとページ読み込みが0.1秒短くなっただけで売り上げが1%も増えることがわかってる。最近の別の調査じゃ、アンケートに対してサイト所有者の過半数がWebアプリのパフォーマンスが悪いせいで利益や顧客を逃していると答えている。

Webサイトってのは一体全体どれくらい速くなればいいんだろうな。ページ読み込みに1秒かかるごとにユーザの4%がアクセスを諦めてどっかに消えちまう。業界上位のECサイトならどこでも1-3秒以内に最初の操作が可能になる、もちろんコンバージョン率は最高だ。Webアプリのパフォーマンス改善に対する投資は大きいしこれからも拡大していくのは誰が見たって明白ってワケ。

ま、改善したいと思うだけなら簡単だけど、実際に改善して結果を出すのは簡単じゃないわな。その改善を手伝うために、この記事でWebサイトのパフォーマンスを10倍向上させるための10のヒントを説明するよ。実はこれ、十分に検証された最適化テクニックを使ってWebアプリを改善する方法とNGINXのTipsを紹介していくシリーズの記念すべき第一回なんだ。あ、ちなみにこのシリーズじゃセキュリティの改善についても触れて行くよ。

1. リバースプロキシサーバの活用とセキュリティ保護

Webアプリが単体のマシンで動いてるのなら、パフォーマンスを改善する方法は明快だよ。単にもっと高速なマシンへ移して、CPU・メモリ・ディスクをガンガン追加すればいい。そうすればWordPressだってNode.jsだって、Javaアプリだって前よりも高速に動くようになる。(アプリがデータベースを利用する場合だって解決策は簡単だろう。速いマシンを2台持って来て、それらをより高速に接続すればいい)

悩ましいのはマシンの速度が問題じゃないかもしれないってことだ。コンピュータが色々な種類のタスク、つまり何千もの接続でユーザとやりとりしたり、ディスクへファイルの読み書きをしたり、アプリケーション上の処理を実行するせいでWebアプリの反応が遅くなることがよくある。メモリ不足なんかでアプリサーバが重くなることもあるし、ディスクへの読み書きみたいな単体のタスクで大量のリクエストを待たせることもある。

そんなとき、ハードウェアをアップグレードする代わりに、リバースプロキシサーバを追加して、そういったタスクの一部を他所に押し付けるっていう全く別の解決方法もあるんだ。リバースプロキシサーバはアプリケーションが乗っているマシンよりも前でトラフィックを処理する。外部のインターネットへ直接接続されてるのはリバースプロキシサーバだけだ。アプリケーションサーバとの通信はより高速な内部ネットワークを介して行われる。

リバースプロキシーを使うことでアプリサーバーはユーザがWebアプリとやり取りするのを待っている必要がなくなり、純粋にレスポンスになるページを生成するのに集中出来る。クライアントの応答を待つ必要がなくなったアプリサーバは本来の最高速度に近いスピードで処理が出来る。

[訳者注釈]
クライアントの応答を待つ、というのがどういうことか補足しとこう。ここで言ってるのはバッファリングと呼ばれる概念だ。クライアントがHTTPリクエストを送りはじめて、それを完全に受け取るまでには時間が少なからずかかる。単なるGETリクエストならほとんど気にすることはないだろう。

でもそれが重たい動画ファイルを投稿するPOSTリクエストなら? 君のWebアプリのプロセスはその動画ファイルを受け取り終わるまでずっと待つ必要がある。そこで、nginxのようなサーバアプリが代理でリクエストを制御するってワケ。

nginxはクライアントが送信し始めた動画ファイルをWebアプリの代わりに受け取っておく、そして完全にリクエストの受信が終わった段階ではじめてWebアプリへリクエストする。Webアプリはローカルのメモリなりディスクなりから動画ファイルを読み込めるから待機時間はほぼない。これがバッファリングだ。

またリバースプロキシはWebサーバの構成を柔軟にしてくれる。例えば、画像ファイルを配信しているサーバに負荷がかかりすぎる時、別の配信サーバを簡単に追加して負荷を分散することが出来る。また、何らかの理由でその配信サーバが落ちてしまった時には、生きているサーバへアクセスを振り分ける事も出来る。

こういった柔軟性から、リバースプロキシはパフォーマンスを向上させる多くの機能に必要だ。

このような機能の他に、アプリケーションのヘルスチェック、専用のリクエスト・ルーティング、高度なキャッシュ機能など様々なリバースプロキシ機能を使うことも出来る。そう、NGINX Plusならね。
(NGINX Plusはnginxの商用版で国内販売も行われている)

[訳者注釈]
railsアプリを組んだことがあればnginx/apacheとunicorn/passengerを使ってサーバを構築したことがあるだろう。webrickあるいはunicorn/passenger自体をWebサーバとして直接HTTPリクエストを受け付ける(80/443ポートをlistenする)ことも出来るが、その前方でnginx/apacheといったサーバアプリを挟んでproxy_passでunicorn/passengerを指定するのがリバースプロキシだ。

そもそもプロキシー(proxy)という単語に馴染みがないって?
プロキシーは代理の、という意味の言葉だ。プロキシーサーバと言えば、クライアントの代理でアクセスしてくれる踏み台サーバのこと。

じゃあ リバース(逆) って何さ?
普通のプロキシー(フォワードプロキシー)はリクエストを代わりに出す 「クライアント側の代理人」 だ。それに対してリバースプロキシーはリクエストを代わりに受ける 「サーバ側の代理人」 というワケ。そこが逆だろう?

2. ロードバランサの追加

ロードバランサの追加はパフォーマンスとセキュリティを向上させる方法の中では簡単な部類に入る。これによって単一のWebサーバをより強力なものにする代わりに、多数のサーバへ負荷を分散できる。アプリケーション自体が微妙なコードでも拡張性に乏しくても関係なく、ロードバランサはユーザ経験を改善してくれるだろう。

さて、まずロードバランサはリバースプロキシサーバの一種だ(前述)。リクエストを受け取って別のサーバに転送するからね。ロードバランサはその転送先サーバを複数持ち、任意のアルゴリズムによって転送の振り分けを決定する。最も簡単な振り分けアルゴリズムはラウンドロビンだ。ラウンドロビンの場合、リクエストを単純にそれぞれのサーバへ割り振って行く。

他の方法としてはアクティブな接続が最も少ないサーバを選んでリクエストを転送する方法がある。NGINX Plusには同じサーバ上の特定のユーザセッションを継続する機能があり、これはセッションの永続性と呼ばれる。

ロードバランサは他のサーバがトラフィックを待つ間に、1台のサーバが過負荷になってしまうことを防ぐ。これによってパフォーマンスが大幅に向上する可能性がある。また低コストのサーバを簡単に追加して使用できるから、Webサーバの性能をアップグレードすることだって優しい。

ロードバランス可能なプロトコルにはHTTP、HTTPS、SPDY、HTTP / 2、WebSocket、FastCGI、SCGI、uwsgi、memcachedなどTCPベースのアプリケーションや他のトランスポート層プロトコルのアプリケーションタイプがある。Webアプリを分析して使用すべき場所とパフォーマンス上のボトルネックを探そう。

またロードバランスに利用されるサーバでロードバランス以外のタスクも処理できるよ。SSLの終端、HTTP/1.xやHTTP/2を利用するクライアントの対応、静的ファイルのキャッシュとかね。

[訳者注釈]
SSLの終端: 例えばNginxでローカルのRails/Unicornへプロキシしているような構成ではSSL通信は大抵の場合Nginxの時点で終わっており、Nginx <=> Rails間の通信は非SSLで行われいる。

そう、ロードバランスによく使われるサーバアプリといえば弊社のNginxだ! 詳しくは「Five Reasons to Choose a Software Load Balancer」や「Load Balancing with NGINX and NGINX Plus, Part 1」を読んでくれよな! NGINX Plusならレスポンス時間に基づく負荷ルーティングやMSのNTLMプロトコルのロードバランス機能のような、より特殊化された機能もサポートしてるからさ。

3. 静的/動的コンテンツのキャッシュ

キャッシュを使うことでHTMLや画像のようなコンテンツをクライアントに素早く配信できる。キャッシュ機能はいくつかの戦略を含む。例えば迅速に配信するためのコンテンツへの前処理や、高速なデバイスへのコンテンツ格納、それらの組合せだ。

考慮すべきキャッシュ機能には2つのタイプがある。

  1. 静的コンテンツのキャッシュ - 画像ファイルやスクリプト(css/js)など、エッジサーバに保存してメモリやディスクから高速に取得する。
  2. 動的コンテンツのキャッシュ - 多くのWebアプリではリクエストに対してデータベースの値などを利用して新しいHTMLが生成される。この生成されたHTMLの1つのコピーを短時間だけキャッシュすることで生成する必要があるページの総数を大幅に減らす事が出来る。

[訳者注釈]
エッジサーバ: エンドユーザと最初に直接やり取りをするサーバ。例えばRails + Nginxのような構成なら、Nginxがエッジサーバにあたる。

例) エンドユーザー <=> Nginx <=> Rails <=> MySQLdその他
エッジサーバであるNginxを指して「ユーザに近い」とも表現する。この構成ではMySQLdは逆にユーザから遠い。

あるページが1秒間に10回のリクエストを受け、1秒間キャッシュした場合ならそのリクエストの90%がキャッシュによって返されるってワケ。仮にページが完全に動的で新しく生成される場合でも、部分的にはキャッシュされた内容でページを作れる。

Webアプリによって生成されたコンテンツをキャッシュするための主な手法が3つある。

まず、アプリサーバの負荷を軽減するためにWebアプリサーバ内で動的コンテンツのキャッシュを行う。そして、静的コンテンツ(キャッシュされた動的コンテンツのキャッシュも含む)もキャッシュする。最後に静的コンテンツをアプリサーバからより高速でユーザに近いマシンへ移動させれば、さらにアプリサーバの負荷は軽減されレスポンス時間は短くなる。

[訳者注釈]
例によってRails+Nginx構成で例えると、今はもう使えないがRailsのキャッシュ機能にページキャッシュというのがある。指定したURLを静的なHTMLとしてpublic以下に保存し、リクエストの度に生成する代わりにそのファイルを返す仕組みだ。これが動的コンテンツのキャッシュのわかりやすい例だろう。

さらにここで生成した静的なHTMLをNginxがrailsへリクエストを回すことなく直接返すことも出来る。これが、より高速でユーザに近い場所への移動だ。

キャッシュを改善することでアプリの処理速度は大幅に向上する。多くのWebページでは大きな画像ファイルなど静的なデータがコンテンツの半分以上を占めている。このようなデータをキャッシングせず送信するには数秒かかることがあるが、データがローカルにキャッシュされていればわずか1秒しかかからない。

nginx/nginx plusでキャッシュを使ってみよう。「proxy_cache_path」「proxy_cache」の二つのディレクティブを使用してキャッシュを設定できる。このディレクティブではキャッシュの場所とサイズ、ファイルがキャッシュに保持される最長時間あたりのパラメータを設定する。

そしてもう1つ。第三のディレクティブ、proxy_cache_use_staleを使うことでコンテンツを提供しているサーバがビジー状態あるいはダウン状態になった時にキャッシュされた古いコンテンツを供給することだってできる。これを使えば例えアプリサーバが落ちても、ユーザから見れば安定稼働してるように映るってワケ。

他にもnginx plusにはキャッシングのより高度な機能が揃っているぞ。例えばキャッシュクリアのサポートや、キャッシュ状態をビジュアライズしてくれる監視用ダッシュボードなど。

4. データを圧縮する

圧縮は非常に大きな速度改善に繋がる可能性が高い。例えば画像、動画、音声ファイルなどには非常に効果的な圧縮形式(jpg,png,mp4,mp3)があって、ファイルサイズを一桁以上も縮小してくれるよね。まったく非圧縮の生データを配信に使う人はいないだろう。

ところがそれに対してHTML、CSS、JavaScriptのようにプレーンなテキストデータは非圧縮形式で送信されることが多々ある。このデータを圧縮するとモバイル接続や貧弱な回線でページを閲覧しているユーザには意外なほど大きな改善となることもある。

そもそも多くの場合ユーザが必要としている情報はテキストデータだけでも十分に事足りる。マルチメディアデータは単に補完や装飾のためのものだ。だからきちんと圧縮されればHTML、JavaScript、CSSなどのコンテンツは30%以上圧縮でき、その分通信時間を短縮出来る。

またSSLを利用する場合、圧縮によってSSLエンコードされるデータ量が削減されるため、データ圧縮にかかるCPUリソースも節約出来る。

テキストデータの圧縮方法は色々ある。例えばSPDやHTTP/2の新しい圧縮スキーマだ、これについては6章で詳しく説明しよう。テキスト圧縮の例としては他にnginxでgzip圧縮をオンにすることが出来る。または、あらかじめ圧縮したテキストデータをgzファイルとして保存しておいて、直接それを配信することも出来る。(gzip_staticディレクティブ)

[訳者注釈]
前提知識として、HTTPは送信するデータをgzip形式で圧縮して通信できる。この場合、ブラウザ側がgzip形式に対応していればリクエストのヘッダにgzip対応と記述される。サーバではそのヘッダを見て圧縮したりしなかったりするというワケ。このgzip圧縮はnginxだとデフォルトでオフとなっている。
 
理由はgzip圧縮する際にCPUリソースを使うからだ。また圧縮の処理でどうやっても多少の追加時間がかかる。にも関わらず速度改善になるのは 圧縮にかかる時間 < 圧縮で削減されるバイト数を通信する時間 だからだ。つまりネットワークがボトルネックになっているためであり、それがモバイル接続や貧弱な回線で特に効果がある理由だ。

どちらの場合にも対応するなら圧縮ファイルと非圧縮ファイルのどちらも用意しなければならないが、そうなると今度はディスク容量を浪費してしまう。最適な改善策は場面ごとに違うのだ。

ちなみに、画像ファイルや音声ファイルなどはpngやmp3といったフォーマットの時点で十分に圧縮されているため、仮にそれを更にgzip圧縮しても僅か1%の圧縮効果も得られないことに注意。

[翻訳]nginxを10倍速くする10のtips(下)へ続く


 このエントリーをはてなブックマークに追加


  

[30]