AWSの部屋

AWS学習者向けのブログです

CloudFormation でインフラ環境を自動構築する

はじめに

マネジメントコンソールで手作業でインフラ環境を構築していくのは、最初は直感的でわかりやすいかと思います。ただ、同じ環境を複数用意する、複数の環境に同じ修正を横展開するといった作業を人力でやり続けることは効率が悪く、設定ミスも発生しやすくなります。今回はそのような時に役立つ CloudFormation を使ってみたいと思います。

使用するサービス

  • AWS CloudFormation・・・AWS リソースを自動構築するためのサービス

CloudFormation の利用の流れ

  1. CloudFormation テンプレートを作成する
  2. テンプレートを適用する
  3. CloudFormation スタックが作成され、それに紐づく形で AWS リソースが自動構築される

スタックとは

CloudFormation で構築された AWS リソースはスタックという集合にまとめられます。テンプレートを修正し、スタックを指定して再度適用することで、スタック上の AWS リソースの設定を変更したり、リソースを削除することができます。

テンプレートとは

スタックの設計図です。JSONYAML 形式で記述します。テンプレートに関する詳細はこちらをご参照ください。

今回のゴール

CloudFormation のテンプレート(yml)を作成し、それを使用して以下のようなインフラ環境を構築する。

作成する AWS リソース

  • VPC
  • サブネット(パブリック×1、プライベート×1)
  • ルートテーブル(パブリック×1、プライベート×1)
  • インターネットゲートウェイ
  • セキュリティグループ(パブリック×1、プライベート×1)
  • EC2(パブリック×1、プライベート×1)

CloudFormation テンプレートの解説

CloudFormation テンプレートはいくつかのセクションに分かれています。本エントリーではよく使われる以下3つのセクションについて説明します。

Resources セクション

構築する AWS リソースの設計を記述するセクション。例えば、VPC の設定は以下のように行います。このテンプレートでは「fjVpc」が VPC リソースを表す論理 ID で、この ID を使ってリソース間の紐づけを行います。

# Resources Section
Resources:
  # VPCの設定
  fjVpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: fjVpc
Parameters セクション

実行時に値を選択(入力)するセクション。例えば、以下のようにインスタンスタイプを変数として定義し、実行時に選択する形式にすることができます。

# Parameters Section
Parameters:
  InstanceType:
    Type: String
    Default: t2.micro
    AllowedValues:
      - t2.micro
      - t2.small
      - t2.medium
    Desctiption: Select EC2 instance type.
  KeyPair:
    Description: Select KeyPair Name.
    Type: AWS::EC2::KeyPair::KeyName
Mappings セクション

変数を Map 形式に定義できるセクション(実行環境によって変わる値を定義するのに用いられることが多い)。例えば、AMI ID はリージョンによって変わるため、以下のように AMI ID を定義します。

# Mappings Section
Mappings:
  RegionMap:
    us-east-1:
      hvm: 'ami-a4c7edb2'
    ap-northeast-1:
      hvm: 'ami-3bd3c45c'

CloudFormation テンプレートの作成

以下のようなテンプレートを作成しました。

AWSTemplateFormatVersion: "2010-09-09"
Description: Network and server resource template

# Parameters Section
Parameters:
  InstanceType:
    Type: String
    Default: t2.micro
    AllowedValues:
      - t2.micro
      - t2.small
      - t2.medium
    Description: Select EC2 instance type.
  KeyPair:
    Description: Select KeyPair Name.
    Type: AWS::EC2::KeyPair::KeyName
    
# Mappings Section
Mappings:
  RegionMap:
    us-east-1:
      hvm: 'ami-a4c7edb2'
    ap-northeast-1:
      hvm: 'ami-3bd3c45c'
      
# Resources Section
Resources:
  ############### VPC ###############
  fjVpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: fjVpc
  
  ############### Subnet, RouteTable, IGW ###############
  
  ## パブリックサブネット
  fjSubnetPublic:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.0.0/24
      VpcId:
        Ref: fjVpc
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: fj-subnet-public
        - Key: Type
          Value: Public
          
  ## プライベートサブネット
  fjSubnetPrivate:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: 10.0.1.0/24
      VpcId:
        Ref: fjVpc
      MapPublicIpOnLaunch: false
      Tags:
        - Key: Name
          Value: fj-subnet-private
        - Key: Type
          Value: Isolated
  
  ## パブリックサブネット用のルートテーブル
  fjRoutePublic:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: fjVpc
      Tags:
        - Key: Name
          Value: fj-route-public
          
  ## パブリックサブネットへルート紐付け
  fjRoutePublicAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: fjRoutePublic
      SubnetId:
        Ref: fjSubnetPublic

  ## パブリックサブネット用ルートテーブルのデフォルトルート
  fjRoutePublicDefault:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId:
        Ref: fjRoutePublic
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId:
        Ref: fjIgw
    DependsOn:
      - fjVpcgwAttachment

  ## プライベートサブネット用のルートテーブル
  fjRoutePrivate:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: fjVpc
      Tags:
        - Key: Name
          Value: fj-route-private
          
  ## プライベートサブネットへルート紐付け
  fjRoutePrivateAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: fjRoutePrivate
      SubnetId:
        Ref: fjSubnetPrivate
  
  # インターネットへ通信するためのゲートウェイの作成
  fjIgw:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: fj-igw
  fjVpcgwAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId:
        Ref: fjVpc
      InternetGatewayId:
        Ref: fjIgw
  
  ############### Security groups ###############
  ## インターネット公開のセキュリティグループの生成
  fjSgPublic:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for public
      GroupName: public
      SecurityGroupEgress:
        - CidrIp: 0.0.0.0/0
          Description: Allow all outbound traffic by default
          IpProtocol: "-1"
      SecurityGroupIngress:
        - CidrIp: 0.0.0.0/0
          Description: from 0.0.0.0/0:80
          FromPort: 80
          IpProtocol: tcp
          ToPort: 80
        - CidrIp: 0.0.0.0/0
          Description: from 0.0.0.0/0:22
          FromPort: 22
          IpProtocol: tcp
          ToPort: 22
      Tags:
        - Key: Name
          Value: fj-sg-public
      VpcId:
        Ref: fjVpc
  
  ## プライベートサブネットインスタンス用のセキュリティグループの生成
  fjSgPrivate:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security Group of private
      GroupName: private
      SecurityGroupEgress:
        - CidrIp: 0.0.0.0/0
          Description: Allow all outbound traffic by default
          IpProtocol: "-1"
      SecurityGroupIngress:
        - CidrIp: 0.0.0.0/0
          Description: from 0.0.0.0/0:22
          FromPort: 22
          IpProtocol: tcp
          ToPort: 22
        - CidrIp: 0.0.0.0/0
          Description: from 0.0.0.0/0:22
          FromPort: -1
          IpProtocol: icmp
          ToPort: -1
      Tags:
        - Key: Name
          Value: fj-sg-private
      VpcId:
        Ref: fjVpc
        
  ############### Server ###############
  
  ## EC2(パブリックサブネット)
  fjEC2Public:
    Type: AWS::EC2::Instance
    Properties: 
      ## Mappingsセクションの値をFindMap関数で取得
      ImageId: !FindInMap [ RegionMap, !Ref 'AWS::Region', hvm]
      ## Parametersセクションの値をRef関数で取得
      InstanceType: !Ref InstanceType
      SubnetId: !Ref fjSubnetPublic
      Tags:
        - Key: Name
          Value: fj-ec2-public
      SecurityGroupIds:
        - !Ref fjSgPublic
      KeyName: !Ref KeyPair
  
  ## EC2(プライベートサブネット)
  fjEC2Private:
    Type: AWS::EC2::Instance
    Properties: 
      ## Mappingsセクションの値をFindMap関数で取得
      ImageId: !FindInMap [ RegionMap, !Ref 'AWS::Region', hvm]
      ## Parametersセクションの値をRef関数で取得
      InstanceType: !Ref InstanceType
      SubnetId: !Ref fjSubnetPrivate
      Tags:
        - Key: Name
          Value: fj-ec2-private
      SecurityGroupIds:
        - !Ref fjSgPrivate
      KeyName: !Ref KeyPair

CloudFormation スタックの作成

作成した CloudFormation テンプレートを使用して CloudFormation スタックを作成します。

Parameters セクションで設定した項目に対して値を入力します。この後はすべてデフォルト設定でOKです。

スタックが作成されました。

AWS リソースの確認

以下のように AWS リソースが作成されていることが確認できました。

VPC

サブネット

ルートテーブル

インターネットゲートウェイ

セキュリティグループ

EC2

動作確認

EC2 の動作確認を行います。

パブリックサブネット用の EC2

SSH で EC2 にログインします。

EC2 へのログインを確認しました。

プライベートサブネット用の EC2

パブリックサブネット用の EC2 から SSH で プライベートサブネット用の EC2 へのログインをすることができました。

また、ping も通ることがわかりました。

さいごに

CloudFormation テンプレートを実際に作成したのは今回が初めてでした。ただ、AWS のネットワークやサーバー構築に関して基本的な知識があればすぐに理解できると感じました(AWS に関する基本的な理解がない場合はかなり厳しそう)。今回はシンプルな構成のインフラ環境でしたが、次は少し複雑な環境構築にも挑戦してみようと思います。