注目の記事

【AWS】VPC環境構築ノウハウ社内資料 2014年4月版

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

よく訓練されたApple信者、都元です。

以前、【AWS】VPC環境の作成ノウハウをまとめた社内向け資料を公開してみるという記事を書きましたが、そこから半年経ち、状況も変わって来ましたのでアップデートを行いたいと思います。

以前のエントリーを読んだ方は、忙しい場合は下記の「2013年10月版からのアップデート」だけを読むといいかもしません。

VPCを利用する理由

AWSは、あらゆる規模のプロジェクトに対応するインフラを提供しています。前述のサーバ数千台規模のプロジェクトしかり、1台構成しかり。大規模プロジェクトであれば当然、オンプレミスと同様にネットワークインフラについての設計を綿密に行う必要がありますが、では、中小規模のプロジェクトにおいてはネットワークの設計をする必要はないのでしょうか。

AWSでは、VPCという「ネットワーク環境」を構築するサービスを提供しています。しかもVPCの利用料金は(VPN接続を除いて)無料です。自ら設計したネットワークの上でEC2サーバを起動(これをEC2-VPCと呼びます)して、そのリソースを利用できるのです。もちろん「ネットワークの事は考えたくない」というニーズもあるでしょう。そういった場合は、VPCを使わずにEC2サーバを利用する(これをEC2-Classicと呼びます)ことも可能です。しかし、そのシステムを大きく育てていくつもりであれば、無料なんですから、最初からネットワークインフラについてもある程度考えておくと良いでしょう。EC2-Classic環境からEC2-VPCへの移行作業というのは、結構大掛かりになってしまいがちなので、尚更です。

従って、弊社で構築するAWSのサーバ環境は、一部の例外を除いて全てVPCを利用しています。無料だからVPC、今後成長した時困らないようにようVPCです。

設計コストの行方

ただし、ここで問題になるのは設計コストかもしれません。いくらネットワーク利用料が無料だからといって、ネットワーク設計をするコストは掛かってしまうでしょう。VPCに触れた事がなければ尚更です。

その点、弊社ではAWSを専門としたチームが毎日のようにVPCネットワークインフラの設計構築をしています。大規模プロジェクトであれば独自のネットワーク設計が必要かと思いますが、今回テーマにしている中小規模のネットワークはどれも似たり寄ったりです。結果、その設計コストは限りなくゼロに近づいています。

というわけで、弊社で利用している、VPC環境の基本構成をここでご紹介します。

基本方針

ここで設計の基本方針を列挙します。とりあえず方針だけで、理由は列挙しません。理由は後から説明したりしなかったり。説明が足りなければ下のコメントで指摘してください。

  • サブネットのレイヤは3層とし、Frontend / Application / Datastore と呼ぶ。
  • 2つのAZを利用したマルチAZ構成とする。
  • Bastion *1サーバをt1.microでFrontendサブネットに構築する。セキュリティグループはVPC default及び、特定IP/CIDRからのSSH接続のみを許可するグループの計2つ。
  • Frontend及びApplicationサブネットのルーティングは、publicとする。
  • Datastoreサブネットのルーティングは、RDS / ElastiCache / Redshift等のマネージドサービス利用の場合はprivate、EC2によるセルフマネージドであればpublicとする。
  • ネットワークの内部通信は制限しない。(VPCのdefaultセキュリティグループを、全インスタンスに付与する)
  • NetworkACLは利用しない。(ALL OK)
  • セキュリティグループのoutbound制御は行わない。(ALL OK)
  • プライベートIPアドレスは制御(固定)しない
  • ドメイン名を1つ確保し、プライベートIPを名前解決できるようにDNSレコードを定義する。
  • 以上の環境を、出来る限りCloudFormationで作る。

各項解説

3層レイヤ

中小規模のシステムは、大抵この3層レイヤがフィットします。各層に、以下のような指針でサーバを配置しましょう。

  • Frontend: ELB, Bastion、等、internet-facing(つまりInternetから直接インバウンド接続を受け付ける)サーバを配置する。
  • Application: (ELB配下の)WebサーバやAPサーバ等、計算処理を主体とするサーバを配置する。
  • Datastore: RDSやMongoDB等、データリポジトリを主体とするサーバを配置する。

マルチAZ

本番システムは、何か不可能な理由がない限りマルチAZ構成を取ります。詳しくはAWSにおける可用性の考え方を参照してください。

マルチAZというのは3AZでも4AZでも良いのですが、東京リージョンにおいて現在制限なく扱えるAZは2つですので、2AZ構成とします。

そしてVPCサブネットは1つのAZに紐付きますので、結果として、各レイヤには2つ、計6サブネットが作成されることになります。

ルーティング等、各種通信制御

まず、VPCのローカルネットワーク内同士の通信については、全ての通信を許可することを基本方針とします。必要に応じて制御を行いますが、基本は全通信OKとしておきます。

次に、メンテナンス経路として必ず踏み台サーバを置きます。メンテナンスをしない時はサーバをstopすることによりバックドアが閉じますし、インスタンスタイプもt1.microで充分です。(参考: )

ルーティングテーブルの種類は、public / private の2種類があります。publicについて、見た目の設定値上は同じですが、考え方により2つ(in-outとout-only)に分類します。

  • public in-out:(Frontend) グローバルIPを持てばIGWを介して外部ネットワークとinbound/outbound通信できる。
  • public out-only:(Application / SelfManaged Datastore) グローバルIPを持てばIGWを介して外部ネットワークとoutbound通信ができる。しようと思えばinbound通信も可能だが、その許可は一切行わない。
  • private:(Managed Datastore) 外部ネットワークと直接通信は一切できない。

privateのルーティングを設定したサブネットには、EC2は配置しません。RDS, ElastiCache, Redshift等のマネージドサービスのインスタンスのみを配置できます。

なぜかといいますと。EC2は、NTPプロトコルを使って外部サーバと時刻の同期を行っています(参考: Amazon Linuxにおけるシステム時計とNTP)。privateは外部ネットワークと通信できませんので、NTPによる時刻同期ができない、ということになってしまいます。この点を解決する仕組みを構築しない限り、privateルーティングのサブネットにEC2を配置するのは実用的でないと判断しています。

一方、RDS等のマネージドサービスは、NTPに依らない別の仕組みによる時刻同期を行っているため、privateに置いても問題はありません。

NetworkACLはハマりどころが多いので、使いません。使わなくても充分セキュアだと認識しています。そしてセキュリティグループのアウトバウンド制御も、同様に使いません。まぁこれはあくまでも基本方針ですので、使いたければどうぞ。

サーバ間の宛先識別

ご存知の通り、TCP/IPプロトコルでは、IPアドレスを使ってノードの識別を行っています。VPCはプライベートIPアドレスを固定できることが一つのメリットであると思いますが、後に出て来るCloudFormationにおいては、IPアドレスを固定することにより、メンテナンス上の問題が発生 *2します。

プライベートIPを固定できれば、サーバ同士がIPアドレスで宛先を認識して通信することができるのですが、この構成ではその代わりに、ドメイン名による通信を行うようにします。それを実現する為の仕組みとしてRoute53を使います。例えば、example.comというhostedZoneについて、以下のように利用します。

  • 表向きはexample.com, foo.example.com, bar.example.com等を利用する。
  • foobar.local.example.com等、*.local.example.comというドメイン名を利用して、それに対するプライベートIPのAレコードを作成する。
  • RDSを利用するときも同様、rds.local.example.comに対するCNAMEレコードで、RDSのエンドポイント名を指定する。

以前、AWSにおける静的コンテンツ配信パターンカタログ(アンチパターン含む)でも触れたのですが、「AWSから当てがわれるIPアドレス・ドメイン名をそのまま利用する」のはアンチパターンです。RDSのホスト名も対象です。従って、Route53を介して、必ずドメイン名でアクセスするようにしましょう。

一つ問題となるのが、AutoScalingを使ったサーバへのアクセスですが、これはIPアドレスが特定できず、また、動的にDNSレコードを作成するのも少々面倒なため、都度都度プライベートIPアドレスを確認しながら利用する、という運用にしています。

$ aws ec2 describe-instances | jq -c -r \
    ".Reservations[].Instances[] | {name: (.Tags[] | select(.Key == \"Name\").Value), privateIp: .PrivateIpAddress}"

このようなコマンドで、各サーバのprivate IPアドレスが確認できます。

以上の構成図

どーん。

vpc-knowhow2

…という環境のCloudFormationテンプレートください。

ですよね。差し上げます。gistに貼っておきました。

https://gist.github.com/miyamoto-daisuke/10446903

動かす前に、事前にRoute53 HostedZoneを作成し、ネームサーバの設定は済ませておいてください。パラメータのHostedZoneにexample.comを指定した場合、スタックが完成すると、example.comでロードバランサ(Frontend)に繋がるようになっています。

配下のWebサーバ(Application)にはApacheをインストールしてindex.htmlを配置しただけです。一応、AutoScaling体制になっていますが、2台固定です。

また、RDS(Datastore)も構築されますが、Webサーバから繋ぐ処理は特に記述していません。MySQL clientをインストールしておきましたが、特に繋ぐ処理は書いていません。Bastion経由でWebサーバにSSHでログインし、下記コマンド等でRDSに接続を試みてみるのも良いでしょう。

$ mysql -h rds.local.example.com -u admin -p

2013年10月版からのアップデート

NATの廃止

最も大きいのはNATの廃止です。以前から「NATは本当に必要なのか」というのは度々議論のネタになっていました。単純にインスタンスコストが掛かるのが最も大きな理由でしょう。また、NATの完全な冗長性確保も不可能です。高トラフィックなサイトでDynamoDB等を利用する場合、NATがボトルネックになってしまうこともあるでしょう。

ところでNATが無い環境下では、各インスタンスそれぞれがpublic IPアドレスを持たなければ、インターネットOutbound通信ができません。つまりyumによるパッケージインストールもできませんし、ログをS3に転送することもできません。しかし、一昔前は「VPC内に置くEC2にpublic IPアドレスを割り当てることは、必ずしもできない」という状況でした。

例えば、VPC内のEC2にpublic IPアドレスを割り当てるためには従来はEIPを振るしかありませんでした。EIPは通常1アカウントにつき5つまでに制限されていますでの、NAT無しに5台以上のVPC構成を作るのは困難でした。この制約が緩和され、EIPに依らないpublic IPアドレスの割り当てが可能になったのが2013/08/20です。

しかし、AutoScalingやCloudFormationにはpublic IPアドレス割り当ての機能が無く、これらによって構築されるEC2インスタンスにpublic IPアドレスを割り当てることも困難でした。これが機能拡張されたのが2013/09/19です。

さらに、Elastic Beanstalkという壁がありました。VPC内にElastic Beanstalk環境を構築する場合、これらのインスタンスにもpublic IPアドレスを割り当てるのが困難でした。この拡張が先日2014/04/09、ついに行われました。

これを以って、VPC上で稼働するEC2インスタンスにpublic IPアドレスが割り当てられないという問題は全て解決したという認識です。従って、NATは必ずしも必要ではない、と言えるようになりました。

ただし、場合によってはNATを使うことによって「内部構造の露出を避けられる」とか、「外部サービスへの接続元IPアドレスを固定できる(外部サービスによるIPアドレスベースの認証がある場合等)」など、NATを上手く利用するケースもまだあるかもしれません。が、基本的にもうNATは不要だと言って良いでしょう。

privateサブネットの活用

前述の通り、インターネットとのInbound / Outbound通信共に行わない(行えない)サブネットがprivateサブネットです。このサブネット内にあるインスタンスはインターネット上にあるNTPサーバとの同期ができなくなるため、運用上の支障があることを指摘しています。

しかし、色々調査してみた所、RDS等のAWSによるマネージドサービスはNTP通信を行っていないことがわかりました。従って、これらのサービスのインスタンスはprivateサブネットに配置しても問題がありません。

一方、EC2上の構築するMongoDB等のサーバは、yumによるパッケージインストールの必要もあると思いますし、NTPによる時刻同期も必要です。そのため、Datastoreをpublicとする必要があります。

まとめ

というわけで、中小規模のAWSネットワーク環境の雛形をご紹介しました。上記のテンプレートはそのまま使う事はできないと思いますが、さまざまな構成の起点に使うと便利です。ご活用下さい。

脚注

  1. 踏み台
  2. Stack updateでEC2インスタンスを作り直すことになった場合、旧インスタンスと新インスタンスのIPアドレスが競合してしまいます。CloudFormationはリソースを作りなおす際、新しいリソースの作成に成功したことを確認した後に、古いリソースを削除するため、この問題を避ける事ができません。