Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

はじめに

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

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

構成図

前提条件

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

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

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

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

構築手順

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

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

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

パラメータ名 用途 備考
スタックの名前 テンプレートから作成するリソース一式の名前 例 prd-stack-vpc-20180801
PJPrefix 構築するプロジェクトの環境を識別するために各コンポーネントの先頭に付与する識別子 例 qiita-prd
InternetALBName TagのNameに設定するALB名 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になれば、ALBとEC2の2台の構築が完了です。

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

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

sudo yum install -y httpd
sudo service httpd start
sudo touch /var/www/html/index.html

8 EC2の管理コンソールのターゲットグループにアタッチされたEC2のステータスがhealthyになっていることを確認します。

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

テンプレート

alb-ec2-02.yml
AWSTemplateFormatVersion: "2010-09-09"
Description:
  ALB and EC2 Instance Create

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

    ParameterLabels:
      InternetALBName:
        default: "InternetALBName"
      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

#InternetALB
  InternetALBName:
    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 ALB
# ------------------------------------------------------------#
  ALBSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-alb-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-alb-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 [ ALBSecurityGroup, 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

# ------------------------------------------------------------#
#  Target Group
# ------------------------------------------------------------#
  TargetGroup: 
    Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
    Properties: 
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" } 
      Name: !Sub "${PJPrefix}-${InternetALBName}-tg"
      Protocol: HTTP
      Port: 80
      HealthCheckProtocol: HTTP
      HealthCheckPath: "/"
      HealthCheckPort: "traffic-port"
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 2
      HealthCheckTimeoutSeconds: 5
      HealthCheckIntervalSeconds: 10
      Matcher: 
        HttpCode: 200
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-${InternetALBName}-tg"
      TargetGroupAttributes: 
        - Key: "deregistration_delay.timeout_seconds"
          Value: 300
        - Key: "stickiness.enabled"
          Value: false
        - Key: "stickiness.type"
          Value: lb_cookie
        - Key: "stickiness.lb_cookie.duration_seconds"
          Value: 86400
      Targets: 
        - Id: !Ref EC2Instance01
        - Id: !Ref EC2Instance02
          Port: 80

# ------------------------------------------------------------#
#  Internet ALB
# ------------------------------------------------------------#
  InternetALB: 
    Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
    Properties: 
      Name: !Sub "${PJPrefix}-${InternetALBName}-alb"
      Tags: 
        - Key: Name
          Value: !Sub "${PJPrefix}-${InternetALBName}-alb"
      Scheme: "internet-facing"
      LoadBalancerAttributes: 
        - Key: "deletion_protection.enabled"
          Value: false
        - Key: "idle_timeout.timeout_seconds"
          Value: 60
      SecurityGroups:
        - !Ref ALBSecurityGroup
      Subnets: 
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a" }
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c" }

  ALBListener: 
    Type: "AWS::ElasticLoadBalancingV2::Listener"
    Properties: 
      DefaultActions: 
        - TargetGroupArn: !Ref TargetGroup
          Type: forward
      LoadBalancerArn: !Ref InternetALB
      Port: 80
      Protocol: HTTP

# ------------------------------------------------------------#
# Output Parameters
# ------------------------------------------------------------#                
Outputs:
#InternetALB
  ALBDNSName:
    Value: !GetAtt InternetALB.DNSName
    Export:
      Name: !Sub "${PJPrefix}-${InternetALBName}-alb-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"
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away