Posted at

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


はじめに

本記事では、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"