Responsive Image as Serviceへの取り組み | サイバーエージェント 公式エンジニアブログ

こんにちは、森野耕平(@kohei_april20)と申します。

最近は社内向けのResponsive Image as ServiceとしてHayabusa(https://hayabusa.io/)というものを開発・運用しています。今回はそのResponsive Image as Serviceとその取り組みについて紹介させて頂きたいと思います。

背景

近年、スマートフォンなど接続デバイスが急増してきたことに加え、リッチなユーザー体験のニーズの高まりによって、ウェブサービスの開発・運用の複雑さが増してきいます。多くの場合ウェブページにおけるデータの大半を占めているのは画像で、この画像を効率よく最適な形で配信することが重要になってきます。

そのためにはデバイスに応じて解像度別の画像や、フォーマット別の画像(例えばwebpが利用可能なデバイスにはwebpを使うなど)を生成したり、更に減色や圧縮といった様々な処理を行ったりした画像群を用意する必要があります。その上で、更に接続デバイスを検知し最適な画像を選択して配信する仕組みを整えなければなりません。

実際私が過去に担当していたプロジェクトでも、解像度別の画像を生成したり複数の圧縮プログラムを通した画像群をリリース前に事前に生成するような仕組みを用意して行っていました。扱っていた画像が多かったこともあって、運用中に追加分の画像を処理するだけでも結構な時間がかかり、素早いリリースが難しく苦労をしていました。

Responsive Image as Serviceというソリューション

この問題を解決する手法としてResponsive Image as Serviceというものがあります。Responsive Image as Serviceとは、画像に動的な操作をするための情報をURLのパラメータなどから取得し、オンデマンドでレスポンシブ画像を生成・配信をサービスとして提供するものです。オリジンに存在する画像をマスタとして使い、要求に応じて画像処理を行いレスポンシブ画像を生成する、画像プロキシのように動作します。

このようにサービスとして画像関連の煩雑な手続き、変換にかかる負荷、キャッシュの取り扱いなどを担うことで、これらの問題を気にすること無く開発・運用ができる体勢を築くことができ、効率化を図ることが出来ます。

操作に関する情報について

URLから次のような情報を取得します。

  • 画像のファイルパス
  • パラメータ(Query String Parameters、独自セパレータによるパラメータ)

画像のファイルパスからオリジナル画像を特定し、パラメータからその画像に行う処理に関する情報(リサイズ、クロップ、圧縮、フォーマット変換など)を取得します。

例)

Query String Parametersの場合
http://<server>/image.jpg?width=300&height=200&format=webp
ドットセパレータによるパラメータの場合
http://<server>/image.w300.h200.jpg

他にもクッキーやヘッダーからの情報を用いる場合もあります。ヘッダーは次のようなものを利用します。

  • Accept:利用可能なファイルフォーマットを検知
  • User-Agent:デバイス判定

また、Http Client Hintsという、クライアントにとって最適なコンテンツを配信するために必要な情報を入れるヘッダがドラフトとして上がっているので、将来的にこのヘッダからwidth、height、DPRなどの情報を取得できるようになっていくと思います。

サービスの例

Hayabusaについて

Hayabusaはオープンソース化を視野に入れて社内で開発しているResponsive Image as Serviceの実装で、現在一部の自社サービス向けに機能を提供しており、現在半年ほど運用実績があります。

使用例

ドットセパレータ方式によるパラメータ指定を採用していて、例えば次のようにURLを指定すればそれぞれそれに対応した画像が得られます。
オプションなし

https://hayabusa.io/test/abema.png

100x50リサイズ(中心でクロップ)

https://hayabusa.io/test/abema.w100.h50.fitcrop.png

2色に減色

https://hayabusa.io/test/abema.pngquant(color2).png

JPEGにフォーマット変換

https://hayabusa.io/test/abema.jpg

メインモジュール

Node.jsで実装をしていて、次のようなメインのモジュールから構成されています。

  • hayabusa
  • レスポンシブ画像の変換と配信を行うためのExpress互換Node.jsミドルウェアで、プラグイン形式で画像処理機能を追加していく設計
  • hayabusa-server
  • hayabusaを使ったExpressベースのwebアプリケーション

hayabusaモジュールはResponsive Image as Serviceとしての機能を汎用的に実装したもので、hayabusa-serverモジュールがこれを利用して社内ユースに特化する形で実装したウェブアプリケーションになっています。

プラグイン

画像処理機能にあたるプラグインは次のようなものがあります。

  • リサイズ
  • クロップ
  • フォーマット変換とauto拡張子指定での自動フォーマット選択
  • 最適化(圧縮)
  • PSDレイヤーとスライス切り出し
  • など

これらの機能は基本的に裏で様々は画像処理プログラムを利用するラッパーのような役割をしていて、リサイズ・クロップ・フォーマット変換は主にImageMagick、GraphicsMagick、最適化ではpngquant、optipng、zopfli、gifsicle、jpegrecompressなどを利用しています。

フロー

処理のフローは下図のように、クライアントが画像のファイルパスと処理内容を含んだリクエストを送り、hayabusaが受け取ったURLやヘッダ情報から処理対象のオリジナル画像と処理内容を判別、オリジナル画像を処理するための画像処理プログラムをアレンジして順に実行し、最終結果画像をクライアントへ返すという流れになっています。

architecture

アーキテクチャ

下図はアーキテクチャを簡易的に表したものです。https://<hayabusa-server>/foo/img1.webpにユーザがアクセスすると、fooという文字列にマッピングされたオリジンサーバ<origin-foo>に置かれているimg1.*となる画像をオリジナル画像として処理した結果を得ます。

オンデマンドで画像を処理するため、どうしてもHayabusaサーバ側でオリジンから画像をダウンロードする時間とそれを処理する時間がかかってしまいます。そのため、配信を極力速く、そして画像処理の負荷をむやみに増やさないために、CDNとキャッシュサーバの二段構えでできるだけキャッシュが効くように構成しています。

architecture

課題

サービス上の画像利用特性の違い
現在の仕組みでは初回アクセス時に変換処理が走り、その後はキャッシュが利用されるようになるので予めキャッシュを作っておきやすいUI用の画像など再利用性の高い画像については非常に効果的ではある一方で、ユーザ投稿画像のように多数で画像がアップロードされた後に必要になるような画像は、画像処理は画像がアップロードされた瞬間にトリガーされた方が良く、今後このような用途にもパフォーマンスを高めて行けるような仕組みを整えていきたいと考えています。
利用プログラムのライセンス
様々な画像処理プログラムを利用する上で、場合によっては有料でかつサーバで動作させることが想定されていないようなものがあります。ライセンス上使えなかったり、サーバ上での使用を前提としたライセンス契約がしづらかったりして、ローカルで使っていた機能をそのままHayabusaで利用可能にするのが難しいケースがあります。

今後の展望

今後の展望として次のような機能拡張を予定しています。

  • フルPSDレンダラ
  • オリジナル画像としてPSDファイルをそのまま利用できると、レイヤー構造を保ったままのマスタ画像さえあればよくなり、より制作フローの効率化が実現できるのでサーバサイドでレンダリングできる機能を実現したいです。現在は暫定的に統合された画像のみレイヤーとスライスの指定に対応しています。
  • 9-slices
  • UI画像ではウィンドウ的な画像の場合、角の四隅、上下左右の辺、中心部のスライス画像を利用してサイズが可変となるものがあり、対応が煩雑になりがちなところをURLの文字列のみで簡単に画像を作れるように。
  • スプライトシート/アトラス
  • スプライトシートやアトラスも製作時、パッキング処理や管理が煩雑になりがちなので、これをサーバサイドで巻きとっていきたいです。
  • ローカル実行用のCLI
  • 現在はNode.jsのExpress互換モジュールとしての実装にとどまっていますが、これをローカル環境CLIで実行できるようにします。
  • ローカル実行用のGUI(フロントエンド開発向け)
  • 開発時にフロントエンドを実装する場合に開発環境がサーバに依存するよりもローカルで完結していた方が勝手が良いので、GUIを整えてローカル環境に簡単に導入できるようになればより開発効率の向上が見込めます。
  • Unity assets
  • スマホアプリは画像を都度配信する訳ではなく、アプリ内にバンドルされるのでサーバ配信型では使えませんが、ローカルでの実行ができればこちらも対応していけると思います。
  • サウンド、動画、フォントサポート
  • 現在は画像のみの機能しかありませんが、同じ基盤上でサウンド・動画・フォントなどの対応も可能なので、サポートをしていきたいと考えています。