対決！Amazon Web Services vs Google Cloud Platform - ストレージにアップロードした公開URLのデータ取得スピード編


こんにちは！
今回はGoogle Cloud SDKのクライアントライブラリをつかってGoogle Cloud Platformを操作してみます。
この記事では[Google Cloud Storage](https://cloud.google.com/storage/docs/overview)を操作します。
具体的には[Google Cloud Storage](https://cloud.google.com/storage/docs/overview)とAWS S3に配置したオブジェクトの取得にかかる時間を比較して対決します！
[以前の投稿](http://qiita.com/saber_chica/items/6d3a413ce73aae0f6607)でCLIを使いましたが、CLIだと実行結果をもとにして次の処理を行うには一手間必要になります。そこでSDKを使ってみたくなったのでした。
そして、書くのが楽しい(と私は感じている)RubyのSDKがあったので、RubyのSDKを使いました！
その名は"gcloud-ruby"です！
今回使用したソースコードはこちらで公開しています！> https://gist.github.com/sohda/20d1b5a2c6c1e438e800

# 前提条件

## 実行環境
* MacOS 10.10.5
* Ruby 2.2.2
    * （YosemiteはRuby2系がプリインストールされています！）

```shell-session
[sky@cloudy ~]$ ruby -v
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]
```

## 前提知識
* Google Cloud Storageの概要を知っている
    * 公式サイトにわかりやすくまとめられています> https://cloud.google.com/storage/
* AWS S3の概要を知っている
    * 公式サイトにわかりやすくまとめられています> https://aws.amazon.com/jp/s3/

# 事前準備

## Service Account 準備
[Gcloud](http://googlecloudplatform.github.io/gcloud-ruby/docs/v0.5.0/Gcloud.html)は、できるだけ簡単に認証を行うことを目指しているそうです。(GcloudというのはGoogle Cloud Platform公式のライブラリのことです！)
Google Cloudは、APIへの接続にProject IDとService Account Credentialsが必要になります。
ここではその準備を行います。
Project IDと鍵ファイル(JSON形式)を使うことでcsloudのサービスを使うことができます。
[公式サイト](http://googlecloudplatform.github.io/gcloud-ruby/docs/v0.5.0/AUTHENTICATION.md)に書いている通りにすればできました。

1. [Google Developers Console](https://console.developers.google.com/project)に行きログインします
1. 新規プロジェクトの作成、または既存プロジェクトを選択します
1. 「APIs & auth」をクリックして「APIs」を選択し、以下のAPIを有効にします
    1. Google Cloud Datastore API
    1. Google Cloud Storage
    1. Google Cloud Storage JSON API
1. 次に「APIs & auth」の「Credentials」を選択します
    * 新しい「service account」を使いたい場合、「Add Credentials」をクリックし、「Service account」を選択します
        * 作成されたら、鍵ファイル(JSON形式)をダウンロードします（リクエストの認可に使用する）
    * 既存のservice accountに新しい鍵を生成したい場合は、既存のservice accountを選択して「Generate new JSON key」をクリックし、鍵ファイル(JSON形式)をダウンロードします
	    * ここでダウンロードしたJSONは後ほどSDKを利用する際に使います

<br>

# Ruby で Google Cloud Storage ！
Google Cloud Storageにファイルをアップロードして、その表示速度を計るまでをSDKを使ったらどうなるかをご紹介します！
ソースコードの全体像を見たい場合はこちらをご覧ください> https://gist.githubusercontent.com/sohda/20d1b5a2c6c1e438e800/raw/9117b34f558ddc7974d3e3ba7986b4ecd9c4bd01/%2520try_gcp.rb


## 認証情報読み込み

```rb:try_gcp.rb
require "gcloud"

gcloud = Gcloud.new ENV["GCP_PROJECT_NAME"], ENV["GCP_SERVICE_ACCOUNT_CREDENCIAL_FILE_PATH"]
```

* 「事前準備」で選択したプロジェクト名と鍵ファイル(JSON形式)のパスを環境変数に設定するか、ENV["GCP_PROJECT_NAME"]を選択したプロジェクト名に、ENV["GCP_SERVICE_ACCOUNT_CREDENCIAL_FILE_PATH"]をダウンロードした鍵ファイル(JSON形式)のパスに書き換えてください
* 環境変数にexportする場合は以下のようにします

```shell-session
# 「2. 新規プロジェクトの作成、または既存プロジェクトを選択します」で選択したプロジェクト名が「target-project-name」の場合
[sky@cloudy ~]$ export GCP_PROJECT_NAME=target-project-name
[sky@cloudy ~]$ echo $GCP_PROJECT_NAME
target-project-name

# ダウンロードした鍵ファイル名が「key.json」で、HOMEディレクトリ直下に配置した場合
[sky@cloudy ~]$ echo $HOME
/path/to/home/
[sky@cloudy ~]$ export GCP_SERVICE_ACCOUNT_CREDENCIAL_FILE_PATH=$HOME/key.json
[sky@cloudy ~]$ echo $GCP_PROJECT_NAME
/path/to/home/key.json
```

## Google Cloud Storage のバケット作成
作成するbucket名を決めて、作成します。
"hello-gcp-sdk-ruby"は私が使ったのでそのまま実行するとエラーメッセージが返ってくるはずです。
エラーメッセージがみたい場合はそのまま実行してみてください！

```rb:try_gcp.rb
storage = gcloud.storage
bucket = storage.create_bucket "hello-gcp-sdk-ruby"
```
※ [公式ドキュメント](http://googlecloudplatform.github.io/gcloud-ruby/docs/master/Gcloud/Storage/Bucket.html)を確認しましたが、現時点(2015/11/06 時点)ではregionの指定機能はないようです。
指定せずに作成するとUS regionに作成されました。
※ 諸事情によりProxy環境下で実行していますが、gcloud-rubyではProxyはシステムのものをつかってくれるのか、SDKを使うときに別途設定する必要はなかったです。嬉しいですね！


## bucketにファイルをアップロードする
アップロードするファイルはhello-gcp-sdk-ruby.htmlです。
今回はsvgが書かれたファイルを準備しました。好きなものを準備してアップロードしてください。

```rb:try_gcp.rb
file = bucket.create_file "hello-gcp-sdk-ruby.html"
```

## アップロードしたファイルを公開する
表示速度を測りたいので公開します。

```rb:try_gcp.rb
file.acl.public!
public_url = file.public_url
```

## 公開URLを表示するまでにかかる時間を計測
どれくらい時間がかかるのか知りたくなったので100回表示した際の平均値を出力するようにします。

```rb:try_gcp.rb
cmd = %Q|curl -s #{public_url} -o /dev/null -w "%{time_total}" 2> /dev/null|

times = []
100.times { times << `#{cmd}`.to_f }
p times.inject(:+) / times.size
```

## 実行結果
約0.15秒！早そうですが、本当に早いのでしょうか…？
比較するために、AWS S3でも試してみることにします！

```shell-session
[sky@cloudy ~]$ bundle exec ruby try_gcp.rb
0.14685000000000004
```

# AWS S3 でも Ruby！
さて、比較対象としてAWS S3だとどうなるのかを試していきます！
せっかくなので解説つきで。
ソースコードの全体像を見たい場合はこちらをご覧ください> https://gist.githubusercontent.com/sohda/20d1b5a2c6c1e438e800/raw/9117b34f558ddc7974d3e3ba7986b4ecd9c4bd01/%2520try_aws.rb

## 事前準備
AWSの鍵を作成してダウンロードしたものをsecret.jsonとして保存します。
作成済みの場合はそちらをご利用ください。

```shell-session
# 値はダミーのものに代えています
[sky@cloudy try_gcp_sdk_ruby]$ cat secrets.json
{
"AccessKeyId" : "AAAAABBBBBCCCCCCDDDDD",
"SecretAccessKey" : "1a2b3C1a2b3C1a2b3C"
}
```

## 認証情報読み込み
準備した鍵ファイルを読み込みます。

```rb:try_aws.rb

require 'aws-sdk'
require 'json'

creds = JSON.load(File.read('secrets.json'))
Aws.config[:credentials] = Aws::Credentials.new(creds['AccessKeyId'], creds['SecretAccessKey'])
```

## AWS S3 のバケット作成
速度測定をするのでリージョンをGCPと合わせておきます。
gcloud-rubyではSDKを使うときに別途設定する必要はなかったですが、AWS SDK Rubyではhttp_proxyを設定します。
bucket名やfile pathは後ほど使うので変数にしておきます。

```rb:try_aws.rb
region = 'us-east-1'
s3 = Aws::S3::Client.new(region: region, http_proxy: ENV['http_proxy'])

bucket_name = "hello-aws-sdk-ruby-v2"
target_file_path = "hello-gcp-sdk-ruby.html"

bucket = s3.create_bucket(bucket: bucket_name)
```

## bucketにファイルをアップロードして公開する
gcloud-rubyではアップロードしてから公開するという手順でしたが、AWS SDK Ruby v2ではアップロード前のオプションとして指定します。

```rb:try_aws.rb
File.open(target_file_path, "rb") do |file|
  s3.put_object(
    bucket: bucket_name, acl: "public-read", body: file, key: target_file_path
  )
end

bucket = Aws::S3::Object.new(bucket_name, target_file_path, region: region)
```

## 公開URLを表示するまでにかかる時間を計測
gcloud-rubyのときと同じなので割愛します。

## 実行結果
約1.2秒！GCPと比較するとストレージにアップロードして表示するだけだとGCPの圧勝ですね！
Google Cloud Storage の方は単体で CDN 付なのでさすがな結果となりました！

```shell-session
[sky@cloudy try_gcp_sdk_ruby]$ bundle exec ruby try_aws.rb 
https://hello-aws-sdk-ruby-v2-2.s3.amazonaws.com/hello-gcp-sdk-ruby.html
1.2278399999999998

```

## 全手順

ここまでの操作は以下の手順で行いました。

```shell-session

[sky@cloudy ~]$ gem install bundler
[sky@cloudy ~]$ bundle -v
Bundler version 1.10.5
[sky@cloudy ~]$ git clone https://gist.github.com/20d1b5a2c6c1e438e800.git try_gcp_sdk_ruby
[sky@cloudy ~]$ cd try_gcp_sdk_ruby
[sky@cloudy try_gcp_sdk_ruby]$ bundle install --path bundle
[sky@cloudy try_gcp_sdk_ruby]$ bundle exec ruby try_gcp.rb
[sky@cloudy try_gcp_sdk_ruby]$ bundle exec ruby try_aws.rb

```

# 対決結果
bucketに配置したファイルの表示時間測定結果はGoogle Cloud Platformの **圧勝** でした！

| Google Cloud Storage | AWS S3 |
|:-----------|:-----------|
| 0.14685000000000004 | 1.2278399999999998 |

# フェアじゃない問題
（※ ここからコメントをいただいてから追記しました）
さて、ここまできて「こんなのGCPが勝つに決まってる！」とお怒りのあなた。
おっしゃる通りです。ここからはフェアに行きましょう。

## AWSもCDN機能を使った場合はどうなるの？
AWS S3 の方もCloud Front経由にして試してみます。結果は如何に…？
URLを引数に渡すと100回curlした結果の平均値を返してくれる、先ほども使った簡単なrubyスクリプトを使って試します。

```count_curl_result.rb
public_url = ARGV.first || nil
cmd = %Q|curl -s #{public_url} -o /dev/null -w "%{time_total}" 2> /dev/null|

times = []
100.times { times << `#{cmd}`.to_f }
p times.inject(:+) / times.size
```

GCPの公開URLは下記のようにして確認します。

```gcp-get_public_url.rb
require "gcloud"

gcloud = Gcloud.new ENV["GCP_PROJECT_NAME"], ENV["GCP_SERVICE_ACCOUNT_CREDENCIAL_FILE_PATH"]
storage = gcloud.storage
bucket = storage.bucket "hello-gcp-sdk-ruby"
file = bucket.file "hello-gcp-sdk-ruby.html"
public_url = file.public_url
p public_url
```

結果は…？

```shell-session
[sky@cloudy try_gcp_sdk_ruby]$ ruby count_time.rb http://d22ys6db6478pa.cloudfront.net/
0.06418999999999998

```
おお…！相当早くなりました！期待通りで嬉しくなります！
念のためGCPの方も実行しておきます。

```shell-session

[sky@cloudy try_gcp_sdk_ruby]$ ruby count_time.rb https://storage.googleapis.com/hello-gcp-sdk-ruby/hello-gcp-sdk-ruby.html
0.37174999999999997
```

## 対決結果（フェアバージョン）
フェアにCDN機能を使った場合だと **AWSの方が約6倍早い** という結果になりました！

| Google Cloud Storage | AWS S3(CloudFront使用版) |
|:-----------|:-----------|
| 0.37174999999999997 | 0.06418999999999998 |

（※ ここまでコメントをいただいてから追記しました）

# 参考URL
* http://googlecloudplatform.github.io/gcloud-ruby/docs/v0.5.0/AUTHENTICATION.md
* https://github.com/GoogleCloudPlatform/gcloud-ruby
* http://docs.aws.amazon.com/sdkforruby/api/Aws.html#config-class_method

# おわりに

最後まで読んでいただきありがとうございました！いかがでしたでしょうか。ご指摘など気になるところを発見した方はコメントしていただければありがたいです。
AWSの方が機能は充実していますが、proxyの設定がシステムとは別途必要ではなかったり、bucketを削除した直後でも削除したbucketと同じ名前のbucketを作り直したりできるところなど、細やかなところでGCPの心遣いを感じられ、使っていて快適に感じました。（AWSのSDKからももちろん心遣いを感じています！）
ツールを使う人のことを考えられているところは開発者としても見習いたいですね！