taisablog

taisa's engineer blog

AWS

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でも確認できるので参考にしてみてください。また気になる点があれば指摘してもらえれば幸いです。

-AWS

執筆者:

関連記事

関連記事はありませんでした