AWS
EC2
elb
CloudFormation

CloudFormationを使ってELBとEC2(2台)を構築する

はじめに

本記事では、AWS CloudFormation管理コンソールを使って、ELBとEC2(2台)を構築する手順を説明しています。(初心者向け)

本記事で掲載しているテンプレートの最新版は、下記に置いてます。
https://github.com/okubo-t/aws-cloudformation

構成図

前提条件

下記の記事の構築手順で、VPCを構築していること。
CloudFormationを使ってVPCを構築する

事前に、EC2のキーペアを生成していること。

PJPrefixの値は、同一にすること。

EC2を1台構成で構築したい場合は、下記の記事で。
CloudFormationを使ってELBとEC2(1台)を構築する

構築手順

1 AWS CloudFormation管理コンソールから、スタックの作成をクリックします。

2 後述のテンプレートを選択します。

3 各パラメータを入力します。

パラメータ名 用途 備考
スタックの名前 テンプレートから作成するリソース一式の名前 例 prd-stack-vpc-20180801
PJPrefix 構築するプロジェクトの環境を識別するために各コンポーネントの先頭に付与する識別子 例 qiita-prd
InternetELBName TagのNameに設定するELB名 web(デフォルト)
KeyPairName AmazonEC2のキーペア。利用するキーペアを選択
EC2InstanceName TagのNameに設定するインスタンス名 web(デフォルト)
EC2InstanceAMI ローンチしたいAMIのIDを入力
EC2InstanceInstanceType インスタンスタイプ t2.micro(デフォルト)
EC2InstanceVolumeType ボリュームのタイプ g2(デフォルト)
EC2InstanceVolumeSize ボリュームのサイズ 30(デフォルト)
SSHAccessSourceIP EC2にSSHアクセスを許可する送信元のIPアドレス

4 後続は、デフォルトのまま次へ次へで、作成します。
作成する前に、下記のチェックをつけること
AWS CloudFormation によってカスタム名のついた IAM リソースが作成される場合があることを承認します。

5 状況が CREATE COMPLETEになれば、ELBとEC2の2台の構築が完了です。

6 管理コンソールの下部の出力から、構築したELBとEC2の2台の情報を確認できます。
  ここで、キーがEC2Instance01EIPとEC2Instance02EIPとELBDNSNameの値をメモしておきます。

7 メモしたEC2InstanceEIP01とEC2InstanceEIP02を元に、構築したEC2の2台にSSHでログインし、httpdをインストールして、起動します。
(テンプレートに組み込むこともできますが、ここでは手動で)

sudo yum install -y httpd
sudo service httpd start

8 管理コンソールの下部のリソースから、作成したELBの管理コンソールへ遷移します。

9 ELBにアタッチされたEC2の2台のステータスがInServiceになっていることを確認します。

10 ブラウザから、先ほどメモしたELBDNSNameの値にWebアクセスしてみます。
下記のような画面が表示されれば完了です。
例)

テンプレート

elb-ec2-02.yml
AWSTemplateFormatVersion: "2010-09-09"
Description:
  ELB and EC2 Instance Create

Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "Project Name Prefix"
        Parameters:
          - PJPrefix
      - Label:
          default: "InternetELB Configuration"
        Parameters:
          - InternetELBName
      - Label:
          default: "EC2Instance Configuration"
        Parameters:
          - KeyPairName
          - EC2InstanceName
          - EC2InstanceAMI
          - EC2InstanceInstanceType
          - EC2InstanceVolumeType
          - EC2InstanceVolumeSize
          - SSHAccessSourceIP

    ParameterLabels:
      InternetELBName:
        default: "InternetELBName"
      KeyPairName:
        default: "KeyPairName"
      EC2InstanceName:
        default: "EC2 Name"
      EC2InstanceAMI:
        default: "EC2 AMI"
      EC2InstanceInstanceType:
        default: "EC2 InstanceType"
      EC2InstanceVolumeType:
        default: "EC2 VolumeType"
      EC2InstanceVolumeSize:
        default: "EC2 VolumeSize"
      SSHAccessSourceIP:
        default: "SSH AccessSourceIP"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  PJPrefix:
    Type: String

#InternetELB
  InternetELBName:
    Type: String
    Default: "web"

#EC2Instance
  KeyPairName:
    Type: AWS::EC2::KeyPair::KeyName
    Default: ""
  EC2InstanceName:
    Type: String
    Default: "web"
  EC2InstanceAMI:
    Type: String
    Default: ""
  EC2InstanceInstanceType:
    Type: String
    Default: "t2.micro"
  EC2InstanceVolumeType:
    Type: String
    Default: "gp2"
  EC2InstanceVolumeSize:
    Type: String
    Default: "30"
  SSHAccessSourceIP:
    Type: String

Resources:
# ------------------------------------------------------------#
#  IAM Role for EC2
# ------------------------------------------------------------# 
  EC2IAMRole: 
    Type: "AWS::IAM::Role"
    Properties: 
      RoleName: !Sub "${PJPrefix}-${EC2InstanceName}-role" 
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Effect: Allow
            Principal: 
              Service: 
                - "ec2.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      Path: "/"
      ManagedPolicyArns: 
        - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
        - "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"

  EC2InstanceProfile: 
    Type: "AWS::IAM::InstanceProfile"
    Properties: 
      Path: "/"
      Roles: 
        - Ref: EC2IAMRole
      InstanceProfileName: !Sub "${PJPrefix}-${EC2InstanceName}-profile"

# ------------------------------------------------------------#
#  EC2Instance AZ:A
# ------------------------------------------------------------#
  EC2Instance01:
    Type: "AWS::EC2::Instance"
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${EC2InstanceName}-01"
      ImageId: !Ref EC2InstanceAMI
      InstanceType: !Ref EC2InstanceInstanceType
      KeyName: !Ref KeyPairName
      IamInstanceProfile: !Ref EC2InstanceProfile
      DisableApiTermination: false
      EbsOptimized: false
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            VolumeType: !Ref EC2InstanceVolumeType
            VolumeSize: !Ref EC2InstanceVolumeSize
      SecurityGroupIds:
        - !Ref ManagedSecurityGroup
        - !Ref WebSecurityGroup
      SubnetId: { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a" }
      UserData: !Base64 | 
        #! /bin/bash
        yum update -y

# ------------------------------------------------------------#
#  EC2Instance AZ:C
# ------------------------------------------------------------#
  EC2Instance02:
    Type: "AWS::EC2::Instance"
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${EC2InstanceName}-02"
      ImageId: !Ref EC2InstanceAMI
      InstanceType: !Ref EC2InstanceInstanceType
      KeyName: !Ref KeyPairName
      IamInstanceProfile: !Ref EC2InstanceProfile
      DisableApiTermination: false
      EbsOptimized: false
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            VolumeType: !Ref EC2InstanceVolumeType
            VolumeSize: !Ref EC2InstanceVolumeSize
      SecurityGroupIds:
        - !Ref ManagedSecurityGroup
        - !Ref WebSecurityGroup
      SubnetId: { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c" }
      UserData: !Base64 |
        #! /bin/bash
        yum update -y

# ------------------------------------------------------------#
#  SecurityGroup for Managed
# ------------------------------------------------------------#
  ManagedSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties: 
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-managed-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-managed-sg"
# Rule
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref SSHAccessSourceIP

# ------------------------------------------------------------#
#  SecurityGroup for ELB
# ------------------------------------------------------------#
  ELBSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-elb-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-elb-sg"
# Rule
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"

        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: "0.0.0.0/0"

# ------------------------------------------------------------#
#  SecurityGroup for Web
# ------------------------------------------------------------#
  WebSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-web-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-web-sg"

# Rule
  WebSecurityGroupIngress: 
    Type: "AWS::EC2::SecurityGroupIngress"
    Properties: 
      IpProtocol: tcp
      FromPort: 80
      ToPort: 80
      SourceSecurityGroupId: !GetAtt [ ELBSecurityGroup, GroupId ] 
      GroupId: !GetAtt [ WebSecurityGroup, GroupId ]

# ------------------------------------------------------------#
#  ElasticIP for EC2Instance01
# ------------------------------------------------------------# 
  ElasticIP01:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc

  ElasticIPAssociate01:
    Type: AWS::EC2::EIPAssociation
    Properties: 
      AllocationId: !GetAtt ElasticIP01.AllocationId
      InstanceId: !Ref EC2Instance01

# ------------------------------------------------------------#
#  ElasticIP for EC2Instance02
# ------------------------------------------------------------# 
  ElasticIP02:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc

  ElasticIPAssociate02:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt ElasticIP02.AllocationId
      InstanceId: !Ref EC2Instance02

# ------------------------------------------------------------#
#  InternetELB
# ------------------------------------------------------------#
  InternetELB: 
    Type: "AWS::ElasticLoadBalancing::LoadBalancer"
    Properties:
      LoadBalancerName: !Sub "${PJPrefix}-${InternetELBName}-elb"
      Scheme: "internet-facing"
      CrossZone: true
      HealthCheck: 
        Target: "TCP:80"
        HealthyThreshold: 2
        UnhealthyThreshold: 2
        Interval: 30
        Timeout: 5
      ConnectionDrainingPolicy:
        Enabled: true
        Timeout: 300
      Listeners: 
        - LoadBalancerPort: 80
          InstancePort: 80
          Protocol: HTTP
      Instances: 
         - !Ref EC2Instance01
         - !Ref EC2Instance02
      SecurityGroups: 
        - !Ref ELBSecurityGroup
      Subnets: 
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a" }
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c" }

# ------------------------------------------------------------#
# Output Parameters
# ------------------------------------------------------------#                
Outputs:
#InternetELB
  ELBDNSName:
    Value: !GetAtt InternetELB.DNSName
    Export:
      Name: !Sub "${PJPrefix}-${InternetELBName}-elb-dnsname"

#EC2Instance01
  EC2Instance01ID:
    Value: !Ref EC2Instance01
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-id"
  EC2Instance01PrivateIp:
    Value: !GetAtt EC2Instance01.PrivateIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-private-ip"
  EC2Instance01EIP:
    Value: !GetAtt EC2Instance01.PublicIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-eip"
  EC2Instance01RoleName:
    Value: !Sub "${PJPrefix}-${EC2InstanceName}-01-role"
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-role-name"

#EC2Instance02
  EC2Instance02ID:
    Value: !Ref EC2Instance02
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-id"
  EC2Instance02PrivateIp:
    Value: !GetAtt EC2Instance02.PrivateIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-private-ip"
  EC2Instance02EIP:
    Value: !GetAtt EC2Instance02.PublicIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-eip"
  EC2Instance02RoleName:
    Value: !Sub "${PJPrefix}-${EC2InstanceName}-02-role"
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-role-name"