俺のアウトプット

調べたこと、試したことを書きます

シンプルなネットワークとサーバーをCloudFormationで構築する 〜1. ネットワーク構築〜

新規にVPCを作成して、Webサーバーを公開するテンプレートを作成します。
今回は、パブリックサブネット内に1つのWebサーバーを立てる、シンプルな構成とします。(APサーバー、DBサーバーは無し)

f:id:kitsugi:20180819161411p:plain

テンプレートを目的ごとに分けます。

  1. ネットワーク構築
  2. セキュリティ設定
  3. サーバー構築

今回は、ネットワーク構築についてです。

ネットワーク構築用のテンプレート

作成したテンプレートは以下になります。

network.template.yaml

AWSTemplateFormatVersion: 2010-09-09
Parameters:
  ProjectCode:
    Type: String
    Description: Enter your project code
  VPCCidr:
    Type: String
    Description: IP Address range for the VPN connected VPC
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
    MinLength: 9
    MaxLength: 18
    Default: 10.0.0.0/16
    AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
  SubnetCidr:
    Type: String
    Description: IP Address range for the VPN connected Subnet
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
    MinLength: 9
    MaxLength: 18
    Default: 10.0.1.0/24
    AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCidr
      Tags:
      -
        Key: Name
        Value: !Ref ProjectCode
  Subnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Ref SubnetCidr
      Tags:
      -
        Key: Name
        Value: !Ref ProjectCode
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
      -
        Key: Name
        Value: !Ref ProjectCode
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
      -
        Key: Name
        Value: !Ref ProjectCode
  Route:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  SubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref Subnet
      RouteTableId: !Ref RouteTable
Outputs:
  VPC:
    Description: VPCId of the newly created VPC
    Value: !Ref VPC
    Export:
      Name: !Sub ${ProjectCode}-vpc
  Subnet:
    Description: SubnetId of the VPN connected subnet
    Value: !Ref Subnet
    Export:
      Name: !Sub ${ProjectCode}-subnet

パラメータ

パラメータを3つ用意しました。

パラメータ 説明
ProjectCode プロジェクトコードを指定します。
各リソースの名前とテンプレート連携(クロススタック参照)で利用します。
VPCCidr VPC用のCIDRブロックを指定します。
デフォルトは10.0.0.0/16です。
SubnetCidr サブネット用のCIDRブロックを指定します。
デフォルトは10.0.1.0/24です。

リソース

リソースのプロパティは最低限に留めています。オプション項目は公式ドキュメントを参照してください。

1. VPCの作成

選択しているリージョンに、新しいVirtual Private Cloud (VPC) を作成します。 パラメータのVPC CIDRブロックを指定します。

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCidr

f:id:kitsugi:20180819161535p:plain

2. サブネットの作成

作成したVPC内にサブネットを作成します。 パラメータのサブネット CIDRブロックを指定します。
この時、デフォルトのルートテーブルが作成されます。 デフォルトのルートテーブルは、宛先がVPC内のみで、それ以外のパケットは破棄されます。

  Subnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Ref SubnetCidr

f:id:kitsugi:20180819170840p:plain

3. インターネットゲートウェイの作成と割り当て

VPCがインターネットとやり取りできるように、まずはインターネットゲートウェイを作成し、VPCに割り当てます。

  InternetGateway:
    Type: AWS::EC2::InternetGateway
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

f:id:kitsugi:20180819170858p:plain

4. ルートテーブルの作成と設定、割り当て

インターネットと接続するためには、ルートテーブルに宛先にインターネットゲートウェイを追加する必要があります。 デフォルトのルートテーブルに追加すると他に影響が出る場合があるため、新規にルートテーブルを作成・設定し、サブネットに割り当てます。

  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
  Route:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  SubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref Subnet
      RouteTableId: !Ref RouteTable

f:id:kitsugi:20180819170922p:plain

出力

後続のテンプレート用にリソースIDをエクスポートします。

パラメータ 説明
VPC VPC IDの参照用に、エクスポート名を {プロジェクトコード}-vpc にします。
Subnet サブネットIDの参照用に、エクスポート名を {プロジェクトコード}-subnet にします。

スタック間の連携については、下記のエントリを参照してください。 oreout.hatenablog.com

スタックの作成

ではスタックを作成してみましょう。

スタックの名前、パラメータを指定します。

f:id:kitsugi:20180819173734p:plain

スタック作成完了後に、出力を確認します。

f:id:kitsugi:20180819174229p:plain

サービス [ネットワーキング & コンテンツ配信] - [VPC] を選択し、各リソースが作成されていることを確認します。

  • VPC
  • サブネット
  • ルートテーブル
  • インターネットゲートウェイ

参考

サンプルテンプレート(JSON)をCloudFormation DesignerでYAMLに変換する

テンプレートを一から全て作成するのは面倒です。
そのような場合、AWS公式のサンプルテンプレートから目的に近いものをベースに作成すると、手間が省けます。

サンプルテンプレート

サンプルテンプレートは各リージョンごとに用意されています。
今回はAsia Pacific (Tokyo) Regionを選択します。

  • サンプルソリューション
    • SharePoint® Foundation 2010
    • Microsoft Windows Server Active Directory
    • Microsoft Windows Server の役割と機能
    • 基本的な WordPress (単一インスタンス)
    • スケーラブルで耐久性の高い WordPress
    • Chef を使用した WordPress のデプロイ
  • アプリケーションフレームワーク
    • 基本的な LAMP スタック
    • スケーラブルで耐久性の高い LAMP スタック
    • Ruby on Rails の基本
    • スケーラブルで耐久性の高い Ruby on Rails
  • サービス
    • Auto Scaling関連 (3)
    • AWS Config関連 (1)
    • Amazon DynamoDB関連 (2)
    • Amazon EC2関連 (3)
    • Amazon ElastiCache関連 (2)
    • AWS Elastic Beanstalk関連 (3)
    • Elastic Load Balancing関連 (4)
    • AWS Identity and Access Management関連 (1)
    • AWS OpsWorks関連 (2)
    • Amazon Relational Database Service関連 (4)
    • Amazon Redshift関連 (2)
    • Amazon Route 53関連 (3)
    • Amazon Simple Storage Service関連 (2)
    • Amazon Simple Queue Service関連 (2)
    • Amazon Virtual Private Cloud関連 (7)

各サンプルの[表示]リンクをクリックすると、JSON形式のテンプレートがブラウザで表示されます。 YAML形式が欲しい場合、CloudFormation Designerを起動してJSONからYAMLへ変換します。

CloudFormation Designer

各サンプルの[デザイナーで表示]リンクをクリックすると、CloudFormation Designerが起動されます。

f:id:kitsugi:20180816071800p:plain

【重要】Canvasペイン内のコンポーネントは操作しないでください。操作すると、テンプレート内にMetadata情報が追加されます。

  1. テンプレートの言語の選択で YAML を選択
  2. 左上の[ファイル]アイコンをクリック
  3. メニューの[保存]をクリック
    • ローカルファイルを選択、ファイル名を入力して[保存]ボタンをクリック
    • ブラウザによってはダウンロードされずにそのまま表示されます。その場合は、ページをローカルに保存してください。

これをベースに自分だけのテンプレートを作成してください。
断片的な書き方は、テンプレートスニペットが参考になります。

参考

Amazon Linux2にExtras Libraryからnginxをインストールする

Amazon Linux2 では、Extras Libraryを使用してメジャーなパッケージ(トピック)をインストールすることができます。

Extras Library

パッケージ(トピック)の種類、バージョンは定期的に更新されるため、amazon-linux-extrasコマンドで確認します。
(2018年8月現在)

$ amazon-linux-extras
  0  ansible2                 available  [ =2.4.2 ]
  1  emacs                    available  [ =25.3 ]
  2  memcached1.5             available  [ =1.5.1 ]
  3  nginx1.12                available  [ =1.12.2 ]
  4  postgresql9.6            available  [ =9.6.6  =9.6.8 ]
  5  postgresql10             available  [ =10 ]
  6  python3                  available  [ =3.6.2 ]
  7  redis4.0                 available  [ =4.0.5  =4.0.10 ]
  8  R3.4                     available  [ =3.4.3 ]
  9  rust1                    available  [ =1.22.1  =1.26.0  =1.26.1  =1.27.2 ]
 10  vim                      available  [ =8.0 ]
 11  golang1.9                available  [ =1.9.2 ]
 12  ruby2.4                  available  [ =2.4.2  =2.4.4 ]
 13  nano                     available  [ =2.9.1 ]
 14  php7.2                   available  [ =7.2.0  =7.2.4  =7.2.5 ]
 15  lamp-mariadb10.2-php7.2  available  [ =10.2.10_7.2.0  =10.2.10_7.2.4  =10.2.10_7.2.5 ]
 16  libreoffice              available  [ =5.0.6.2_15 ]
 17  gimp                     available  [ =2.8.22 ]
 18  docker=latest            enabled    [ =17.12.1  =18.03.1 ]
 19  mate-desktop1.x          available  [ =1.19.0  =1.20.0 ]
 20  GraphicsMagick1.3        available  [ =1.3.29 ]
 21  tomcat8.5                available  [ =8.5.31 ]

enabledがインストール済、availableが未インストールの状態です。
初期状態ではphpやruby、Go言語、Python3系はインストールされていないことがわかります。(Python2系はインストールされています)

nginxのインストール

今回はnginxをインストールします。 nginxのバージョンを確認してインストールします。

$ sudo amazon-linux-extras install nginx1.12 -y

インストールされたか確認をします。 ステータスがenabledになっているか確認をします。

$ amazon-linux-extras | grep nginx
  3  nginx1.12=latest         enabled    [ =1.12.2 ]

nginxのバージョンを確認します。

$ nginx -v
nginx version: nginx/1.12.2

nginxの起動

nginxを起動します。

$ sudo systemctl start nginx
ローカル(EC2)上から確認
$ curl -I localhost
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Tue, 14 Aug 2018 21:04:21 GMT
Content-Type: text/html
Content-Length: 3520
Last-Modified: Wed, 13 Dec 2017 18:36:55 GMT
Connection: keep-alive
ETag: "5a317347-dc0"
Accept-Ranges: bytes
外部(ブラウザ)から確認

EC2インスタンスのパブリックIPアドレスをブラウザに指定して開きます。
ローカルから確認出来ており、接続出来ない場合はセキュリティーグループとネットワークACLを確認してください。

f:id:kitsugi:20180815061411p:plain

自動起動

EC2インスタンス起動時にnginxを起動する場合と確認方法は以下になります。

自動起動

$ sudo systemctl enable nginx

確認

$ systemctl is-enabled nginx
enabled

参考

iperfを利用して東京〜USリージョン間のEC2ネットワークスループットを計測してみた

米国リージョンは、政府用を除くと、下記の4つがあります。(2018年8月現在)

  • us-east-1: 米国東部(バージニア北部)
  • us-east-2: 米国東部 (オハイオ)
  • us-west-1: 米国西部 (北カリフォルニア)
  • us-west-2: 米国西部 (オレゴン)

f:id:kitsugi:20180813055417p:plain

東京リージョンEC2から各リージョンEC2へ、どの程度のネットワークスループットがあるのか?
気になったので計測してみました。
計測にはオープンソースのiperf(アイパーフ)を利用し、EC2はAmazon Linux2を選択しました。

EC2

各リージョンにEC2インスタンスを作成します。

  • AMIの選択
    • Amazon Linux2
  • インスタンスタイプの選択
    • t2.micro
  • インスタンスの設定
    • ネットワーク: デフォルトVPC
    • サブネット: 優先順位なし(AZのデフォルトサブネット)
  • セキュリティグループの設定
    • インバウンド1行目(サーバー側の場合)
      • タイプ: カスタムTCPルール
      • ポート範囲: 5001
      • ソース: 任意の場所
    • インバウンド2行目
      • タイプ: SSH
      • ポート範囲: 22
      • ソース: 任意の場所

iperf

インストール

Amazon Linux2にEPELリポジトリを追加してからインストールします。

sudo yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
sudo yum install -y iperf
サーバー側(米国リージョン)

オプション-sを付けてリッスン状態にします。
デフォルトではTCP 5001番を利用します。

$ iperf -s
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: xx.x KByte (default)
------------------------------------------------------------
クライアント側(東京リージョン)

サーバー側をリッスン状態にした後に、クライアント側からコマンドを叩きます。 オプションは-cで、サーバー側のIPアドレスを指定します。

$ iperf -c xxx.xxxx.xxx.xxx(サーバー側のIPアドレス)

接続出来ない場合は、サーバー側のTCP 5001番(デフォルト)が利用出来るか、セキュリティグループ、ネットワークACLを見直してください。

計測結果

参考にシンガポールも計測しました。
それぞれ10回計測し、上位2値と下位2値を取り除いた値の平均値を取っています。 計測値は時間帯やAZ、経路など、状況によって変わるため、参考程度に見てください。

リージョン AZ Mbits/sec
バージニア北部 us-east-1b 68.82
オハイオ us-east-2b 73.42
北カルフォルニア us-west-1b 108.66
オレゴン us-west-2b 111.8
シンガポール ap-southeast-1b 160.5

参考

リージョン間で同じEC2キーペアを利用する

EC2キーペアはリージョン単位で管理されます。
他リージョンで同じキーペアを利用したい場合、既に利用しているキーペアの公開鍵をインポートします。

EC2キーペアおさらい

  • 公開鍵暗号方式
  • ローカル側で秘密鍵を管理
  • 公開鍵をAWS側(リージョン単位)で管理
  • EC2 Linuxインスタンス作成時、(AWS側が)公開鍵を~/.ssh/authorized_keysにコピーして起動
  • ローカル側はパスワードなしにSSH接続

前提条件

  • ローカル上に公開鍵(*.pem.pub) または秘密鍵(*.pem)のファイルがある
  • ssh-keygenが利用できる環境である

キーペアのインポート

マネジメントコンソールからインポートします。

  1. インポートしたいリージョンを選択
  2. サービス [コンピューティング] - [EC2] を選択
  3. ナビゲーションペインから [ネットワーク & セキュリティ] - [キーペア] を選択
  4. [キーペアのインポート]ボタンをクリック

公開鍵の場合

[パブリックキーをファイルからロードする] で、公開鍵(*.pem.pub)ファイルを選択します。必要に応じて、キーペア名を変更します。
[インポート]ボタンをクリックするとキーペアが作成されます。

f:id:kitsugi:20180810070342p:plain

秘密鍵の場合

秘密鍵(*.pem)から公開鍵を取得します。

ssh-keygen -y -f 秘密鍵(*.pem)ファイルのパス

パスワードがある場合は入力します。

$ ssh-keygen -y -f test.pem
Enter passphrase: ********
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDOlCcrzRabj2k5F5SO9w43Vij10u7NJFS略

ssh-rsa以降をコピーして、パブリックキーの内容にペーストします。

キーペア名を入力して、[インポート]ボタンをクリックするとキーペアが作成されます。

f:id:kitsugi:20180810070449p:plain

参考

CloudFormationスタック完了時だけメールを受信する

前回のエントリで、スタックイベントをメールで受信できるようになりました。

しかし、全てのイベントを受信するため、大量のメールが届きます。

完了時だけひょっこりメールを受け取りたい!!

最初は、メッセージのフィルタリング機能を使えばできると思ったのですが、メッセージ属性 に対してのフィルタリングであって メッセージ本文 ではできません。

そこで、間に AWS Lambda を挟んで、完了イベントのみ再通知する仕組みを検討します。

概要

大まかな流れは以下になります。

f:id:kitsugi:20180806074701p:plain

  1. ユーザーがスタックを作成
  2. CloudFormationがスタックイベントをLambdaに送信
  3. Lambdaでメッセージをチェック、スタックの完了メッセージならSNSメール送信
  4. ユーザーがメールを受信

トピックの作成

Lambda用とMail用の2つのトピックを作成します。
サービス [アプリケーション統合] - [Simple Notification Service] を選択して、Lambda用のトピックを作成します。

Lambda用
  • トピック名: cloudformation_filter
  • サブスクリプション
    • ここでは作成しません。(Lambda作成時に作成される)
Mail用
  • トピック名: cloudformation
  • サブスクリプション
    • プロトコル: Email
    • エンドポイント: 送信したいメールアドレス

Mail用は前回の記事を参照してください。

AWS Lambdaの作成

次にLambdaを作成します。
サービス [コンピューティング] - [Lambda] を選択し、[関数の作成]ボタンをクリックします。

関数の作成

f:id:kitsugi:20180806074738p:plain

  1. 設計図を選択
  2. テキストボックスにsnsと入力してEnterキー、設計図をフィルタリング
  3. sns-messageを選択
  4. [設定]ボタンをクリック
基本的な情報

f:id:kitsugi:20180806074759p:plain

  • 名前: filterMessage
  • ロール: テンプレートから新しいロールを作成
  • ロール名: lambda_send_sns
  • ポリシーテンプレート: SNN 発行ポリシー
SNSトリガー

f:id:kitsugi:20180806074819p:plain

  • SNSトピック: cloudformation_filter
  • トリガーの有効化: ON

問題なければ、[関数の作成]ボタンをクリックします。

Designer

Lambdaの左側に SNS、右側にAmazon CloudWatchAmazon SNSが表示されていることを確認します。

f:id:kitsugi:20180808175626p:plain

関数コード

以下のコードを入力します。
************は自身のAWSアカウントを指定してください。

var aws = require('aws-sdk');
var sns = new aws.SNS();
let result = {};

exports.handler = async (event, context) => {
    try {
        const mail = event.Records[0].Sns;
        const found = mail.Message.match(/^(?=[\s\S]*ResourceStatus='CREATE_COMPLETE')(?=[\s\S]*ResourceType='AWS::CloudFormation::Stack')/);
    
        if (found) {
            result = await sns.publish({
                Subject: mail.Subject,
                Message: mail.Message,
                TopicArn: "arn:aws:sns:ap-northeast-1:************:cloudformation"
            }).promise();            
        }
  } catch (err) {
      console.log(err);
      return err;
  }

  return result;
};

補足

  • デフォルトのランタイムはNode.js 8.10です。async, await, promiseを利用しています。
  • 改行を含む文字列、メッセージ内に ResourceStatus='CREATE_COMPLETE' かつ ResourceType='AWS::CloudFormation::Stack' を含む正規表現でチェックしています。
  • メールの件名と本文は、受信したメールをそのまま設定しています。わかりやすい内容に変更してください。
Lambdaの保存

右上の[保存]ボタンをクリックします。
「保存されました。」と表示されれば正常です。

f:id:kitsugi:20180806074845p:plain

Lambdaのテスト

メールが受信できるか確認をします。
[テスト]ボタンをクリックして、テストイベントの設定をします。

f:id:kitsugi:20180808182219p:plain

  • 新しいテストイベントの作成: ON
  • イベントテンプレート: SNS
  • イベント名: myEvent

JSON文字列内の "Message" 値を変更します。

"Message": "ResourceStatus='CREATE_COMPLETE' ResourceType='AWS::CloudFormation::Stack'"

[作成]ボタンをクリック、テストイベントmyEventが選択されている状態で、再度[テスト]ボタンをクリックします。

メールが届けば成功です。これで準備が整いました。

CloudFormationスタックの作成

スタックの作成時にLambda用のトピックを指定します。

f:id:kitsugi:20180808184820p:plain

既存のAmazon SNSトピックでLambda用のcloudformation_filterを選択します。
スタック作成後、メールが1件だけ届いたでしょうか?

まとめ

AWS Lambdaを挟むことによって、メールをフィルタリングすることが出来ました。 今回はメール通知ですが、Slackに通知したりするのも面白そうです。

参考

CloudFormationスタックイベントをSNSでメール通知する

スタック作成時に通知オプションを設定すると、スタックイベントの内容を通知することができます。

トピックの作成

マネジメントコンソールから、サービス [アプリケーション統合] - [Simple Notification Service] を選択します。

トピックを選択して、[新しいトピックの作成]ボタンをクリック

f:id:kitsugi:20180805170748p:plain

トピック名を入力して[トピックの作成]ボタンをクリック

f:id:kitsugi:20180805170818p:plain

トピックが作成されます。

サブスクリプションの作成

トピック一覧から作成したトピックのARNリンクをクリックして、トピックの詳細を開きます。

f:id:kitsugi:20180807053215p:plain

[サブスクリプションの作成]ボタンをクリック

f:id:kitsugi:20180805171112p:plain

プロトコルとエンドポイントを入力します。

  • プロトコル: Email
  • エンドポイント: 送信したいメールアドレス

f:id:kitsugi:20180805171231p:plain

[サブスクリプションの作成]ボタンをクリックすると、確認用のメールが送信されます。
メール内のConfirm subscriptionリンクをクリックすると確認済となり、これで利用可能となります。

f:id:kitsugi:20180805211140p:plain

メールの確認

メールが受信できるか確認します。
トピックの詳細画面、左上の[トピックに発行]ボタンをクリック

f:id:kitsugi:20180807054753p:plain

件名とメッセージを入力して[メッセージの発行]ボタンをクリック

f:id:kitsugi:20180807063341p:plain

メールが届いたら成功です。

スタックの作成

CloudFormation スタック作成時に、スタックイベントがメール通知されるか確認をします。

テンプレートの選択

サンプルテンプレートの選択で、LAMP Stackを選択します。

f:id:kitsugi:20180807060622p:plain

通知オプションの設定

[オプション] - [アドバンスト] の通知オプション 既存のAmazon SNSトピックで、作成したトピックを指定します。

f:id:kitsugi:20180807061909p:plain

メールの確認

スタックを作成した後、メールが届いているか確認をします。イベントの件数とメール件数が一致していれば成功です。

f:id:kitsugi:20180807061524p:plain

スタック作成時だけでなく、更新、削除時もメール通知されます。

まとめ

スタックイベントをメールで受信できるようになりました。
全てのイベントを受信するため、結構な量になります。フィルタリング機能はないのでしょうか?

参考