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:
/var/www/html: "https://github.com/taisa831/sandbox-docker-compose-go-mysql/tree/master"
services:
sysvinit:
nginx:
enabled: "true"
ensureRunning: "true"
files:
- "/etc/nginx/nginx.conf"
sources:
- "/var/www/html"
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
で設定した内容を実行するにはUserData
でcfn-init
コマンドを実行する必要があります。
DBInstance:
Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
config:
packages:
yum:
mysql-server: []
commands:
01mysql:
command: "mysql -uroot < /tmp/setup.mysql"
services:
sysvinit:
mysqld:
enabled: "true"
ensureRunning: "true"
files:
/tmp/setup.mysql:
content: !Sub |
CREATE DATABASE example default character set utf8 collate utf8_general_ci;
GRANT ALL ON example.* TO user@'%' identified by 'password';
FLUSH PRIVILEGES;
mode: "000644"
owner: "root"
group: "root"
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} --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
EIP:
DependsOn: InternetGatewayAttachment
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NAT:
Type: AWS::EC2::NatGateway
Properties:
AllocationId:
Fn::GetAtt:
- EIP
- AllocationId
SubnetId: !Ref PublicSubnet
Tags:
- Key: Name
Value: !Sub "${Prefix}-nat-gateway"
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でも確認できるので参考にしてみてください。また気になる点があれば指摘してもらえれば幸いです。