EC2 四級講座

今更ですが、世に解説が溢れると自分でもやってみたくなるというのはデジャウ゛ではないでしょうか。しかし、ここに書かれていることを理解すれば四級合格も夢ではありません。四級合格に興味が無い人は http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/GettingStartedGuide/getting-started-ipv4.html を参考にさくっとインスタンスを作成しましょう。

EC2 は AWS でも比較的古いサービスです。AWS リージョン毎に複数のデータセンター群 (アベイラビリティーゾーン、AZ) があり、AWS のインフラ上で動く仮想マシーンを時間貸ししてくれます。古いので過去の歴史をいくらか引きずっています。昔は全てのユーザーのインスタンスが同じプライベート IP アドレス空間に存在していたのですが、VPC (Virtual Private Cloud) というサービスが登場してユーザーがプライベートなネットワークアドレス空間 (これのことも VPC と呼んじゃってます) を複数持てるようになりました。今ではこちらがふつーなので、以下では EC2-VPC のことだけ考えましょう。

EC2 インスタンスへのリソース割り当て

仮想マシーンなのでハイパーバイザーがいて

  • CPU
  • メモリ
  • ネットワークカード (NIC)
  • ディスク

などをインスタンスに提供してくれます。仮想化としては準仮想化と完全仮想化をサポートしてますが、近頃は完全仮想化が主流なので今後は完全仮想化だけ考えましょう。

CPU とメモリ

インスタンスのタイプを選ぶことで種類と量が決定されます。インスタンスタイプは沢山ありますが、先ずは T (tiny?) か、そうでなければ C (compute?) か M (memory?) を試してみるのが良いのではないかと思います。これらアルファベットが大まかなファミリーを表しており、数字は大きい方が新しいです。

NIC

2 種類用意されてるのですが、ふつー ENI (Elastic Network Interface) というのが最低 1 つ割り当てられます。性能はインスタンスタイプによって異なります。複数の ENI をインスタンスに付けることも可能ですが、増やしてもトータルの帯域が増えたりするわけではありません。VPC 的には ENI は、後述の VPC サブネット内の IP アドレスの確保という意味合いが強いです。プライマリなプライベートアドレスが VPCDHCP サーバーから割り当てられます。IPv6 も使えるようになりましたが、ここでは IPv4 の話だけ。(ふつーじゃない方は ENA (Elastic Network Adapter) なのですが、お値段の張るインスタンスタイプでだけ使えます)

ディスク

ハイパーバイザーの動くローカルハードウェアに載っているディスクとネットワークディスクの 2 つが使えます。ローカルの方をインスタンスストアと呼んでいて、インスタンスタイプによって数やサイズが異なります (1 つも付いてこないインスタンスタイプもあります)。インスタンスストアのお値段はインスタンス時間貸し料金に込み込みです。ネットワークディスクの方は EBS (Elastic Block Store) といいます。EBS もまた種類があり特性が異なります (現在 5 種類)。1 つあたり最大 16TiB までいけます。お値段はサイズや利用に応じて変わってきます。どちらも、インスタンスに複数アタッチでき、EBS ボリュームはインスタンスから外して付け直すなども可能です (アンマウントはしましょう)。インスタンスストアはバックアップなど自分で面倒を見る必要がありますが、EBS にはスナップショット機能があり AWS が S3 へのバックアップとリストアを面倒みてくれ、また冗長化もある程度されています。

EC2 インスタンスに対する基本の操作は以下の 4 つあるのですが (インスタンスのライフサイクル)、

このうち StopInstances と StartInstances は EBS ボリュームがルートボリュームとして指定されている EC2 インスタンスに対してのみ行えます。EC2 インスタンスが走っているホストは複数のユーザーにより共用されていて、止めたり消されたインスタンスはそのホスト上から一度居なくなり、空いたリソースを他のユーザーが使う可能性があります。ローカルディスクであるインスタンスストアが、停止されたインスタンスの用に維持されることもなく、一旦止めるという操作もできません。ルートボリュームが EBS であるインスタンスにもインスタンスストアを付けて利用することはできるのですが、Stop したあと Start すると新たにホストが割り当てられてインスタンスストアは真っ新なものが提供されます。

インスタンス作成の主要パラメータ

EC2 インスタンスを作るには RunInstances という API を呼び出します。「Request Parameters」という節に API のパラメーターが列挙されていますが、ふつー指定するのは下記の情報、でしょうか。

  • ImageId
  • InstanceType
  • KeyName
  • SecurityGroup
  • SubnetId
AMI

ImageId は、AMI (Amazon Machine Images) の ID を指定します。AMI は、ルートボリュームを含むディスクデータの参照情報 (ブロックデバイスマッピングと呼ばれています)、仮想化方法、ルートデバイス名などのメタ情報をまとめたものです。EBS ボリュームがルートの場合にはスナップショットの ID などが、インスタンスストアがルートの場合には S3 に置かれたデータへの参照情報がブロックデバイスマッピングに記載されているはずです。AMI は AWS で用意している Amazon Linux AMI の他、Red Hat Enterprise Linux, Ubuntu, CentOS など色々揃っています。Windows Server もあります。

インスタンスタイプ

InstanceType は、インスタンスタイプの名前を指定します。t2.xlarge とか。

RSA 鍵ペア

KeyName は、登録してある EC2 Key Pair を指定します。EC2 インスタンスには決してログインする必要はなく、色々作りこんだりすれば勝手に色々サービスを起動して Web サーバーになったり queue を処理するワーカーになったりするのですが、ログインしたい場合にインスタンスに認証情報を伝える 1 つの手段として EC2 Key Pair が用意されています。これは実際は RSA 公開鍵に名前を付けて登録してあるものです。

EC2 ではインスタンスの ID やプライベート・パブリック ID アドレスがインスタンス内部から参照できるように インスタンスメタデータ を提供しています。インターフェースとしては http://169.254.169.254/ を利用していて例えば

$ curl http://169.254.169.254/latest/meta-data/ami-id

とリクエストを投げると RunInstances で指定された AMI ID が取得できます。インスタンス作成時に指定された EC2 Key Pair の公開鍵も、作成されたインスタンスから http://169.254.169.254/ にアクセスすることにより取得可能です。Amazon Linux AMI を含め多くの AMI では cloud-init をそれぞれカスタマイズしてインストールしていて、インスタンス初回起動時には公開鍵をデフォルトユーザーの ~/.ssh/authorized_keys に自動で登録するようになってます。Windows の場合には、パスワードが自動生成のうえ公開鍵で暗号化されたものが提供されるので、秘密鍵で復号して RDP できるようになってます。

ログインなどの認証にもちろん SSH や RDP を使う必要もないし、ユーザーの設定がされた AMI からインスタンスを作成した場合には引き続きその認証情報が使えたり、起動時にやっぱりリセットされてしまうなどは、AMI の中身次第です。

セキュリティーグループ

SecurityGroup は、セキュリティーグループの ID を指定します。セキュリティーグループというのは、実際にはインスタンスではなく ENI にくっつくファイアウォール機能のことです。ENI を特別指定しないと自動で ENI が作られて eth0 としてインスタンスに割り当てられるのですが、その ENI のセキュリティーグループを指定します。セキュリティーグループはステートフルで、許可を追加していきます。つまり、許可が無ければ拒否です。「10.0.3.0/24 からの TCP 22 ポートへのアクセスは通す」や「8.8.8.8 への UDP 53 ポートへのアクセスは通す」のような設定が可能です。

サブネット

SubnetId は、インスタンス作成時に自動作成される ENI の所属するサブネットを指定します。VPC を作成するときに CIDR で IP アドレスの範囲を指定するのですが、更にサブネットに分けて用途に応じたインスタンスの配置が可能です。VPC 内にサブネットを作成するときには AZ を指定する必要があります。

さて、これで AMI の情報を元に指定したインスタスタイプのインスタンスがとあるサブネット内に作成されて、セキュリティーグループで許可された IP アドレスから RSA 秘密鍵を使って接続できそうなのですが、接続のことを考えるのにもう少しサブネットとその周りのことを。

VPC のサブネット

サブネットは VPC のアドレス範囲内の更に小さなまとまりでしたが、各サブネットには仮想ゲートウェイが存在しており DHCPデフォルトゲートウェイとして提供されます。このゲートウェイは ネットワーク ACL とルートテーブルという属性を持っています。

ネットワーク ACL

ネットワーク ACL はサブネットに対するファイアウォールです。これはステートレスで、ルールとして許可も拒否も書けます。

ルートテーブル

ルートテーブルは、CIDR などで指定された IP パケットの行き先に対して、次のルーティングは誰にするかを指定するもので、基本的なルーティング先は以下の三つ。

ローカルはデフォルトで設定されていて外すことはできません。つまり、ルーティング的には VPC 内のパケットは必ず宛先サブネットまでは転送されてしまいます。遮断の為にはセキュリティーグループやネットワーク ACL の設定が必要です。

インターネットゲートウェイは、VPC とインターネットに接続されていてスタティック NAT をしてくれる仮想的なゲートウェイです。VPC 内のアドレスは通常はプライベートネットワークなので、そのままではインターネットには出られません。ENI に割りふられた IP address にグローバル IP アドレスを紐付けることができ、インターネットゲートウェイは紐付けに基いて NAT をしてくれます。グローバル IP アドレスの紐付けは、サブネットによって自動で行われる場合と、EIP (Elastic IP address) を借りて手動で割り当てる方法の 2 つがあります。

ENI を指定した場合には ENI の付いているインスタンスが一旦そのパケットを受け何らかの処理をします。例えば、外部からの接続を受けたくない場合、インターネットゲートウェイの設定のないサブネットを作ります。ただ、このままだとインターネットに出ていくこともできず不便です。そこで、ENI を転送先に指定することにより、別サブネットのインスタンスへパケットを転送し、そのインスタンスがダイナミック NAT してインターネットゲートウェイに転送することができます (NAT インスタンス)。でも、最近は NAT ゲートウェイというマネージド NAT インスタンスが出たので、そちらを使う方が主流になっているかも...。

他には下記の転送先があります

というわけで

インターネットからインスタンスまでの概念上の登場人物を列挙してみました。VPC は Virtual って頭に付いてるだけあって実体は外にあって、というのが私のイメージで、EC2 っていうといつも下のような絵を思いうかべています。レイヤーがゴチャっとなっている中々いびつな絵なんですけれどもね...。

Base64 でエンコードする度に、文字列はどんどんと大きくなっていくのでありました。

SES は API も呼べますが SMTP インターフェースも備えています。認証は AUTH LOGIN や AUTH PLAIN が使えます。その際の Username と Password の取得方法が以下のページ。
Obtaining Your Amazon SES SMTP Credentials - Amazon Simple Email Service
方法が 2 通り紹介されていて、1 つは Management Console から、もう一つは Secret Access Key を使います。で、後者がちょっと気になるわけですが、

The following pseudocode shows the algorithm that converts an AWS Secret Access Key to an Amazon SES SMTP password.

key = AWS Secret Access Key;
message = “SendRawEmail”;
versionInBytes = 0x02;
signatureInBytes = HmacSha256(message, key);
signatureAndVer = Concatenate(versionInBytes, signatureInBytes);
smtpPassword = Base64(signatureAndVer);

pseudocode! その下に Java の場合もあるんですが手元の環境に javac が入ってなくてダウンロードするのが面倒臭かったので、Python でやってみました。

import base64
import hashlib
import hmac
import sys


key = sys.argv[1].encode()
message = b"SendRawEmail"
versionInBytes = b"\x02"
signatureInBytes = hmac.new(key, message, hashlib.sha256).digest()
signatureAndVer = versionInBytes + signatureInBytes
smtpPassword = base64.b64encode(signatureAndVer).decode()

print(smtpPassword)

これで、以下のように SMTP Password を吐き出してくれます。

$ python SMTPPassword.py 'fs+ZTtZ9WcqCzBVTql/3Fg0/icXhO01zi3683llk'
ArvpA+Ub71jSDRTQWbMDuNrJ2f3jLXk7TeBvAFe9gT7x

これがちゃんと動くか確認したいので IAM Policy を適切に設定した後に

import base64
import sys

username = sys.argv[1].encode()
password = sys.argv[2].encode()
plain = base64.b64encode(b"\x00" + username + b"\x00" + password)

print(plain.decode())

ってのをまた用意しまして

$ openssl s_client -crlf -quiet -starttls smtp -connect email-smtp.us-east-1.amazonaws.com:25 2> /dev/null
HELO localhost
250 email-smtp.amazonaws.com
AUTH PLAIN AEFLSUFKRVpaQ0lPUEtaUTZTU1BBAEFydnBBK1ViNzFqU0RSVFFXYk1EdU5ySjJmM2pMWGs3VGVCdkFGZTlnVDd4
235 Authentication successful.
QUIT
221 Bye

わ〜い、successful したよ!!

あとはこの SMTP Password をクライアントなりサーバに設定するだけです。