CloudformationでEC2+ネットワーク構築


EC2を使うことはほぼないですが、前回画面ポチポチしながら作成した内容をCloudformationで構築します。(nginxのコンフィグ設定もできますが、そこまではやっていません。)

【15分でできる】EC2(Web+DB)ネットワーク構築 〜画面ポチポチ〜

事前準備

aws-cli で credentials が設定されていて、AWSコンソールにキーペアがあることを前提に進めます。cloudformationのパラメータは次のように設定しています。

 

AWSTemplateFormatVersion: "2010-09-09"
Parameters:
Prefix:
Type: String
Default: example
DBName:
Type: String
Default: example
DBUsername:
Type: String
Default: user
DBPassword:
Type: String
Default: password
KeyPair:
Description: KeyPair Name
Type: AWS::EC2::KeyPair::KeyName
Default: "my-key"

VPCを作成する

IPv4 CIDERブロックを「10.0.0.0/16」にしてVPCを作成し、DNSホスト名を利用するのでVPCのDnsSupportとDnsHostNamesをtrueにします。

Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "10.0.0.0/16"
Tags:
- Key: "Name"
Value: !Sub "${Prefix}-vpc"
EnableDnsSupport: true
EnableDnsHostnames: true

パブリックサブネットを作成する

作成したVPCを選択し、IPv4 CIDERブロックを「10.0.1.0/24」にして作成します。

PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: "10.0.1.0/24"
AvailabilityZone: "ap-northeast-1a"
VpcId: !Ref VPC
Tags:
- Key: "Name"
Value: !Sub "${Prefix}-public-subnet"

プライベートサブネットを作成する

作成したVPCを選択し、IPv4 CIDERブロックを「10.0.2.0/24」にして作成します。

PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: "10.0.2.0/24"
AvailabilityZone: "ap-northeast-1c"
VpcId: !Ref VPC
Tags:
- Key: "Name"
Value: !Sub "${Prefix}-private-subnet"

インターネットゲートウェイを作成する

インターネットゲートウェイを作成し作成したインターネットゲートウェイをVPCにアタッチします。

InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub "${Prefix}-internet-gateway"
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway

VPCにパブリックルートテーブルを作成する

作成したVPCにパブリックルートテーブルを作成し、作成したパブリックルートテーブルにパブリックサブネットを関連付けます。パブリックサブネットに関連づけたルートテーブルのルートを、送信先を「0.0.0.0/0」としターゲットにインターネットゲートウェイを設定します。

PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: "Name"
Value: !Sub "${Prefix}-public-route-table"
PublicRoute:
Type: AWS::EC2::Route
DependsOn: InternetGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet

WebサーバーとしてEC2を作成する

作成したVPCとパブリックサブネットを設定し、ElasticIPを関連付けます。nginx、mysql、golangはAWS::CloudFormation::Initに定義しインストールしサービスを起動するようにしています。ソースもGitHubから取得しています。また、cfn-init でうまくセットアップできていない場合は /var/log/cfn-init.log にログが出てるのでそれを確認すれば原因がわかります。

WebInstance:
Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
config:
packages:
yum:
nginx: []
mysql: []
golang: []
sources:
/opt/example: "https://github.com/taisa831/sandbox-docker-compose-go-mysql/tarball/master"
services:
sysvinit:
nginx:
enabled: "true"
ensureRunning: "true"
files:
- "/etc/nginx/nginx.conf"
sources:
- "/opt/example"
Properties:
ImageId: ami-034dad9bdb65ec34b
InstanceType: t2.micro
KeyName: !Ref KeyPair
SecurityGroupIds:
- !Ref WebSecurityGroup
Tags:
- Key: "Name"
Value: !Sub "${Prefix}-web-server"
BlockDeviceMappings:
- DeviceName: "/dev/xvda"
Ebs:
VolumeType: "gp2"
VolumeSize: 8
SubnetId: !Ref PublicSubnet
UserData:
Fn::Base64: !Sub |
 #!/bin/bash
yum -y update
yum -y install cfn-bootstrap
/opt/aws/bin/cfn-init --stack ${AWS::StackName} --resource WebInstance --region ${AWS::Region}
WebEIP:
Type: AWS::EC2::EIP
Properties:
InstanceId: !Ref WebInstance
Tags:
- Key: Name
Value: !Sub "${Prefix}-web-eip"

セキュリティグループ(ファイアウォール)を設定する

Webサーバのセキュリティグループは80ポートと22ポートを0.0.0.0/0として作成します。DBサーバのセキュリティグループは、プライベートサブネットからしかアクセスしないの「10.0.1.0/24」からのアクセスを22ポートと3306ポートに限定します。

WebSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Prefix}-web-security-group"
GroupDescription: WebSecurityGroup
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: "0.0.0.0/0"
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: "0.0.0.0/0"
Tags:
- Key: "Name"
Value: !Sub "${Prefix}-web-security-group"
VpcId: !Ref VPC
DBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Prefix}-db-security-group"
GroupDescription: DBSecurityGroup
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: "10.0.1.0/24"
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
CidrIp: "10.0.1.0/24"
Tags:
- Key: "Name"
Value: !Sub "${Prefix}-db-security-group"
VpcId: !Ref VPC

DBサーバーとしてEC2を作成する

作成したVPCとプライベートサブネットを設定します。パブリックIPは不要なので設定しません。

mysql-serverはAWS::CloudFormation::Initを使ってインストールしてサービス起動するようにしています。Initで設定した内容を実行するにはUserDatacfn-initコマンドを実行する必要があります。

DBInstance:
Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
configSets:
installAndRunDB:
- config1
- config2
config1:
packages:
yum:
mysql-server: []
services:
sysvinit:
mysqld:
enabled: "true"
ensureRunning: "true"
files:
/tmp/setup.mysql:
content: !Sub |
CREATE DATABASE ${DBName} default character set utf8 collate utf8_general_ci;
GRANT ALL ON ${DBName}.* TO ${DBUserName}@'%' identified by '${DBPassword}';
FLUSH PRIVILEGES;
mode: "000644"
owner: "root"
group: "root"
config2:
commands:
01mysql:
command: "mysql -uroot < /tmp/setup.mysql"
Properties:
ImageId: ami-034dad9bdb65ec34b
InstanceType: t2.micro
KeyName: !Ref KeyPair
SecurityGroupIds:
- !Ref DBSecurityGroup
Tags:
- Key: "Name"
Value: !Sub "${Prefix}-db-server"
BlockDeviceMappings:
- DeviceName: "/dev/xvda"
Ebs:
VolumeType: "gp2"
VolumeSize: 8
SubnetId: !Ref PrivateSubnet
UserData:
Fn::Base64: !Sub |
 #!/bin/bash
yum -y update
yum -y install cfn-bootstrap
/opt/aws/bin/cfn-init --stack ${AWS::StackName} --configsets installAndRunDB --resource DBInstance --region ${AWS::Region}

パブリックゲートウェイにNATゲートウェイを作成する

DBサーバーにMySQLをインストールするために、プライベートルートテーブルを作成しパブリックサブネットのNATゲートウェイを通してインターネットへ接続可能にします。

PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: "Name"
Value: !Sub "${Prefix}-private-route-table"
PrivateRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NAT
SubnetPrivateRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTable
SubnetId: !Ref PrivateSubnet
NAT:
Type: AWS::EC2::NatGateway
Properties:
AllocationId:
Fn::GetAtt:
- EIP
- AllocationId
SubnetId: !Ref PublicSubnet
Tags:
- Key: Name
Value: !Sub "${Prefix}-nat-gateway"
EIP:
DependsOn: InternetGatewayAttachment
Type: AWS::EC2::EIP
Properties:
Domain: vpc

cloudformation適用

aws cloudformation deploy --template /path/to/ec2.yml --stack-name example-stack --profile {profile}

Nginxを設定する

「/etc/nginx/conf.d/nginx.conf」を編集し「root」をコメントアウトしlocationに「proxy_pass http://127.0.0.1:9000;」を追加します。

server {
listen       80 default_server;
listen       [::]:80 default_server;
server_name  localhost;
#root         /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
proxy_pass http://127.0.0.1:9000;
}    server {
# --- 省略 ---

nginxを再起動します。

sudo service nginx restart

アプリケーションをセットアップする

/opt/example/.envを編集しMYSQL_HOSTにDBサーバーのプライベートIPを指定し、go run main.goでアプリケーションを起動します。

MYSQL_HOST={DBサーバープライベートIP}
MYSQL_USER=user
MYSQL_PASSWORD=password
MYSQL_SCHEMA=example
LISTEN_PORT=:9000

動作確認をする

パブリックDNS + /users にアクセスするとJSONが表示されます。

DBレコードを確認する

Webサーバから mysql コマンドで DBサーバのMySQLにログインしてレコードができていることを確認します。

$ mysql -uuser -p -h {DBサーバーIP} example
mysql> select * from users;
+----+-----------+----------+
| id | firstname | lastname |
+----+-----------+----------+
|  1 | John      | Doe      |
+----+-----------+----------+
1 row in set (0.00 sec)

まとめ

細かいコンフィグ設定箇所はコンテナになれば不要なので深堀りせず手動で更新しましたが、これでほぼ画面で操作した内容をcloudformationに置き換えることができました。全てのコードはGitHubでも確認できるので参考にしてみてください。また気になる点があれば指摘してもらえれば幸いです。


関連記事