おぎろぐはてブロ

なんだかんだエンジニアになって10年以上

botoでSample IMDB Movie DataをDynamoに挿入する

CloudSearch用のサンプルデータとして、IMDBのMovie Data Sampleが公開されています。

これを検証用にDynamoDBに入れたかったのでざっくりコードを書いた。 HashKeyをstring指定でテーブルを作り、実行。

# -*- coding: utf-8 -*-
import decimal
import json
import boto.dynamodb2
import boto.dynamodb2.table

conn = boto.dynamodb2.connect_to_region('ap-northeast-1')
imdb = boto.dynamodb2.table.Table('imdb', connection=conn)

with open('moviedata2.json') as f:
    records = json.load(f, parse_float=decimal.Decimal)

with imdb.batch_write() as batch:
    for record in records:
        data = record['fields']
        data['id'] = record['id']
        for key in ['directors', 'genres', 'actors']:
            if key in data:
                data[key] = set(data[key])

        # テスト用なので上書きOKにしてる
        batch.put_item(data, overwrite=True)

これでうまく挿入はされるが、release_dateはunixtimeとかで入れるべきである。

米国アマゾンのデジタルコンテンツビジネス戦略2012 (CD+冊子)

米国アマゾンのデジタルコンテンツビジネス戦略2012 (CD+冊子)

  • 作者: インターネットメディア総合研究所
  • 出版社/メーカー: インプレスR&D
  • 発売日: 2011/10/27
  • メディア: 単行本(ソフトカバー)
  • クリック: 1回
  • この商品を含むブログを見る

awscli s3apiでの日本語を含むオブジェクト操作

日本語などnon-ascii文字列を含むオブジェクト操作のメモ

"nonあascii" (unicode: \u3042) というオブジェクト名を想定します。

aws s3 rm

ふつうに日本語で実行できる。 (おそらく実行環境に依る)

# Key does not exist
aws s3 rm "s3://bucket/non\u3042ascii"

# delete successful
aws s3 rm "s3://bucket/nonあascii"

s3api delete-object

こちらも同様。 ただ、s3コマンドと違ってファイルの存在を確認しないため、バージョニングが有効だと存在しないファイルにデリートマーカをつくる。

# successful
aws s3api delete-object --bucket "bucket" --key "nonあascii"

# fail (create delete marker to non\\u3042ascii)
aws s3api delete-object --bucket "bucket" --key "non\u3042ascii"

s3api delete-objects

複数指定したい場合は、delete-objects。

以下は失敗パターン。 defaultencoding=asciiの環境だと 'ascii' codec can't encode character u'\u3042' in position 24: ordinal not in range(128) のエラーが出る。

aws s3api delete-objects --bucket "bucket" \
--delete '{"Objects":[{"Key":"non\u3042ascii"}],"Quiet":false}'

aws s3api delete-objects --bucket "bucket" \
--delete '{"Objects":[{"Key":"nonあascii"}],"Quiet":false}'

sitecustomize.pyでデフォルトエンコーディングをUTF-8に変更するとどちらもXML不正エラーが出る。

A client error (MalformedXML) occurred when calling the DeleteObjects operation: The XML you provided was not well-formed or did not validate against our published schema

何が起きているかというと、XMLがおかしい。

0x0000:  4500 0074 8db1 4000 4006 10a5 ac1f 18d5  E..t..@.@.......
0x0010:  36e7 a052 bab8 0050 ddd0 5f96 019d f618  6..R...P.._.....
0x0020:  5018 0073 9c94 0000 3c44 656c 6574 653e  P..s....<Delete>
0x0030:  3c4f 626a 6563 743e 3c4b 6579 3e6e 6f6e  <Object><Key>non
0x0040:  e381 8261 7363 6969 3c2f 4b65 793e 3c2f  ...ascii</Key></
0x0050:  4f62 6a65 6374 3e3c 5175 6965 743e 6661  Object><Quiet>fa
0x0060:  6c73 653c 2f51 7569 6574 3e3c 2f44 656c  lse</Quiet></Del
0x0070:  6574 653e                                ete>

ということで、XML実体参照で送る。

aws s3api delete-objects --bucket "bucket" \
--delete '{"Objects":[{"Key":"non&#x3042;ascii"}],"Quiet":false}'

まとめ

  • s3apiの引数でJSONを指定する場合、non-asciiな文字列はXML実体参照で入れる

JSONとXMLの変換の闇でした。

セキュリティグループをつくるいろいろ

アカウントを新しく使いはじめる場合や、新しいサブネットを作成したときに、ベースのセキュリティグループをとりあえずセットアップしたいパターンはよくあると思う。 とりあえず、ログインするために、会社のIPからのアクセスを許可するとか。

以下のシナリオを考えてみる。

  • ELB + WEBサーバ (EC2)+ DB (RDS) の構成
  • Public Subnet, Private Subnetを定義
  • 会社のIPからのSSHアクセスを許可する

これを元に以下の構成を考える。

f:id:i_ogi:20140504193315p:plain

NATインスタンスがいないとか、いろいろあるけど、今回のテーマではないので割愛。

aws cliコマンドライン

まず空のセキュリティグループを作成して、そこにルールを追加する形になる。

gista4f525ebfea05615851c

  • aws ec2 create-security-group コマンドを叩いてセキュリティグループを作成。セキュリティグループIDを取得。
  • aws ec2 authorize-security-group-ingressコマンドでIngressのルールを追加

コマンド実行の戻りを変数に入れて、というのがシェルスクリプトだとちょっと面倒です。楽しようと、上では--queryでGroupIdのみ取得していますが、エラーが起きた時のハンドリングができていません。

セキュリティグループを参照したり、変数の嵐となってつらいので上では1個だけ作成しています。

スクリプト (botoの場合)

gist01f77d9a4959ac1b88f4

セキュリティグループをルールに含めたセキュリティグループをつくるのはスクリプト楽ですね。

botoのboto.ec2.securitygroupは、ingress (inbound)前提となっていて、egress (outbound)も使う場合は、 ec2.connectionのauthorize_security_groupauthorize_security_group_egressあたりを利用することになると思います。

他の各言語のAWS SDKなどでも、Create Security Groupして、そこに対してルールを追加する同様の感じとなります。

CloudFormation

CloudFormerで作成した例。CloudFormerについてはクラスメソッドさんのブログが分かり易い。

gist11554515

上のようなJSONを生成して、例えばAWS CLIでキックする。

aws cloudformation create-stack --stack-name generate-security-group --template-body file://generate-securitygroup.json

このJSONでは、VPC IDが決め打ちになっているが、もちろんVPC自体も作成できる。

問題点としては、リソース名 (Security Group Name)が、CloudFormationによる自動生成となること。タグで代替できるが、タグは重複を許容するのが、場合によってはあんまりよろしくない。 あと、CloudFormationスタックにセキュリティグループが紐付いているので、スタックを削除すると削除しちゃうので、適宜、Stack Policyを設定する。

まとめ

実際に運用する場合には、VPCと組み合わせて、

  • Public Subnet、Private Subnetを定義し、NATインスタンスを起動させる
  • 会社側のIP制限ルールのため、踏み台インスタンスを用意してElastic IPを付与したい

とかいろいろあると思う。

こうなってくると、登場人物が増えてくるのと、案件によって変わってきたりして、ちゃんとがっちり構築しようとすると厳しかったりするが、できるところから、ルールが決まっている部分について自動化しておくと、完全な自動構築までの道のりもクリアになる。

S3のバケットを100個以上使えるよう別アカウントのバケットの権限を移譲する

2015/08/12 Update

2015/08/04より、S3のバケットの作成上限100の上限緩和申請ができるようになりました。

Bucket Limit Increase: You can now increase your Amazon S3 bucket limit per AWS account. All AWS accounts have a default bucket limit of 100 buckets, and starting today you can now request additional buckets by visiting AWS Service Limits.

Amazon S3 Introduces New Usability Enhancements

以下、クロスアカウントでの利用には引き続き有用だと思いますので残しておきます。


AWSではいろいろなパラメータに上限値が定められていますが、S3のバケット数の1アカウント上限100件もその中の1つです。 上限緩和申請できるパラメータとそうでないパラメータがありますが、バケット数についてはどうも後者の模様

ということで、S3バッドノウハウ。アクセス権限を移譲することで、100個以上のバケットを利用する方法と、できないことについてまとめます。

制限についておさらい

バケットの制約と制限 - Amazon Simple Storage Service から

  • 各 AWS アカウントは、同時に最大100個のバケットを所有できます。
  • 1 つのバケット内に格納できるオブジェクトの数に制限はありません。
  • 使用するバケットの数によってパフォーマンスに差が出ることもありません。すべてのオブジェクトを 1 つのバケットに格納することができます。また、複数のバケットに分けて整理してもかまいません。

バケットは100個だけど中身の数は無制限。

(AWS ConsoleなどGUIではバケットの中にフォルダがあるイメージですが、バケットの下は実際はフラットで、delimiterとprefixを指定することでディレクトリっぽい階層構造のアクセスを行っています。 プレフィックスと区切り記号によるキーの階層的なリスト を参照)

100個の制限はアカウントに紐づく制限のため、IAMでは解決できません。

なぜバケットを分けるか

バケットを分けるケースを考えてみます。 独自ドメインのホスティングなどは当然必要ですが、それ以外について。

環境 (development/production) による分離

バケット名だけを差し替えて、開発環境と本番環境を切り替えられるように。 こちらについては、権限の強いアカウントでミスオペレーションをして困ったことにならないように、開発環境と本番環境で、アカウントも変えてもよいでしょう。

複数人で開発していて、各ブランチや各個人の開発環境でバケットを切り替えようとするとあっという間に100個なんぞ超えてしまうので、開発環境はバケット下にディレクトリを切って使うことになるかなと思うのですが、でも、そうすると本番と挙動がずれる感があり難しいところ。

権限管理のオペレーションのシンプルさ

IAMポリシーは arn:aws:s3:::my_corporate_bucket/home/bob/* みたいにバケットの一部へリソースを絞り込むことができるので、権限を管理することはできます。が、バケットに対して権限を指定するほうがシンプルで分かり易いという面はあります。 あと、GUIで見るときに、権限が無いのでバケットのrootとかが開けなくてわかりづらいとかはあります。(Windowsのフォルダアクセス管理と同じ感じ)

リージョンを変える必要がある

たとえば、サービスを日本を中心に展開していたり、日本語のコンテンツは東京リージョンに設置するのが、アップロードもダウンロードも高速でよいですし、コンテンツごとにアクセス元の偏りがはっきりしているなら、それに合わせたリージョン配置にするとレイテンシーが最適化されます。 が、そんなに偏ってもなかったり、管理がめんどいなら、CloudFrontを使いましょう。GET以外もエッジ経由でproxyできるようになっています。

解決案

Consolidated Billingで支払いについては複数アカウントを束ねることはできますが、権限についてはまったく分離された別アカウント状態となります。 バケットのGranteeに追加することで権限移譲ができだいたいの処理は可能です。 少なくとも最初のバケットを作って権限移譲をするまでは、バケットを作成したアカウントで作業をする必要があります。

操作用のアカウントをIAMで管理するAWSアカウントを A 、 S3バケットXを作成するAWSアカウントを B とします。

  1. BでS3バケットXを作成する
  2. BでS3バケットXのgranteeにAを設定する。あと、必要に応じてロギング設定をする
  3. AのIAMでXにアクセスできるpolicyのIAMアカウントをつくる。自身のバケットと同じように設定できます
  4. AのIAMアカウントでS3バケットXにアクセスする

シンプルですが、いくつかできないことがあります。

  • Bのアカウントでバケット一覧でXが表示されない
  • バケットXに対するオペレーションはBのアカウントでしかできない
    • バケット作成、削除、権限変更、アクセスログ設定
  • バケットのアクセスログは同じアカウントのバケットにしか設定できない

まとめ

サンタさん、100個以上バケットを使えるS3がほしいです。 100個以上使えるようになった!

  • 将来的にバケット100個が枯渇しそうな設計はなるべくしない
  • 100個超えるような場合は権限移譲である程度解決できる