前回に続き、春の初心者・学習教材シリーズです。今回はHTTPレスポンスの圧縮というドがつく基礎になります。
これを適用することに、どのような狙いがあり、どのような効果があるのかを復習していきます。
目次
はじめに
WEBサーバーからのレスポンス圧縮なんてモンは20年以上前から適用されておるわけですが、基本過ぎてそういう記事も世から消え去っては出てというか、まぁたまには改めて振り返るのも良いんじゃないかという気持ちです。現行システムで何気なく設定されている、セキュリティ関連やBOT対策などは、過去にどういう経緯があってそうなったのか、を若手エンジニアが感じづらい部分もあるので、雑学的にでも伝えるとよいと思いまんた
— 外道父 | Noko (@GedowFather) March 28, 2024
今回の圧縮もこういう感じで、当然のように設定されている項目について、1つ1つ改めて目的と効果を調べることって多分あまりないだろうし、その1例として振り返ってみておけば、その後は自身で気になって調べたり聞いたりするようになるのではないか、という期待です。
そーいえば、定期的にWEBサーバーって省略してよくない?(=アプリケーション・サーバーが直に受ければよくない?)って話題を見たりもしますが、WEBサーバーに簡単に委ねられる機能の1つにコレがあるので、個人的にはほぼ全てに近い環境で、WEBサーバーはいた方がいいとは思っていますが、
コンテナとかで構成を再設計する機会がきたら、改めて考えてみるってのはイィとは思います。まぁよっぽどの利点を証明できない限りは、WEB+APP にするのが無難だと思いますよ、ということで参りましょう。
圧縮の設定
Nginx の場合は以下の設定説明を参考に、だいたいこんな感じの設定を放り込めばOKです。gzip_types はただの例なので、可能性のあるものを列挙しておけばよいです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
gzip on; gzip_http_version 1.0; gzip_min_length 1000; gzip_vary on; gzip_static on; gzip_proxied any; gzip_types text/plain text/html text/css text/xml application/json application/xhtml+xml application/xml application/x-javascript; |
リクエストヘッダに『Accept-Encoding: gzip』を含む場合、クライアントが「gzip圧縮して返しても大丈夫よ」って言ってるので、サーバー側はレスポンスを返す際に条件を満たしていれば、レスポンス・ボディ部分を圧縮した上で、レスポンス・ヘッダに『Content-Encoding: gzip』を付与して「gzipで圧縮しておいたよ」と返します。
WEBサーバーに限らず、CloudFront といった CDN も同等の機能を持っていて、圧縮メリットがあると判断した場合は有効にしておくとよいでしょう。
圧縮の目的
ここはエンジニアとしての話というても、自宅パソコンでも何かしら圧縮機能は使っているでしょうから、説明は不要でしょうが……メインは容量の縮小、次いで複数データを1ファイルにまとめる、ってところで今回は容量縮小を”手段”としています。
では目的はというと、ネットワーク転送容量の縮小と、それからくるデータ転送費用の削減となります。AWS VPC のネットワーク小話 で少し触れたところで、今回別枠で記事にした形となります。
超昔だと、インターネットが貧弱だったり、携帯デバイスなどが貧弱……というかパケット通信料が高かったりしたので、そっちを助ける意味合いが強かったです。
オンプレミスだと 1Gbps いくら、という費用形態なのでデータ通信量は従量課金じゃないし、「ギガが足りない」のような言い回しをする世代には信じられないかもですが、ガラケーで通信しまくるだけで月額数万円以上も青天井に請求される『パケ死』とい現象があった時代の話でして、インターネット歴史の雑学として覚えておいてください(テストにはでません:^)
圧縮のメリットとデメリット
メリットの1つは既に書いた通り、クラウド上でのデータ転送費用を安くできる可能性が高いです。もう1つはデータ通信時間の短縮で、容量が小さくなるのだから、当然その転送時間も短くなるため、全体の処理時間も短くなる可能性を秘めています。
デメリットはCPUパワーを必要とするところです。パソコンで圧縮すると進行ゲージが表示される通り、そのくらいの時間とCPUパワーを必要とします。ただ、パソコン上だとストレージから読み込んで、さらに結果を書き込む処理が入りますが、WEBサーバーではメモリ上での作業となるのでほぼ純粋にCPUパワーだけになります。
サーバー側で圧縮するCPUパワーと時間、そしてクライアント側で解凍するCPUパワーと時間、が上乗せされる形になるため、データ転送時間の短縮と合わせると実は全体として速くなるか遅くなるかは、内容次第となります。
また、圧縮処理はリクエストを受けてからアプリケーション・DB・KVS・外部APIなど様々な処理を行ってレスポンスを返すまでの中の1つに過ぎないため、処理全体としては無効/有効で何%くらいCPU使用率が変わるかはこれも内容次第ですが、割合的にはそこまで大きいことはないはずで、極端にCPU使用率が跳ね上がるということもないと思われます。
──という感じのため、絶対圧縮した方がいいよ、とは書きません。コンテンツには圧縮効果が低いモノもありますし、平均レスポンス・サイズが小さめなら不要と判断することもありえます。
それでも一応、考え方を示しておくと、コンテンツの平均容量が 数KB や 数十KB 以上かつ、平文がほとんどならば、圧縮を有効にした方が優位と思われ、そういう対象の条件に設定すると概ね良い効果になると考えてよいです。
容量圧縮の効果
ではここで gzip 圧縮の効果の程度を知るために、適当な平文コンテンツをいくつかの容量毎に、圧縮レベルを変えて容量の変化を記録したものを見てみましょう。コンテンツは AWS の費用JSONを拝借して、容量調整した成形済みのものと、それを jq を通して一行に変換した余計なスペースを削除したものを使用しています。
圧縮効果の特徴をザックリまとめると、
※↑はデータ不足ですが、容量を合わせても1割弱の差を確認済み
という感じで設定の判断材料としていきます。
圧縮レベルについて
gzip の場合(他の類似形式も)、レベル 1 ~ 9 があります。0 は圧縮しない、-1 はシステムにおけるデフォルト値となることが多いです。システムのデフォルト値の多くは 6 になっています。これは”妥協点”と呼ばれており、速度と圧縮率のバランスを考えた結果であり、実際の多くはそれが推奨値になると考えてよいです。
今回はCPUパワー(=圧縮時間)についてはデータで触れませんでしたが、最大の 9 にするとやや重みを感じることは確実で、よほどその圧縮率にメリットを見いだせる場合でなければ、使うことは推奨されない程度のものになります。
gzip コマンドだとデフォルトは 6 で、Apache も説明によるとZlib のデフォルトなので 6、Nginx は 1 となっています。プログラミング言語のライブラリだと、1, 6, 9 など色々あるっぽいです。
現行システムの調整
これらを踏まえて、眼前のシステムを最適化できるか考えていきます。ここでいう最適化とは、前記事で触れた AWS における OUTPUT データ転送費用についてを主題としますが、それに限った話でもないので現状を把握して考察してみると何かしら良いことがあるかもですね。
圧縮の確認
まずは稼働中のWEBサーバーの設定を確認しにいき、設定としてはどうなっているかを確認するとよいでしょう。設定には条件がありますし、アクセスログにはサイズ数値は残ってもそれが圧縮されたものかどうかは残していないと思うので、実際にレスポンスが圧縮されているかを軽く確認しておきます。
1 2 3 4 5 6 7 8 |
$ sudo tcpdump -i any -w /tmp/test.cap port 80 Ctrl+C $ tcpdump -vvv -r /tmp/test.cap | less # レスポンスヘッダの gzip を確認できればOK HTTP/1.1 200 OK Content-Encoding: gzip |
差し支えなければ、リクエストを投げて確認したりもできます。
1 2 3 4 5 6 7 8 |
# 解凍できることを確認する $ curl -v http://localhost/10kb.json -H "Host: example.com" -H "Accept-Encoding: gzip" | gzip -d | less # 平文状態の容量を確認する $ curl -v http://localhost/10kb.json -H "Host: example.com" | wc -c # 圧縮状態の容量を確認する $ curl -v http://localhost/10kb.json -H "Host: example.com" -H "Accept-Encoding: gzip" | wc -c |
調整効果の試算
もし圧縮されていなかったり、圧縮レベルが 1 など低かった場合、仮に圧縮レベル 6 に調整した場合にどのくらいのデータ転送量の削減になるか、そしてコスト削減になるのかを試算してみます。別に細かく試算しなくても、エイヤで適用しても即死するような変化まではならないと思いますが、CPUコストはイィとしても動作確認くらいは済ませてから変更するようにしましょう。
以下のデータは、とあるWEBサーバー1台分の、ローテート済みの1ログファイルを手元に持ってきて、awk や grep を使ってサラッと集計したものです。最終的には削減割合を知りたいだけなので、それを知るためのサンプリング量があれば良しです。
大雑把ではありますが、容量の桁で集計するだけでも大体把握することが可能です。既存の Nginx デフォルト値 gzip_comp_level 1; で運用されたものを、6 にするとどうなるかを含めて内容をまとめると、
仮に 25% のレスポンス容量の削減効果があるとした場合、OUTPUT データ転送費用に月額 $10,000 かかっているとしたら、ただ設定を調整するだけで月額 $2,500 を削減できることになります。
圧縮レベルの変更でコレなので、もし元が非圧縮状態であれば、垂涎モノのイーピン……もといコスト削減になると思われ、シレッとその数値を提示して成果アピールとしたら良いのではないでしょうか。
その他の調整案
今回は簡単に設定を変えるだけでコストを安く、ってのとその知識がメインなので話はだいたい終わりですが、せっかくなので関連で余談をしておきます。JSON の短縮
当然の話ですが、サーバーから返された JSON データは、クライアントが受け取ってシステムとして扱うので、JSON としてのスペースや改行による見た目の成形は全く機能に関係ありません。なので成形用の文字列は一切含めずに返した方が、機能に影響なく容量の縮小となるため、元が成形済みならばそれを変えるだけでもコスト削減になります。これは JavaScript とか他のデータ種類でも言えることです。
あまり神経質になりすぎる必要はないところですが、もしあえて成形してしまっている場合は、それを解除するだけなのでコスパ良い調整となります。
他の圧縮形式
ただの圧縮の話だと他にも圧縮形式がたくさんありますが、HTTP においては他には Brotli が考えられます。CloudFront も対応している形式です。- 「Brotliアルゴリズム」gzipに代わる高速圧縮術
- WEBスピード向上・転送量削減する Brotli 圧縮と CDN の連携 | REDBOX Labo
- 新しい圧縮アルゴリズムBrotliをnginxで試す #nginx – Qiita
圧縮率が上がる代わりにCPUパワーも多く必要とするのと、導入作業の手間、クライアント条件などを考えると、そこそこ限定的な適用になりそうです。
まぁでも色んな圧縮形式で遊んでみるってのは、地味な技術力向上にも繋がると思いますので、何事も複数の選択肢を知るってのは良いことです。
おわりに
今回のレスポンス・コンテンツ圧縮の話だと、もう設定されているからそれでエェやんとか、効果的っぽいなら設定入れといたらエェんやろ、で済ませてもイィ程度の話ではあります。ただ自分的な本質としては、gzip on; の設定を見た時に、それが何を表して何のためにそうしたのか、を逐一気にして知識の隙間を埋めていくエンジニアであって欲しいし、データ転送費用を見た時にその最適化の可能性に向かって調査できる基礎技術力を大切にしてほしい、という思いがあります。
これが単独気味に運用しているシステムなら、サクッとザックリ適用してみて──って勢いでも良いのですが、それなりの人数が関わるシステムの場合はやはりその根拠を高い質で示すことで、理解と早い適用を得られますし、その知識の伝搬がされて地が固まっていくというものです。
たかが圧縮1つでこんな長ったらしい記事になるくらいには、インターネットの技術知識は深いですし、それが何百何千以上あるので、基本は浅く広く、しかしたまには深く潜り込むバランス感をもって、ピチピチな新卒の皆さんも、枯れた(界隈では良い意味)オッサンも、地道に気長に頑張っていきましょう:-)