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自体に画像のリサイズ機能つかないかな。