セキュリティグループをつくるいろいろ
アカウントを新しく使いはじめる場合や、新しいサブネットを作成したときに、ベースのセキュリティグループをとりあえずセットアップしたいパターンはよくあると思う。 とりあえず、ログインするために、会社のIPからのアクセスを許可するとか。
以下のシナリオを考えてみる。
- ELB + WEBサーバ (EC2)+ DB (RDS) の構成
- Public Subnet, Private Subnetを定義
- 会社のIPからのSSHアクセスを許可する
これを元に以下の構成を考える。
NATインスタンスがいないとか、いろいろあるけど、今回のテーマではないので割愛。
aws cliコマンドライン
まず空のセキュリティグループを作成して、そこにルールを追加する形になる。
- aws ec2 create-security-group コマンドを叩いてセキュリティグループを作成。セキュリティグループIDを取得。
- aws ec2 authorize-security-group-ingressコマンドでIngressのルールを追加
コマンド実行の戻りを変数に入れて、というのがシェルスクリプトだとちょっと面倒です。楽しようと、上では--query
でGroupIdのみ取得していますが、エラーが起きた時のハンドリングができていません。
セキュリティグループを参照したり、変数の嵐となってつらいので上では1個だけ作成しています。
スクリプト (botoの場合)
セキュリティグループをルールに含めたセキュリティグループをつくるのはスクリプト楽ですね。
botoのboto.ec2.securitygroupは、ingress (inbound)前提となっていて、egress (outbound)も使う場合は、
ec2.connectionのauthorize_security_group
、authorize_security_group_egress
あたりを利用することになると思います。
他の各言語のAWS SDKなどでも、Create Security Groupして、そこに対してルールを追加する同様の感じとなります。
- AWS SDK for PHP: authorizeSecurityGroupIngress, createSecurityGroup
- AWS SDK for Ruby: Class: AWS::EC2::SecurityGroup — AWS SDK for Ruby
- AWK SDK for Java: createSecurityGroup
CloudFormation
CloudFormerで作成した例。CloudFormerについてはクラスメソッドさんのブログが分かり易い。
上のような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を付与したい
とかいろいろあると思う。
こうなってくると、登場人物が増えてくるのと、案件によって変わってきたりして、ちゃんとがっちり構築しようとすると厳しかったりするが、できるところから、ルールが決まっている部分について自動化しておくと、完全な自動構築までの道のりもクリアになる。
boto.s3で大文字を含むバケットにアクセスする
大文字を含むバケットをUS Standardでは作ることができるのですが、そのバケットにbotoでアクセスしようとすると以下のように怒られます。
Bucket names cannot contain upper-case characters when using either the sub-domain or virtual hosting calling format.
言ってることは解りますが、じゃ、どうしたらアクセスできるのか。というと、calling_formatというオプションを明示的に指定する必要があります。
calling format とは
S3へのアクセススタイル。以下が定義されている。 詳しくは s3/connection.py を参照。
- SubdomainCallingFormat: サブドメイン。これがデフォルト
- VHostCallingFormat: 独自ドメインだと思う
- OrdinaryCallingFormat: パススタイル
- ProtocolIndependentOrdinaryCallingFormat: Ordinaryだけどscheme (http/https) を明示しない何か
大文字を含んでいる場合は OrdinaryCallingFormat を利用すればよいです。
指定方法
設定ファイル
[s3] calling_format = boto.s3.connection.OrdinaryCallingFormat
S3Connectionのコンストラクタ
from boto.s3.connection import OrdinaryCallingFormat, S3Connection conn = S3Connection(calling_format=OrdinaryCallingFormat)
2.13.2 以前のboto
仕様が変わっていて、設定ファイルでの指定は不可。また、クラスもしくはクラス名ではなく、インスタンスを渡す必要がある。
from boto.s3.connection import OrdinaryCallingFormat, S3Connection conn = S3Connection(calling_format=OrdinaryCallingFormat())
Pythonで整数であることをチェックする
引数が整数であることを確認するのに、こんなコードをよく書いてるんだけど
if type(arg) not in (int, long): raise InvalidArgumentError
ちょっと冗長じゃないかな。先日は (int, tuple) とか書いてて危ない思いをしたりも。。
どうにかならんものかとつぶやくと、
if not isinstance(arg, (int, long)):
って教えてもらった。isinstanceでタプル渡せるの知らなかった。(2.2 or later)
なんだが、単語数は減ったけど文字数が減ってない。
あと、bookはintのサブクラスなため、isinstance([bool instance], int) は True となるけど、素直に文字列変換すると (True|False) の文字列になる点が要注意。
>>> arg = True >>> print type(arg), arg <type 'bool'> True >>> type(arg) not in (int, long) True >>> not isinstance(arg, (int, long)) False
Python 2系での整数の扱い
int (整数), long (長整数) の違いは、もちろんサイズ。
C言語っぽいが、Pythonのintは「C言語の long 型を使って実装されており、少なくとも 32 ビットの精度があります 」で、long (長整数型)は「精度の制限がありません」。
長整数のレンジの数を扱うようなことは普通のお仕事ではあんまりないと思うのだけど、intで表現できる値でもlongに入っていることはあるのが面倒ですね。longを受け取るようにすると今度は巨大な整数がやってくる可能性がでてくるわけで。。
>>> type(1) <type 'int'> >>> type(1L) <type 'long'>
整数についてはクラスツリーはこうなる
numbers.Number --- Integral -+- int --- bool +- long
Python 3系での扱い
Python 3系では、longが無くなり、intに統一されている。 すっきり。
numbers.Number --- Integral --- int --- bool
なので、こう書ける
if type(arg) is not int: raise InvalidArgumentError
リストをn個ずつのサブリストに分割 (Python)
愚直にやればもちろんできるんだけど、パフォーマンスが気になったり、かっこよく書きたいとか思うわけです。
>>> a = range(15) >>> a [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] >>> [a[i:i+3] for i in range(0,15,3)] [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13, 14]]
range(0, 15, 3) が [0, 3, 6, 9, 12] に展開されて、スライスしていく感じ。
zip(*[iter(s)]*n) で n 個ずつ分割
探しているとこんなのがでてくる。
iterable の、左から右への評価順序が保証されます。そのため zip(*[iter(s)]*n) を使ってデータ系列を n 長のグループにするクラスタリングすることができます。
ということで、15個の要素を持つリストを3個ずつのサブリストに分割したい場合、
>>> zip(*[iter(range(15))]*3) [(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14)]
となるのだそうだ。なんで?
続きを読む