CloudFront+S3の画像配信システムに、サムネイルとかに使う画像のリサイズ機能を追加してみる。
要するにオリジナル画像がこのURLだとすると、
http://xxx.cloudfront.net/sample.jpg
こういうURLで100×100にリサイズできるようにする。
http://xxx.cloudfront.net/resize/100x100/sample.jpg
システム構成
元の構成はこういう想定。
画像はS3に保存され、アクセスは全てCloudFront経由。
よくあるやつですね。
これを特定URLの場合は画像変換サーバを通してリサイズするようにする。
画像変換サーバはEC2で、ちゃんとELBを使って冗長化もする。
画像変換サーバの設定
今回はnginxのimage_filterモジュールを使う。
もちろんsmall_lightを使ってもいいし、自前で開発してもいい。
インストールはAmazonLinuxであれば、ただyum install nginxするだけ。
それ以外の場合ではたぶん、--with-http_image_filter_moduleをつけてnginxをコンパイルしないといけない。
※EPELのnginxは流石にバージョンが古すぎる。
image_filterもその1つなんだけど、AmazonLinuxの標準レポジトリのnginxは、公式rpmには含まれないいくつかのモジュールが追加されているので、確認してみると面白いかも。
# nginx -V ... configure arguments: ... --with-http_image_filter_module ...
サイズを完全に自由指定できるようにするには、こんな感じで設定する。
server { listen 80 default_server; server_name _; root /var/nginx_root; location ~ ^/resize/(\d+)x(\d+)/(.*)$ { set $width $1; set $height $2; set $path $3; image_filter resize $width $height; image_filter_jpeg_quality 90; rewrite ^ /$path break; } }
これでドキュメントルート配下のファイルのリサイズが確認できるはず。
ただ開発環境はいいが、本番環境にこの設定を入れるとDOS攻撃が怖い。
例えば幅を1ずつ増やしてDOS攻撃されると、
for i in {1..10000};do curl http://xxx.cloudfront.net/resize/${i}x1000/sample.jpg;done
画像変換サーバのCPUや、前段にキャッシュ用Proxyがある場合はそのメモリを使いきってしまうかもしれない。
これを防ぐ1つの方法は、cookpadのtofuのようにURLに予測不可能なハッシュ値を含めること。
http://img.cpcdn.com/cms_articles/3453/100x100c/cdcf6aff4c78c6619b67372f129bdef2.jpg?u=9769263&p=1418093860
しかしその場合はハッシュ関数とSEEDが漏れたらURLを変更しないといけなくなるかもしれない。まぁ直リン禁止なら問題ないけど。
もう1つの方法は、本番系は特定のサイズ指定のみ許可すること。
今回はこっち。
server { listen 80 default_server; server_name _; root /var/nginx_root; location ~ ^/resize/(100x100|200x200)/(.*)$ { set $type $1; set $path $2; if ($type = 100x100) { set $width 100; set $height 100; } if ($type = 200x200) { set $width 200; set $height 200; } image_filter resize $width $height; image_filter_jpeg_quality 90; rewrite ^ /$path break; } location ~ ^/resize/ { return 404; } }
運用がちょっと面倒で応用も効きづらいけど、そのぶん実装は簡単。
設定が冗長になるのはchefとかで動的に作ればそんなに面倒じゃないはず。
CloudFront設定&nginxのProxy設定
事前にELBを作成して画像変換サーバをその配下に入れておく。
そしてManagementConsoleのCloudFront設定で、/resize/で始まる場合はS3でなくELBに渡す設定する。
- 『Origins』でELBをOriginとして追加
- 『Behaviors』でPath Patternを『resize/*』、OriginをELBにしたBehaviorを追加
あとはnginxでCloudFrontにProxyするように設定する。
ここで1つ注意があって、Proxyしたら2回CloudFrontを通ることになるんだけど、1回目通った時にループ検出のために付与されるViaヘッダをクリアしないといけない。
というわけでnginxのlocationに追加する設定は以下の2行。
proxy_set_header Via ""; proxy_pass http://d2sloa042uzrzo.cloudfront.net;
これで作業は完了。
http://d2sloa042uzrzo.cloudfront.net/kuma.jpg http://d2sloa042uzrzo.cloudfront.net/resize/100x100/kuma.jpg
まとめ
いろいろめんどくさいから、CloudFront自体に画像のリサイズ機能つかないかな。