Amazon S3を33%安く使う方法

暑いですね、汗 汗 汗(汗3)。


Amazon Simple Storage Service、略してS3というストレージサービスがあります。
少し前に、Amazon Web Services BlogでReduced Redundancy Storage(Amazon RRS)が発表されました。
http://aws.typepad.com/aws/2010/05/new-amazon-s3-reduced-redundancy-storage-rrs.html



簡単に説明すると、S3の可用性は99.999999999%ですが、RRSは99.99%となっていて少し可用性が低いので33%安く利用出来ますよという事です。
blogから引用

RRS pricing starts at a base tier of $0.10 per Gigabyte per month, 33% cheaper than the more durable storage.

99.999999999%と99.99%の差で33%安いとなれば、お薦めですね。

使い方

http://docs.amazonwebservices.com/AmazonS3/latest/index.html?DataDurability.html

肝心な使い方ですが、コマンドベースだとs3cmdにx-amz-storage-class:REDUCED_REDUNDANCYつけてあげるだけですので、簡単ですね。


しかし、これだと新規でputする場合にしか使えません。
既存のS3に保存してあるファイルをRRSに置き換えたいという人が多いと思います。
その場合は、x-amz-storage-class: REDUCED_REDUNDANCYとx-amz-metadata-directive: COPYを付けてあげればよいです。

PUT /my-image.jpg HTTP/1.1
Host: bucket.s3.amazonaws.com
Date: Wed, 28 Oct 2009 22:32:00 GMT
x-amz-copy-source: /bucket/my-image.jpg
Authorization: AWS 0223123223RW0EXG2:0RQf4/cRonhpaBX5sCYVf1bNRuU=
x-amz-storage-class: REDUCED_REDUNDANCY
x-amz-metadata-directive: COPY


これで安くS3が使えて嬉しいなと言いたいところですが、S3のコンソールにはstrage-class情報は表示されていません(2010/07/14の時点)。
表示可能なものは無いか探したところ、ありました、S3 Browser!

S3 Browser

http://s3browser.com/working-with-reduced-redundancy-storage-rrs.php

早速ダウンロードして使ってみると、Strage Classのプロパティが表示されるうえに
strage-classをREDUCED_REDUNDANCYに変更してApplyするだけで変更されます。

しかも、あるじゃないですか、まとめて一括変換!これがやりたい!!


□Apply to Child object


チェックを入れてApplyすれば一気に変換してくれると思いチェックボックスをチェックすると



世の中甘くないですね><


AWS SDK for Java

普段、よくEclipseを利用しているのでAWS SDK for Javaで、まとめてやればいいんじゃないかと思い、やってみました。
指定バケット配下のファイルを全てRRSに変更します。

package yone098;

import java.io.IOException;

import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.Headers;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.amazonaws.services.s3.model.StorageClass;

public class S3Rrs {
  public static void main(String[] args) throws IOException {
    String accessKeyId = "アクセスキー";
    String secretAccessKey = "シークレットアクセスキー";

    AWSCredentials credentials = new BasicAWSCredentials(accessKeyId,
        secretAccessKey);
    
    ClientConfiguration configuration = new ClientConfiguration();
    configuration.setSocketTimeout(60 * 1000 * 3);
    
    AmazonS3 s3 = new AmazonS3Client(credentials);
    String bucketName = "指定するバケット名";

    long start = System.currentTimeMillis();
    try {
      // 一覧取得
      ObjectListing objectListing = s3
          .listObjects(new ListObjectsRequest()
              .withBucketName(bucketName));
      final String REDUCED_REDUNDANCY = StorageClass.ReducedRedundancy
          .toString();
      
      // copy指定metadata
      final ObjectMetadata metaData = new ObjectMetadata();
      metaData.setHeader(Headers.METADATA_DIRECTIVE, "COPY");
      for (S3ObjectSummary objectSummary : objectListing
          .getObjectSummaries()) {
        System.out.println("file name = "  + objectSummary.getKey() + "  "
            + "(size = " + objectSummary.getSize() + ")"
            + " (strage class = " + objectSummary.getStorageClass()
            + ")");
        final String keyStr = objectSummary.getKey();
        final String storageClass = objectSummary.getStorageClass();
        final String bucket = objectSummary.getBucketName();
        // StrageClassがRRSじゃない場合にのみRRSに
        if (!REDUCED_REDUNDANCY.equals(storageClass)) {
          long start1 = System.currentTimeMillis();
          S3Object object = s3.getObject(new GetObjectRequest(
              bucketName, keyStr));
          PutObjectRequest req = new PutObjectRequest(bucket, keyStr,
              object.getObjectContent(),
              object.getObjectMetadata());
          // RRSの指定を行う
          req.setStorageClass(StorageClass.ReducedRedundancy);
          req.setMetadata(metaData);
          s3.putObject(req);
          long end1 = System.currentTimeMillis();
          System.out.println(" time=" + (end1 - start1) + "ms");
        }
      }
      System.out.println();
      long end = System.currentTimeMillis();
      System.out.println("### All time = [" + (end - start) + "]ms ###");
    } catch (AmazonServiceException ase) {
      System.out
          .println("Caught an AmazonServiceException, which means your request made it "
              + "to Amazon S3, but was rejected with an error response for some reason.");
      System.out.println("Error Message:    " + ase.getMessage());
      System.out.println("HTTP Status Code: " + ase.getStatusCode());
      System.out.println("AWS Error Code:   " + ase.getErrorCode());
      System.out.println("Error Type:       " + ase.getErrorType());
      System.out.println("Request ID:       " + ase.getRequestId());
    } catch (AmazonClientException ace) {
      System.out
          .println("Caught an AmazonClientException, which means the client encountered "
              + "a serious internal problem while trying to communicate with S3, "
              + "such as not being able to access the network.");
      System.out.println("Error Message: " + ace.getMessage());
    }
  }
}

10Mのファイルを60ファイルほど一括でRRSにしてみたところ、10Mのファイル1つの変更で約200〜300秒かかりました。
会社帰りに流しておくと良いのではないでしょうか。


監視社会

S3のコンソールからはRRSかどうか確認出来ないし、RRSにも変更出来ないから
S3にあるファイル群を一括RRSにするプログラム書いたんだよねーなんて話していたら、数時間後にAWS Blogに以下のpostが。

AWS Management Console Support for S3 RRS

http://aws.typepad.com/aws/2010/07/aws-management-console-support-for-s3-rrs.html


早速使ってみると、コンソールからRRSかどうか確認出来る上にRRSに更新まで可能になってます。
さすがですね、Amazonさま!


まるでAmazonに私がCloud Watchされてるかのようなタイミングでした。


S3BrowserのようにRRSかどうかを参照しつつ、更新出来るものは需要あるだろうから作って公開しようかななんていた矢先の事です。


しかしS3のコンソールでは、S3 Browser Proにあるようにバケット指定で一括変換はまだ対応されてないようです。
ということで、現在無償で一括でRRSにするものは無いと思いますので、是非、使ってみてください(自己責任でお願いします)


まとめ

RRSとRSS、EBSとESB、EC2とECC、RDSとグラディウスAWSのサービス名をうっかり間違えそうになった事が何度かあります。


個人的には、RRSへの乗換を是非お薦めしたいです。


Java以外の言語でも、リクエストのヘッダに情報をつけるだけですので是非実装してみてください。


RRSのBlogポスト直後のAWS Blogのポスト

Amazon S3 and Amazon SNS - Best Friends Forever
http://aws.typepad.com/aws/2010/07/amazon-s3-and-amazon-sns-best-friends-forever.html


AWS and yone098 - Best Friends Foreverでお願いします。