ラベル s3 の投稿を表示しています。 すべての投稿を表示
ラベル s3 の投稿を表示しています。 すべての投稿を表示

2014年2月18日火曜日

Dockerってなんじゃ?(S3プライベートレジストリ)



前回に引き続き、プライベートレジストリです。

前回の方法では、レジストリのコンテナを載せたサーバー自体が落ちてしまったときに登録されたコンテナイメージが全てなくなってしまいます。
dockerのregistryコンテナには、設定ファイルが存在し、永続化のオプションとしてバックエンドにS3を使うことができます。

それでは早速試してみます。


レジストリ側の設定


レジストリのコンテナをbashで起動します。
$ docker run -t -i registry /bin/bash

レジストリに入ったら設定ファイルがある/docker-registry/config/フォルダに移動します。
S3用のサンプルがあるのでそれをconfig.ymlとして使います。

dockerのレジストリでは設定ファイルに_env:VARIABLENAMEとなっている部分があり、環境変数をセットしている部分です。起動時に-eオプションでその環境変数をコンテナに渡すことが出来ます。

このS3用のファイルは、AWSキーやバケット名などに環境変数をセットできるようになっているので起動時にパラメータ渡しが可能です。今回はそのまま変更なしで使います。
# cd /docker-registry/config
# mv config.yml config.yml.org
# cp config_s3.yml config.yml
# cat config.yml
~(略)~
prod:
    storage: s3
    boto_bucket: _env:AWS_BUCKET
    s3_access_key: _env:AWS_KEY
    s3_secret_key: _env:AWS_SECRET
    s3_bucket: _env:AWS_BUCKET
    s3_encrypt: true
    s3_secure: true
    secret_key: REPLACEME
    s3_encrypt: true
    s3_secure: true
    storage_path: /images


接続を終了し、コミットします。
# exit;
# docker ps -a
CONTAINER ID        IMAGE                         COMMAND                CREATED             STATUS              PORTS                    NAMES
1e1e890b9647        registry:0.6.5                /bin/bash              6 minutes ago       Exit 0                                       grave_wozniak

# docker commit 1e1e890b9647 memorycraft/registry


コミットしたS3用のレジストリイメージを起動します。その際前述のように、-eオプションでAWSキーやバケット名などを環境変数として渡します。
また、SETTINGS_FLAVOR環境変数は、config.ymlのprodと対応しています。これによって設定ファイルの各モードを起動時に選択することができます。
# docker run -p 5000:5000 -e SETTINGS_FLAVOR=prod -e AWS_KEY=XXXXXXX -e AWS_SECRET=YYYYYYYYY -e AWS_BUCKET=memorycraft-docker-registry -d memorycraft/registry
#
#
# docker ps -a
CONTAINER ID        IMAGE                         COMMAND                CREATED             STATUS              PORTS                    NAMES
1e1e890b9647        registry:0.6.5                /bin/bash              6 minutes ago       Exit 0                                       grave_wozniak
413aa68a3ad1        memorycraft/registry:latest   /docker-registry/run   9 hours ago         Up 9 hours          0.0.0.0:5000->5000/tcp   prickly_davinci

これで、S3対応のレジストリができました。




S3レジストリへの登録


それではクライアント側からこのレジストリにpushしてみます。流れは前回の記事と同じです。

# docker ps -a
CONTAINER ID        IMAGE                       COMMAND                CREATED             STATUS              PORTS                                          NAMES
ee60c633c8f9        memorycraft/centos:latest   /usr/bin/supervisord   3 days ago          Up 3 days           0.0.0.0:49189->22/tcp, 0.0.0.0:49190->80/tcp   happy_brattain
c118bcc97b1e        539c0211cd76                /bin/bash              5 days ago          Exit 0

# docker commit ee60c633c8f9 176.34.16.242:5000/memorycraft/centos
b939188f4672b83d03e90ad12c4ad9e2ccdfa66d2f50fd44ae18ef314eee5c5b

# docker images
REPOSITORY                              TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
176.34.16.242:5000/memorycraft/centos   latest              b939188f4672        14 seconds ago      443.8 MB
memorycraft/centos                      latest              d0c94b943ba2        4 days ago          437.9 MB

# docker push 176.34.16.242:5000/memorycraft/centos
The push refers to a repository [176.34.16.242:5000/memorycraft/centos] (len: 1)
Sending image list
Pushing repository 176.34.16.242:5000/memorycraft/centos (1 tags)
539c0211cd76: Image successfully pushed
380423464fbc: Image successfully pushed
dc52da789c75: Image successfully pushed
b2ab60219415: Image successfully pushed
52b555115035: Image successfully pushed
a149f9038d0e: Image successfully pushed
3897f6889349: Image successfully pushed
bd1a450e0e46: Image successfully pushed
da6f1a424b7c: Image successfully pushed
4a8d2a1dab88: Image successfully pushed
af06476dc08c: Image successfully pushed
65ec465a844b: Image successfully pushed
318326461017: Image successfully pushed
fc6935aadec7: Image successfully pushed
9022a04f5b3f: Image successfully pushed
4787e46941f7: Image successfully pushed
30f9368972bb: Image successfully pushed
dc6de6feb9a9: Image successfully pushed
d0c94b943ba2: Image successfully pushed
b939188f4672: Image successfully pushed
Pushing tags for rev [b939188f4672] on {http://176.34.16.242:5000/v1/repositories/memorycraft/centos/tags/latest}


無事にpushできたようです。
それではS3バケットを覗いてみると、リポジトリのメタデータやイメージなどがアップされているのがわかります。





レジストリを消してみる


ここで、一度、レジストリ側のサーバーが壊れてしまった。もしくはインスタンスが消えてしまった場合を想定して、0から別のサーバーにレジストリを立てて見たいと思います。

# docker run -t -i registry /bin/bash
# cd /docker-registry/config
# mv config.yml config.yml.org
# cp config_s3.yml config.yml
# cat config.yml
# exit


# docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS               NAMES
927bb1ccf9dc        registry:0.6.5      /bin/bash           About a minute ago   Exit 0                                  trusting_mccarthy
# docker commit 927bb1ccf9dc memorycraft/registry
# docker run -p 5000:5000 -e SETTINGS_FLAVOR=prod -e AWS_KEY=XXXXXXXXXXXXXXXX -e AWS_SECRET=YYYYYYYYYYYYYYYY -e AWS_BUCKET=memorycraft-docker-registry -d memorycraft/registry /docker-registry/run.sh



S3からpull


念のためクライアント側もイメージもコンテナも消した状態で、S3レジストリを指定してrunしてみます。
# docker run -t -i 176.34.16.242:5000/memorycraft/centos /bin/bash
Unable to find image '176.34.16.242:5000/memorycraft/centos' (tag: latest) locally
Pulling repository 176.34.16.242:5000/memorycraft/centos
b939188f4672: Download complete
da6f1a424b7c: Download complete
dc6de6feb9a9: Download complete
af06476dc08c: Download complete
b2ab60219415: Download complete
65ec465a844b: Download complete
4a8d2a1dab88: Download complete
3897f6889349: Download complete
a149f9038d0e: Download complete
539c0211cd76: Download complete
30f9368972bb: Download complete
52b555115035: Download complete
bd1a450e0e46: Download complete
318326461017: Download complete
4787e46941f7: Download complete
380423464fbc: Download complete
dc52da789c75: Download complete
d0c94b943ba2: Download complete
9022a04f5b3f: Download complete
fc6935aadec7: Download complete
bash-4.1#

ちゃんと取得できて、コンテナを立ち上げることが出来ました。
これで、S3の高い堅牢性可用性をバックエンドにしたレジストリができました。

以上です。

2013年9月26日木曜日

EBSってなんじゃ?(cryptsetup + S3 + IAM Roleでディスク暗号化鍵をS3で管理)

EBSの暗号化の方法のひとつとしてcryptsetupという技術があります。cryptsetupはパスフレーズで暗号化されたディスクにアクセス(マウント)しますが、今回は鍵ファイルを使用してアクセスする手順をまとめました。
また、鍵ファイルを同じインスタンス内に置かないためにS3から鍵ファイルを取得し、マウントした後に削除するようにします。

それでは手順を追ってみます。

まずcryptsetupで/dev/xvdfにアタッチされたEBSボリュームの暗号化を行い、マウントします。
# yum install -y cryptsetup
# yum install xfsprogs -y
# cryptsetup luksFormat -c aes -h sha256 /dev/xvdf
# cryptsetup luksOpen /dev/xvdf encrypted
# ls -l /dev/mapper/
# mkfs.xfs /dev/mapper/luks
# mkdir /mnt/vol
# mount -t xfs /dev/mapper/luks /mnt/vol
# mkdir /mnt/vol/data


次に鍵ファイルを作成し、cryptsetupに登録します
# dd if=/dev/urandom of=/root/encrypted_key bs=1 count=1024
# cryptsetup luksAddKey /dev/xvdf /root/encrypted_key


S3バケットを作成し、そこへ鍵を保管します




自動で暗号化+マウントするために起動スクリプトをつくり、S3から鍵ファイルを取得しマウントするようにします。終わったらAMIを作成します。
# cat /etc/init.d/cryptmount
-----------
#!/bin/sh
#
#
# chkconfig: 345 60 16

aws s3 get-object --region ap-northeast-1 --bucket luks-key --key xvdf_luks_key /boot/xvdf_luks_key
cryptsetup luksOpen /dev/xvdf luks --key-file /boot/xvdf_luks_key
mount -t xfs /dev/mapper/luks /mnt/vol
rm -rf /boot/xvdf_luks_key
-----------

# rm -rf /root/xvdf_luks_key



IAM Roleを作成し、S3へのアクセスを許可します



作成したAMIの起動時にIAM Roleを指定します




これで、鍵がない状態ではEBSをマウントすることができなくなりました。
以上です。

2013年5月28日火曜日

Fluentdってなんじゃ?(設定ファイルをリモートから読み込む)

fluentdのドキュメントに以下のような記載がありました。

Configuration File - (3) Include Directive
# absolute path
include /path/to/config.conf

# if using a relative path, the directive will use 
# the dirname of this config file to expand the path
include extra.conf

# glob match pattern
include config.d/*.conf

# http
include http://example.com/fluent.conf

最後の行、、、HTTPから設定ファイルを取得してインクルードできるみたいです。

ということで試してみました。

common.conf
<source>
  type tail
  format apache
  pos_file /var/log/td-agent/httpd-access.log.pos
  path /var/log/httpd/access_log
  tag apache.access
</source>

<match apache.access>
  type s3

  aws_key_id xxxxxxxxxxxxxxxxxxx 
  aws_sec_key yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
  s3_bucket hoge-bucket
  path logs/
  buffer_path /var/log/fluent/s3
  flush_interval 10s 
</match>

これをS3にアップロードしてWEBホスティングしてみます。


そして、EC2では以下のように設定します。

/etc/td-agent/td-agent.conf
include http://s3-ap-northeast-1.amazonaws.com/hoge-bucket/conf/fluentd/common.conf
# /etc/init.d/td-agent start


httpdのコンテンツにアクセスしてしばらくたつと、、、


無事出力されていました!

これの何がいいかというと、

  • 各サーバーに共通の設定ファイルなどを外部化しておき、一括変更をかけられる (再起動は必要ですが、定期的に再起動をかけるようにしておくという手もあります)
  • いっそ全設定を外出しして完全に外部で管理する

などが可能になります。

以上です。

2013年4月17日水曜日

EMRってなんじゃ?(別アカウントのS3に入出力)

EMRをつかって、別のアカウントのS3バケットにアクセスしたい時があります。

ここでは例として、アカウントAのEMRからアカウントBのS3のログを集計して、アカウントBのS3バケットへ出力してみます。

まず、アカウントBのS3バケットのACLを設定します。
方法は以前の記事と同じようにSDKで設定します。
S3ってなんじゃ?(S3のログファイルを別アカウントでダウンロード


$src = 'memorycraft-log';//入力ログバケット
$target = 'memorycraft-archive';//出力先バケット
$owner_canonical_id = 'オーナー(アカウントB)の標準ユーザーID';
$other_canonical_id = '別アカウント(アカウントA)の標準ユーザーID';
$s3 = new AmazonS3(array('key'=>'オーナーのアクセスキー','secret'=>'オーナーのシークレットキー');
$s3->set_region(AmazonS3::REGION_APAC_NE1);

$res = $s3->set_bucket_acl($src, array(
  array('id' => AmazonS3::USERS_LOGGING,     'permission' => AmazonS3::GRANT_READ_ACP),
  array('id' => AmazonS3::USERS_LOGGING,     'permission' => AmazonS3::GRANT_WRITE),
  array('id' => $other_canonical_id,         'permission' => AmazonS3::GRANT_FULL_CONTROL),
  array('id' => $owner_canonical_id,         'permission' => AmazonS3::GRANT_FULL_CONTROL),
));

$res = $s3->set_bucket_acl($target, array(
  array('id' => $other_canonical_id,         'permission' => AmazonS3::GRANT_FULL_CONTROL),
  array('id' => $owner_canonical_id,         'permission' => AmazonS3::GRANT_FULL_CONTROL),
));



このままの状態でEMR(Hive)で出力すると出力されたファイルは以下のようになります。





パーミッションがありません。
これは出力するファイル自体のパーミッションの設定がおかしいためです。
これに権限を追加するにはHiveの場合は、HiveScriptの先頭に以下のコマンドを追加します。

set fs.s3.canned.acl=BucketOwnerFullControl;

再度実行すると、出力されたファイルにも権限が与えられているのがわかります。





この説明に関しては、最近日本語化されたEMRの公式ドキュメントにPigやカスタムJARの場合の対応法とともに詳しく記載されています。
http://docs.aws.amazon.com/ja_jp/ElasticMapReduce/latest/DeveloperGuide/emr-s3-acls.html

これで、アカウントをまたいだリソース集計が可能になります。
以上です。

2013年4月15日月曜日

EMRってなんじゃ?(HiveでS3のログをJSTに変換して1日分をまとめる)

S3のwebホスティングで、ログ出力の設定をしていた場合、ログファイルが大量に出力されます。
ログの記録時間は標準時で出力されていてわかりづらいです。

今回はEMRのHiveを利用して、日本時間の0時〜翌日の0時までのログを1ファイルにまとめてみたいと思います。


HiveScript



HiveScriptは以下のようにします。
まずフォーマット解析用のJARを読み込みます。

add jar /home/hadoop/hive/contrib/hive-contrib-0.8.1.jar;

入力用のテーブルを定義します。LOCATIONは引数から受け取ります。
ネックとなるのは、S3のログは基本的に半角スペース区切りですが、項目の値の中に半角スペースが入り込むため、「FIELDS TERMINATED BY」句ではなく、SERDEを使用して正規表現でログの1行を項目分けします。
今回は日付フィールドはそのまま年月日と時間部分に分けてしまいます。
CREATE EXTERNAL TABLE IF NOT EXISTS log (
 bucket_owner string,
 bucket_name string,
 log_time string,
 log_timezone string,
 remote_ip string,
 requester string,
 request_id string,
 operation string,
 key string,
 request_uri string,
 http_status string,
 error_code string,
 bytes_sent string,
 object_size string,
 total_time string,
 turn_around_time string,
 referer string,
 user_agent string,
 version_id string
) ROW FORMAT SERDE 'org.apache.hadoop.hive.contrib.serde2.RegexSerDe'
WITH SERDEPROPERTIES (
  "input.regex" = "([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*) ([^ \"]*|\"[^\"]*\") ([^ ]*) ([^ ]*) (-|[0-9]*) (-|[0-9]*) (-|[0-9]*) (-|[0-9]*) ([^ ]*) ([^ \"]*|\"[^\"]*\") ([^ ]*)",
      "output.format.string" = "%1$s %2$s %3$s %4$s %5$s %6$s %7$s %8$s %9$s %10$s %11$s %12$s %13$s %14$s %15$s %16$s %17$s %18$s %19$s"
)
LOCATION '${INPUT_BUCKET_LOCATION}';



出力用のテーブルを定義します。LOCATIONは引数から取得します。
パーティションはyyyy,mm,ddという単位で分割します。
CREATE EXTERNAL TABLE IF NOT EXISTS log_archive (
 bucket_owner string, 
 bucket_name string, 
 log_time string,  
 remote_ip string, 
 requester string, 
 request_id string, 
 operation string, 
 key string, 
 request_uri string, 
 http_status string, 
 error_code string, 
 bytes_sent string, 
 object_size string, 
 total_time string, 
 turn_around_time string, 
 referer string, 
 user_agent string, 
 version_id string
 )
 PARTITIONED BY (yyyy string, mm string, dd string)
 ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' STORED AS TEXTFILE LOCATION '${OUTPUT_BUCKET_LOCATION}'; 


集計設定をします。
DynamicPartitionを無効にし、出力をgz圧縮します。
set hive.exec.dynamic.partition= false;
set hive.exec.compress.output = true;


集計します。
対象日のYYYY、MM、DDを引数から受け取ります。
ポイントとなるのは日付の扱いで、日時分割した年月日の方のカラムを0:00:00としてタイムスタンプに変換しJST⇛UTC変換したもので当日分のログとしてフィルタしています。
INSERT INTO TABLE log_archive PARTITION (yyyy='${YYYY}', mm='${MM}', dd='${DD}')
SELECT
 bucket_owner,
 bucket_name,
 concat("[", from_utc_timestamp(from_unixtime(unix_timestamp(concat(log_time, " ", log_timezone), '[dd/MMM/yyyy:HH:mm:ss Z]')), 'JST')," +0900]") as jsttime,
 remote_ip,
 requester,
 request_id,
 operation,
 key,
 request_uri,
 http_status,
 error_code,
 bytes_sent,
 object_size,
 total_time,
 turn_around_time,
 referer,
 user_agent,
 version_id
 FROM
 log
 WHERE
 unix_timestamp(concat(log_time, " ", log_timezone), '[dd/MMM/yyyy:HH:mm:ss Z]') >= unix_timestamp(to_utc_timestamp(concat('${YYYY}-${MM}-${DD}', ' 00:00:00'), 'JST')) 
AND
 unix_timestamp(concat(log_time, " ", log_timezone), '[dd/MMM/yyyy:HH:mm:ss Z]') < unix_timestamp(to_utc_timestamp(concat(date_add('${YYYY}-${MM}-${DD}', 1),' 00:00:00'), 'JST'))
 SORT BY
 jsttime
;



実行


Hiveジョブフローの主な起動設定などは以下の通りです。
Extra ArgsでHiveスクリプトに渡す引数を設定しています。





実行すると以下のように出力されます。




中身を見ると以下のように1日分がまとまっています。
また、日時の部分はJSTに変換して出力されています。

b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,myfirst-bucket,[2013-03-20 01:29:44 +0900],10.115.82.47,arn:aws:iam::821635308497:user/iam-user,52A74398D7C4DBFE,REST.GET.VERSIONING,-,"GET /myfirst-bucket?versioning HTTP/1.1",200,-,162,-,28,-,"-","S3Console/0.4",-
b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,myfirst-bucket,[2013-03-20 01:29:44 +0900],10.115.82.47,arn:aws:iam::821635308497:user/iam-user,7CB888CE4F00C988,REST.GET.BUCKET,-,"GET /myfirst-bucket?prefix=&max-keys=100&marker=&delimiter=/ HTTP/1.1",200,-,1254,-,1044,1043,"-","S3Console/0.4",-
b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,myfirst-bucket,[2013-03-20 01:29:44 +0900],10.15.128.6,arn:aws:iam::821635308497:user/iam-user,473043B904924EB1,REST.GET.LOCATION,-,"GET /myfirst-bucket?location HTTP/1.1",200,-,142,-,29,-,"-","S3Console/0.4",-
b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,myfirst-bucket,[2013-03-20 01:29:44 +0900],10.15.149.57,arn:aws:iam::821635308497:user/iam-user,E1A726EAAA9ED195,REST.GET.LOCATION,-,"GET /myfirst-bucket?location HTTP/1.1",200,-,142,-,47,-,"-","S3Console/0.4",-
b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,myfirst-bucket,[2013-03-20 01:29:49 +0900],10.115.82.47,arn:aws:iam::821635308497:user/iam-user,648FA2CF0669ED48,REST.GET.VERSIONING,-,"GET /myfirst-bucket?versioning HTTP/1.1",200,-,162,-,26,-,"-","S3Console/0.4",-
b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,myfirst-bucket,[2013-03-20 01:29:49 +0900],10.115.82.47,arn:aws:iam::821635308497:user/iam-user,4620CF20BB92DE10,REST.GET.BUCKET,-,"GET /myfirst-bucket?prefix=&max-keys=100&marker=&delimiter=/ HTTP/1.1",200,-,1254,-,601,600,"-","S3Console/0.4",-
b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,myfirst-bucket,[2013-03-20 01:29:51 +0900],10.115.82.47,arn:aws:iam::821635308497:user/iam-user,AD489CC457FB430B,REST.GET.ACL,-,"GET /myfirst-bucket?acl HTTP/1.1",200,-,1317,-,406,-,"-","S3Console/0.4",-
b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,myfirst-bucket,[2013-03-20 01:29:51 +0900],10.115.144.24,arn:aws:iam::821635308497:user/iam-user,700100DED6F992D5,REST.GET.NOTIFICATION,-,"GET /myfirst-bucket?notification HTTP/1.1",200,-,115,-,21,-,"-","S3Console/0.4",-
b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,myfirst-bucket,[2013-03-20 01:29:51 +0900],10.115.144.24,arn:aws:iam::821635308497:user/iam-user,EEF9831033863E2B,REST.GET.LOGGING_STATUS,-,"GET /myfirst-bucket?logging HTTP/1.1",200,-,932,-,414,-,"-","S3Console/0.4",-

〜〜中略〜〜

+0900],10.89.198.21,b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,139A543A03AB1E01,REST.GET.ACL,-,"GET /?acl=&x-amz-security-token=AQYGQXBwVGtubLMUm1%2BEi%2BJ69YRAEYru2QGPPayzdgIKktgcIhBULb3hBbeFaN3DjPydiCyvDuvQ7g4kucsy0nQsWnAMBR2zfK1R%2B7Y7%2BdVgxVeH2fVJ9FQLFTkg2kkCaVj%2Fj%2FEWi8r%2F1hShQ4prv8OLBfDFETmtdw%3D%3D HTTP/1.1",200,-,1317,-,14,-,"-","aws-sdk-java/unknown-version Linux/2.6.18-194.17.4.el5.acc4xen Java_HotSpot(TM)_Server_VM/20.12-b01",-
b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,myfirst-bucket,[2013-03-20 17:36:44 +0900],10.89.198.21,b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,5FA69CCF9D1F8ABF,REST.GET.BUCKET,-,"GET /?max-keys=0&x-amz-security-token=AQAGQXBwVGtu2GApXRs%2FDi9mf5qWz55MQRSyS9v%2FnmGvqPo7lsP%2BAkR1V57UhWpJlAn6tYGp2tOL%2BC6Ai1BrRVOWUdUp6JkO%2FMqep7zd4h%2B5xJP8HtcO6pEWEV6t%2BikUh0qYfG8TatCWKvh15j6qu3XExYR5fnveRw%3D%3D HTTP/1.1",200,-,237,-,19,19,"-","aws-sdk-java/unknown-version Linux/2.6.18-194.17.4.el5.acc4xen Java_HotSpot(TM)_Server_VM/20.12-b01",-
b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,myfirst-bucket,[2013-03-20 17:36:44 +0900],10.89.198.21,b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543,EB88F164094C5ECB,REST.GET.ACL,-,"GET /?acl=&x-amz-security-token=AQEGQXBwVGtuw71vZ%2F%2BhXOKS3VoBVeYAGzc2Csc0R73DDmsCndxf9cMwWqyG9fGRYM%2F%2BJVpuKk4gdS%2Ftr64M7O2VA%2BmzWfPUz8eSwYA3zvEmdBvC8JJrDlvDImPfhqi8mXHF5weAqj2byecuDWLTqJ7AQZBtY3b8dQ%3D%3D HTTP/1.1",200,-,1317,-,26,-,"-","aws-sdk-java/unknown-version Linux/2.6.18-194.17.4.el5.acc4xen Java_HotSpot(TM)_Server_VM/20.12-b01",-


これで日本時間でのアクセス集計ができました。 以上です。

2013年3月20日水曜日

S3ってなんじゃ?(S3のログファイルを別アカウントでダウンロード)

S3でWEBホスティングをする場合、アクセスログを別のバケットに出力することができます。
そのS3のアクセスログに別のアカウントからアクセスしてダウンロードなどをしたい場合があります。
今回はその方法を紹介します。

S3のアクセスログを設定にするには、AWSコンソールでS3のバケットの設定をします。


Webホスティングの設定をしていると、上図のように設定することで、memorycraft-logのlogs/以下にこのバケットのアクセスログが出力されます。



このログファイルの一覧をSDK(PHP)を使って別アカウントから取得してみます。
$s3 = new AmazonS3(array('key'=>'別アカウントのアクセスキー','secret'=>'別アカウントのシークレットキー'));
$s3->set_region(AmazonS3::REGION_APAC_NE1);
$objects = $s3->get_object_list($bucket);
echo "objects=".print_r($objects, true);


権限がないため取得できないため、出力もされません
objects=


また、ログファイルを別アカウントでダウンロードしようとすると、、、、
$s3 = new AmazonS3(array('key'=>'別アカウントのアクセスキー','secret'=>'別アカウントのシークレットキー'));
$s3->set_region(AmazonS3::REGION_APAC_NE1);
$res = $s3->get_object('memorycraft-log', 'logs/2013-03-17-05-34-13-347E8EF10D93460B');
print_r($res);


権限がないためアクセスできません。
CFResponse Object
(
    [header] => Array
        (
            [x-amz-request-id] => 8702DF59DF4D6E86
            [x-amz-id-2] => G/Vvv2NzYqTDbI3WAUHgtp2GCqyBQdMoMaivWNii2utRXezRJUxD/hvCOQjBTCGw
            [content-type] => application/xml
            [transfer-encoding] => chunked
            [date] => Tue, 19 Mar 2013 17:44:30 GMT
            [server] => AmazonS3
            [_info] => Array
                (
                    [url] => https://memorycraft-log.s3-ap-northeast-1.amazonaws.com/logs/2013-03-17-05-34-13-347E8EF10D93460B
                    [content_type] => application/xml
                    [http_code] => 403
                    [header_size] => 254
                    [request_size] => 751
                    [filetime] => -1
                    [ssl_verify_result] => 0
                    [redirect_count] => 0
                    [total_time] => 0.644282
                    [namelookup_time] => 0.551464
                    [connect_time] => 0.551971
                    [pretransfer_time] => 0.615103
                    [size_upload] => 0
                    [size_download] => 231
                    [speed_download] => 358
                    [speed_upload] => 0
                    [download_content_length] => -1
                    [upload_content_length] => 0
                    [starttransfer_time] => 0.644058
                    [redirect_time] => 0
                    [certinfo] => Array
                        (
                        )

                    [primary_ip] => 27.0.1.206
                    [redirect_url] => 
                    [method] => GET
                )

            [x-aws-request-url] => https://memorycraft-log.s3-ap-northeast-1.amazonaws.com/logs/2013-03-17-05-34-13-347E8EF10D93460B
            [x-aws-redirects] => 0
            [x-aws-stringtosign] => GET

application/x-www-form-urlencoded
Tue, 19 Mar 2013 17:44:30 GMT
/memorycraft-log/logs/2013-03-17-05-34-13-347E8EF10D93460B
            [x-aws-requestheaders] => Array
                (
                    [Content-Type] => application/x-www-form-urlencoded
                    [Date] => Tue, 19 Mar 2013 17:44:30 GMT
                    [Authorization] => AWS XXXXXXXXXXXXXXXXXX:UocmEFK0Cv4YveXulElQxR2J2cw=
                    [Expect] => 
                )

        )

    [body] => <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>8702DF59DF4D6E86</RequestId><HostId>G/Vvv2NzYqTDbI3WAUHgtp2GCqyBQdMoMaivWNii2utRXezRJUxD/hvCOQjBTCGw</HostId></Error>
    [status] => 403
)



ここで、現状のアクセス権限をみてみます。
バケットは以下のような設定です。
  • memorycraft(バケットオーナー):フルコントロール
  • LogDelivery(AWSのログ出力ユーザー):更新、権限チェック




また、ログファイル自体は以下のようになっています。
  • memorycraft(バケットオーナー):フルコントロール
  • s3-log-service(ファイルオーナー):フルコントロール




このように、通常だとAWSのログサービスとバケットオーナーにしか権限が与えられていないためアクセスできません。


ここで、バケットにSDKから権限を設定してみます。
$target = 'memorycraft-log';
$owner_canonical_id = 'オーナーの標準ユーザーID';
$other_canonical_id = '別アカウントの標準ユーザーID';
$s3 = new AmazonS3(array('key'=>'オーナーのアクセスキー','secret'=>'オーナーのシークレットキー'));
$s3->set_region(AmazonS3::REGION_APAC_NE1);

$res = $s3->set_bucket_acl($target, array(
  array('id' => AmazonS3::USERS_LOGGING,     'permission' => AmazonS3::GRANT_READ_ACP),
  array('id' => AmazonS3::USERS_LOGGING,     'permission' => AmazonS3::GRANT_WRITE),
  array('id' => $other_canonical_id,         'permission' => AmazonS3::GRANT_FULL_CONTROL),
  array('id' => $owner_canonical_id,         'permission' => AmazonS3::GRANT_FULL_CONTROL),
));

ここでいう標準ユーザーIDは、canonical_idとも言い、S3でのリソース共有に使用されるAWSアカウントに紐づく識別子です。標準ユーザーIDはアカウントのセキュリティページで確認できます。



 設定が完了すると、AWSコンソール上では以下のように権限が追加されたことが確認できます。




一覧取得とダウンロードを再度試してみます。

ファイルの一覧はできるようになりました。
objects=Array
(
    [0] => logs/
    [1] => logs/2013-03-17-02-33-50-04A9653054E658C0
    [2] => logs/2013-03-17-02-34-38-B68F986342D3F643
    [3] => logs/2013-03-17-02-37-03-633F736726FEDD7D
    [4] => logs/2013-03-17-03-15-48-4930EA30E66582CB
    [5] => logs/2013-03-17-03-15-49-29960D2BF338C203
    [6] => logs/2013-03-17-03-16-26-ED6062BA31A1B490
    [7] => logs/2013-03-17-05-16-45-99184D52B11E91BA
    [8] => logs/2013-03-17-05-16-58-ADD438AA47F74064
    [9] => logs/2013-03-17-05-29-26-D7BF5ECA4A0EB11C
    [10] => logs/2013-03-17-05-29-56-32C233B0252FF726
    [11] => logs/2013-03-17-05-34-13-347E8EF10D93460B
    [12] => logs/2013-03-17-05-35-02-65E964A6CE7F2809
    [13] => logs/2013-03-17-05-38-17-3C15C22739B25B64
    [14] => logs/2013-03-17-14-29-27-CE3C5D978D0410EC
    [15] => logs/2013-03-18-02-17-28-C2CA7570ADE47218
    [16] => logs/2013-03-19-15-16-34-97BE8A99E16EAB77
    [17] => logs/2013-03-19-15-16-39-80209A13C1DD677A
    [18] => logs/2013-03-19-15-17-03-551FB7D0F64B54AE
    [19] => logs/2013-03-19-15-30-00-3108CB5D1133380D
    [20] => logs/2013-03-19-15-32-53-7866E864159240D4
    [21] => logs/2013-03-19-15-33-15-B5E022E930A4DE0F
    [22] => logs/2013-03-19-15-36-45-F8194516221FF1F4
    [23] => logs/2013-03-19-15-37-26-E8855A5E344B083F
    [24] => logs/2013-03-19-17-15-41-444231C9F7F6FD69
    [25] => logs/2013-03-19-17-16-07-1FE5A409A6EEE3F3
    [26] => logs/2013-03-19-17-16-40-9D477C0727E9CF9F
    [27] => logs/2013-03-19-17-34-22-AB7B385D4C73382E
    [28] => logs/2013-03-19-17-42-05-69E17011ED7FEC05
    [29] => logs/2013-03-19-19-15-26-C806C3AB6A82BABE
    [30] => logs/2013-03-19-19-15-51-C75A28CBEA846E35
    [31] => logs/2013-03-19-19-15-59-01AD1F375E4639C3
    [32] => logs/2013-03-19-19-29-22-573E6302C719D044
    [33] => logs/2013-03-19-19-32-06-6724AB9B33D97F52
    [34] => logs/2013-03-19-19-34-13-60F916D79985BDF0
    [35] => logs/2013-03-19-19-34-37-6A6430488AEB5195
    [36] => logs/2013-03-19-19-38-02-29AF737531B69E29
)


しかしダウンロードはできません。
CFResponse Object
(
    [header] => Array
        (
            [x-amz-request-id] => C6D3DB10776F16FA
            [x-amz-id-2] => owcKsbILBrXmdTdu8XG81Rpe2ozrZmgOniQz5dUlUzH2b5c6TVVg3yEwue8q+QqI
            [content-type] => application/xml
            [transfer-encoding] => chunked
            [date] => Tue, 19 Mar 2013 20:03:25 GMT
            [server] => AmazonS3
            [_info] => Array
                (
                    [url] => https://memorycraft-log.s3-ap-northeast-1.amazonaws.com/logs/2013-03-17-05-34-13-347E8EF10D93460B
                    [content_type] => application/xml
                    [http_code] => 403
                    [header_size] => 254
                    [request_size] => 751
                    [filetime] => -1
                    [ssl_verify_result] => 0
                    [redirect_count] => 0
                    [total_time] => 0.318179
                    [namelookup_time] => 0.206527
                    [connect_time] => 0.209524
                    [pretransfer_time] => 0.277762
                    [size_upload] => 0
                    [size_download] => 231
                    [speed_download] => 726
                    [speed_upload] => 0
                    [download_content_length] => -1
                    [upload_content_length] => 0
                    [starttransfer_time] => 0.317943
                    [redirect_time] => 0
                    [certinfo] => Array
                        (
                        )

                    [primary_ip] => 27.0.1.76
                    [redirect_url] =>
                    [method] => GET
                )

            [x-aws-request-url] => https://memorycraft-log.s3-ap-northeast-1.amazonaws.com/logs/2013-03-17-05-34-13-347E8EF10D93460B
            [x-aws-redirects] => 0
            [x-aws-stringtosign] => GET

application/x-www-form-urlencoded
Tue, 19 Mar 2013 20:03:25 GMT
/memorycraft-log/logs/2013-03-17-05-34-13-347E8EF10D93460B
            [x-aws-requestheaders] => Array
                (
                    [Content-Type] => application/x-www-form-urlencoded
                    [Date] => Tue, 19 Mar 2013 20:03:25 GMT
                    [Authorization] => AWS AKIAIHEOBPDCINSPTZMQ:2unBCNgC8U6OIxpFPOppb5vOF70=
                    [Expect] =>
                )

        )

    [body] => <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>C6D3DB10776F16FA</RequestId><HostId>owcKsbILBrXmdTdu8XG81Rpe2ozrZmgOniQz5dUlUzH2b5c6TVVg3yEwue8q+QqI</HostId></Error>
    [status] => 403
)


これはダウンロード権限がファイルについているためです。
AWSコンソールからロギング設定をすると、S3がログに出力する際のファイル権限が自動で設定されるため、変更できないように見えますが、API経由で設定すると出力時のファイル権限を任意の内容に設定出来ます。

$src = 'myfirst-bucket';
$target = 'memorycraft-log';
$prefix = 'logs/';
$owner_canonical_id = 'オーナーの標準ユーザーID';
$other_canonical_id = '別アカウントの標準ユーザーID';
$s3 = new AmazonS3(array('key'=>'オーナーのアクセスキー','secret'=>'オーナーのシークレットキー'));

$res = $s3->enable_logging($src, $target, $prefix, array(
  'users' => array(
    array('id' => $owner_canonical_id,     'permission' => AmazonS3::GRANT_FULL_CONTROL ),
    array('id' => $other_canonical_id,     'permission' => AmazonS3::GRANT_FULL_CONTROL ),
  )
));


このようにAPI経由で設定したら、設定後に出力されたファイルの権限は以下のようになります。



このファイルを取得をしてみます。

CFResponse Object
(
    [header] => Array
        (
            [x-amz-id-2] => KF+Szr8tu2uvDLpz0DHl8ZNxbUmIqLtwXhJ9TISQvKUQR9x15RUNZzmpRFmfB910
            [x-amz-request-id] => EFE2ACA5DADC8273
            [date] => Tue, 19 Mar 2013 21:02:05 GMT
            [last-modified] => Tue, 19 Mar 2013 19:38:03 GMT
            [etag] => "ada5251bcbd676782e98153cd0cca821"
            [accept-ranges] => bytes
            [content-type] => text/plain
            [content-length] => 304
            [server] => AmazonS3
            [_info] => Array
                (
                    [url] => https://memorycraft-log.s3-ap-northeast-1.amazonaws.com/logs/2013-03-19-19-38-02-29AF737531B69E29
                    [content_type] => text/plain
                    [http_code] => 200
                    [header_size] => 345
                    [request_size] => 751
                    [filetime] => 1363721883
                    [ssl_verify_result] => 0
                    [redirect_count] => 0
                    [total_time] => 0.25437
                    [namelookup_time] => 0.139468
                    [connect_time] => 0.1404
                    [pretransfer_time] => 0.202015
                    [size_upload] => 0
                    [size_download] => 304
                    [speed_download] => 1195
                    [speed_upload] => 0
                    [download_content_length] => 304
                    [upload_content_length] => 0
                    [starttransfer_time] => 0.254312
                    [redirect_time] => 0
                    [certinfo] => Array
                        (
                        )

                    [primary_ip] => 27.0.1.206
                    [redirect_url] => 
                    [method] => GET
                )

            [x-aws-request-url] => https://memorycraft-log.s3-ap-northeast-1.amazonaws.com/logs/2013-03-19-19-38-02-29AF737531B69E29
            [x-aws-redirects] => 0
            [x-aws-stringtosign] => GET

application/x-www-form-urlencoded
Tue, 19 Mar 2013 21:02:04 GMT
/memorycraft-log/logs/2013-03-19-19-38-02-29AF737531B69E29
            [x-aws-requestheaders] => Array
                (
                    [Content-Type] => application/x-www-form-urlencoded
                    [Date] => Tue, 19 Mar 2013 21:02:04 GMT
                    [Authorization] => AWS XXXXXXXXXXXXXXXXXX:qqBlRlhCi9XzHcFE9Guc8J/W0yc=
                    [Expect] => 
                )

        )

    [body] => b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543 myfirst-bucket [19/Mar/2013:18:22:39 +0000] 10.186.158.41 b00fb3b3fbeb37e2dc44f010cdbeff2c31bc467a2022f00401bf18abbf9e4543 22988F1BD9A4CBA1 REST.GET.LOCATION - "GET /myfirst-bucket?location HTTP/1.1" 200 - 142 - 21 - "-" "S3Console/0.4" -

    [status] => 200
)

無事取得できました!
以上です。

2013年2月23日土曜日

Storage Gatewayってなんじゃ?(EC2のCentOSにS3をマウント)

AWSにStorageGatewayというサービスがあります。StorageGatewayはオンプレミスのデータをS3へ直接保存するための接続サービスですが、EC2の世界でも利用することができます。

今回は、StorageGatewayでS3の領域をVPC内のEC2にマウントしてみたいと思います。


ゲートウェイの作成


StorageGatewayの画面の左ペインに「Deploy a new Gateway on Amazon EC2」というリンクをクリックします。




するとダイアログが立ち上がり、アクティベーションダイアログが起ち上がります。
赤枠の「Launch Gateway AMI」のリンクをクリックします。



StorageGateway用のAMIの説明画面が開きます。
「Continue」ボタンをクリックします。




AMIの選択画面になり、「1-Click Launch」と「Launch with EC2 Console」のタブがあるので選択肢、TokyoリージョンのAMIをクリックします。料金表をみるとGatewayインスタンスはxlargeから使用可能のようです。




すると確認画面が表示されるので、「Continue」をクリックします。



先ほど「Launch with EC2 Console」を選んだので、EC2のダイアログが起ち上がります。
一番グレードの低いxlargeを選択し、今回はVPCのprivateサブネットを選択します。



あとは通常どおりに進んでいきます。ここではプライベートIPを10.0.1.5に割り当てます。




そのまま次へ進んでいきます。EBSボリュームの設定画面が表示されます。
StorageGatewayではストレージボリューム以外にもキャッシュやバッファ用のボリュームが必要です。ここで追加することも可能ですが、今回はこのまま進み、後から追加することにします。



セキュリティグループのところでは、あとでiscsiを使用するため、このインスタンスをマウントするEC2からiscsiインターフェスでマウントするためデフォルトポート3260をセキュリティグループに含めます。また、設定作業用のSSHポートもあけておきます。
  • 22  10.0.0.0/16
  • 3260 10.0.0.0/16




後はそのまま進み、インスタンスが起ち上がったらEIPを付与します。
VPCの場合でもアクティベーションに使用するので、一時的にEIPをつけておきます。




ここで、先ほどのStrageGatewayの画面にもどり、IPアドレス欄にEIPを入力して、「Proceed to Activation」をクリックします。



するとStrageGatewayのコンソールにゲートウェイのVMの設定が表示されます。
タイムゾーンを選択し、Gatewayの名前を入力して「Activate My Storage Gateway」をクリックします。


すると、以下のような画面になり、ゲートウェイが表示されます。



次に、キャッシュとバッファ用のEBSを作成し、ゲートウェイインスタンスにアタッチします。(インスタンス作成の時に同時に行なっても構いません)


StorageGatewayの画面に戻り、「Create Volume」をクリックするとダイアログが現れるので、キャッシュボリュームとアップロードバッファのデバイスをそれぞれ選択します。今回はキャッシュボリュームに20GB、バッファボリュームに10GBのボリュームを割り当てます。




「Next」をクリックすると、このキャッシュとバッファのボリュームのそれぞれ使用容量のアラームの設定画面が表示されるので、適宜設定していきます。
今回はデフォルトのままメールアドレスを入力し次へすすみます。




すると、ゲートウェイ用ボリュームの設定になります。ここでは実際に使用するファイルストレージとしての容量とiSCSIのターゲットのエンドポイントを決めるIDを入力します。
ここでは、容量は1TB、iSCSIターゲットのIDをmemorycraft-sgwとして最後に「Create Volume」ボタンをクリックします。




するとゲートウェイボリューム作成完了と表示されます。



ここまで出来たらゲートウェイ自体は完了です。




ゲートウェイボリュームをEC2(CentOS)にマウント


次に、作成したゲートウェイボリュームをEC2上のCentOSインスタンスにマウントします。

このボリュームをマウントするためのインスタンスを新たに立ち上げます。



ここから先は、このインスタンスにSSH接続しての設定になります。
StorageGatewayはiSCSIインターフェースなので、このインスタンスをiSCSIイニシエータにするための設定を行います。

まずiSCSIイニシエータツールをインストールして、設定ファイルをStorageGateway用に変更し、起動します。
AWSの資料では、設定は例としてカスタマイズしたほうがよいそうで、それにならって変更します。
実際には用途に応じて適切な値を設定することをお勧めします。
# yum install -y iscsi-initiator-utils
# vim /etc/iscsi/iscsid.conf
~

node.session.timeo.replacement_timeout = 600
node.conn[0].timeo.noop_out_interval = 60
node.conn[0].timeo.noop_out_timeout = 600

~
# /etc/init.d/iscsi start


iscsiadmコマンドでネットワーク内のiscsiターゲットを探してみます。
ゲートウェイインスタンスはVPC内で10.0.1.5を設定したため、10.0.1.5を探します。
# iscsiadm --mode discovery --type sendtargets --portal 10.0.1.5:3260
iscsid を起動中:                                           [  OK  ]
10.0.1.5:3260,1 iqn.1997-05.com.amazon:memorycraft-sgw


StorageGatewayで作成したボリュームが見つかりました。
これに接続します。
# iscsiadm  --mode node --targetname iqn.1997-05.com.amazon:memorycraft-sgw --portal 10.0.1.5:3260,1 --login
Logging in to [iface: default, target: iqn.1997-05.com.amazon:memorycraft-sgw, portal: 10.0.1.5,3260] (multiple)
Login to [iface: default, target: iqn.1997-05.com.amazon:memorycraft-sgw, portal: 10.0.1.5,3260] successful.


次に、デバイスとしてアタッチされているか見てみます。
# ls -l /dev/disk/by-path/
合計 0
lrwxrwxrwx 1 root root  9  2月 23 18:09 2013 ip-10.0.1.5:3260-iscsi-iqn.1997-05.com.amazon:memorycraft-sgw-lun-0 -> ../../sda
lrwxrwxrwx 1 root root 11  2月 23 17:54 2013 xen-vbd-2049 -> ../../xvde1


/dev/sdaにアタッチされているようです。
これをマウントしてみます。
# mkfs.ext4 /dev/sda

ke2fs 1.41.12 (17-May-2010)
/dev/sda is entire device, not just one partition!
Proceed anyway? (y,n) y
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
67108864 inodes, 268435456 blocks
13421772 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=4294967296
8192 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
 32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
 4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
 102400000, 214990848

Writing inode tables: done                           
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 23 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.

# mkdir /mnt/sgw
# mount /dev/sda /mnt/sgw
# df -h

Filesystem            Size  Used Avail Use% マウント位置
/dev/xvde1            6.0G  2.6G  3.1G  47% /
none                  3.7G     0  3.7G   0% /dev/shm
/dev/sda             1008G  200M  957G   1% /mnt/sgw


おー、ちゃんと1TBがマウントされたようです。
ここに、以前s3syncのときにつかった大量のファイルを置いてみます。
# cd /mnt/sgw/

# git clone https://github.com/mirrors/perl.git
# git clone https://github.com/apache/cassandra.git
# git clone https://github.com/v8/v8.git
# git clone https://github.com/symfony/symfony.git
# git clone https://github.com/torvalds/linux.git

# tree -L 2 .
.
|-- cassandra
|   |-- CHANGES.txt
(略)
|   `-- tools
|-- linux
|   |-- COPYING
(略)
|   `-- virt
|-- perl
|   |-- AUTHORS
(略)
|   `-- x2p
(略)
|   `-- utils
|-- symfony
|   |-- CHANGELOG-2.0.md
(略)
|   `-- src
`-- v8
    |-- AUTHORS
(略)
    `-- tools

# du -sh

1.9G .


ファイル配置も問題ないようです。
また、s3cmdやs3fsのようにファイルリストの取得だけで重くなってしまうことはないようですが、キャッシュボリュームがあるためかも知れません。ファイル総量がキャッシュを超えた時の挙動などはまたの機会に試してみたいと思います。
また、EBSをPIOPSにしたり、EBSOptimizedインスタンスにした場合など、ボトルネックや、冗長化などなど考えると奥が深そうです。

現在のところでは、接続先のS3領域はS3コンソールやAPIからは秘匿されているようです。これらにアクセスできるとまた別の使い方ができるかもしれません。

以上です。

2013年2月18日月曜日

S3ってなんじゃ?(ダウンロードだけを特定のIPに制限する)


S3の特定のフォルダだけダウンロードをIP制限したい場合があります。
こういった場合には、S3のBucketPolicyが便利です。

たとえば以下のようなフォルダ内の画像ファイルにアクセスします。




すると以下の画像が表示されます。




この画像が入っているフォルダに対してIP制限してみます。
バケットポリシー画面を開きます。




ここに以下のように入力します。
{
  "Version": "2008-10-17",
  "Id": "Policy1360868462747",
  "Statement": [
    {
      "Sid": "Stmt1360868457064",
      "Effect": "Deny",
      "Principal": {
        "AWS": "*"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::myfirst-bucket/img/*",
      "Condition": {
        "NotIpAddress": {
          "aws:SourceIp": "xxx.xxx.xxx.xxx/32"
        }
      }
    }
  ]
}

ジェネレータで作成しても構いません。

内容としては、
  • 全てのAWSアカウントを対象に
  • myfirst-bucket/img/*にマッチするファイルへの
  • s3のGetObjectのアクセスに関して
  • xxx.xxx.xxx.xxx/32以外のIPからの要求を
  • 拒否します

ということになります。

「Save」で設定したあと、再度アクセスしてみると



このようにエラーとなり、xxx.xxx.xxx.xxxのIP以外からのアクセスでは画像が表示されなくなりました。
他にもActionを色々変えたり追加することによって、アップロードを制限したり、一覧を制限したりすることができます。

以上です。

2013年2月12日火曜日

S3ってなんじゃ?(CloudFrontでアクセス制御:Origin Access Identity × 署名付きURL)

CloudFrontでアプリを通してのアクセス以外にはコンテンツを配信させたくないという場合があります。
ここでは、CDPデザインパターンに以下のようなパターンがありました。

CDP:Private Cache Distributionパターン


このパターンによると、CloudFrontに対して署名付きURLを送信することで、アクセス条件を制限することができるようです。今回はこれを使ってみたいと思います。



S3バケットの作成


まず、S3バケットをひとつ用意して適当にファイルをアップします。




ちなみにindex.htmlは以下の様な内容です。
<html>
  <head>
    <title>private</title>
  </head>
  <body>
    <h1>This is Private Contents</h1>
  </body>
</html>



CloudFrontディストリビューションの作成


次にCloudFrontでディストリビューションを追加します。「Create new Distribution」をクリックします。
すると、以下のようにディストリビューションの設定画面が表示されるので、以下のように入力します。



Origin Settings


  • Origin Domain Name:先ほど追加したS3バケットを選択
  • Origin ID:自動で設定されるので今回はこのまま
  • Restrict Bucket Access:Yes(YesにするとCloudFrontからしかS3にアクセスできないようにできます)
  • Origin Access Identity:Create a New Identity(S3にアクセスするこのCloudFrontの接続子です)
  • Comment:コメントです
  • Grant Read Permissions on Bucket:Yes(S3に対してCloudFrontからのBucketPolicyを設定します。)

Default Cache Behavior Settings


  • PathPattern:Default
  • View Protocol Policy:HTTP and HTTPS
  • Object Caching:Use Origin Cache Headers(Customizeにすると次の3項目が設定出来ます。)
  • Minimum TTL:0(最短TTL、デフォルト24時間)
  • Forward Cookies:None(キャッシュURLにCookieを含めることができます)
  • Whitelist Cookies:Forward Cookiesを有効にすると、Whitelist内のCookieだけをオリジンに転送することができます。
  • Forward Query Strings:No(YesにするとQueryParameter付きURLでキャッシュできます)
  • Restrict View Access:Yes(署名付きURLでしかアクセスできないようにします)
  • Trusted Signers:Self (Specify Accoutsにチェックを入れると別アカウントの署名も有効になります。)




Distribution Settings


ここの項目はひとまずデフォルトのままにしておきます。



ここまで設定できたら「Create Distribution」をクリックします。
すると以下のように、CloudFrontをprivateアクセスするために必要な次の手順が表示されます。


ざっくり訳すと以下のような内容です。

Step1:S3バケットへのアクセス制限

  •  ディストリビューションを新規作成したままS3の設定をいじっていないのであれば、S3バケットはCloudFrontとバケットオーナーからしかアクセスを受け付けないように正しく設定されています。

Step2:署名付きURL

  •  信頼された署名者用にCloudFrontのキーペアを作成します。
  •  署名付きURLを作成するには、コーディングするかサードパーティツールを使用する必要があります。
  •  ディストリビューションへ信頼された署名者を追加します。

どうやら自動でS3のパーミッションが変更されたようです。
S3のパーミッションを見てみます。
S3のバケットのPermissionsからEdit Bucket Policyをクリックします。
すると以下のように設定されています。

{
 "Version": "2008-10-17",
 "Id": "PolicyForCloudFrontPrivateContent",
 "Statement": [
  {
   "Sid": "1",
   "Effect": "Allow",
   "Principal": {
    "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity E366VCXL4Q6CB9"
   },
   "Action": "s3:GetObject",
   "Resource": "arn:aws:s3:::memorycraft-private/*"
  }
 ]
}

Cloud Front Origin Access Identityとして設定されています。


また、作成されたディストリビューションを確認すると、以下のように無事登録されているのがわかります。
ディストリビューションのホスト名は
dapubd7a26puj.cloudfront.net
となっています。



この段階でブラウザでアクセスすると以下のようになります。


これは、すでに署名付きURLしか受け付けないため、通常のURLではエラーになるためです。



署名付きURLの作成


次に署名付きURLを作成してみたいと思います。

まず、署名をするためのCloudFront用のキーペアを取得します。
AWSの証明書のページで「一対の鍵」のタブをクリックします。


CloudFrontの一対の鍵という項目があるので、「新しい一対の鍵を作成する」をクリックします。


作成完了のモーダルウィンドウが表示されるので、確認して閉じます。


すると、「一対の鍵」にCloudFront用のキーペアが追加されているのがわかります。
「一対の鍵ID」というキーペアIDが表示され、下にダウンロードリンクが現れます。


「(公開鍵をダウンロード)」をクリックすると、rsa-キーペアID.pem, pk-キーペアID.pemというファイルがダウンロードされます。


どこか適当な環境にSDKをダウンロードします。
ここではPHPのSDKを利用します。
また、keysというディレクトリを作りpk-キーペアID.pemを配置します。
$tree -L 2 .

.
├── app
│   ├── key.php
│   ├── sdk -> sdk-1.6.0
│   ├── sdk-1.6.0
│   └── sdk-latest.zip
└── keys
    └── pk-キーペアID.pem


そして、以下のようにAmazonCloudFrontクラスを使って署名付きURLを生成します。
cat key.php

<?php
require_once('sdk/sdk.class.php');
date_default_timezone_set('Asia/Tokyo');

//CloudFrontクラスを初期化
$cf = new AmazonCloudFront(array('key'=>'通常のAWSアクセスキー', 'secret'=>'通常のAWSシークレットキー'));

//キーペアIDをセット
$cf->set_keypair_id('CloudFrontのキーペアID');

//配置した鍵ファイルの中身をセット
$cf->set_private_key(file_get_contents(dirname(__FILE__).'/../keys/pk-キーペアID.pem'));

//index.htmlのURLを期限付きで取得します(expireはstrtotimeが解釈できる文字列かUnixTimeならOK)
$url = $cf->get_private_object_url('dapubd7a26puj.cloudfront.net', 'index.html', strtotime('+5 minutes'));
echo $url;


確認


これを実行します。
# php app/key.php

http://dapubd7a26puj.cloudfront.net/index.html?Expires=1360671363&Key-Pair-Id=APKAIGNAXKEEXGUYEZ5A&Signature=UQantZ1gIp5-mm6d6Lb-HKQr1jxKDRW2NfFyC-E2dDx33ekBbFjLKmO6vHCOhv7liBrfcaTFc7~yKZCd4P1tfZE4TltU6TilhnAUFT6mCC-Db-dmnrU7XbcWSjt29~yOVVhLTv5RoPtIjoW~iPFR2BTyoM~4vlV40y9ypbO5M1U_


URLが出力されました。
これをつかってアクセスしてみます。




表示されました!

先ほどのコードでは5分後がアクセス期限でした。
5分以上経過してからアクセスすると、、、



ちゃんとアクセス拒否になりました。


おまけ


ユーザーがアクセスするごとにURLの期限を設定したい場合があります。
その場合は、以下のように元のURLにクエリーパラメータを付与してあげるといいかもしれません。
$url = $cf->get_private_object_url('dapubd7a26puj.cloudfront.net', 'index.html?u='.session_id(), strtotime('+5 minutes'));


以上の手順の多くはAWSのAPI経由でも設定可能です。必要に応じてプログラムで設定てもよいかもしれません。
また、署名の仕方や署名ポリシーにはいくつかパターンがあるため、機会があればもっといろいろな方法を試してみたいと思います。