yoya-thumberとは
SmartNewsは本日、yoya-thumberをOSSとして公開しました。yoya-thumberはSmartNews社内で利用されているサムネイル画像生成プロキシです。HTTPサーバーとして動作し、画像URLをリクエストとして受け取って縮小した画像を返します。
yoya-thumberが解決する課題
スマートニュースに対して頂くご意見の1つに、通信量が多すぎる、というものがあります。実際、私も1ユーザとして、通信量多すぎだろ、常識的に考えて……という感想を持っていました。
これにはいくつかの原因が挙げられますが、記事中の画像がスマホ向けに最適化されていない(場合がある)、というのも大きな問題の1つでした。例えば、見かけ上は小さな1枚の画像を表示するために、実は2MBの転送量を使ってしまっている、といったケースが存在していました。yoya-thumberを使うことで、スマホ向けに適切に変換された画像をサーバーサイドで生成し、ネットワーク帯域を節約することができます。実際、SmartNewsでは、SmartViewとyoya-thumberの導入により、通信量を大幅に削減することができました。
本ポストでは、yoya-thumberのOSS公開を記念して、以下では、yoya-thumber導入時に我々がどのようなトラブルに遭遇したか、裏話を紹介します。
Content-type信用できなさすぎ問題
yoya-thumberでは、当初はオリジンサーバーから送られてくるContent-typeを信用していました一部で利用していました。しかし、数%のユーザーに対して実際に機能解放を行ってテストしてみると、Content-typeと現実があまりにも食い違う事例がたくさんでてきました。
なぜかダブルクォートがついてる……みたいな微妙な問題から始まり、Content-typeはJPEG、拡張子はPNG、中身はBMP、みたいなおそろしい事例や、挙句の果てには画像だと主張しているが実際には中身はJavaScriptが入っている(そのJavaScriptが実際の画像をロードする)など、様々な問題が噴出しました。そこで、送られてくるContent-typeは一切信用せず、中身に基づいてファイル形式を推定することにしました。
ByteMobile問題
サーバー側に来るエラーログを眺めていると、1.1.1.4
といった謎のIPアドレスから始まる画像URLをオリジンURLとするアクセスが少数ながら存在しました。もちろん、このようなリクエストはエラーになり、ユーザーには画像が表示されません。
調査の結果、USのモバイルネットワークでは、ByteMobileというソフトウェアを使って、imgタグの中の画像URLを書き換えることによって画像にプロキシを通して通信量を削減するという行為が行われていることが明らかになりました。
日本でも昨年、通信の最適化が大きな話題になりましたが、似たような話ですね。
SmartNewsの立場からすると、弊社のサーバーからアクセスできないようなURLに書き換えられてしまうと困ってしまいます。そこで、HTML送出時にCache-Control: no-transform
というヘッダを付与することで、HTMLの書き換えを禁止しました。
エラーチェック抜け問題
yoya-thumberには画像を読み込む箇所がいくつかあり、その中の1ヶ所でエラーチェックが抜けていました。このため、読み込んだ画像が壊れていた場合に、いろいろあって最終的にはサーバーのプロセス自体が死んでしまうというトラブルが発生しました。同じ画像URLでのアクセスが来るたびにサーバーが死んでいき、最終的には全台が死んでしまいます。
対症療法としてsupervisordを導入して自動的にサーバーを再起動するようにしましたが、根本原因の発見まで、胃が痛い日々が続きました。多くの方に不具合を体験させてしまうことになってしまい、その節はご迷惑をおかけしました。
同時実行数の制限
当初は画像圧縮の同時実行数に特に制限をつけていませんでしたが、メモリ消費量が当初の想定より大きかったので、圧縮作業の同時実行数をCPUのコア数と同じ数に制限しました。メモリ消費量が劇的に減りました。
同時接続数の制限
ファイルディスクリプタの上限を100ぐらいに減らして負荷テストを行うとたまに死ぬので、前段のnginxでサーバーへの同時接続数に制限をかけました。(カーネルの設定を変更して、ファイルディスクリプタ数の上限も増やしてありますが。)
htcachecleanが過剰なディスクIOを行う問題
サービス開始当初は、オリジンサーバーへのアクセスを減らすためにローカルディスクで画像をキャッシュしていました。このために、Apacheのmod_proxyを使ってフォワードプロキシを仕立てていました。
このApacheでのキャッシュを削除するためにhtcachecleanコマンドがcronで定期実行されていたのですが、このhtcachecleanが過剰なディスクIOを行い、結果としてサービスが停止する問題がありました。htcachecleanが悪いという話ではなく、どうも負荷パターンとの相性の問題のようです。
フォワードプロキシの効果が思ったほどではなかったこともあり、フォワードプロキシ自体を無効化しました。
トラブルを見越したアプリ設計が功を奏した
当初から、予想外のトラブルが発生するかもしれないという想定で、yoya-thumberの有効無効はサーバー側のフラグで切り替えられるようになっていました。フラグをオフにすると、それをアプリ側で検知して、次回アクセス時からはオリジン画像を直接取得しに行きます。導入当初はエラーチェック抜けとhtcachecleanの2つの致命的な問題があったので、このフラグ機能がなければ、本当に大問題に発展するところでした。
成果
詳細なA/Bテストは実施しておりませんが、1ユーザー当たり、数MB/日の通信量削減に貢献している計算になります。1ヶ月に直すと、毎日使っていただいているユーザーの場合で100MB〜数百MB程度の効率化になっています。期待したほど劇的な成果ではなかったという気もしていますが、そういった地道な改善の積み重ねがニュースアプリ通信量比較で1番通信量が低かったという結果に結びついたのだと信じて、これからも改善を積み重ねていきたいと思っています。具体的には、次はleptonにトライしてみたいですね。
おまけ:Go 1.7の効果
サービス導入時は処理系としてGo 1.5を用いていましたが、Go 1.7に上げるとGCでのstop the worldが劇的に改善するらしいという話を聞きつけました。本番環境に適用(図中赤線のタイミングでデプロイしました)してみたところ、レスポンスタイムの改善は劇的ではなかったものの、CPU使用率が劇的に減少しました。ロードアベレージも1.5から0.5程度にまで改善し、予想を遥かに超える嬉しい結果となりました。
おわりに
yoya-thumberは、go-thumberを改造し、YoyaさんがImageMagick対応など、いくつかの機能を追加したものです。素晴らしいソフトウェアを開発されたPixiv様とYoya様に感謝します。
yoya-thumberは既にSmartNewsで半年以上運用されており、安定して動作しています。Githubで公開中ですので、画像を縮小して配信したいとき、ぜひご利用下さい。
(追記):開発者のyoyaさんによる資料です。こちらもあわせてお読み下さい。