S3にアップロードされる jpg画像をリアルタイムで webP にエンコードしたかったので
色々やってみた。

 

要件

S3 バケットには毎月1000万枚の2.2MB程度の jpg画像がアップロードされると仮定
これをアプリが引っ張る前にできるだけで速く webP にエンコードする必要性があります。

webP にエンコードする理由としては、画像の容量が小さくなることでユーザーのストレスを無くす、S3 にかかる料金を安くする、CloudFront の料金を安くするため。

ちなみに何でアプリでやらないかというと、iPhone で撮った生の画像を iPhone でエンコードを行うと、アプリがフリーズするぐらい結構重い処理だからです。致し方なし。

 

① Lambda だけで全てやってみる

S3 にアップロードをトリガーとして Lambda を起動して、Lambda でエンコードから S3 にアップロード、既存の jpg画像を削除を行うと、最小スペックで 22秒という脅威的な数値を叩き出します。
ちなみにメモリの使用量は 108/107MB 程度です。重いなあ…

全ての Lambda スペックで試し、一番安いスペックは 256MB でした。
これでも 11秒です。

256MB
枚数 秒数 課金 1枚辺り実行時間 無料枠 1秒辺り課金額
10000000 110000000 452.028 11 1600000 0.00000417
20000000 220000000 910.728

ちなみにひと月で 10000万枚を捌き切るにはこの Lambda 関数は並列で 42 個、必要になります。
$452 * 42 = 約210万… センキューAWS!

これに CloudFront の料金も加算したらとんでもない額ですね。

 

② エンコード用のサーバーを用意してみる not EC2


Lambda 関数を2つ用意し、Lambda – webP request(128MB) は Convert Server にある変換用サーバーにリクエストを送り、Convert Server がステータスコード 200 以外を返したときは、Lambda – webP encoder を呼ぶ係、Lambda – webP encoder は Convert Server  に代わってエンコードしてくれます。

Convert Server は Lambda の料金を節約するために、リクエストを受け取った瞬間に 200 を返すようにしています。サーバーにリクエストがくれば途中で天変地異や、タイミングよくサーバーが壊れる、リソース不足以外で、きちんと動作するようなコードであれば対策はできるであろうと思います。

この構成にしたところ、通常時(Lambda – webP encoderが呼ばれない)であれば、課金対象実行時間が 1,500ms となるので、Lambda だけだと
{ (1.5 * 10000000) – 3200000 } * 0.00000208 = $24.54/月 となり、結構安く済みます。

っと思いきやよく考えると
S3 から同じリージョンの EC2 への送信は 0円 に対して、AWS 外から S3 に画像を引っ張るのはお金がかかります。今回は1画像 2.2MB * 10000万枚となり、22000GBを毎月S3から持ってきます。
これだけで毎月40万近くかかります?

 

③ エンコード用のサーバーを EC2 にしてみる


先程も言った通り、S3 から同リージョンの EC2 へは無料で送受信できます。
なので、この構成でやると Lambda と EC2 と ELB の料金が発生します。

Lambda に関しては EC2 に対してリクエストをするだけなので $24.54 で済みます。

c4.large で 1000枚をエンコード


18:36 1枚辺り 1.1秒
途中で s3 socket HANG UP, ENOMEM が発生し、1000枚中10枚程度がエンコードに失敗しました。

 

c4.xlarge で 1000枚をエンコード


9:08 1枚辺り 0.5秒
ENOMEM に陥ることはなくなったものの、s3 socket HANG UP は発生しました。

この socket hang up が若干厄介で、解決方法がない。
恐らく、複数 VM からやってあげればこのエラーは無くなるような気がする。

ということで、Auto scaling グループとそれを配下とした ELB を作成し、Lambda がその ELB にアクセスするようにすればひと月で捌ききれるようになるはず。

ひと月で 1000万枚を捌ききるには c4.large で 4台が必要になるので

Lambda = $24.54
S3 = 0.025 * 6000 = $150
EC2 (c4.large * 4) = $360
ELB(時間:0.027 * 720 + GB: 0.008(GETリクエストのみ、200B計算)= $19.5
CloudFront (webP 600KB * 10000000 = 6000GB * 0.14) = $840
合計:$1394

 

④ 諦めて変換をしない

S3 にかかる料金は
S3: 0.025 * 22000(22TB) = $550
CloudFront: 0.135 * 22000 = $2970
合計:$3520

 

まとめ

1番安く済むのは EC2 にエンコード用のサーバーを用意するでした。
諦めて変換しないよりは大幅に安くできることが分かった。

ただただ jpg を webp にコンバートするだけのコードなので、技術的負債が増えるわけでもないので最初は大変ですが、ユーザーにとっても会社にとっても優しいのでやるのはアリなのではないかと思います。

jpg to webP のコードに関しては後日、記事にしようかと思います。