image rails lambda imagemagick golang

minne の画像配信システムのリアーキテクトと作品画像の画質向上ついて

image rails lambda imagemagick golang

minne 事業部のシニアエンジニアリングリードのshiro16です。最近は年末に組んだ PC でずっとエオルゼアを旅しています。

少し時間が経ってしまいましたが、昨年 minne では画像配信のシステムのアーキテクチャを刷新しました。 更にユーザが登録した作品画像を配信する際の画質の向上を行ったのでその内容をご紹介いたします。

画像配信システムのリアーキテクト

そもそもリアーキテクトが必要になった経緯なのですが、以前の構成では okara と呼ばれる内製のツールが使われていました。okara に関しての詳細は弊社 yano3 が公開している資料を参考にして頂ければと思います。

長年 okara を使って来た訳なのですが yano3 が技術部から EC 事業部に異動した事により以前よりメンテナンスに時間を割けなくなってしまったという理由があり、okara のメンテナンスを事業部で行っていくのか?他社の製品を使うのか?もしくは別の何かで作り直すのか?と言った選択肢がありました。 その中で色々な検討を行った結果アーキテクチャを刷新して作り直すという選択をしました。

解決したかった課題

okara を運用する上で minne 事業部では解決したい課題がありました。 それは以下の 2 点です。

  1. インスタンスの管理が大変
  2. minne 事業部が管理しているが実は別事業部も相乗りしており考慮する事項が増えている

1 に関しては弊社にはプラットフォームグループ(以下 PFG)と呼ばれる事業部を横断したインフラチームがいるのですが、様々な理由から minne 事業部ではインフラの管理をこの PFG に頼っていません。それに加え okara の為のインスタンス(通称 imageproxy server)は台数も多く、割と頻繁にアラートが上がり増設する作業も若干属人化しているという問題がありました。

2 に関しては現状の okara を使った構成を minne 事業部側で変えたいとなった場合でも他の事業部にまで影響が出てしまう可能性があり、構成を変更する際のコストが通常よりもかかってしまっていました。

この 2 つの問題を解決する為に考えたのが後述する構成になります。

以前の構成

まずは okara を使っていた以前の構成図はこちらになります。

旧アーキテクチャ

以前の構成では前段に CDN として CloudFront を挟み、弊社のプライベートクラウド(通称 nyah)上に imageproxy server を複数台立てていました。その imageproxy server がオリジナルの画像のある S3 や bayt(詳細はこちら) などから画像を取得して動的に変換して画像を返すという構成になっていました。

シン・構成

続いてこちらがシン・アーキテクチャの構成図になります。

シン・アーキテクチャ

AWS Lambda を利用したシンプルな動的画像変換を行う構成にしました。前段に CDN があるのは変わらず API Gateway から Lambda が起動され Lambda 上のアプリケーションが S3 や bayt などからオリジナルの画像を取得して動的に変換して画像を返すという構成になっています。Lambda 上で動くアプリケーションに関しては Golang で作成しており、リクエスト(顧客の要求)からオーダーメイドの画像を作成するという処理を行っているので Custom-Made Image(通称 CMI)と名付けました。

移行に関しては minne では Android/iOS の両アプリが okara 用の URL の生成を行っているという理由から URL の形式に関しては okara と互換性を持たせました。実際の切り替え自体は CDN の向き先を変えるだけなので特に苦労する点なども発生しませんでした。

Golang を使った画像の拡大縮小などの処理に関しては個人ブログの記事にまとめていますのでそちらを参照してください。(Golang で色々頑張ったのですが ICC プロファイルに対応していないので結局 ImageMagick 頼りになった処理が多々あります)

移行結果

移行した結果 Serverless である Lambda を使用する事により、インスタンスの管理をする必要がなくなり大幅に管理コストが減りました。 また、各事業部ごとの差異は環境変数で管理するような実装にしたので、各事業部でシン・構成を構築する事により変更が容易になりました。構築に関しても AWS の SAM CLI を使って容易に行えます。

S3 の料金体系として同一リージョン内のデータの転送は無料というもの条件があります。 元々 minne ではオリジナルの画像を S3 に保存していました。今回の変更により Nyah 上の imageproxy server から S3 に画像を取得にいくという方法から、 Lambda が取得しにいくという方法に変更になりました。 結果として S3 からの画像のダウンロード料金が無料になり月間約 90 万円ほどのインフラコストの削減に繋がりました。

作品画像の画質向上

続いては作品画像の画質の向上についてです。 minne では長年サービス運用を行っている中でユーザから他社のサービスと比べて登録した作品の画像の画質が汚くなるという意見を頂く機会が多くなりました。作家にとってはせっかく一眼レフのカメラで綺麗に作品を撮っても画像をアップロードすると汚くなるのでは、モチベーションの低下や購入する側に正確に作品の良さが伝わらないなどの問題が発生してしまいます。

それらの課題を解決する為に昨年の 11 月に画質の向上を行うリリースを行ったのでどのように画質の向上を行ったかをご紹介します。

画像アップロード時の仕組み

minne に作品画像がアップロードされた際の仕組みは下記のようになります。minne は Rails を使用して構成されているのですが、アップロードされたオリジナルの画像と決められたサイズに ImageMagick を使用してリサイズした数パターンの画像を S3 にアップロードしています。

画質が低下する原因

画質が低下する主な原因は下記の画像を参照して頂くとわかりやすいのですが、元々 minne ではアップロードされたオリジナルの画像をアスペクト比を保ち長辺が 640px になるようにリサイズして保存していました。そのリサイズ後の画像を元に短辺を 640px に動的に変換して表示を行っていた為に、画像が引き伸ばされ結果として画質が低下するという問題に繋がっていたのです。

解決方法

この課題を解決する為に下記のような方式に変更しました。基本的な流れは同じですが、リサイズ時に長辺が 1600px になるようにリサイズし、その画像を元に短辺を 640px に動的に変換して表示を行うようになりました。オリジナルのみを保存し、オリジナルから直接動的に変換する方法も検討しましたが、オリジナルの画像サイズが大きいほど、またオリジナルとリサイズする際のサイズ差が大きいほど処理に時間がかかってしまいました。上記でも述べたとおり動的画像変換には Lambda を使っており、 Lambda の課金体系が起動時間に比例するものである点と、今後デバイスが進化していく中で画像のサイズもより大きくなっていった場合に、サービス側でインフラコストの制御が難しくなるという点から 1600px の画像を生成する方式を採用しています。

既存の登録されている画像に関しては、全ての画像に関して 1600px の画像を作り直しました。Sidekiq の job を使って S3 からオリジナルの画像を取得して、長辺が 1600px の画像を生成して S3 にアップロードするという処理を書き、約 1 億件の画像を 5 日程かけて再作成しました。Sidekiq を EKS の cluster で稼働させる事によって S3 からのダウンロード料金を節約するという事も行い、インフラコスト的には 30 万円以内で終わらせることができました。

結果

上記のような方法で画質の改善を図った結果が下記になります。直接画像を表示して確認して貰えれば違いがわかるかと思います。

シン

まとめ

今回は画像周りのリアーキテクトや画質向上についてご紹介しました。 リアーキテクトにより削減したコストを画質向上などのユーザにより良い価値を提供するという部分に当てることができ、画質向上リリース時は多くのユーザに喜んで貰えたようでよかったです。

余談ではありますが、今回紹介した以外にも minne ではアーキテクチャの刷新などを行う事により、 2020 年はコロナの影響もありアクセス数/流通額共に伸びている状態ではあるものの 2019 年比のインフラコストを約 2 割ほど削減する事に成功しました。 引き続きアーキテクチャの進化とユーザへのより良い価値提供が出来るように頑張っていきたいと思います。